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

198 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 작업 지시 — MergeHub 8차 (고른 파일에 고른 내용을 · 작업마다 화면이 사실대로)
대상: `D:\Sorce\MergeTool\MergeHub` (플러그인) · `D:\Sorce\MergeTool\UbisamPlatform` (셸)
전제: 1~7차 지시서는 모두 끝났다([../진행상황.md](../진행상황.md)).
**이번 지시서가 나온 곳** — 7차 D-1 에서 시험 저장소를 길마다 새로 만들어 **병합 · pull · rebase · cherry-pick** 을 MergeHub 만으로 끝까지 했다.
네 길 모두 커밋까지 갔고 줄끝 · BOM 이 바뀐 파일은 0 이었다. 대신 **새로 걸린 것이 넷**이고,
그중 하나(N1)는 **고른 파일에 다른 파일의 내용을 써 버린다.** 근거와 수치는 [../history/2026-09-14-7차.md](../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](../진행상황.md) 은 **대기 → 완료로 상태만**
3. [../history/changelog.md](../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 이름처럼 **눈이 아닌 값**으로)
- 커밋 해시