ksj09406 d25f374a0e 8차 F-1: 병합 커밋 본문에 충돌 해결 내역을 자동으로 적기
충돌 탭 파일 단위 Accept 는 소스에 주석이 안 남고, 병합 커밋 본문은 비어 있었고,
Git 탭 로그는 끄면 사라져 누가 어느 파일을 어떻게 풀었는지 남는 곳이 없었다(사용자 요청).

- MergeResolutionLog: 기록 파일(첫 줄 MERGE_HEAD) 읽기 · 쓰기, 본문 만들기, 메시지에 붙이기
- GitService: 병합 중일 때만 .git/MERGEHUB_RESOLUTIONS 에 적고, CommitMerge 가 본문을 붙인다.
  병합 커밋 · 작업 취소 · 새로고침 때 쓸모없는 기록을 치운다
- 충돌 탭 Accept Mine/Theirs/Both · [Save] · Save & Commit · 파일 남기기/지우기 · 머지 주석 자동이 적는다
- Git 탭 메시지 칸에 본문을 미리 채우고, 풀 때마다 새로 채운다(사람이 고치면 두고 커밋 때 붙임)
- 하네스 MergeSaveTest 183건(21건 추가), 22벌 1,094건 통과

확인: 병합 "충돌 해결 26개 · 김승준"(줄 단위 7 · Theirs 15 · 남기기 1 · 지우기 1 · 주석 자동 2),
pull 26개, Accept Mine 기록 뒤 [작업 취소] 로 기록 파일 없음. 줄끝 · BOM 0/33, 회귀 없음.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 09:33:32 +09:00

17 KiB
Raw Permalink Blame History

작업 지시 — MergeHub 8차 (고른 파일에 고른 내용을 · 작업마다 화면이 사실대로)

대상: D:\Sorce\MergeTool\MergeHub (플러그인) · D:\Sorce\MergeTool\UbisamPlatform (셸) 전제: 1~7차 지시서는 모두 끝났다(../진행상황.md).

이번 지시서가 나온 곳 — 7차 D-1 에서 시험 저장소를 길마다 새로 만들어 병합 · pull · rebase · cherry-pick 을 MergeHub 만으로 끝까지 했다. 네 길 모두 커밋까지 갔고 줄끝 · BOM 이 바뀐 파일은 0 이었다. 대신 새로 걸린 것이 넷이고, 그중 하나(N1)는 고른 파일에 다른 파일의 내용을 써 버린다. 근거와 수치는 ../history/2026-09-14-7차.md 의 D-1 에 있다.

7차 D-1 에서 잰 것 병합 pull rebase cherry-pick
끝낸 커밋 부모 둘 부모 둘 rebase 완료 1/1 부모 하나 · 원래 메시지
줄끝 · BOM 이 바뀐 파일 0 / 33 0 / 33 0 / 31 0 / 33
마지막 Save 뒤 파일 비교 탭 충돌 정리 끝 충돌 정리 끝 남은 차이 3줄 남은 차이 3줄
A-2 · B-1 안 남 안 남 안 남 안 남

공통 규칙

  • A → B → C → D → E 순서로 진행한다. A 는 사용자의 파일을 망가뜨리는 것이라 무엇보다 먼저다.
  • 항목 하나를 끝낼 때마다 빌드하고 실제로 실행해 화면을 확인한 뒤 보고한다.
  • 항목 하나가 끝나면 기록을 세 군데에 남기고 로컬 커밋한다(4차 D-2 규칙).
    1. 상세는 doc/history/YYYY-MM-DD-8차.md — 원인 · 판단 · 확인한 결과
    2. ../진행상황.md 은 대기 → 완료로 상태만
    3. ../history/changelog.md 에 한 줄 — 날짜 · 무엇을 왜 · 커밋
  • 푸쉬하지 않는다.
  • 실제 소스를 열지 않는다(2026-09-11 결정). 시험 재료는 C1Repo 로 만든 저장소로 한다. 씨앗이 고정돼 몇 번을 만들어도 같다. 고치기 전에 문제가 다시 나는 것부터 보이고, 고친 뒤 안 나는 것을 보인다.
  • 기존 검사 하네스는 계속 통과해야 한다. 로직을 건드리면 테스트를 먼저 늘린다.
  • 빌드 경고 0 · 오류 0 을 유지한다. 앱이 실행 중이면 UbisamBase.Shell 을 먼저 종료한다.
  • 사용자 설정 파일(D:\UbisamConfig\Settings\MergeHubSettings.json)은 시험 전에 백업하고, 끝나면 바이트 그대로 되돌린다. 창 기억(ShellWindowPlacement.json)은 시험 뒤 "기억 없음" 으로 둔다.
  • 자동 조작으로 확인할 때는 사람이 갈 수 있는 길만 쓴다(7차 D-1 에서 배움). 켤 때 뜨는 사용자 이름 창을 먼저 [저장] 으로 닫는다 — UI 자동화는 떠 있는 창 뒤의 단추도 누를 수 있다. 접힌 영역(원격 · Pull)은 사람처럼 펴고 누른다.
  • 결과 확인은 줄끝만 보지 않는다. 커밋된 UnitN.cs 안의 class 이름이 UnitN 인지(내용이 다른 파일로 뒤바뀌지 않았는지)도 본다.
  • 플랫폼을 고치면 다른 소비 프로그램에 미치는 영향을 함께 적고 UbisamPlatform/doc/history/changelog.md 에 남긴다. 배포는 사용자 확인 뒤.

A. 사용자의 파일을 망가뜨리는 것

A-1. 고른 충돌 파일 대신 다른 파일이 보이고, 그 화면을 저장하면 고른 파일에 써진다 (7차 D-1 N1)

  • 다시 내는 순서(병합 · pull 둘 다 났다):
    1. 새 저장소를 열고 [Merge → 현재 브랜치로](또는 Pull). 파일 비교 탭은 한 번도 열지 않는다.
    2. Git 탭 충돌 목록에서 Common/Util/Unit47.cs 를 고르고 [3분할로 열기].
    3. 파일 비교 탭이 열리는데 파일 콤보 · 칸 제목 · 내용이 첫 충돌 파일 Common/Util/Unit15.cs 다.
    4. [다음 충돌] → Take Theirs → [Save].
    5. Common/Util/Unit47.cs 가 Unit15 의 내용(123줄 · class Unit15 · 고른 줄 · 머지 주석)으로 스테이징되고 Unit15 는 UU 그대로. 로그 "충돌 해결(직접 편집 …): Common/Util/Unit47.cs". 화면은 30초 동안 Unit15 · "남은 충돌 1개" 그대로다.
  • 파일 비교 탭을 먼저 한 번 열어 두면 안 난다. 7차 D-1 네 길은 그렇게 해서 끝까지 갔다.
  • 짚이는 곳(코드를 읽어 짚은 것 — 원인부터 확인할 것):
    • 파일 비교 화면은 탭을 처음 열 때 만들어진다. 그때 목록 세우기(RebuildListAfterRefreshAsync → LoadCommitAsync → 첫 파일 LoadFileAsync)와 넘겨받은 요청(DeliverPendingOpenConflict → OpenConflictAsync → SelectedFile = Unit47)이 겹친다.
    • LoadFileAsync 는 늦게 끝난 로드를 IsGitMode 로만 버린다(HomeViewModel.cs "읽어 오는 사이에 파일 비교 모드로 넘어갔으면 버린다"). 읽기 시작한 경로가 지금 고른 파일과 같은지는 보지 않는다. 같은 주석이 "경로는 새 파일인데 내용은 옛 파일인 상태" 를 이미 겪었다고 적고 있다.
    • [Save] 는 화면에 실린 줄을 지금 고른 파일 경로에 쓴다. 화면 내용이 어느 파일의 것인지 대조하지 않는다.
  • 방향:
    1. 늦게 끝난 로드는 읽기 시작한 경로 · 커밋이 지금 것과 다르면 버린다.
    2. 저장은 화면에 실린 내용의 경로를 들고 있다가 그 경로와 지금 고른 파일이 다르면 쓰지 않는다(쓰기 전에 막고 알린다). 두 겹으로 막는다.
    3. 같은 뿌리가 있는지 본다 — 저장 뒤 다음 파일 열기(ReloadAfterResolveAsync), 파일 지우기/남기기 띠, 파일 콤보를 빠르게 바꾸기, git mergetool 세 벌 열기.
  • 판정 기준:
    • 위 순서 그대로(파일 비교 탭을 처음 여는 것이 [3분할로 열기])에서 고른 파일(Unit47)이 열린다. 병합 · pull · rebase · cherry-pick 넷 다.
    • 어떤 순서로도 [Save] 가 화면에 보이는 파일이 아닌 파일에 쓰지 않는다. 하네스로 "로드 도중 선택이 바뀜 · 저장 경로와 화면 경로가 다름" 을 만든다.
    • 네 길 결과 커밋에서 class 이름이 다른 UnitN.cs 0 개. 저장 뒤 다음 파일이 저절로 열리는 흐름(6차 A-2 · B-1)은 그대로.

B. 작업마다 화면이 사실대로 말하게

B-1. rebase · cherry-pick 에서 마지막 저장 뒤 "남은 차이" 가 뜬다 (7차 D-1 N2)

  • rebase · cherry-pick 마지막 충돌 파일을 [Save] 하면 파일 비교 탭이 두 칸(짧은해시 · Weld/Unit1.cs)으로 떨어져 "남은 차이 3줄" 을 보인다. 충돌 탭은 "충돌 정리 끝 - [계속] 으로 rebase 를 이어 갑니다"(cherry-pick 은 "[커밋] 으로 …")라 두 화면이 다른 말을 한다. 7차 A-1 과 같은 증상.
  • 짚이는 곳: A-1 의 MergeSavePlan.ResolvedMergeSummary · 제목 "병합 전 내 쪽" 이 IsMergeInProgress(병합 표시)일 때만 쓰인다. 7차 C-2 에서 만든 GitOperationState · OperationLabels 를 따르지 않는다.
  • 방향: 작업이 진행 중이고 충돌이 0 이면 작업에 맞게 말한다 — rebase 는 "충돌 정리 끝 · [계속] 으로 이어 갈 수 있습니다", cherry-pick · revert 는 "… [커밋] 으로 끝낼 수 있습니다". 제목의 "병합 전 내 쪽" 도 작업에 맞는 이름(rebase 는 "올릴 자리")으로.
  • 판정 기준: 네 길 모두 마지막 [Save] 뒤 파일 비교 탭의 요약 · 제목이 충돌 탭과 같은 말을 한다. 병합 글은 7차 A-1 그대로(하네스로 확인).

B-2. 바뀌는 것 없는 cherry-pick 에서 [커밋] 이 켜지고 누르면 실패한다 (7차 D-1 N4)

  • 이미 들어 있는 커밋(HEAD)을 cherry-pick 하면 CHERRY_PICK_HEAD 만 생긴다. 충돌 탭 [커밋] 이 켜지고, 누르면 로그에만 "[cherry-pick 커밋] 실패: No changes; nothing to commit." 이 남는다. 진행 줄은 "충돌 정리 끝 - [커밋] 으로 cherry-pick 을 끝냅니다" 그대로라 같은 단추를 또 누르게 된다. [작업 취소] 로는 빠져나온다. (시작 로그 "적용됨 - 확인한 뒤 [커밋] 으로 끝냅니다" 도 이 경우엔 맞지 않다.)
  • 방향: 스테이징된 변경이 없으면 [커밋] 을 끄고 "가져올 변경이 없습니다 - [작업 취소] 로 끝냅니다" 로 말한다. git 처럼 빈 cherry-pick 을 커밋으로 남기는 길(--allow-empty)을 둘지는 사용자에게 묻고 정한다 — 정하기 전에는 두지 않는다.
  • 판정 기준: 빈 cherry-pick 에서 [커밋] 이 꺼져 있고 안내가 [작업 취소] 를 말한다. 누를 수 있는 단추가 실패로 끝나는 일이 없다. 충돌이 있던 cherry-pick 은 7차 C-2 그대로.

B-3. MERGED 칸 줄 번호가 결과 파일 번호가 아니다 (7차 D-1 N3) — 먼저 정한다

  • Motion/Unit0.cs 에서 고른 줄 · 머지 주석으로 결과가 3줄 늘어 상태바는 "3366행 · 총 3366줄" 인데, MERGED 칸 끝 번호는 3363 이고 줄마다 LOCAL 칸과 같은 번호다. 네 자리가 잘리지는 않는다(6차 B-4 · 7차 D-1 에서 확인).
  • 짚이는 곳: MERGED 칸 번호는 LocalNumberText — 정렬할 때 정한 번호를 쓰고 편집 뒤 다시 매기지 않는 것으로 보인다. 새로 들어간 주석 줄은 번호가 비는지 확인한다.
  • 설계였을 수도 있다(LOCAL 과 같은 번호여야 옆 칸과 맞대 보기 쉽다). 고치기 전에 사용자에게 두 가지를 보이고 정한다. ① MERGED 는 결과 파일 번호(상태바 · 이동 칸 · 컴파일 오류와 같은 번호) ② 지금처럼 내 쪽 번호, 대신 칸 제목이나 상태바에 그렇다고 적는다.
  • 판정 기준: 정한 대로 MERGED 칸 번호 · 상태바 행 · [이동] 칸 · 코드 검사 "N행" 이 같은 기준을 쓴다. 번호가 서로 달라 보이는 곳이 없다(스크린샷).

C. 7차에서 남긴 것

C-1. revert 충돌을 화면으로 끝까지

  • 7차 C-2 에서 revert 는 rebase · cherry-pick 과 같은 길(AfterConflicts · CommitPendingOperation)을 쓰게 했지만 화면으로 끝까지 해 본 적이 없다.
  • 생성기(C1Repo)에 revert 가 충돌을 내는 갈래를 더한다(같은 씨앗, 기존 경우는 그대로).
  • 판정 기준: 이력 탭에서 커밋을 골라 [Revert] → 충돌을 네 칸으로 풀고 → 충돌 탭 [커밋] 까지 MergeHub 만으로. 칸 제목 "되돌리는 커밋 …" · 진행 표시 "revert 진행 중" · 원래 revert 메시지. 줄끝 · BOM 바뀐 파일 0, class 이름 검사 0.

C-2. cherry-pick 이 원래 작성자를 남기는지 눈으로

  • 시험 저장소의 모든 커밋 작성자가 시험 이라 7차 C-2 에서는 코드로만 확인했다.
  • 생성기에서 상대 쪽 커밋의 작성자를 다른 사람으로 만든다.
  • 판정 기준: cherry-pick · revert 커밋의 작성자가 git 과 같다(cherry-pick 은 원래 작성자 · 커밋한 사람은 지금 사용자). git 으로 같은 일을 한 결과와 견준다.

C-3. 창 크기 조절에서 안 본 것 (7차 E-4, 플랫폼)

  • 화면 가장자리 스냅(창을 왼쪽 · 오른쪽 끝으로 끌어 반쪽, Win+← / →)을 두 모니터(150% · 100%)에서 본다. 반쪽 크기가 최소 1280 × 720 보다 작아지는 경우의 모양도.
  • 기억이 없을 때 첫 Win+↓ 는 XAML 기본 크기(1600 × 900 DIP)로 내려오고, 두 번 누르기로 복원하면 작업 영역의 80% 다. 둘을 같게 맞출지 본다.
  • 잠금 화면 · 알림 · 이름 창 · 확인 창이 보통 크기 창의 가운데에 뜨는지 스크린샷으로 남긴다(7차 E-4 판정 기준 중 기록이 얇은 것).
  • 보통 크기 창 가장자리의 1px Windows 테두리 선은 그대로 둘지 사용자와 정한다.
  • 판정 기준: 위 넷을 실제 픽셀로 확인해 적는다. 고친다면 플랫폼 changelog 에 영향을 적고, 배포는 사용자 확인 뒤.

D. 문서

D-1. 웹 문서를 7차 · 8차에 맞게

  • doc\web\index.html(사용자 매뉴얼 · 발표 자료 · 남은 일 · 개발 현황)이 6차에서 멈춰 있다. 7차의 [Save] 이름 · 충돌 네 칸 머리 글 · rebase / cherry-pick 제목과 끝내기 단추 · 이력 [모든 브랜치] · pull 뒷일 · 폴더 차례 끌어 놓기 · 이름 창 잠금 · 창 크기 조절이 없다. 매뉴얼의 "알아 둘 한계" 목록에는 7차에서 사라진 것이 남아 있다.
  • A · B 를 고친 뒤 한 번에 맞춘다. 지난 기록(history)은 그때 이름 그대로 둔다.
  • 판정 기준: 문서에 적힌 단추 이름 · 흐름이 지금 화면과 같다(화면 문구를 grep 으로 대조). "알아 둘 한계" 에 8차 끝 기준으로 남은 것만 있다. 발행된 아티팩트도 같은 내용으로 갱신.

E. 확인

E-1. 네 길(+ revert)을 사람 순서 그대로 다시 끝까지

  • A ~ C 를 고친 뒤 저장소를 새로 만들어 병합 · pull · rebase · cherry-pick · revert 를 각각 처음부터 끝까지 한다.
  • 7차 D-1 과 달리 파일 비교 탭을 미리 열어 두지 않는다 — 충돌 탭의 [3분할로 열기] 로 처음 연다(A-1 을 그대로 밟는 순서).
  • 7차 D-1 과 같은 표로 적는다. 새로 걸린 것은 고치지 말고 적는다 — 그 목록이 9차가 된다.
  • 판정 기준: 다섯 길 모두 MergeHub 만으로 끝까지. 줄끝 · BOM 이 바뀐 파일 0 · 충돌 표식 0 · class 이름이 다른 Unit 파일 0. 7차 A-1 · A-2 · B-1 과 8차 A-1 · B-1 · B-2 가 다시 나지 않는다.

F. 추가 요청

F-1. 병합 커밋 메시지 본문에 충돌 해결 내역을 자동으로 적기 (2026-09-17, 사용자)

사용자: "파일단위 머지할때 이력은 그럼 어디남아?" → 제안 중 "병합 커밋 메시지 본문에 해결 내역을 자동으로 적기" 로 정함. "스타일 맞추는건 안할거야"(비교할 때만 스타일을 맞추고 저장은 원래대로 하는 선택지는 만들지 않는다).

지금 모양 — 충돌을 어떻게 풀었는지가 오래 남는 곳이 없다.

어디 남는 것 한계
소스 네 칸에서 줄을 가져오면 // [이름] 날짜 주석 충돌 탭 파일 단위 Accept 는 고른 쪽 파일을 바이트째 써서 주석이 없다
git 병합 커밋 부모 둘 · 제목 · 작성자(git user.name) "이 파일은 Theirs 로 골랐다" 가 직접 적히지 않는다 — 7차 D-1 · 8차 A-1 의 병합 커밋 본문은 비어 있었다
Git 탭 로그 충돌 해결(Theirs): 경로 화면에만 있고 끄면 사라진다
플랫폼 로그 버튼 클릭: Accept Theirs 파일 이름도 사람도 없다

방향

  • 병합(및 pull) 중에 충돌을 풀 때마다 무엇으로 풀었는지 적어 둔다 — 줄 단위(Save) · Accept Mine · Accept Theirs · Accept Both · 파일 남기기 · 파일 지우기 · 머지 주석 자동. 앱을 껐다 켜도 이어지게 .git 에 적고, 처음 충돌 목록(MERGEHUB_CONFLICTS)과 같은 규칙(첫 줄 MERGE_HEAD · 병합 커밋 · 작업 취소 · 새로고침 때 쓸모없으면 치움)을 따른다.
  • [병합 커밋](Save & Commit 포함) 때 본문에 "충돌 해결 N개 · 이름" 과 갈래별 파일 목록을 붙인다. Git 탭 메시지 칸에도 미리 채워 보인다. 사람이 칸을 고쳐도 커밋 때 블록은 붙는다(유일한 기록이므로).
  • cherry-pick · revert · rebase 는 원래 커밋 메시지를 쓰므로 이번에는 적지 않는다.

판정 기준

  • 만든 저장소를 MergeHub 로 병합해 여러 방법으로 풀고 [병합 커밋] 하면, git log -1 --format=%B 본문에 방법별 파일이 모두 적힌다. 파일 수 합 = 푼 충돌 수.
  • 병합 커밋 · 작업 취소 뒤 기록 파일이 남지 않는다. 다음 병합에 지난 기록이 섞이지 않는다.
  • 병합 제목 · 커밋 방식 · 줄끝 등 7차 · 8차 결과는 그대로(회귀 없음).

이번 지시서에서 하지 않는 것

  • 실제 소스로 병합해 보기 — 실제 소스는 열지 않는다(2026-09-11 결정).
  • 머지 주석을 병합에서 끄기 — 6차 B-5 에서 "소스에 넣고 비교 · 병합에서는 무시" 로 정했다. 7차 D-1 네 길에서도 그대로 동작했다.
  • 빠른 비교(크기 · 수정일)가 내용만 바뀐 파일을 놓치는 것 — 폴더 비교의 알려진 한계로 둔다.
  • 같은 줄 접기(4차 B-2) · 외부 편집기(4차 B-4) · 로그인·권한 · Gitea PR — 미룬 그대로 둔다.
  • 파일 단위 Accept 가 고른 쪽 파일의 줄끝을 그대로 가져오는 것(7차 C-2) — 고른 대로라 결함으로 보지 않는다.

보고 형식

항목마다 이렇게 적는다.

  • 고치기 전에 문제가 다시 나는 것을 어떻게 보였는지 (만든 저장소의 어느 파일로)
  • 무엇이 원인이었는지 한두 줄
  • 어떤 파일을 어떻게 고쳤는지
  • 실제로 실행해 확인한 결과 (바이트 · 줄끝 · 충돌 수 · class 이름처럼 눈이 아닌 값으로)
  • 커밋 해시