# 작업 지시 — 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 가 다시 나지 않는다. --- ## 이번 지시서에서 하지 않는 것 - **실제 소스로 병합해 보기** — 실제 소스는 열지 않는다(2026-09-11 결정). - **머지 주석을 병합에서 끄기** — 6차 B-5 에서 "소스에 넣고 비교 · 병합에서는 무시" 로 정했다. 7차 D-1 네 길에서도 그대로 동작했다. - 빠른 비교(크기 · 수정일)가 내용만 바뀐 파일을 놓치는 것 — 폴더 비교의 알려진 한계로 둔다. - 같은 줄 접기(4차 B-2) · 외부 편집기(4차 B-4) · 로그인·권한 · Gitea PR — 미룬 그대로 둔다. - 파일 단위 Accept 가 고른 쪽 파일의 줄끝을 그대로 가져오는 것(7차 C-2) — 고른 대로라 결함으로 보지 않는다. --- ## 보고 형식 항목마다 이렇게 적는다. - 고치기 전에 문제가 **다시 나는 것**을 어떻게 보였는지 (만든 저장소의 어느 파일로) - 무엇이 원인이었는지 한두 줄 - 어떤 파일을 어떻게 고쳤는지 - 실제로 실행해 확인한 결과 (바이트 · 줄끝 · 충돌 수 · class 이름처럼 **눈이 아닌 값**으로) - 커밋 해시