Ubisam-MergeTool/doc/history/2026-09-16-8차.md
ksj09406 90f662ba9b 8차 B-2: 가져올 것이 없으면 [커밋] 을 끄고 나가는 길을 말한다
이미 들어 있는 커밋을 cherry-pick 하면 [커밋] 이 켜졌고, 누르면 로그에
"[cherry-pick 커밋] 실패: No changes; nothing to commit." 만 남았다. 진행 줄도 그대로였다.

- GitService.HasStagedChanges() 를 두고 적용 로그 · 커밋 앞 막음에 쓴다
- GitWorkspace.PendingHasChanges 를 상태로 들고, 진행 줄이 그 값을 본다
- CanFinishOperation 은 cherry-pick · revert 에서 커밋할 것이 있어야 켜진다
- 병합 커밋은 트리가 같아도 정상이라 걸지 않는다
- 하네스 MergeSaveTest 155건(진행 줄 7건), 18벌 921건 통과

확인: HEAD 커밋을 다시 cherry-pick → [커밋] 꺼짐 · "가져올 변경이 없습니다 - [작업 취소] 로 끝냅니다" ·
[작업 취소] 로 빠져나옴. 충돌 24건짜리 보통 cherry-pick 은 그대로 커밋까지(원래 메시지).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 13:44:07 +09:00

12 KiB

8차 — 고른 파일에 고른 내용을 · 작업마다 화면이 사실대로 (2026-09-16 ~)

지시서: ../작업지시/8차.md. 7차 D-1 에서 새로 걸린 넷과 7차에서 남긴 것 · 웹 문서.

A-1. 고른 충돌 파일 대신 다른 파일이 보이고, 그 화면을 저장하면 고른 파일에 써진다 (2026-09-16, 완료)

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

파일 비교 탭을 한 번도 열지 않은 채 Git 탭 충돌 목록에서 Common/Util/Unit47.cs 를 고르고 [3분할로 열기].

확인 결과
파일 비교 탭 화면 (merge base) Common/Util/Unit15.cs — 고른 파일이 아니라 첫 충돌 파일
[다음 충돌] → Take Theirs → [Save] 눌린다
git M Common/Util/Unit47.cs · UU Common/Util/Unit15.cs
스테이징된 Unit47.cs 내용 123줄 · public sealed class Unit15 — Unit15 의 내용(고른 줄 · 머지 주석까지)이 Unit47 에 써졌다
로그 충돌 해결(직접 편집, UTF-8 (BOM) · CRLF): Common/Util/Unit47.cs
저장 뒤 화면 30초 동안 Unit15 · "남은 충돌 1개" 그대로

7차 D-1 에서 병합 · pull 둘 다 이렇게 났다(그때는 파일 비교 탭을 먼저 열어 두고 네 길을 확인했다).

원인

파일 비교 화면은 탭을 처음 열 때 만들어진다. 그때 두 가지가 겹친다.

  1. 목록 세우기(RebuildListAfterRefreshAsync → LoadCommitAsync)가 첫 파일(Unit15)을 읽는다.
  2. 넘겨받은 요청(DeliverPendingOpenConflict → OpenConflictAsync)이 고른 파일(Unit47)을 고른다.

LoadFileAsync 는 읽기가 끝난 뒤 늦게 끝난 결과를 IsGitMode 로만 버렸다. 읽기 시작한 경로가 지금 고른 파일과 같은지는 보지 않았다 (같은 자리 주석이 "경로는 새 파일인데 내용은 옛 파일" 을 이미 겪었다고 적고 있었다 - 데모 전환에서). 그래서 Unit15 의 내용이 화면을 덮었고, [Save] 는 화면 내용을 지금 고른 파일(Unit47)에 썼다.

고친 것

파일 무엇
Services/MergeSavePlan.cs IsStaleLoad(읽은 경로 · 읽은 커밋, 지금 경로 · 지금 커밋) — 읽는 사이에 파일 · 커밋이 바뀌었으면 버린다. ShownFileMismatch(화면에 실린 파일, 저장할 파일) — 다르거나 실린 것이 없으면 까닭을 돌려준다
ViewModels/HomeViewModel.cs _shownFile — 화면에 실제로 실린 파일. LoadFileAsync 는 행을 만들기 전과 화면에 옮기기 직전 두 곳에서 IsStaleLoad 로 버리고, 실을 때 _shownFile 을 적는다. 버린 뒤 화면이 지금 고른 파일과 다르면 ReloadWhenShownIsStale 로 한 번 더 읽는다(되풀이 방지 플래그). ClearPanes 는 _shownFile 을 지운다
〃 쓰기 세 길(SaveAndCommitAsync · SaveResolvedAsync · DecideDeletionAsync)이 아무것도 쓰기 전에 ShownFileMismatch 로 막고 알린다
하네스 MergeSaveTest 검사 15건 — 같은 파일 · 다른 파일 · 다른 커밋 · 선택 없음 · 경로 구분자 · 대소문자 · 해시 대소문자, 저장 막기(같음 · 다름 · 두 이름이 까닭에 들어감 · 화면이 빔 · 저장할 파일이 빔)

읽기 쪽만 고치고 끝내지 않았다. 되돌릴 수 없는 쓰기 앞에 문을 하나 더 뒀다.

고치는 중에 한 번 더 걸렸다. 버리기만 했더니 [3분할로 열기] 로 처음 열 때 화면이 빈 채로 남았다(단추도 없음). 버린 뒤 다시 읽는 ReloadWhenShownIsStale 을 넣어 고쳤다.

확인한 것 (길마다 저장소를 새로 만들어, 파일 비교 탭을 미리 열지 않고)

길 [3분할로 열기] 뒤 화면 Save 뒤 git 스테이징된 Unit47 안의 class
병합 (merge base) Common/Util/Unit47.cs · 남은 충돌 1개 · REMOTE feature/theirs M Unit47.cs · UU Unit15.cs Unit47
pull 같음 같음 Unit47
rebase 같음 · REMOTE 다시 올리는 내 커밋 1839bd3 같음 Unit47
cherry-pick 같음 · REMOTE 가져오는 커밋 f707175 같음 Unit47

이어서 병합을 사람 순서 그대로 끝까지(파일 비교 탭을 미리 열지 않고) 했다.

확인 결과
파일 단위 Accept Theirs 15개 24 → 9, 1.0초
3분할로 푼 파일 NewPanel · Unit47 · 한글설명 · LineEnding · Unit0(6) · 띠 둘 · View250 · Unit1(6) — 저장할 때마다 다음 파일이 저절로 열림
마지막 Save 뒤 12b0402 · Weld/Unit1.cs · 병합 전 내 쪽 · 충돌 정리 끝 · 병합 커밋을 할 수 있습니다 · 내 쪽 대비 받은 변경 3줄 (7차 A-1 그대로)
병합 커밋 3ba6314 부모 둘 · Merge branch 'feature/theirs' into feature/mine · 워킹트리 Clean
줄끝 · BOM · 인코딩이 첫 부모와 다른 파일 0 / 33 · 충돌 표식 0줄
class 이름이 파일 이름과 다른 Unit 파일 0

하네스 18벌 906건 통과(MergeSaveTest 140). 빌드 경고 0 · 오류 0. 설정 파일은 바이트 그대로 되돌림.

남긴 것

  • 자동 조작 쪽: Save 직후 찍은 화면이 Git 탭이라 파일 비교 탭 제목이 빈 것으로 읽혔다. 화면은 제대로였다(스크린샷).
  • 7차 D-1 때 넣은 우회(파일 비교 탭을 먼저 한 번 열기)는 스위치(D7_NO_PREOPEN)로 껐다 켤 수 있게 두었다. 8차 E-1 은 끈 채로 한다.

B-1. rebase · cherry-pick 에서 마지막 저장 뒤 "남은 차이" 가 뜬다 (2026-09-16, 완료)

고치기 전에 다시 났다 (길마다 저장소를 새로 만들어)

마지막 한 파일(Weld/Unit1.cs)만 남기고 23개를 파일 단위로 풀고, 남은 하나를 네 칸에서 Take Theirs 6번 → [Save].

길 파일 비교 탭 충돌 탭
rebase bbdc66e · Weld/Unit1.cs · 남은 차이 6줄 충돌 정리 끝 - [계속] 으로 rebase 를 이어 갑니다
cherry-pick 30c2133 · Weld/Unit1.cs · 남은 차이 6줄 충돌 정리 끝 - [커밋] 으로 cherry-pick 을 끝냅니다
병합 (회귀) c3691b2 · Weld/Unit1.cs · 병합 전 내 쪽 · 충돌 정리 끝 · 병합 커밋을 할 수 있습니다 · 내 쪽 대비 받은 변경 6줄 같은 말

두 화면이 다른 말을 한다. 방금 받아 놓은 6줄이 "아직 할 일" 로 읽힌다 — 7차 A-1 과 같은 증상인데 병합에서만 고쳐져 있었다.

원인

7차 A-1 에서 만든 MergeSavePlan.ResolvedMergeSummary · MergeBaseHeader 가 병합 전용이었다. 화면도 _workspace.IsMergeInProgress(MERGE_HEAD 기준)로 물었다. rebase 는 MERGE_HEAD 가 없고 cherry-pick 은 CHERRY_PICK_HEAD 라 둘 다 "병합 중이 아님" 이 되어 보통 요약("남은 차이 N줄")으로 떨어졌다. 7차 C-2 에서 만든 작업 상태(GitOperationState · OperationLabels)를 쓰지 않고 있었다.

고친 것

파일 무엇
Services/OperationLabels.cs ResolvedSummary(작업, 남은 충돌, 차이 줄) — 끝말은 진행 줄과 같은 FinishHint(병합 커밋 · [계속] · [커밋])를 쓴다. 차이 줄은 작업마다 무엇인지 적는다(받은 변경 · 올린 변경 · 가져온 변경 · 되돌린 변경). BaseHeader(작업, 짧은해시, 경로) — 병합 "병합 전 내 쪽", rebase "올릴 자리", cherry-pick "가져오기 전 내 쪽", revert "되돌리기 전 내 쪽"
Services/MergeSavePlan.cs 병합 전용이던 ResolvedMergeSummary · MergeBaseHeader 를 걷어냈다(글은 위로 옮김)
ViewModels/HomeViewModel.cs 요약 · BASE 제목이 IsMergeInProgress 대신 IsOperationInProgress · OperationState.Kind 를 본다
하네스 MergeSaveTest 병합 글이 예전과 같은지 6건 + 작업별 요약 · 제목 10건

확인한 것 (같은 순서로 다시)

길 마지막 Save 뒤 파일 비교 탭 충돌 탭
rebase 3e51519 · Weld/Unit1.cs · 올릴 자리 · 충돌 정리 끝 · [계속] 으로 rebase 를 이어 갑니다 · 올릴 자리 대비 올린 변경 6줄 같은 말
cherry-pick 충돌 정리 끝 · [커밋] 으로 cherry-pick 을 끝냅니다 같은 말
병합 (회귀) d091ca2 · Weld/Unit1.cs · 병합 전 내 쪽 · 충돌 정리 끝 · 병합 커밋을 할 수 있습니다 · 내 쪽 대비 받은 변경 6줄 같은 말

cherry-pick 의 BASE 제목에 꼬리가 없는 것은 맞다 — 그때 BASE 는 HEAD 가 아니라 이력에서 고른 가져올 커밋이라 "가져오기 전 내 쪽" 이라고 하면 틀린 말이 된다(제목은 14a9476 · Weld/Unit1.cs, HEAD 는 45bdbf5).

하네스 18벌 914건 통과(MergeSaveTest 148). 빌드 경고 0 · 오류 0. 설정 파일은 바이트 그대로 되돌림.

B-2. 바뀌는 것 없는 cherry-pick 에서 [커밋] 이 켜지고 누르면 실패한다 (2026-09-16, 완료)

고치기 전에 다시 났다 (새로 만든 저장소, 이력에서 HEAD 커밋을 골라 [Cherry-pick])

때 결과
[Cherry-pick] 로그 cherry-pick 적용됨 - 확인한 뒤 충돌 탭의 [커밋] 으로 끝냅니다. · .git 에 CHERRY_PICK_HEAD
충돌 탭 머리 cherry-pick 진행 중 · 진행 줄 충돌 정리 끝 - [커밋] 으로 cherry-pick 을 끝냅니다 · [커밋] 켜짐
[커밋] 을 눌러 봄 로그 [cherry-pick 커밋] 실패: No changes; nothing to commit. · HEAD 그대로 · 알림은 읽히지 않음
그 뒤 진행 줄 · [커밋] 이 그대로라 같은 단추를 또 누르게 된다. [작업 취소] 로는 빠져나온다

원인

  • 끝내기 단추(CanFinishOperation)는 "작업이 진행 중 · 충돌 0" 만 봤다. 커밋할 것이 있는지는 보지 않았다.
  • 진행 줄(OperationLabels.ProgressText)도 충돌이 0 이면 무조건 "끝낼 수 있다" 고 말했다.
  • 시작 로그(BuildPatchResult)는 적용 결과가 Applied 면 늘 "확인한 뒤 [커밋] 으로 끝냅니다" 였다.
  • 실제로 막은 것은 libgit2 였고, 그 말(No changes; nothing to commit.)이 로그에 그대로 나왔다.

고친 것

파일 무엇
Services/GitService.cs HasStagedChanges() — 인덱스가 HEAD 와 다른가. cherry-pick 적용 로그가 이걸 보고 "cherry-pick 했지만 가져올 변경이 없습니다 - [작업 취소] 로 끝냅니다." 로 적는다(revert 는 "되돌릴"). CommitPendingOperation 은 커밋 전에 같은 것을 보고 사람 말로 막는다
Services/IGitService.cs 선언
Services/GitWorkspace.cs PendingHasChanges — 상태를 읽을 때 함께 읽어 둔다. 진행 줄이 이 값을 쓴다
Services/OperationLabels.cs ProgressText(…, hasChanges) — 충돌 0 인데 가져올 것이 없는 cherry-pick · revert 는 "가져올(되돌릴) 변경이 없습니다 - [작업 취소] 로 끝냅니다". 병합 · rebase 는 따지지 않는다(병합 커밋은 트리가 같아도 정상이다)
ViewModels/GitViewModel.cs CanFinishOperation — cherry-pick · revert 는 커밋할 것이 있어야 켜진다
하네스 MergeSaveTest 진행 줄 검사 7건(없음 · revert · 있음 · 충돌 남음 · 병합 · rebase · 기본값이 예전과 같은지)

병합 커밋에는 걸지 않았다. 처음에 CommitMerge 에도 같은 막음을 넣었다가 뺐다 — 병합은 가져올 변경이 없어도(양쪽이 같게 고친 경우 등) 커밋이 정상이다.

확인한 것 (저장소를 새로 만들어)

경우 결과
① HEAD 커밋을 다시 [Cherry-pick] 로그 cherry-pick 했지만 가져올 변경이 없습니다 - [작업 취소] 로 끝냅니다. · 진행 줄 가져올 변경이 없습니다 - [작업 취소] 로 끝냅니다 · [커밋] 꺼짐 · [작업 취소] 켜짐
① [작업 취소] CHERRY_PICK_HEAD 없어짐 · 머리 표시 · 진행 줄 사라짐
② 회귀 - 충돌 24건짜리 보통 cherry-pick 충돌 중 [커밋] 꺼짐 → 다 풀면 진행 줄 충돌 정리 끝 - [커밋] 으로 cherry-pick 을 끝냅니다 · [커밋] 켜짐 → 누르면 cherry-pick 커밋: e35f7b7 상대 쪽 작업(부모 하나 · 원래 메시지)

하네스 18벌 921건 통과(MergeSaveTest 155). 빌드 경고 0 · 오류 0. 설정 파일은 바이트 그대로 되돌림.

정하지 않은 것

  • git 의 --allow-empty 처럼 빈 cherry-pick 을 커밋으로 남기는 길은 지시서대로 두지 않았다. 사용자와 정한 뒤에 넣는다.