Ubisam-MergeTool/doc/history/2026-09-14-7차.md
ksj09406 aebfd6b089 7차 C-2: rebase · cherry-pick 충돌을 작업에 맞게 부르고 MergeHub 만으로 끝까지
화면이 모든 충돌을 "병합" 으로 불렀다 - rebase REMOTE 가 "THEIRS", cherry-pick 에 "병합 진행 중",
다 풀면 cherry-pick 을 끝낼 단추가 없고, 로그에 RebaseContinue() · Commit(), 다른 브랜치 커밋은 고를 수 없었다.
GitOperationState 로 칸 제목(올릴 자리 · 다시 올리는 내 커밋 · 가져오는 커밋) · 머리 글 · 진행 표시 ·
끝내기 단추(병합 커밋 · 계속 · 커밋)를 정한다. cherry-pick · revert 는 원래 메시지 · 작성자로 커밋.
머지 주석 자동 풀기 · 알림도 같은 뒷일. 이력 탭 [모든 브랜치].
rebase · cherry-pick 모두 줄끝 바뀐 파일 0, MergeSaveTest 125건 통과.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 16:03:42 +09:00

29 KiB

7차 — 병합 화면이 사실대로 말하게 · 병합 말고 다른 충돌 길 (2026-09-14 ~)

지시서: ../작업지시/7차.md. 6차 D-1 에서 새로 걸린 세 가지와 6차에서 뺀 pull · rebase · cherry-pick 충돌.

A-1. 마지막 충돌을 저장하면 "남은 차이" 가 뜬다 (2026-09-14, 완료)

고치기 전에 다시 났다

새로 만든 저장소를 git 으로 병합하고 Weld/Unit1.cs 하나만 남긴 채 나머지 25개를 상대 쪽으로 풀었다(준비만 git). MergeHub 파일 비교 탭에서 그 파일의 충돌 6개를 Take Theirs → [Save].

때 칸 제목 요약 충돌 탭
충돌 파일 열림 (merge base) Weld/Unit1.cs · 네 칸 남은 충돌 6개 -
마지막 Save 뒤 (git 충돌 0, 병합 중) 90d2a8c · Weld/Unit1.cs · 두 칸 남은 차이 6줄 충돌 정리 끝 - 병합 커밋을 할 수 있습니다

두 화면이 다른 말을 한다. 파일 비교 탭만 보면 덜 푼 것으로 읽힌다.

(처음 두 번은 재현 준비가 틀렸다 - 충돌 탭에서 22개를 골라 한 번에 Accept 하려 했는데 자동 조작의 목록 찾기가 엉뚱한 목록을 잡아 한 개만 풀렸고, 그다음은 준비 스크립트가 모든 충돌을 풀어 버렸다. 세 번째에 저장소를 새로 만들어 git 으로 준비했다.)

원인

  • [Save] 뒤 ReloadAfterResolveAsync → LoadCommitAsync 가 충돌이 0 이면 보통 화면(HEAD ↔ 워킹트리)으로 연다. 방금 저장한 파일이 목록에 있어 그대로 골라진다.
  • 보통 화면의 요약은 남은 차이 N줄, BASE 제목은 짧은해시 · 경로 뿐이라 병합 중이라는 사정도, 그 차이가 방금 받은 변경이라는 것도 말하지 않았다.

고친 것

파일 무엇
Services/MergeSavePlan.cs ResolvedMergeSummary(병합 중, 남은 충돌, 차이 줄) — 병합 중 · 충돌 0 이면 "충돌 정리 끝 · 병합 커밋을 할 수 있습니다", 차이가 있으면 "· 내 쪽 대비 받은 변경 N줄". 아니면 null. MergeBaseHeader — 짧은해시 · 경로 · 병합 전 내 쪽
ViewModels/HomeViewModel.cs UpdateSummaries: Git 모드 두 칸에서 위 요약을 쓴다. LoadFileAsync: 병합 중이고 BASE 가 HEAD 면 제목에 "병합 전 내 쪽"
하네스 MergeSaveTest 요약 · 제목 검사 7건(병합 아님 · 충돌 남음 → 보통 요약, 차이 0 · 6 · 음수, "남은" 이 안 들어감)

화면 틀(두 칸)과 파일 목록은 바꾸지 않았다. 병합 커밋 뒤에는 병합 중이 아니므로 지금처럼 보통 화면이다(6차 B-8).

확인한 것 (같은 준비로 다시)

때 칸 제목 요약 · 상태바
충돌 파일 열림 (merge base) Weld/Unit1.cs 남은 충돌 6개
마지막 Save 뒤 (git 충돌 0) 9ab2256 · Weld/Unit1.cs · 병합 전 내 쪽 충돌 정리 끝 · 병합 커밋을 할 수 있습니다 · 내 쪽 대비 받은 변경 6줄
충돌 탭 - 충돌 정리 끝 - 병합 커밋을 할 수 있습니다 (말이 같다)
[병합 커밋] 뒤 (cea61d2, 부모 둘) cea61d2 · Weld/Unit1.cs 차이 없음 · 병합 진행 중 표시 없음

MergeSaveTest 70건 통과. 빌드 경고 0 · 오류 0. 설정 파일은 바이트 그대로 되돌림.

A-2. 3분할에서 줄을 골라도 로그는 "3-Way 안 씀" 이라고 한다 (2026-09-14, 완료)

고치기 전에 다시 났다

새로 만든 저장소를 git 으로 병합(충돌 26)하고 MergeHub 로 열었다. 파일 비교 탭이 첫 충돌 파일 Common/Util/Unit15.cs 를 네 칸으로 연다.

확인 결과
칸 네 칸 · REMOTE 제목 REMOTE · feature/theirs
머리의 3-Way 콤보 (3-Way 안 씀) — 네 칸이 떠 있는데 콤보는 안 쓴다고 말한다
[다음 충돌] → Take Theirs 로그 10행 (3-Way 안 씀) 반영
Undo → Take Mine 로그 없음 — 무엇을 골랐는지 남지 않는다

원인

  • ApplyRemote 가 로그에 3-Way 콤보 값(RemoteBranch)을 썼다. 충돌 파일은 콤보를 안 건드리고 네 칸을 띄우므로(6차 A-3) 그 값이 (3-Way 안 씀) 이다.
  • TakeLocalLine 은 줄이 이미 내 것과 같으면 표시만 남기고 로그 없이 돌아갔다. 충돌에서 Take Mine 은 대개 이 경우다.
  • 콤보는 병합 충돌에서 가리킬 것이 없는데도 그대로 보였다.

고친 것

파일 무엇
Services/MergeNames.cs SideLabel · TakeTheirsLog · TakeMineLog. 병합의 두 쪽이면 Theirs(이름) · Mine(이름), 이미 내 것이면 "으로 정함". 콤보로 고른 브랜치 3-Way 는 지금 문구 그대로("7행 develop 반영", "5행 내 원본으로 되돌림")
ViewModels/HomeViewModel.cs 충돌 파일을 열 때 상대 이름(GetMergeTheirsName)을, git mergetool 세 벌은 파일 이름을 들고 있는다. Take Theirs · Take Mine 로그가 위 함수를 쓴다. Take Mine 이 내용을 안 바꿔도 병합의 두 쪽이면 로그를 남긴다. IsConflictThreeWay · ShowBranchCombo · ConflictSideText
Views/HomeView.xaml 충돌 네 칸이면 3-Way 콤보를 감추고 그 자리에 "병합 충돌 · REMOTE = 상대 이름"
하네스 MergeSaveTest 로그 검사 12건(충돌 Theirs · 삭제 · 이름 모름, 브랜치 3-Way 그대로, Mine 정함 · 되돌림 · 삭제, mergetool 파일 이름, 공백 이름)

확인한 것 (같은 저장소)

확인 결과
충돌 네 칸 머리 병합 충돌 · REMOTE = feature/theirs, 3-Way 콤보 안 보임. 화면 어디에도 "3-Way 안 씀" 없음
Take Theirs 로그 10행 Theirs(feature/theirs) 반영
Undo → Take Mine 로그 10행 Mine(feature/mine) 으로 정함
한쪽이 지운 충돌(Recipe/ToBeDeleted.cs, 두 칸) 콤보가 다시 보임 — 네 칸이 아니므로 지금 그대로
git 충돌 26 그대로(저장 안 함)

MergeSaveTest 82건 통과. 빌드 경고 0 · 오류 0. 설정 파일은 바이트 그대로 되돌림. 콤보로 브랜치를 고르는 3-Way 는 병합이 아닐 때만 쓰는 길이라 화면으로는 따로 안 봤다 — 문구가 그대로인 것은 하네스로 확인했다.

B-1. 병합 커밋 뒤에도 .git/MERGEHUB_CONFLICTS 가 남는다 (2026-09-14, 완료)

고치기 전에 다시 났다 (새로 만든 저장소, MergeHub 로 병합)

때 MERGE_HEAD MERGEHUB_CONFLICTS
병합 전 없음 없음
MergeHub [Merge] 뒤 있음 있음
[작업 취소] 뒤 없음 남음
다시 병합 → 터미널에서 풀고 커밋 → 앱 [새로고침] 없음 남음
(6차 D-1) MergeHub [병합 커밋] 뒤 없음 남음

원인

GitService.RememberMergeConflicts 가 병합 때 적기만 하고, 병합 커밋 · 작업 취소 · 상태 다시 읽기 어디에도 치우는 코드가 없었다. 첫 줄의 MERGE_HEAD 와 대조하므로(GetMergeConflictTotal) 다음 병합의 수가 틀리지는 않았다 - 남의 .git 에 우리 파일이 남는 것만 문제였다.

고친 것

파일 무엇
Services/MergeConflictRecord.cs (새 파일) IsStale(MERGE_HEAD, 첫 줄) — 병합 중이 아니거나, 빈 파일이거나, 첫 줄이 지금 MERGE_HEAD 와 다르면 쓸모없음
Services/GitService.cs ForgetStaleMergeConflicts() — 쓸모없으면 지운다. 잠김 등으로 못 지워도 다른 일을 막지 않는다. CommitMerge 뒤 · AbortOperation(작업 취소) 뒤에 부른다
Services/IGitService.cs 선언
Services/GitWorkspace.cs ReadState 처음에 부른다 — 새로고침 · 저장소 열기 · 모든 Git 작업 뒤. 터미널에서 끝낸 병합도 여기서 치운다
하네스 MergeSaveTest IsStale 검사 6건(병합 아님 · 빈 MERGE_HEAD · 같은 병합은 둠 · 다른 병합 · 대소문자와 공백 · 빈 파일)

확인한 것 (세 경우 모두 저장소를 새로 만들어)

경우 병합 중 끝난 뒤
① [Merge] → [작업 취소] 파일 있음 · 진행 줄 "남은 충돌 24개 · 푼 것 2개 (2/26)" MERGE_HEAD 없음 · 파일 없음
② [Merge] → git 으로 풀기만 → [새로고침] → [병합 커밋] 다 풀고 새로고침해도 파일 그대로 · "충돌 정리 끝 · 푼 것 26개" (병합 중에는 안 지운다) 커밋 5f9d5d9(부모 둘) · 파일 없음
③ [Merge] → 터미널에서 풀고 커밋 → [새로고침] 새로고침 전에는 파일 남음 새로고침 뒤 파일 없음

MergeSaveTest 88건 통과. 빌드 경고 0 · 오류 0. 설정 파일은 바이트 그대로 되돌림.

C-1. pull 이 낸 충돌에는 머지 주석 자동 풀기가 안 걸린다 (2026-09-14, 완료)

준비 — 원격을 흉내 낸 저장소

만든 저장소를 git clone --bare 로 떠서 origin 으로 달고, 그 bare 저장소의 feature/mine 을 feature/theirs 커밋으로 옮겼다. 내 feature/mine 이 origin/feature/mine 을 추적하게 하면 pull 이 [Merge] 와 같은 병합 기준 · 같은 26개 충돌을 낸다. (bare 폴더는 지우지 않고 시각을 붙인 새 이름으로 만들었다.)

고치기 전에 다시 났다

확인 [Merge] (6차 D-1) Pull
알림 충돌 24건 (머지 주석만 다른 2건은 자동으로 풀림) … 없음
로그 충돌 26건 · 자동 해결 두 줄 · "자동으로 풀었습니다. 남은 충돌 24건." "충돌 26건 - 해결이 필요합니다." 뿐
git 충돌 24 26 (주석만 다른 2개도 남음)
진행 줄 남은 충돌 24개 · 푼 것 2개 (2/26) 남은 충돌 26개 · 푼 것 0개 (0/26)
Git 탭 커밋 메시지 칸 Merge branch 'feature/theirs' into feature/mine 비어 있음
병합 커밋 제목 - Merge branch 'feature/theirs' into feature/mine — 받아 온 origin/feature/mine 이 아니라 같은 커밋을 가리키는 로컬 브랜치 이름

원인

  • GitViewModel.PullAsync 는 Git.Pull 만 불렀다. 머지 주석 자동 풀기(ResolveNoteOnlyConflicts) · 알림 · 커밋 메시지 기본값은 모두 MergeAsync 안에만 있었다.
  • 메시지 칸이 비면 병합 커밋이 CleanMergeMessage 로 상대 이름을 찾는데, 들어오는 커밋을 가리키는 로컬 브랜치를 먼저 본다. 이번 준비처럼 같은 커밋을 가리키는 로컬 브랜치가 있으면 그 이름이 들어간다.

고친 것

파일 무엇
ViewModels/GitViewModel.cs MergeAsync 의 뒷일을 AfterMerge(결과, 기본 메시지) 로 빼고 PullAsync 도 부른다. 기본 메시지는 병합 커밋이 남을 때(충돌 · 병합됨)만 채운다 — 이미 최신 · 앞으로 감기면 채우지 않는다
Services/MergeNames.cs PullMergeMessage(추적 브랜치, 내 브랜치) — git 과 같은 Merge remote-tracking branch 'origin/x' into y. 추적 브랜치를 모르면 Merge into y
하네스 MergeSaveTest pull 메시지 검사 5건(모양 · 공백 · 추적 모름 · 내 브랜치 모름 · 다시 읽으면 원격 브랜치 이름)

추적 브랜치 이름은 작업에 들어가기 전에 화면 스레드에서 읽는다(브랜치 목록이 화면의 것이다).

확인한 것 (저장소 · bare 원격 새로 만들어)

확인 ① Pull ② [Merge] (회귀)
알림 충돌 24건 (머지 주석만 다른 2건은 자동으로 풀림) - 충돌 탭에서 … 같음
로그 fetch → 병합 시작 origin/feature/mine -> feature/mine → 충돌 26건 → 자동 해결 Unit46.cs · Unit45.cs → 남은 충돌 24건 병합 시작 feature/theirs -> feature/mine 부터 같음
git 충돌 24 24
진행 줄 남은 충돌 24개 · 푼 것 2개 (2/26) 같음
커밋 메시지 칸 Merge remote-tracking branch 'origin/feature/mine' into feature/mine Merge branch 'feature/theirs' into feature/mine
병합 커밋 (git 으로 풀고 새로고침 → [병합 커밋]) 7280418 부모 둘 · Merge remote-tracking branch 'origin/feature/mine' into feature/mine -

MergeSaveTest 93건 통과. 빌드 경고 0 · 오류 0. 설정 파일은 바이트 그대로 되돌림.

C-2. rebase · cherry-pick 충돌을 네 칸으로 끝까지 (2026-09-15, 완료)

준비

시험 저장소(C1Repo)를 그대로 썼다. feature/mine(내 쪽 작업) · feature/theirs(상대 쪽 작업)가 같은 바탕에서 커밋 하나씩이라 rebase(feature/mine 을 feature/theirs 위로)도 cherry-pick(상대 쪽 작업을 feature/mine 에)도 병합과 같은 26개 충돌을 낸다. 지시서는 생성기에 갈래를 더하라고 했지만 필요 없어서 바꾸지 않았다.

고치기 전에 다시 났다

rebase (Git 탭 [선택 브랜치 위로 rebase])

# 무엇 근거
R1 네 칸 제목이 두 쪽을 말하지 않는다. rebase 에서는 LOCAL 이 올릴 자리(feature/theirs), REMOTE 가 다시 올리는 내 커밋인데 LOCAL feature/theirs · REMOTE REMOTE · THEIRS · 머리 글 병합 충돌 · REMOTE = THEIRS
R2 Take Theirs 가 내 커밋 줄을 가져오는데 화면은 그 사정을 모른다 Common/Util/Unit15.cs Take Theirs → feature/mine 에만 있는 줄 1/1 들어감
R3 로그가 코드 이름을 적는다 rebase 1/1 단계에서 충돌 26건 - 해결 후 RebaseContinue() 를 호출하세요.
R4 머지 주석 자동 풀기가 안 걸린다 충돌 26(병합은 24)
- 끝내는 길은 됐다 다 풀자 [계속] 켜짐 · [병합 커밋] 꺼짐, [계속] → rebase 완료 (1/1 단계), 이력 내 쪽 작업 이 상대 쪽 작업 위에

cherry-pick

# 무엇 근거
C1 다른 브랜치의 커밋을 고를 수 없다. Git 탭 이력은 HEAD 에서 닿는 커밋만 보인다 이력 목록에 상대 쪽 작업 없음. GetCommits 가 시작점 없이 CommitFilter 를 써서 libgit2 기본(HEAD)만
C2 이미 있는 커밋(HEAD)을 cherry-pick 하면 CHERRY_PICK_HEAD 가 남는데 진행 표시도 [작업 취소] 도 없다 — 빠져나올 길이 없다 로그 cherry-pick 적용됨 - 확인 후 Commit() 을 호출하세요. · 머리 표시 없음 · [작업 취소] 꺼짐
C3 충돌 중 머리가 "병합 진행 중", REMOTE REMOTE · THEIRS, 머리 글 병합 충돌 · REMOTE = THEIRS git 으로 cherry-pick 을 시작해 앱으로 연 화면
C4 충돌을 다 풀면 진행 표시 · [작업 취소] · [병합 커밋] 이 모두 사라진다. 끝내려면 변경 · 커밋 탭에서 메시지를 직접 쳐야 한다(원래 메시지 안 채워짐) 다 푼 뒤 머리 표시 없음 · 작업 취소 꺼짐 · 병합 커밋 꺼짐. 메시지를 쳐서 [커밋] 하면 CHERRY_PICK_HEAD 는 치워짐

원인

  • 화면이 충돌을 모두 "병합" 으로만 다뤘다. 진행 중인지는 IsMergeInProgress(병합 표시 또는 충돌 있음)로만 봐서, cherry-pick 은 충돌이 0 이 되면 진행 중이 아니게 됐다.
  • 상대 이름은 MERGE_HEAD 로만 찾았다(GetMergeTheirsName) — rebase · cherry-pick 에는 MERGE_HEAD 가 없어 "THEIRS".
  • 머지 주석 자동 풀기 · 알림은 [Merge] · pull 에만 걸려 있었다(7차 C-1).

고친 것

파일 무엇
Models/GitModels.cs GitOperationKind(None · Merge · Rebase · CherryPick · Revert) · GitOperationState(두 쪽 이름 · 들어오는 커밋 · 단계)
Services/OperationLabels.cs (새 파일) 작업별 글 — 진행 표시(rebase 1/1 · cherry-pick 진행 중), LOCAL 제목(rebase 는 올릴 자리 feature/theirs), REMOTE 제목(다시 올리는 내 커밋 … · 가져오는 커밋 … · 되돌리는 커밋 …), 네 칸 머리 글, 로그 이름, 끝내기 단추(병합 커밋 · 계속 · 커밋), 진행 줄(병합 글은 예전 그대로)
Services/GitService.cs GetOperationState — git 작업 표시로 판단: rebase 는 rebase-merge/onto · head-name · 지금 단계 커밋, cherry-pick · revert 는 CHERRY_PICK_HEAD · REVERT_HEAD. GetPendingCommitMessage(원래 메시지, # 줄 뺌). CommitPendingOperation — cherry-pick 은 git 처럼 원래 작성자, 커밋한 사람은 지금 사용자. GetCommits(allBranches). 로그의 Commit() · RebaseContinue() 문구를 [커밋] · [계속] 으로
Services/GitWorkspace.cs OperationState · IsOperationInProgress(충돌 수와 따로) · ShowAllBranchesInHistory. 진행 표시 · 진행 줄이 작업을 따른다
ViewModels/GitViewModel.cs 뒷일(AfterConflicts)을 병합 · pull · rebase 시작 · rebase 계속 · cherry-pick · revert 가 함께 쓴다 — 머지 주석 자동 풀기 · 알림("다 풀면 [계속] 으로 …") · cherry-pick · revert 메시지 채우기. FinishOperationCommand — 병합 커밋 / 계속 / 커밋. [작업 취소] 가 진행 중인 작업이면 켜짐
Views/GitView.xaml 충돌 탭 끝내기 단추 글자 · 명령이 작업을 따른다. 진행 줄은 IsOperationInProgress 로 보인다. 이력 탭에 [모든 브랜치] 체크(사용자 결정, 기본 꺼짐)
ViewModels/HomeViewModel.cs 충돌 파일을 열 때 작업 상태로 LOCAL · REMOTE 제목 · 머리 글 · Take 로그 이름을 정한다. 병합 커밋 메시지 자동 채우기는 병합일 때만
하네스 MergeSaveTest 작업별 글 검사 32건(진행 표시 · 칸 제목 · 머리 글 · 로그 이름 · 끝내기 단추 · 진행 줄, 병합 글이 예전과 같은지)

확인한 것 (모두 저장소를 새로 만들어 MergeHub 만으로)

① rebase

때 결과
[선택 브랜치 위로 rebase] 알림 충돌 24건 (머지 주석만 다른 2건은 자동으로 풀림) - … 다 풀면 [계속] 으로 rebase 를 이어 갑니다. · 로그 … 충돌 26건 - 풀고 나서 [계속] 을 누르세요. + 자동 해결 두 줄
충돌 중 머리 rebase 1/1 · [작업 취소] 켜짐 · 진행 줄 남은 충돌 24개 · 끝내기 단추 [계속](꺼짐)
네 칸 (Unit15.cs) LOCAL 올릴 자리 feature/theirs · REMOTE REMOTE · 다시 올리는 내 커밋 a0fead2 내 쪽 작업 · 머리 글 rebase 충돌 · LOCAL = 올릴 자리 feature/theirs · REMOTE = 다시 올리는 내 커밋 a0fead2 · "3-Way 안 씀" 없음
Take Theirs → Save 로그 10행 Theirs(커밋 a0fead2) 반영
23개 파일 단위 Accept Theirs · Io/LineEnding.cs 는 네 칸에서 Take Theirs → Save 진행 줄 충돌 정리 끝 - [계속] 으로 rebase 를 이어 갑니다 · [계속] 켜짐
[계속] rebase 완료 (1/1 단계) · 진행 표시 · [작업 취소] 사라짐. 이력 c1296a7 내 쪽 작업 ← 1bdc05b 상대 쪽 작업 ← 바탕
결과 커밋 (부모와 바이트로) 바뀐 파일 32 · 줄끝 · BOM · 인코딩이 부모와 다른 파일 0 · 충돌 표식 0

② cherry-pick

때 결과
이력 [모든 브랜치] 끄면 상대 쪽 작업 없음 → 켜면 보임, 골라 [Cherry-pick]
시작 알림 충돌 24건 (…) - … 다 풀면 [커밋] 으로 cherry-pick 을 끝냅니다. · 로그 cherry-pick 충돌 26건 - 풀고 나서 충돌 탭의 [커밋] 으로 끝냅니다. + 자동 해결
충돌 중 머리 cherry-pick 진행 중 · [작업 취소] 켜짐 · 끝내기 단추 [커밋](꺼짐)
네 칸 LOCAL feature/mine · REMOTE REMOTE · 가져오는 커밋 0862180 상대 쪽 작업 · 머리 글 cherry-pick 충돌 · REMOTE = 가져오는 커밋 … · 로그 10행 Theirs(커밋 …) 반영
23개 Accept Theirs(24 → 1) · LineEnding.cs 네 칸 → Save 진행 줄 충돌 정리 끝 - [커밋] 으로 cherry-pick 을 끝냅니다 · [커밋] 켜짐 · 진행 표시 그대로
[커밋] (메시지 칸 비움) cherry-pick 커밋: ebe2a64 상대 쪽 작업 — 원래 메시지, 부모 하나, CHERRY_PICK_HEAD 없어짐, 진행 표시 · [작업 취소] 사라짐
결과 커밋 바뀐 파일 32 · 줄끝 · BOM · 인코딩이 부모와 다른 파일 0 · 충돌 표식 0 · LineEnding.cs BOM · CRLF 그대로

③ 바뀌는 것 없는 cherry-pick (HEAD 커밋을 다시)

때 결과
[Cherry-pick] CHERRY_PICK_HEAD 생김 · 머리 cherry-pick 진행 중 · [작업 취소] 켜짐 (예전에는 둘 다 없었다)
[작업 취소] CHERRY_PICK_HEAD 없어짐 · 표시 사라짐

MergeSaveTest 125건 · EncTest 48 · FolderTest 62 · ReportTest 31 · SearchTest 63 통과(GitModels.cs 를 함께 컴파일). 빌드 경고 0 · 오류 0. 설정 파일은 바이트 그대로 되돌림.

확인하며 알게 된 것 · 남긴 것

  • 파일 단위 Accept 는 고른 쪽 파일을 바이트째 쓴다. 상대가 줄끝을 CRLF → LF 로 바꾼 Io/LineEnding.cs 를 파일 단위로 Accept Theirs 하면 그 줄끝이 그대로 들어온다(처음 확인에서 1개로 잡힘). 결함이 아니라 고른 대로다 — 6차 D-1 처럼 이런 파일은 네 칸에서 풀면 내 쪽(rebase 는 올릴 자리) 줄끝을 따른다.
  • 원래 작성자 남기기는 이 저장소로 구별되지 않았다. 시험 저장소의 모든 커밋 작성자가 같다(시험). 코드로만 확인했다.
  • ③ 에서 [커밋] 도 켜진다. 바뀐 것이 없어 누르면 빈 커밋 오류가 날 것으로 보인다. 로그 적용됨 - 확인한 뒤 [커밋] 으로 끝냅니다 도 이 경우엔 맞지 않다. 고치지 않고 적어 둔다(D-1 에서 다시 본다).
  • revert 충돌은 같은 길(AfterConflicts · CommitPendingOperation)을 쓰지만 화면으로 끝까지는 안 봤다.
  • 자동 조작 쪽: cherry-pick 첫 확인에서 [모든 브랜치] 를 켠 직후 목록 선택이 앱에 안 닿아 Accept 가 한 개만 풀렸다. 기다림을 넣고 다시 해 24 → 1 로 확인했다.

E-2. 폴더 비교 탭에 폴더를 하나씩 끌어다 놓으면 첫 칸 → 둘째 칸 차례로 (2026-09-14, 추가 요청, 완료)

사용자: "폴더비교 탭 첫번째 드래그 첫번째 폴더 경로에 들어가고 두번째는 드래그앤드롭은 두번째로 들어가게해줘 경로텍스트 박스에 안넣어도 그냥 화면에다 넣어둬 기능 동작하도록".

원인 (코드로 확인)

  • 화면 아무 데나 놓으면 FolderView.View_Drop → GitWorkspace.RouteDroppedPaths → RequestFolderCompare(왼쪽, null). 폴더 하나는 늘 첫 칸으로 갔다. 두 번째 폴더를 놓아도 첫 칸을 덮어써서, 경로 칸 위에 정확히 놓지 않으면 두 폴더를 견줄 수 없었다.
  • 경로 칸 위에 놓는 길(PreviewDrop → DropIntoSlot)은 그 칸에 제대로 들어갔다.
  • 고치기 전 모습은 화면으로 따로 찍지 않았다 — 위 경로를 코드로 따라가 확인했다.

고친 것

파일 무엇
Services/DropSlotOrder.cs (새 파일) 놓는 차례. Take() 는 첫 칸 → 둘째 칸 → 첫 칸 … 을 주고, Placed(칸) 은 칸에 바로 놓은 것도 차례에 쳐서 다음을 반대 칸으로
Views/FolderView.xaml.cs 화면에 폴더 하나를 놓으면 DropFolder(폴더, 차례 칸) — 양쪽이 차면 바로 견준다(원래 있던 동작). 경로 칸 위에 놓으면 그 칸에 넣고 차례를 넘김(두 개를 한꺼번에 놓아 양쪽을 채웠으면 다음은 첫 칸). 두 개 · 파일을 놓는 길은 그대로(명령줄과 같은 판단)
하네스 PathTest 차례 검사 8건

확인한 것 (실제 탐색기 창에서 마우스로 끌어다 놓기)

경로 칸을 비우고, 탐색기에서 폴더를 끌어 폴더 비교 화면의 빈 곳(경로 칸이 아닌 곳)에 놓았다.

# 놓은 것 · 자리 첫 칸 둘째 칸
1 왼쪽폴더 · 화면 왼쪽폴더 (빔)
2 오른쪽폴더 · 화면 왼쪽폴더 오른쪽폴더 — 바로 비교, 결과에 a.txt
3 세번째폴더 · 화면 세번째폴더 오른쪽폴더
4 왼쪽폴더 · 둘째 경로 칸 위 세번째폴더 왼쪽폴더
5 오른쪽폴더 · 화면 (차례: 첫 칸) 오른쪽폴더 왼쪽폴더

PathTest 62건 통과. 빌드 경고 0 · 오류 0.

E-3. 사용자 이름을 저장하기 전에는 화면을 쓸 수 없게 (2026-09-14, 추가 요청, 완료)

사용자: "사용자 이름을 저장하고 화면이 움직이거나 바뀌도록 잠궈야해". 저장해야만 화면을 움직이거나 바꿀 수 있게 잠근다고 읽었다. 켤 때 뜨는 이름 창은 플랫폼(UbisamBase.Core/Settings/UserNameDialog)의 것이다.

고치기 전에 쟀다 (켤 때 이름 창)

확인 결과
이름 창 주인 · 본 창 주인이 본 창, 본 창 입력 막힘 — 여기까지는 됐다
제목줄을 끌기 이름 창이 움직였다 (663,377 → 863,477)
저장 없이 닫기(WM_CLOSE, Alt+F4 와 같은 길) 이름 창이 닫히고 본 창을 그대로 쓸 수 있었다 — 설정 탭으로 옮겨짐. 이름은 안 받음
설정 폴더 바뀐 것 없음

고친 것 (플랫폼)

파일 무엇
UbisamBase.Core/Settings/UserNameDialog.xaml.cs OnClosing 에서 저장 전이면 닫기를 막고 "이름을 저장해야 시작합니다." 를 보인 뒤 이름 칸으로 포커스. 저장하면 닫힌다. 제목줄 끌어 옮기기(DragMove) 뺌
UbisamBase.Core/Settings/UserNameDialog.xaml 제목줄의 끌기 처리기 뺌

플랫폼 규칙대로 사용자 확인을 받고 scripts\publish-platform.ps1 로 D:\UbisamPlatform\bin 을 갱신했다(Core Release 경고 0 · 오류 0, 파일 48 · pdb 0). MergeHub 는 빌드 때 그 폴더를 통째로 복사하므로 다시 빌드해 앱 폴더의 Core 에 새 문구가 들어간 것을 확인했다. 그 폴더를 쓰는 다른 소비 프로그램(UbisamBase.App · TestApp 등)도 다시 빌드하면 같은 잠금을 받는다 — 플랫폼 changelog 에 적었다.

확인한 것 (새 Core 로 앱을 켜서)

확인 결과
제목줄 끌기 움직이지 않음
저장 없이 닫기(WM_CLOSE) 이름 창 그대로 · 본 창 입력 막힘 그대로, 창에 "이름을 저장해야 시작합니다."
[저장] 이름 창 닫힘 · 본 창 입력 가능 · 탭 옮기기 됨
설정 폴더 그날 로그 파일 말고 바뀐 것 없음(같은 이름으로 저장)

MergeHub 코드는 바꾸지 않았다. 빌드 경고 0 · 오류 0.

E-1. [해결로 저장] 이름을 [Save] 로 (2026-09-14, 추가 요청, 완료)

사용자: "해결로 저장은 LOCAL SAVE 버튼이잖아? 그냥 Save 로 이름 바꿔주고". 병합 중에만 보이는 그 단추는 LOCAL 칸 내용을 이 파일에만 쓰고 스테이징한다. 바로 아래 Save & Commit 과 짝이 되게 이름만 바꿨다. 하는 일은 그대로다.

바꾼 것

파일 무엇
Views/HomeView.xaml 단추 글자 해결로 저장 → Save (툴팁 "이 파일만 풀어서 저장하고 스테이징합니다. 커밋은 하지 않습니다 (Ctrl+S)" 그대로)
ViewModels/HomeViewModel.cs 작업 이름(RunAsync) · 알림 Save · 파일 · 남은 충돌 N건 / … 남은 충돌 없음 - 이제 커밋할 수 있습니다
Services/MergeSavePlan.cs Save & Commit 이 막힐 때 안내 "[Save] 로 이 파일만 저장해 두고 …"
doc/web/index.html 매뉴얼 · 단축키 · 한계 · 시연 대본 · 비교표의 이름

지난 기록(history · 6차 지시서)은 그때 이름 그대로 두었다. 코드 주석의 옛 이름도 그 시점 설명이라 두었다.

확인한 것 (새로 만든 저장소, MergeHub 로 병합한 뒤)

확인 결과
병합 중 네 칸의 단추 Save · 옆에 Save & Commit. "해결로 저장" 단추 없음 · 화면 전체 글자에도 없음
Io/Unit12.cs 충돌 1개 Take Theirs → [Save] 알림 "Save · Unit12.cs · 남은 충돌 23건". git 충돌 24 → 23, 파일 M 스테이징, HEAD 그대로(커밋 없음)
저장 뒤 남은 충돌 첫 파일(Common/Util/Unit15.cs)이 네 칸으로 열림

빌드 경고 0 · 오류 0.

바로잡음 (A-1 때 발견) — 처음에 "하네스는 이 파일들을 컴파일하지 않아 그대로" 라고 적었는데 틀렸다. MergeSaveTest 가 MergeSavePlan.cs 를 컴파일하고, 막힘 안내에 "해결로 저장" 이 들어 있는지 보고 있었다. 찾을 때 하네스 폴더를 빠뜨렸고 하네스를 돌리지 않았다. A-1 에서 기대값을 [Save] 로 고치고 "옛 이름이 없다" 검사를 더했다. 설정 파일은 백업 → CSharpBraceStyle 끔 → 시험 → 바이트 그대로 되돌림(해시 같음).

(자동 조작 쪽: 앱을 새로 켠 뒤에는 충돌 목록에 고른 것이 없어 [3분할로 열기] 가 아무것도 안 열었다. 목록에서 파일을 고르고 다시 했다.)