사용자가 정했다. 누가 언제 반영했는지는 소스에 남아야 하므로 머지 주석은 지금처럼 넣는다. 대신 비교하고 병합할 때는 그 줄을 무시한다. git 은 이 규칙을 모른다. 병합은 주석 줄도 글자로 보고 충돌을 낸다. 그래서 무시는 MergeHub 가 한다. - 2-Way, 3-Way, 폴더 비교에서 머지 주석을 차이로 세지 않는다 - 줄 끝에 붙은 주석은 조각만 떼고 견준다 - git 이 낸 충돌이 주석 차이뿐이면 묻지 않고 풀고, 두 주석을 다 남긴다 - 무시는 보는 방식일 뿐 파일에서 지우지 않는다. 설정에서 끌 수 있다 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
226 lines
17 KiB
Markdown
226 lines
17 KiB
Markdown
# 작업 지시 — MergeHub 6차 (병합을 끝까지 믿고 쓸 수 있게)
|
|
|
|
대상: `D:\Sorce\MergeTool\MergeHub` (플러그인) · `D:\Sorce\MergeTool\UbisamPlatform` (셸)
|
|
전제: 1~5차 지시서는 모두 끝났다([../진행상황.md](../진행상황.md)).
|
|
|
|
**이번 지시서가 나온 곳** — 5차 C-1 에서 만든 저장소(파일 303개 · 충돌 22개)로
|
|
병합을 MergeHub 만으로 커밋까지 해 봤다. 끝까지는 갔지만 **걸린 것이 23가지**였다.
|
|
그 목록이 이 지시서다. 근거와 수치는 [../history/2026-09-11-5차.md](../history/2026-09-11-5차.md) 의 C-1 에 있다.
|
|
|
|
| C-1 에서 잰 것 | 값 |
|
|
|---|---|
|
|
| 병합 결과 | git 과 똑같다 (UU 20 · AA 1 · UD 1 · M 7) |
|
|
| 3분할로 푼 파일의 줄끝 | **커밋에 CR 0개** (바탕은 줄마다 CRLF) — 105,025B → 101,810B |
|
|
| 파일 단위 Accept 결과 | 고른 쪽과 바이트까지 같다 (CRLF 그대로) |
|
|
| 충돌 탭 Accept 한 번 | 약 1.7초 (저장소 전체를 다시 읽는다) |
|
|
|
|
---
|
|
|
|
## 공통 규칙
|
|
|
|
- **A → B → C → D 순서로 진행한다.** A 는 파일이나 이력을 망가뜨리는 문제라 제일 먼저다.
|
|
- **항목 하나를 끝낼 때마다** 빌드하고 실제로 실행해 화면을 확인한 뒤 보고한다.
|
|
- **항목 하나가 끝나면 기록을 세 군데에 남기고 로컬 커밋한다**(4차 D-2 규칙).
|
|
1. **상세**는 `doc/history/YYYY-MM-DD-주제.md` — 원인 · 판단 · 확인한 결과
|
|
2. [../진행상황.md](../진행상황.md) 은 **대기 → 완료로 상태만**
|
|
3. [../history/changelog.md](../history/changelog.md) 에 한 줄 — 날짜 · 무엇을 왜 · 커밋
|
|
- **푸쉬하지 않는다.**
|
|
- **실제 소스를 열지 않는다**(2026-09-11 결정). 시험 재료는 우리가 만들어 쓴다.
|
|
- **확인은 C-1 과 같은 저장소로 한다.** 시험 저장소 생성기(`C1Repo`)는 씨앗이 고정돼 있어
|
|
몇 번을 만들어도 같다. 고치기 전에 문제가 **다시 나는 것부터 보이고**, 고친 뒤 안 나는 것을 보인다.
|
|
- 기존 검사 하네스 20벌(698건)은 계속 통과해야 한다. 로직을 건드리면 **테스트를 먼저 늘린다.**
|
|
- 빌드 경고 0 · 오류 0 을 유지한다. 앱이 실행 중이면 `UbisamBase.Shell` 을 먼저 종료한다.
|
|
- 플랫폼을 고치면 다른 소비 프로그램에 미치는 영향을 함께 적고
|
|
`UbisamPlatform/doc/history/changelog.md` 에 남긴다.
|
|
|
|
---
|
|
|
|
## A. 파일이나 이력을 망가뜨리는 것
|
|
|
|
C-1 에서 "심각" 으로 적은 다섯 가지다. **A-1 · A-2 는 뿌리가 같다** — 병합 중에 "파일 하나를 풀어서 저장한다"
|
|
는 동작이 따로 없고 커밋까지 한 번에 묶여 있다. 둘을 같이 봐도 된다. 기록은 따로 남긴다.
|
|
|
|
### A-1. 3분할로 푼 파일을 저장하면 CRLF 가 LF 로 바뀐다
|
|
|
|
- 충돌 파일을 3분할에서 풀고 저장하면 **줄끝이 전부 LF 가 된다.** CP949 파일도 같다.
|
|
실제로 고친 줄은 12줄인데 git 에는 **파일 전체(3,367줄)가 바뀐 것**으로 나온다.
|
|
현장 저장소에서 이러면 리뷰할 diff 가 사라지고, 다른 사람의 병합과 또 충돌한다.
|
|
- 상태바는 `CRLF`, 저장 형식은 `원본 유지` 였다. **화면이 말한 것과 다르게 썼다.**
|
|
- 짚이는 곳: `GitService.ResolveConflictWithContent` 가 저장 형식을 안 보고 글을 그대로 쓴다.
|
|
파일 단위 Accept 는 blob 을 바이트째 쓰므로 멀쩡하다 - 3분할 저장만의 문제다.
|
|
- "원본" 이 무엇인지 정해야 한다. 충돌 중 워킹트리 파일에는 git 이 LF 로 충돌 표시를 넣어 두므로
|
|
**그 파일을 보고 판단하면 틀린다.** 내 쪽(ours) blob 의 줄끝을 기준으로 삼는 쪽이 맞아 보인다.
|
|
- **판정 기준**: C-1 저장소에서 `Motion/Unit0.cs` 를 3분할로 풀어 저장했을 때 줄 수와 CR 수가 같다.
|
|
`git diff --cached --stat` 이 고친 줄만 보여 준다. CP949 파일(`Common/Util/한글설명.cs`)도 같다.
|
|
`저장 형식` 에서 LF 를 고르면 그때는 LF 로 쓴다.
|
|
|
|
### A-2. Save & Commit 이 "실패" 라는데 파일은 이미 써지고 스테이징됐다
|
|
|
|
- 충돌이 남아 있으면 커밋에서 막힌다. 그런데 **그 앞의 "쓰고 스테이징" 은 이미 끝난 뒤**라
|
|
충돌은 22 → 21 로 줄었는데 화면은 `실패: 아직 해결되지 않은 충돌이 21건` 이라고 한다.
|
|
- 그 뒤로 화면은 계속 **저장 안 한 편집**으로 여긴다. 다른 파일을 고르거나 Git 탭에서 Accept 를 누를 때마다
|
|
확인 창이 다시 뜬다. 여기서 "저장하고 계속" 을 누르면 한 번 더 쓴다.
|
|
- Git 모드에는 **커밋 없이 저장하는 길이 없다.** Ctrl+S 도 Save & Commit 이다.
|
|
병합 중에 파일 하나씩 풀어 둘 수가 없으니 22개를 한 번에 끝내야만 한다.
|
|
- 방향: 병합 중에는 **"이 파일 해결로 저장"** 이 따로 있어야 한다(쓰고 스테이징까지, 커밋은 안 함).
|
|
Save & Commit 은 남은 충돌이 있으면 **아무것도 하기 전에** 막고 그 까닭을 말한다.
|
|
저장이 끝나면 편집 상태를 깨끗이 한다.
|
|
- **판정 기준**: 충돌 22개 중 하나를 풀어 저장하면 충돌이 21개가 되고, 화면은 성공이라고 한다.
|
|
그 뒤 다른 파일을 골라도 확인 창이 안 뜬다. 남은 충돌이 있을 때 Save & Commit 을 누르면
|
|
파일도 스테이징도 안 바뀐다.
|
|
|
|
### A-3. 충돌 파일을 고르면 상대(Theirs) 칸이 안 나온다
|
|
|
|
- 병합 중에 충돌 파일을 고르면 **BASE 와 내 것 두 칸뿐**이다. 3-Way 콤보는 `(3-Way 안 씀)`.
|
|
**상대가 무엇을 바꿨는지 안 보인 채 풀게 된다.** 콤보에서 브랜치를 골라야 세 칸이 된다.
|
|
- 짚이는 곳: `HomeViewModel.LoadFileAsync` 의 충돌 갈래가 지역 변수 `threeWay` 만 켜고
|
|
화면 틀을 정하는 `IsThreeWay` 는 안 켠다.
|
|
- 병합 중의 상대 쪽은 브랜치가 아니라 **인덱스(stage 3)** 에서 온다. 콤보에 브랜치 이름을 고르게 하는 것도 뜻이 어긋난다.
|
|
- **판정 기준**: 병합 중에 충돌 파일을 고르면 콤보를 건드리지 않아도 LOCAL · BASE · REMOTE · MERGED 네 칸이 뜬다.
|
|
REMOTE 제목은 상대 브랜치 이름을 보여 준다. 충돌이 아닌 파일을 고르면 지금처럼 동작한다.
|
|
|
|
### A-4. 상대가 지운 파일을 3분할이 다룰 줄 모른다
|
|
|
|
- 내가 고치고 상대가 지운 파일(UD)을 열면 REMOTE 칸이 비고, 범례는 **"Theirs only 89"** 라고 한다.
|
|
실은 **내 쪽에만 있는** 줄이다. 충돌은 10번 줄 하나로만 잡힌다.
|
|
- 진짜 물음은 **"이 파일을 남길까 지울까"** 인데, 3분할에는 지우는 길도 상대가 지웠다는 말도 없다.
|
|
(충돌 탭에만 `상대쪽 삭제`, 충돌 탭 Accept Theirs 를 누르면 제대로 지워진다.)
|
|
- 반대 경우(내가 지우고 상대가 고침, DU)도 같이 본다. C-1 저장소에는 없으니 생성기에 하나 넣는다.
|
|
- 방향: 줄 단위로 끌고 가지 말고 **파일 단위 선택**(남긴다 / 지운다)으로 보여 준다.
|
|
- **판정 기준**: `Recipe/ToBeDeleted.cs` 를 열면 "상대가 이 파일을 지웠습니다" 가 보이고,
|
|
남기기 · 지우기를 고를 수 있다. 범례가 줄의 주인을 바르게 말한다. DU 도 같은 식으로 풀린다.
|
|
|
|
### A-5. 양쪽 새 파일에 Accept Both 를 누르면 빌드가 깨진다
|
|
|
|
- 양쪽이 같은 이름으로 새로 만든 파일(AA)에 Accept Both 를 누르면 **두 파일을 바이트째 이어 붙인다.**
|
|
결과 파일 **한가운데에 BOM(EF BB BF)이 하나 더** 들어가고, 같은 이름의 class 가 두 번 나온다.
|
|
- "둘 다" 는 줄 단위에서는 뜻이 있지만 **파일 단위에서는 거의 늘 틀린 결과**를 낸다.
|
|
- 방향: 이어 붙이더라도 뒤쪽 파일의 BOM 은 떼고, 줄끝을 앞쪽에 맞춘다.
|
|
그리고 파일 단위 Both 가 정말 필요한지 따진다 - 3분할에서 줄 단위로 둘 다 고르는 길이 이미 있다.
|
|
없애거나, 누르기 전에 "파일 두 개를 그대로 이어 붙입니다" 를 알린다.
|
|
- **판정 기준**: `Ui/Views/NewPanel.cs` 에 Both 를 적용한 결과에 BOM 이 맨 앞 한 번만 있다.
|
|
줄끝이 한 가지로 맞다. Both 를 남긴다면 누르기 전에 무엇이 일어나는지 알 수 있다.
|
|
|
|
---
|
|
|
|
## B. 헷갈리거나 막히는 것
|
|
|
|
### B-1. 충돌 목록에서 3분할 화면까지 가는 길이 끊겨 있다
|
|
|
|
- 병합 뒤 파일 비교 탭의 **파일 목록이 비어 있다**(0개). 그 탭의 [새로고침] 을 눌러야 22개가 나온다. 앱을 새로 켜도 같다.
|
|
- 충돌 탭 목록을 **두 번 눌러도** 아무 일이 없다.
|
|
- 안내 글이 "**홈 탭**에서 해결하세요" · "홈 탭의 3분할 화면" 이라고 한다. 홈 탭은 없다(파일 비교 탭).
|
|
- **판정 기준**: 병합이 끝나면 파일 비교 탭의 목록이 바로 채워진다. 충돌 탭에서 파일을 두 번 누르면
|
|
파일 비교 탭으로 넘어가 그 파일이 세 칸으로 열린다(A-3). 안내 글이 실제 탭 이름을 말한다.
|
|
|
|
### B-2. 확인 창이 엉뚱한 파일 이름을 댄다
|
|
|
|
- 고친 것은 `Motion/Unit0.cs` 인데 "**ToBeDeleted.cs** 에 저장하지 않은 편집이 있습니다" 라고 한다.
|
|
새로 고른 파일 이름을 가져다 쓴다(`SelectedFile` 이 이미 바뀐 뒤에 묻는다).
|
|
- 그 확인 창이 **파일 콤보를 고르는 한가운데서** 뜬다. 취소하면 콤보는 무엇을 가리키는지 불분명하다.
|
|
- **판정 기준**: 확인 창이 편집한 파일의 이름을 댄다. 취소하면 콤보와 화면이 원래 파일 그대로다.
|
|
|
|
### B-3. 커밋 메시지가 엉뚱하게 남는다
|
|
|
|
- 커밋 메시지가 **처음 연 충돌 파일 것으로 굳는다.** 다른 파일을 봐도, 앱을 껐다 켜도, 병합 커밋이 끝난 뒤에도
|
|
"Merge: Common/Util/Unit15.cs 충돌 해결" 이다.
|
|
- 충돌 탭의 [병합 커밋] 은 병합할 때 채워 둔 "Merge branch 'feature/theirs' into feature/mine" 을 안 쓰고
|
|
**"Merge commit '1742d8f...'"** 로 남긴다. git 이 주석으로 넣어 두는 **"#Conflicts:" 와 파일 목록**도 그대로 커밋된다.
|
|
- **판정 기준**: 병합 커밋 메시지 첫 줄에 브랜치 이름이 들어가고 `#` 로 시작하는 줄이 없다.
|
|
병합이 끝나면 파일 비교 탭의 커밋 메시지가 비워진다.
|
|
|
|
### B-4. MERGED 칸 줄 번호가 네 자리에서 잘린다
|
|
|
|
- 3분할의 MERGED 칸에서 2949 → "294", 2950~2959 → 전부 "295". **1,000줄 넘는 파일은 줄 번호를 못 읽는다.**
|
|
위의 세 칸은 멀쩡하다.
|
|
- **판정 기준**: `Motion/Unit0.cs`(3,367줄)를 열었을 때 네 칸 모두 끝 줄 번호까지 온전히 보인다.
|
|
7,479줄(실제 소스 최대 크기)짜리 만든 파일로도 확인한다.
|
|
|
|
### B-5. 머지 주석은 소스에 넣고, 병합 뒤에는 그 줄을 무시한다
|
|
|
|
**정한 것 (2026-09-14, 사용자)** — 머지 주석은 지금처럼 **소스에 넣는다.** 누가 언제 반영했는지는 소스에 남아야 한다.
|
|
대신 **비교하고 병합할 때는 그 줄을 무시한다.**
|
|
|
|
- 지금의 문제: Take Theirs 한 자리마다 "// [김승준] 2026-09-14" 가 들어갔다. 다음 병합 때 양쪽이 서로 다른 주석을
|
|
같은 자리에 달고 있으면 **그 주석 줄끼리 충돌**하고, 비교 화면에는 주석 줄이 차이로 잡힌다.
|
|
- **git 은 이 규칙을 모른다.** 병합(libgit2)은 주석 줄도 글자로 보고 충돌을 낸다. 그러므로 무시는 MergeHub 가 한다.
|
|
1. **비교 화면(2-Way · 3-Way · 폴더 비교)** 에서 머지 주석을 차이로 세지 않는다.
|
|
알아보는 함수는 이미 있다(`MergeNote.LooksLikeNote`). 지금은 2-Way 의 줄 필터만 규칙을 받고 3-Way 정렬(`ThreeWayAligner`)은 규칙을 안 받는다.
|
|
2. **줄 끝에 붙은 주석**(`코드 // [이름] 날짜`)도 있다(`MergeNote.Append`). 이 경우는 줄을 통째로 버리지 말고
|
|
**주석 조각만 떼고** 견준다. 코드가 같고 주석만 다르면 같은 줄이다.
|
|
3. **git 이 낸 충돌이 머지 주석 차이뿐이면** 사람에게 묻지 않고 MergeHub 가 푼다. 양쪽 주석을 모두 남기는 쪽으로 푼다
|
|
(이력은 지우지 않는다). 충돌 수에도 세지 않는다.
|
|
4. 무시는 **보는 방식**이다. 파일에서 주석을 지우지 않는다. 설정에서 끌 수 있고, 끄면 지금처럼 차이로 보인다.
|
|
- 사람이 우연히 같은 모양(`// [글자] 0000-00-00`)으로 쓴 주석도 무시된다. 이 한계는 설정 설명에 적는다.
|
|
- **판정 기준**
|
|
- 만든 저장소에서 양쪽 브랜치가 같은 코드 줄 위에 **서로 다른 머지 주석만** 달고 병합하면,
|
|
git 은 충돌을 내지만 MergeHub 는 그 파일을 "충돌 0" 으로 보고 한 번에 풀린다. 두 주석이 다 남는다.
|
|
- 코드도 다르고 주석도 다르면 **코드 충돌만** 묻는다.
|
|
- 줄 끝 주석만 다른 줄은 비교 화면에서 같은 줄로 나온다.
|
|
- 설정을 끄면 지금처럼 차이·충돌로 보인다.
|
|
- 생성기(`C1Repo`)에 이 경우들을 넣어 D-1 에서도 확인한다.
|
|
### B-6. 충돌 탭에서 하나씩 누를 때마다 오래 기다린다
|
|
|
|
- Accept 한 번마다 **저장소 전체를 다시 읽는다.** 한 개 약 1.7초. 22개면 기다리는 것만 40초쯤이다.
|
|
- 여러 개를 골라 한 번에 Accept 하는 길이 없다.
|
|
- **판정 기준**: 여러 개를 골라 한 번에 Mine/Theirs 를 적용할 수 있다. 다시 읽기는 끝에 한 번만 한다.
|
|
C-1 저장소의 같은 줄 충돌 15개를 한 번에 풀었을 때 걸린 시간을 고치기 전과 견줘 보고한다.
|
|
|
|
### B-7. 상태바가 병합 중에 믿을 수 없는 값을 보인다
|
|
|
|
- 인코딩·줄바꿈을 결과가 아니라 **충돌 표시가 들어간 워킹트리 파일**에서 읽는다.
|
|
양쪽이 다 CRLF 인 새 파일도 "UTF-8 · 혼합" 으로 나온다(A-1 과 같은 뿌리).
|
|
- "남은 차이 6줄" 은 충돌을 다 정해도 그대로다. "남은 충돌" 은 오른쪽 칸에만 나온다.
|
|
- **판정 기준**: 병합 중 상태바의 인코딩·줄바꿈이 내 쪽(ours) 파일 기준이다. 상태바에 남은 충돌 수가 나오고 풀면 준다.
|
|
|
|
### B-8. 병합 커밋 뒤 파일 비교 탭이 어정쩡하게 남는다
|
|
|
|
- 목록은 비고, 3분할 틀에 병합과 상관없는 파일(`Common/Util/Unit103.cs`)이 BASE 칸에만 뜨고, 언어는 "평문" 이다.
|
|
- **판정 기준**: 병합 커밋이 끝나면 파일 비교 탭이 병합 전과 같은 보통 상태로 돌아간다(선택 커밋 = 방금 만든 병합 커밋).
|
|
|
|
---
|
|
|
|
## C. 사소한 것
|
|
|
|
### C-1. 로그와 목록 다듬기
|
|
|
|
- 병합 로그에 "충돌 22건 - 해결이 필요합니다." 가 **두 번** 찍힌다.
|
|
- 충돌 탭 목록의 종류 표시가 들쭉날쭉하다. `상대쪽 삭제` 만 있고 양쪽 새 파일(AA)은 표시가 없다.
|
|
몇 개 풀었는지(예: 3/22)도 없다.
|
|
- 로그가 CP949 를 **"ks_c_5601-1987"** 로 적는다. 5차 A-1 에서 이름을 CP949 로 모았는데 이 자리가 빠졌다.
|
|
- **판정 기준**: 로그가 한 번만 찍힌다. 목록에 충돌 종류(양쪽 수정 · 상대 삭제 · 내가 삭제 · 양쪽 새 파일)와
|
|
풀린 개수가 보인다. 로그의 인코딩 이름이 상태바와 같다.
|
|
|
|
---
|
|
|
|
## D. 확인
|
|
|
|
### D-1. 병합을 처음부터 다시 끝까지 해 본다
|
|
|
|
- A~C 를 다 고친 뒤 **C-1 저장소를 새로 만들어** 같은 병합을 처음부터 다시 한다.
|
|
이번에는 A-4 에 맞춰 생성기에 DU(내가 지우고 상대가 고침)를 하나 더 넣는다.
|
|
- 5차 C-1 과 **같은 표로** 결과를 적는다. 23가지 중 무엇이 사라졌고 무엇이 남았는지 한 줄씩.
|
|
- 새로 걸린 것이 있으면 C-1 때처럼 **고치지 말고 적는다.** 그 목록이 7차가 된다.
|
|
- **판정 기준**: 충돌 22개(DU 포함 23개)를 MergeHub 만으로 풀어 병합 커밋까지 간다.
|
|
커밋된 파일 중 **줄끝이 바탕과 달라진 파일이 0개**다(파일 단위 · 3분할 둘 다).
|
|
A 의 다섯 가지가 다시 나지 않는다.
|
|
|
|
---
|
|
|
|
## 이번 지시서에서 하지 않는 것
|
|
|
|
- **실제 소스로 병합해 보기** — 실제 소스는 열지 않는다(2026-09-11 결정). 만든 저장소로만 한다.
|
|
- **rebase · cherry-pick 충돌** — 병합과 같은 3분할을 쓰지만 이번에는 병합만 끝까지 믿을 수 있게 만든다.
|
|
- 같은 줄 접기(4차 B-2) · 외부 편집기(4차 B-4) · 로그인·권한 — 미룬 그대로 둔다.
|
|
|
|
---
|
|
|
|
## 보고 형식
|
|
|
|
항목마다 이렇게 적는다.
|
|
|
|
- 고치기 전에 문제가 **다시 나는 것**을 어떻게 보였는지 (C-1 저장소의 어느 파일로)
|
|
- 무엇이 원인이었는지 한두 줄
|
|
- 어떤 파일을 어떻게 고쳤는지
|
|
- 실제로 실행해 확인한 결과 (바이트 · 줄끝 · 충돌 수처럼 **눈이 아닌 값**으로)
|
|
- 커밋 해시 |