748 lines
54 KiB
Markdown
748 lines
54 KiB
Markdown
# 6차 — 병합을 끝까지 믿고 쓸 수 있게
|
||
|
||
지시서: [../작업지시/6차.md](../작업지시/6차.md) · 재료: [2026-09-11-5차.md](2026-09-11-5차.md) 의 C-1
|
||
|
||
확인은 5차 C-1 과 같은 시험 저장소로 한다. 생성기(`C1Repo`)는 씨앗이 고정돼 있어 몇 번을 만들어도 같다.
|
||
만든 뒤 `feature/mine` 에서 `feature/theirs` 를 병합해 **충돌 22개** 상태로 두고 시작한다. 실제 소스는 열지 않는다.
|
||
|
||
---
|
||
|
||
## A-2. Save & Commit 이 "실패" 라는데 파일은 이미 써지고 스테이징됐다 (2026-09-14, 완료)
|
||
|
||
지시서 순서는 A-1 이 먼저지만 A-2 를 먼저 했다. 둘은 뿌리가 같다 - 병합 중에 "파일 하나만 풀어서 저장"
|
||
하는 길이 없고 커밋까지 묶여 있다. 그 길을 먼저 만들어야 A-1(줄끝)을 고칠 자리가 한 곳으로 정해진다.
|
||
|
||
### 고치기 전에 다시 났다
|
||
|
||
`Io/Unit12.cs` 의 충돌 하나를 3분할에서 Take Theirs 로 정하고 Save & Commit 을 눌렀다.
|
||
|
||
| 본 것 | 값 |
|
||
|---|---|
|
||
| 화면 | `Save & Commit 실패: 아직 해결되지 않은 충돌이 21건 있습니다.` |
|
||
| git 충돌 수 | 22 → **21** (실패라면서 줄었다) |
|
||
| 그 파일 상태 | `M` — 이미 써지고 스테이징됐다 |
|
||
| 편집 상태 | 여전히 "편집 1건", Save & Commit 단추 켜짐 |
|
||
|
||
### 무엇이 원인이었나
|
||
|
||
`SaveAndCommitAsync` 가 **쓰기 → 스테이징 → 커밋** 을 한 덩어리로 돌렸다. 남은 충돌은 마지막 커밋 단계
|
||
(`GitService.CommitMerge`)에서야 예외로 막혔다. 그 앞 두 단계는 되돌릴 수 없는 일인데 이미 끝난 뒤였다.
|
||
실패로 끝났으니 편집 상태도 안 치웠다 - 그래서 다른 파일을 고를 때마다 확인 창이 다시 떴다.
|
||
|
||
그리고 Git 모드에는 커밋 없이 저장하는 길이 **아예 없었다.** Ctrl+S 도 Save & Commit 이었다.
|
||
|
||
### 고친 것
|
||
|
||
| 자리 | 무엇 |
|
||
|---|---|
|
||
| `Services/MergeSavePlan.cs` (새 파일) | "지금 커밋해도 되는가" 를 가르는 판단만 따로 뺐다. 지금 저장하는 파일은 셈에서 빼고, 그 밖에 하나라도 남으면 막는다. 화면 코드에 두면 또 순서가 뒤바뀌기 쉬워서 뺐다 |
|
||
| `SaveAndCommitAsync` | 병합 중이면 **아무것도 쓰기 전에** 먼저 판단하고, 막히면 그대로 멈춘다. 알림은 "커밋하지 않았습니다. 파일은 바꾸지 않았습니다. [해결로 저장] 으로…" |
|
||
| `SaveResolvedAsync` (새 명령) · [해결로 저장] 단추 | 병합 중에만 보인다. 이 파일만 쓰고 스테이징한다. 커밋은 안 한다. 저장한 내용을 새 "원본" 으로 삼아 편집 상태를 치운다 |
|
||
| Ctrl+S | 병합 중이면 [해결로 저장]. 병합이 아니면 지금까지와 같다 |
|
||
| 확인 창의 "저장하고 계속" | 병합 중 충돌 파일이면 워킹트리에만 쓰지 않고 충돌까지 푼다. 예전에는 충돌 표시만 사라지고 git 은 여전히 충돌로 보는 어정쩡한 상태가 됐다 |
|
||
|
||
### 화면에서 해 보다 둘이 더 나왔다
|
||
|
||
**① 저장하자 파일 목록이 0개가 됐다.** 로그에 `[파일 목록] 다른 작업 진행 중이라 건너뜀`.
|
||
|
||
`LoadCommitAsync` 가 게이트에 막혀 건너뛰고도 **빈 결과로 목록을 갈아엎었다.** 본문 로드(`LoadFileAsync`)는
|
||
이미 `if (!loaded) return;` 으로 막고 있었는데 목록만 빠져 있었다.
|
||
새로고침은 저장소 상태를 읽는 도중(게이트를 쥔 채) 화면 목록을 바꾸고, 그 변화가 목록 다시 세우기를 부르므로
|
||
이 건너뜀은 **새로고침이 있을 때마다 난다.** 5차 C-1 의 "병합 뒤 파일 목록이 비어 있다"(6차 B-1)도 같은 원인으로 보인다.
|
||
|
||
- 건너뛰면 지금 목록을 그대로 둔다(`listed` 를 보고 돌아간다).
|
||
- 해결로 저장 뒤에는 **게이트가 풀릴 때까지 기다렸다가**(`GitWorkspace.WhenIdleAsync`) 목록을 다시 세운다.
|
||
처음에는 새로고침 직후 곧바로 불렀는데 5ms 뒤에도 또 건너뛰었다 - 다른 화면이 게이트를 잠깐 쥔다.
|
||
- 방금 푼 파일은 충돌 목록에서 빠지므로 **남은 충돌의 첫 파일이 저절로 열린다.**
|
||
|
||
**② 충돌을 전부 "내 것" 으로 정하면 저장할 수가 없었다.** Take Mine 은 글을 바꾸지 않는다(이미 내 것이다).
|
||
그래서 "고친 것 없음" 이 되어 단추가 꺼지고, git 은 계속 충돌로 봤다.
|
||
충돌로 열린 파일이고 3분할에서 충돌을 다 정했으면 고친 글이 없어도 저장할 수 있게 했다.
|
||
3분할이 아닐 때는 켜지 않는다 - 충돌 수를 셀 수 없는데 내 것으로 덮으면 안 된다.
|
||
|
||
### 확인한 것 (새로 만든 저장소, 충돌 22개에서 시작)
|
||
|
||
| 확인 | 결과 |
|
||
|---|---|
|
||
| 다른 충돌이 남은 채 Save & Commit | "커밋하지 않았습니다 · 파일은 바꾸지 않았습니다". git 충돌 **22 그대로**, 파일 `UU` 그대로, 커밋 없음, 편집 남음 |
|
||
| [해결로 저장] | "해결로 저장 · Unit12.cs · 남은 충돌 21건". git 충돌 **21**, 파일 `M`, 커밋 없음, 편집 상태 깨끗 |
|
||
| 저장 뒤 파일 목록 | **21개**로 다시 서고, 남은 충돌 첫 파일(`Common/Util/Unit15.cs`)이 세 칸으로 열림 |
|
||
| 그 뒤 다른 파일 고르기 | 확인 창 안 뜸, 파일 열림 |
|
||
| Take Mine 만 하고 저장 | 단추가 켜지고 저장됨. git 충돌 **20**, 내 줄이 들어감 |
|
||
| 병합 중 Ctrl+S | 해결로 저장이 불림. git 충돌 **19**, 커밋 없음, 목록 19개 |
|
||
| Git 탭 충돌 목록 | 푼 세 파일이 빠짐 |
|
||
|
||
화면 확인 **25건 통과**. 검사 하네스는 `MergeSaveTest` 18건을 새로 만들어 **21벌 716건 통과** · 빌드 경고 0 · 오류 0.
|
||
|
||
### 이번에 안 한 것
|
||
|
||
- **줄끝(CRLF → LF)은 그대로다.** 해결로 저장도 지금은 `ResolveConflictWithContent` 를 거치므로 A-1 이 남아 있다.
|
||
저장 길이 한 곳으로 모였으니 A-1 은 거기서 고친다.
|
||
- 로그의 첫 번째 "건너뜀" 은 여전히 찍힌다. 기다렸다 다시 세우므로 화면에는 티가 안 난다.
|
||
새로고침 도중에 목록을 다시 세우려는 것 자체는 B-1 에서 본다.
|
||
|
||
---
|
||
|
||
## A-1. 3분할로 푼 파일을 저장하면 CRLF 가 LF 로 바뀐다 (2026-09-14, 완료)
|
||
|
||
### 고치기 전에 다시 났다
|
||
|
||
새로 만든 저장소에서 A-2 의 [해결로 저장] 으로 두 파일을 풀었다.
|
||
|
||
| 파일 | 원래 | 저장 뒤 | git 에 나온 변경 |
|
||
|---|---|---|---|
|
||
| `Io/Unit12.cs` (200줄 · UTF-8 BOM) | CRLF | **CR 0 / LF 200** | 399줄 (실제로 고친 것은 1줄 + 머지 주석) |
|
||
| `Common/Util/한글설명.cs` (11줄 · CP949) | CRLF | **CR 0 / LF 11** | 21줄 |
|
||
|
||
### 무엇이 원인이었나
|
||
|
||
두 가지가 겹쳤다.
|
||
|
||
1. 편집기가 주는 글(`BuildLocalText`)은 줄마다 `\n` 이다. `GitService.ResolveConflictWithContent` 는
|
||
**인코딩만 맞추고 줄끝은 아예 보지 않고** 그 글을 그대로 썼다. 그래서 늘 LF 가 됐다.
|
||
2. 인코딩을 맞출 "원본" 으로 **워킹트리 파일**을 읽었다. 충돌 중의 워킹트리에는 git 이 LF 로 충돌 표시를 넣어 두므로
|
||
줄끝을 봤더라도 "혼합" 으로 읽혔을 것이다. 보통 저장(`TextFile.Write`)은 그 "혼합" 을 무조건 CRLF 로 썼다.
|
||
|
||
### 고친 것
|
||
|
||
| 자리 | 무엇 |
|
||
|---|---|
|
||
| `TextFile.Encode` (새 함수) | 글을 저장할 바이트로 만드는 규칙을 한 곳으로 모았다. "원본 유지" 가 무엇을 따를지를 **원본 바이트로 받는다.** 인코딩은 원본에서 판별, 줄끝은 원본에서 **가장 많은 것**, 원본에 줄끝이 없으면 CRLF. BOM 은 맨 앞에 한 번만 붙이고 글 첫머리의 U+FEFF 는 뗀다 |
|
||
| `TextFile.Write` | 디스크의 파일을 원본으로 넘겨 `Encode` 를 쓴다. "혼합" 은 이제 많은 쪽을 따른다(예전에는 무조건 CRLF) |
|
||
| `GitService.ResolveConflictWithContent` | 원본을 워킹트리가 아니라 **내 쪽(ours) blob** 으로 잡는다. 내 쪽이 없으면 상대 쪽, 그것도 없으면 공통 조상. 고른 저장 형식을 받는다 |
|
||
| `HomeViewModel` 세 자리 | 해결로 저장 · Save & Commit · 확인 창의 "저장하고 계속" 이 모두 고른 저장 형식을 넘긴다 |
|
||
|
||
로그 글도 따라 바뀌었다. `충돌 해결(직접 편집, ks_c_5601-1987)` 이 `충돌 해결(직접 편집, CP949 · CRLF)` 로 나온다
|
||
(6차 C-1 의 "로그가 CP949 를 ks_c_5601-1987 로 적는다" 가 이 자리였다).
|
||
|
||
### 확인한 것 (새로 만든 저장소, 3분할 + [해결로 저장])
|
||
|
||
| 파일 | 한 일 | 결과 |
|
||
|---|---|---|
|
||
| `Io/Unit12.cs` | Take Theirs 1 | CR 200 = LF 200 · BOM 한 번 · 내 쪽 대비 **+2 −1** (고른 줄 + 머지 주석) |
|
||
| `Motion/Unit0.cs` (3,363줄) | 충돌 6개 | CR 3,367 = LF 3,367 · 내 쪽 대비 **+8 −4** (5차 C-1 에서는 +3,367 −3,363) |
|
||
| `Common/Util/한글설명.cs` | Take Theirs 1 | CR 11 = LF 11 · CP949 그대로(UTF-8 로 안 읽힘) · '가접' 들어감 |
|
||
| `Io/LineEnding.cs` | 내 쪽 CRLF · 상대가 LF 로 바꾼 파일에서 Take Theirs | **내 쪽을 따라 CRLF** (CR 61 = LF 61) |
|
||
| `Io/Unit20.cs` | 저장 형식 줄바꿈을 **LF 로 고르고** 저장 | 고른 대로 LF (CR 0 · LF 182) |
|
||
|
||
화면 확인 **10건 통과**. `EncTest` 에 저장 바이트 규칙 22건을 넣었다(16 → 38).
|
||
|
||
### 검사 모음이 옛 실행 파일로 돌고 있었다
|
||
|
||
A-1 검사를 넣고 하네스를 돌리다 **세 벌이 빌드가 안 된다**는 것을 알았다. 검사 모음 스크립트가 빌드 출력을 버리고
|
||
`bin` 에 남아 있던 **옛 실행 파일**을 그대로 돌려서, 그동안 통과로 세어지고 있었다.
|
||
|
||
| 하네스 | 무엇이 깨졌나 | 언제부터 |
|
||
|---|---|---|
|
||
| `LangTest` (52건) | 토크나이저의 줄 넘김 상태를 `bool` 로 넘겼다. 5차 B-1 에서 `CarryOver` 로 바뀌었다. 고치자 **1건이 실제로 실패** - XAML 언어 이름을 옛 이름("XML / HTML")으로 기대하고 있었다 | 5차 B-1 (09-11) |
|
||
| `ReportTest` (31건) | 프로젝트에 `CarryOver.cs` · `MarkupTokenizer.cs` 가 빠져 있었다 | 5차 B-1 (09-11) |
|
||
| `TokenTest` (15건) | **`Program.cs` 가 없다.** 언제 왜 사라졌는지 모른다. 실행 파일은 09-06 것이었다 | 알 수 없음 |
|
||
|
||
- 5차 B-1 · B-2 · C-1 과 6차 A-2 기록의 "전체 N건 통과" 에는 **이 세 벌의 옛 결과가 섞여 있다.**
|
||
고치고 새로 빌드해 돌려 보니 LangTest 의 이름 기대값 1건 말고는 모두 통과했다 - 제품 코드가 틀린 것은 없었다.
|
||
- `LangTest` 는 부르는 모양과 이름 기대값만 새 형식으로 맞췄다. 검사 내용은 그대로다.
|
||
- `ReportTest` 는 빠진 파일 둘을 넣었다.
|
||
- `TokenTest` 는 되살릴 원본이 없어 **같은 범위(C# 줄 하나를 갈래로 쪼개기)로 새로 썼다**(22건). 예전 15건과 같은 검사는 아니다.
|
||
- 검사 모음은 이제 **모두 다시 빌드하고(`--no-incremental`), 이번에 새로 만든 실행 파일이 아니면 실패로 센다.**
|
||
|
||
그렇게 돌린 결과 **21벌 745건 통과** · 빌드 경고 0 · 오류 0.
|
||
|
||
|
||
---
|
||
|
||
## A-3. 충돌 파일을 고르면 상대(Theirs) 칸이 안 나온다 (2026-09-14, 완료)
|
||
|
||
### 고치기 전에 다시 났다
|
||
|
||
새로 만든 저장소에서 병합 중에 `Io/Unit12.cs` 를 골랐다.
|
||
|
||
| 본 것 | 값 |
|
||
|---|---|
|
||
| 3-Way 콤보 | `(3-Way 안 씀)` |
|
||
| 칸 | BASE 와 LOCAL 두 칸. **REMOTE · MERGED 없음** |
|
||
| BASE 제목 | `(merge base) Io/Unit12.cs` — 읽기는 충돌 갈래로 읽었다 |
|
||
|
||
### 무엇이 원인이었나
|
||
|
||
`LoadFileAsync` 는 충돌 파일이면 git 이 들고 있는 세 벌(BASE · 내 쪽 · 상대 쪽)을 읽고 행도 세 칸으로 만든다.
|
||
그런데 켠 것은 지역 변수 `threeWay` 뿐이었다. 화면 틀을 정하는 `IsThreeWay` 는 **콤보에서 브랜치를 골랐을 때만** 참이라,
|
||
세 칸짜리 행이 두 칸 틀에 실렸다. REMOTE 칸 제목도 늘 `REMOTE · THEIRS` 라 어느 브랜치를 끌어오는지 알 수 없었다.
|
||
|
||
### 고친 것
|
||
|
||
| 자리 | 무엇 |
|
||
|---|---|
|
||
| `HomeViewModel.IsThreeWay` | "콤보로 고른 3-Way" 또는 **"충돌 파일이라 켠 3분할"** 이면 참. 충돌 쪽은 따로 둔 깃발(`_conflictThreeWay`)이다 |
|
||
| `LoadFileAsync` | 행을 싣기 **전에** 틀부터 바꾼다. 읽기 갈래는 이제 **콤보로 고른 3-Way 만** 본다(`IsBranchThreeWay`) |
|
||
| `ClearPanes` | 깃발을 내린다 |
|
||
| `GitService.GetMergeTheirsName` (새 함수) | 들어오는 커밋(MERGE_HEAD)을 가리키는 브랜치 이름 → 병합 메시지의 이름 → 짧은 해시 차례로 찾는다 |
|
||
| `Services/MergeNames.cs` (새 파일) | 병합 메시지 첫 줄에서 브랜치·태그 이름을 꺼낸다. 저장소 없이 검사하려고 따로 뺐다 |
|
||
| REMOTE 칸 제목 | `REMOTE · feature/theirs` 처럼 상대 이름을 보인다 |
|
||
|
||
**콤보 값은 건드리지 않는다.** 콤보를 상대 브랜치로 바꿔 두는 쪽도 생각했지만, 그러면 병합이 끝난 뒤에도 보통 파일이
|
||
그 브랜치와 3-Way 로 열려 사람이 고르지 않은 비교가 남는다.
|
||
|
||
읽기 갈래를 콤보에만 묶은 까닭 - 앞 파일이 충돌이라 켜 둔 3분할을 그대로 들고 오면, 충돌이 아닌 다음 파일을
|
||
`(3-Way 안 씀)` 이라는 이름의 브랜치와 견주려다 깨진다.
|
||
|
||
`MergeHeads` 를 쓰려 했는데 이 프로젝트의 LibGit2Sharp(0.32)에는 없었다. `.git/MERGE_HEAD` 파일의 첫 줄을 읽는다.
|
||
|
||
### 확인한 것 (새로 만든 저장소)
|
||
|
||
| 확인 | 결과 |
|
||
|---|---|
|
||
| 콤보를 안 건드리고 충돌 파일(`Io/Unit12.cs`)을 연다 | 콤보는 `(3-Way 안 씀)` 그대로, LOCAL (Mine) · BASE · REMOTE (Theirs) · MERGED **네 칸**, 충돌 잡힘 |
|
||
| REMOTE 칸 제목 | `REMOTE · feature/theirs` |
|
||
| 다른 충돌 파일(XAML)로 옮김 | 네 칸, 충돌 잡힘 |
|
||
| 콤보 없이 Take Theirs → 해결로 저장(A-2) | 단추 켜짐, "남은 충돌 21건", 다음 충돌 파일도 네 칸 |
|
||
| `git merge --abort` 뒤 충돌 아닌 파일을 연다 | 콤보 `(3-Way 안 씀)`, **두 칸**, 해결로 저장 단추 안 보임 |
|
||
| 콤보로 브랜치를 고른다 / 되돌린다 | 네 칸(`REMOTE · feature/theirs`) / 두 칸 — 예전과 같다 |
|
||
|
||
화면 확인 **18건 통과**. `MergeSaveTest` 에 병합 메시지 이름 검사 14건을 넣었다(18 → 32).
|
||
검사 **21벌 759건 통과**(전부 새로 빌드한 실행 파일) · 빌드 경고 0 · 오류 0.
|
||
|
||
### 확인하다 본 것 (B-1 으로 넘긴다)
|
||
|
||
`git merge --abort` 뒤 파일 비교 탭의 [새로고침] 을 눌렀는데 **파일 목록이 병합 때의 21개 그대로**였다(커밋 파일은 303개).
|
||
A-2 에서 "건너뛴 목록 불러오기가 목록을 비우지 않게" 막았으므로 이제 **비지 않고 옛 목록이 남는다.**
|
||
새로고침 도중 게이트를 쥔 채로 목록 다시 세우기가 불리는 뿌리는 그대로라, B-1 에서 본다.
|
||
이번 확인에 쓴 `Io/Unit12.cs` 는 두 목록에 다 있어 결과에는 영향이 없다.
|
||
|
||
|
||
---
|
||
|
||
## A-4. 상대가 지운 파일을 3분할이 다룰 줄 모른다 (2026-09-14, 완료)
|
||
|
||
### 고치기 전에 다시 났다
|
||
|
||
시험 저장소 생성기에 **J. 내가 지웠는데 상대가 고침(DU)** 을 넣었다(지시서가 요구). 이제 충돌은 23개다.
|
||
|
||
| 파일 | git | 화면 |
|
||
|---|---|---|
|
||
| `Recipe/ToBeDeleted.cs` (상대가 지움) | `UD` | 네 칸, 지운 쪽 칸이 빔, 범례 **`Theirs only 89`** (실은 내 쪽에만 있는 줄), 충돌 1개, 지웠다는 말 없음 |
|
||
| `Recipe/ToBeDeletedByMe.cs` (내가 지움) | `DU` | 네 칸, `Theirs only 0`, 충돌 1개, 지웠다는 말 없음 |
|
||
|
||
### 무엇이 원인이었나
|
||
|
||
지운 쪽의 blob 이 없으면 `GetThreeWayContent` 는 그쪽 글을 빈 문자열로 준다. 화면은 그걸 "상대가 줄을 전부 지운 파일" 로 보고
|
||
줄 단위 3분할을 만들었다. 사람이 정할 것은 **"이 파일을 남길까 지울까"** 하나인데, 그 물음이 화면 어디에도 없었다.
|
||
파일 단위 해결(`ResolveConflict`)은 이미 "없는 쪽을 고르면 지운다" 로 바르게 동작했다 - 화면이 그 길로 안내하지 못했을 뿐이다.
|
||
|
||
### 고친 것
|
||
|
||
| 자리 | 무엇 |
|
||
|---|---|
|
||
| `Services/DeletionConflict.cs` (새 파일) | 어느 쪽이 지웠는지, **남기기 · 지우기가 각각 어느 쪽을 고르는 것인지**를 정한다. 지운 쪽에 따라 뒤바뀌므로(UD 는 남기기=내 것, DU 는 남기기=상대 것) 한 곳에 모았다 |
|
||
| `LoadFileAsync` | 한쪽이 지운 충돌이면 줄 단위 3분할로 안 간다. **남아 있는 파일을 BASE 와 두 칸으로** 보여 준다 - 남은 쪽이 무엇을 고쳤는지는 보인다. 칸 제목이 `feature/mine · 내 쪽 (상대는 지움)` 처럼 누가 지웠는지 말한다 |
|
||
| 범례 위 띠 | "상대(feature/theirs)가 이 파일을 지웠습니다. 내 쪽(feature/mine)은 고쳤습니다. 파일을 남길까요, 지울까요?" 와 [파일 남기기] · [파일 지우기] |
|
||
| [파일 남기기] | 파일이 있는 쪽을 골라 스테이징. **고친 것이 있으면 고친 대로 남긴다**(해결로 저장과 같다) |
|
||
| [파일 지우기] | 없는 쪽을 골라 지우고 스테이징. 고친 것이 있으면 한 번 묻는다 |
|
||
| 뒷정리 | 해결로 저장 · 남기기 · 지우기가 같은 `ReloadAfterResolveAsync` 를 쓴다. 남은 충돌 첫 파일이 열린다 |
|
||
|
||
### 확인한 것 (새로 만든 저장소, 충돌 23개에서 시작)
|
||
|
||
| 확인 | 결과 |
|
||
|---|---|
|
||
| 보통 충돌(`Io/Unit12.cs`) | 띠 없음, 네 칸 그대로 |
|
||
| 상대가 지운 파일을 연다 | 띠 "상대(feature/theirs)가 이 파일을 지웠습니다…", 단추 둘 켜짐, **두 칸**, 제목 `feature/mine · 내 쪽 (상대는 지움)`, 거꾸로 센 범례 없음, 콤보 그대로 |
|
||
| 거기서 [파일 지우기] | "지움 · ToBeDeleted.cs · 남은 충돌 22건", git 충돌 22, 워킹트리에서 파일 없어짐, 스테이징 `D `, 다음 충돌 파일이 열림 |
|
||
| 내가 지운 파일을 연다 | 띠 "내 쪽(feature/mine)에서 이 파일을 지웠습니다…", 제목 `feature/theirs · 상대 쪽 (나는 지움)`, 두 칸 |
|
||
| 거기서 [파일 남기기] | "남김 · ToBeDeletedByMe.cs · 남은 충돌 21건", git 충돌 21, 상대가 고친 줄이 들어간 파일이 생김, CRLF 그대로(CR 70 = LF 70), 스테이징 `A ` |
|
||
| Git 탭 충돌 목록 | 두 파일이 빠짐 |
|
||
|
||
화면 확인 **23건 통과**. `MergeSaveTest` 에 지운 충돌 검사 16건을 넣었다(32 → 48).
|
||
검사 **21벌 775건 통과**(전부 새로 빌드한 실행 파일) · 빌드 경고 0 · 오류 0.
|
||
|
||
### 화면을 보다 하나 더 고쳤다 — BASE 칸 제목
|
||
|
||
두 칸으로 보여 줄 때 BASE 칸 제목이 `a751a6b · Recipe/ToBeDeleted.cs` 로 나왔다. 지금 커밋과 견주는 것처럼 읽히지만
|
||
실제로 보이는 것은 **공통 조상**이다. 제목을 정하는 조건이 "3분할인가" 만 봤기 때문이다.
|
||
충돌 파일이면 두 칸이어도 `(merge base) 경로` 로 적게 했다.
|
||
|
||
다시 확인 **5건 통과** — 보통 충돌은 네 칸, 두 지운 파일 모두 BASE 제목이 `(merge base) …`, 지우기 → 남은 충돌 22건, 남기기 → 21건.
|
||
이 고침은 화면 코드(`HomeViewModel`)만 건드려 검사 하네스에는 걸리지 않는다. 앞의 775건은 마지막 서비스 코드 변경 뒤에 돌린 값이다.
|
||
|
||
### 시험 저장소 생성기가 반쯤 지운 채 멈췄다
|
||
|
||
다시 만들려는데 생성기가 `다른 프로세스가 사용 중` 으로 멈추고 저장소가 **빈 폴더**로 남았다. 앱을 끄는 명령과 생성기를
|
||
동시에 돌렸고, 그 뒤에도 셸이 이 폴더를 작업 폴더로 잡고 있어 **뿌리 폴더를 지울 수 없었다.**
|
||
생성기가 뿌리는 두고 **안만 비우게** 고쳤다. 제품 코드와는 상관없다.
|
||
|
||
|
||
---
|
||
|
||
## A-5. 양쪽 새 파일에 Accept Both 를 누르면 빌드가 깨진다 (2026-09-14, 완료)
|
||
|
||
### 고치기 전에 다시 났다
|
||
|
||
새로 만든 저장소에서 Git 탭 충돌 목록의 `Ui/Views/NewPanel.cs`(양쪽이 같은 이름으로 새로 만듦, `AA`)에 Accept Both 를 눌렀다.
|
||
|
||
| 본 것 | 값 |
|
||
|---|---|
|
||
| 누르기 전에 묻는가 | **안 묻고 바로 이어 붙였다** |
|
||
| 결과 파일의 BOM | **2개** — 맨 앞 하나, 가운데 하나 |
|
||
| `class NewPanel` | **2번** |
|
||
|
||
### 무엇이 원인이었나
|
||
|
||
`GitService.ResolveConflict` 의 Both 갈래가 두 blob 을 **바이트째** 이어 붙였다. 양쪽이 BOM 으로 시작하면 뒤쪽 BOM 이
|
||
결과 한가운데에 그대로 남는다. 줄끝이 다르면 한 파일 안에 CRLF 와 LF 가 섞인다.
|
||
그리고 파일 단위 "둘 다" 는 같은 이름의 형식이 두 번 나오는 결과를 거의 늘 만드는데, 누르기 전에 그걸 알릴 자리가 없었다.
|
||
|
||
### 고친 것
|
||
|
||
| 자리 | 무엇 |
|
||
|---|---|
|
||
| `Services/BothJoin.cs` (새 파일) | 두 벌을 각자 제 인코딩으로 **글로 풀어** 이어 붙인 뒤, **내 쪽 파일의 형식**(인코딩 · BOM · 줄끝)으로 다시 쓴다. 규칙은 A-1 의 `TextFile.Encode` 를 그대로 쓴다. 한쪽이 비면 다른 쪽을 손대지 않고 돌려준다 |
|
||
| `GitService.ResolveConflict` (Both) | 바이트 이어 붙이기 대신 `BothJoin.Join` |
|
||
| Git 탭 Accept Both | 누르면 먼저 묻는다 — "내 것 다음에 상대 것을 한 파일로 이어 붙입니다… 같은 형식이 두 번 나와 빌드가 안 될 수 있습니다. 줄마다 골라 둘 다 넣으려면 파일 비교 탭의 3분할에서 하세요." 단추에 같은 뜻의 도움말 |
|
||
|
||
**Both 를 없애지는 않았다.** 설정 파일이나 목록 파일처럼 이어 붙이는 게 맞는 경우가 있다. 대신 누르기 전에 결과를 알게 했다.
|
||
|
||
### 확인한 것 (새로 만든 저장소, 충돌 23개에서 시작)
|
||
|
||
| 확인 | 결과 |
|
||
|---|---|
|
||
| 양쪽 새 파일에 Accept Both | 먼저 묻는다(단추: 예 · 아니오) |
|
||
| [아니오] | git 은 여전히 `AA`, 파일 바이트 그대로, 충돌 23 |
|
||
| 다시 눌러 [예] | BOM **1개(맨 앞)**, CR 4 = LF 4, 내 것 다음에 상대 것, 가운데 U+FEFF 없음, git `M`, 충돌 22 |
|
||
| 양쪽이 고친 보통 충돌(`Io/Unit20.cs`)에 Accept Both | BOM 1개, CR 362 = LF 362, 양쪽 줄이 다 들어감, 충돌 21 |
|
||
|
||
화면 확인 **14건 통과**. `EncTest` 에 이어 붙이기 검사 10건을 넣었다(38 → 48). CP949 인 내 파일과 UTF-8 BOM 인 상대 파일을 이어도
|
||
CP949 로 쓰이고 양쪽 한글이 다 읽히는지까지 본다.
|
||
검사 **21벌 785건 통과**(전부 새로 빌드한 실행 파일) · 빌드 경고 0 · 오류 0.
|
||
|
||
### A 묶음을 마치며
|
||
|
||
A 의 다섯 가지(줄끝 · 반쯤 된 실패 · 상대 칸 · 지운 파일 · Both)가 모두 끝났다. 끝까지 풀어 커밋까지 가는 확인은 D-1 에서
|
||
처음부터 다시 한다. A 를 하다 B-1 으로 넘긴 것 - **새로고침 도중 게이트를 쥔 채 목록 다시 세우기가 불려 건너뛰는 뿌리** -
|
||
는 아직 그대로다(지금은 목록이 비지 않고 옛 목록이 남는다).
|
||
|
||
|
||
---
|
||
|
||
## B-1. 충돌 목록에서 3분할 화면까지 가는 길이 끊겨 있다 (2026-09-14, 완료)
|
||
|
||
### 고치기 전에 다시 났다
|
||
|
||
새로 만든 저장소에서 파일 비교 탭을 한 번 열어 둔 채 Git 탭으로 `feature/theirs` 를 병합했다(충돌 23개).
|
||
|
||
| 본 것 | 값 |
|
||
|---|---|
|
||
| 파일 비교 탭 파일 목록 ([새로고침] 안 누름) | **304개** — 병합 전 커밋 파일 그대로. 충돌 23개가 아니다 |
|
||
| 충돌 탭 목록을 두 번 누름 | **아무 일 없음** |
|
||
| 충돌 탭 안내 글 · 병합 알림 | "**홈 탭**의 3분할 화면" · "**홈 탭**에서 해결하세요" — 그런 탭은 없다 |
|
||
|
||
5차 C-1 때는 목록이 **0개** 였다. 6차 A-2 에서 "건너뛴 목록 불러오기가 목록을 비우지 않게" 막아서 이제는 **옛 목록이 남는다.**
|
||
|
||
### 무엇이 원인이었나
|
||
|
||
**목록.** 새로고침은 저장소 상태를 읽는 동안(게이트를 쥔 채) 화면 목록을 갈아 끼우고, 끝나자마자 Git 탭이 제 일로
|
||
게이트를 다시 잡는다. 파일 비교 화면은 새로고침 신호를 받자마자 목록을 다시 세우려다 **"다른 작업 진행 중" 으로 건너뛰었다.**
|
||
A-2 · A-4 에서는 저장한 뒤에만 기다렸다 다시 세우게 했고, 새로고침 자체는 그대로였다.
|
||
|
||
**두 번 누르기.** 충돌 목록과 파일 비교 화면을 잇는 통로가 없었다. 폴더 비교 → 파일 비교에는 있던 것이다.
|
||
|
||
### 고친 것
|
||
|
||
| 자리 | 무엇 |
|
||
|---|---|
|
||
| `HomeViewModel.OnWorkspaceRefreshed` | 새로고침 뒤 목록 세우기를 **게이트가 풀릴 때까지 기다렸다** 한다(`RebuildListAfterRefreshAsync`). 막히면 몇 번 더 해 본다 |
|
||
| `GitWorkspace.RequestOpenConflict` (새 통로) | "이 충돌 파일을 3분할로 열어 달라" 요청을 **남겨 두고** 파일 비교 탭으로 옮긴다. 폴더 비교의 넘겨주기와 같은 방식이다 - 파일 비교 화면은 탭을 처음 열 때 만들어지므로 받을 사람이 없을 수 있다 |
|
||
| `HomeViewModel.OpenConflictAsync` | 요청을 받으면 목록에 그 파일이 설 때까지 기다렸다 고른다. 충돌 파일이라 A-3 대로 **네 칸**으로 열린다. 이 요청 때문에 화면이 새로 만들어졌으면 **기본 저장소를 새로 열지 않고** 지금 저장소로 목록만 세운다 |
|
||
| Git 탭 충돌 목록 | 두 번 누르기 · Enter · **[3분할로 열기]** 단추. 이진 파일은 줄 단위로 못 본다고 알린다 |
|
||
| 두 번 누르기 받는 법 | `MouseBinding` 이 아니라 목록의 `MouseDoubleClick` - 항목이 마우스 누름을 먼저 처리해 `MouseBinding` 은 항목 위에서 안 온다 |
|
||
| 안내 글 · 병합 알림 | "…줄마다 고르려면 목록을 두 번 누르거나 [3분할로 열기] 를 누르세요. 파일 비교 탭에서 세 칸으로 열립니다." · "충돌 23건 - 충돌 탭에서 파일을 두 번 누르면 파일 비교 탭에서 세 칸으로 풉니다." |
|
||
|
||
### 확인한 것 (새로 만든 저장소, MergeHub 로 병합)
|
||
|
||
| 확인 | 결과 |
|
||
|---|---|
|
||
| 병합 알림 | "충돌 23건 - 충돌 탭에서 파일을 두 번 누르면…" (홈 탭 없음) |
|
||
| 병합 뒤 파일 비교 탭, [새로고침] 안 누름 | 목록 **23개** |
|
||
| 충돌 탭 안내 글 | 두 번 누르기 · [3분할로 열기] 를 알려 줌, 홈 탭 없음 |
|
||
| 목록 두 번 누르기 | 파일 비교 탭으로 넘어가 `Io/Unit20.cs` 가 네 칸으로 열림, 충돌 잡힘 (약 1초) |
|
||
| [3분할로 열기] | `Motion/Unit0.cs` 가 네 칸으로 (약 0.8초) |
|
||
| Enter | `Io/LineEnding.cs` 가 네 칸으로 (약 1초) |
|
||
| **파일 비교 탭을 한 번도 안 연 새 앱**에서 두 번 누르기 | 파일 비교 탭이 새로 만들어지며 `Io/Unit12.cs` 가 네 칸으로, 저장소는 `mergehub-c1` 그대로, 목록 23개 |
|
||
| `git merge --abort` 뒤 **Git 탭에서만** [새로고침] | 파일 비교 탭 목록이 커밋 파일 304개로 돌아옴, REMOTE 칸 없음 |
|
||
| 열기만 하는 동안 git 충돌 수 | 23 그대로 |
|
||
|
||
화면 확인 **17건 통과**(Enter 1건은 처음에 목록 아래쪽 행을 골랐다가 자동 조작이 못 찾아 실패 - 가상화된 목록이라 화면 밖 행은
|
||
안 만들어진다. 위쪽 행으로 다시 해 통과). 이번에 고친 파일은 검사 하네스가 쓰지 않아 앞의 785건이 그대로 유효하다.
|
||
빌드 경고 0 · 오류 0.
|
||
|
||
|
||
---
|
||
|
||
## B-2. 확인 창이 엉뚱한 파일 이름을 댄다 (2026-09-14, 완료)
|
||
|
||
### 고치기 전에 다시 났다
|
||
|
||
새로 만든 저장소(충돌 23개)에서 `Io/Unit12.cs` 를 3분할로 열어 Take Theirs 로 고친 뒤, 파일 콤보에서 `Io/Unit20.cs` 를 **마우스로** 골랐다.
|
||
|
||
| 본 것 | 값 |
|
||
|---|---|
|
||
| 확인 창 첫 줄 | "**Unit20.cs** 에 저장하지 않은 편집이 있습니다." — 고친 것은 Unit12.cs |
|
||
| [취소] 뒤 콤보 | **`Io/Unit20.cs`** — 새 파일을 가리킨 채 |
|
||
| [취소] 뒤 화면 | `(merge base) Io/Unit12.cs`, 고친 내용 그대로 — **콤보와 화면이 서로 다르다** |
|
||
|
||
(자동 조작은 콤보 항목을 UIA 로 고르지 않고 진짜 마우스로 눌렀다. UIA 로 고르면 확인 창이 뜨는 순간 그 호출이 안 돌아와 앱이 굳는다 - 5차 C-1 에서 겪었다.)
|
||
|
||
### 확인하다 찾은 것 둘
|
||
|
||
**① [저장하고 계속] 이 엉뚱한 파일에 썼다 — 이것이 가장 컸다.**
|
||
`Io/Unit20.cs` 를 고치고 `Io/Unit12.cs` 로 옮기며 [저장하고 계속] 을 눌렀다. 충돌은 23 → 22 로 줄었는데 `Io/Unit20.cs` 는 여전히 `UU` 였다.
|
||
저장소를 들여다보니 **`Io/Unit12.cs` 안에 `class Unit20` 이 들어가 있었고, 해결된 것(`M`)으로 스테이징돼 있었다.**
|
||
고친 파일의 글이 다른 파일을 덮어쓰고, 덮인 파일은 풀린 것으로 처리됐다. 병합 결과가 망가지는 버그다.
|
||
|
||
**② B-1 의 빈틈 — 저장소를 먼저 열고 파일 비교 탭을 처음 열면 목록이 끝내 0개.**
|
||
B-1 확인 때는 늘 파일 비교 탭을 먼저 열어 두었다. 순서를 바꾸자 파일 콤보가 0개였다.
|
||
그 화면의 자동 열기는 이미 열린 저장소면 아무것도 안 하고, 새로고침 신호는 화면이 생기기 전에 지나갔다.
|
||
|
||
### 무엇이 원인이었나
|
||
|
||
모두 한 뿌리다. 파일 콤보에서 다른 파일을 고르면 **`SelectedFile` 이 먼저 새 파일로 바뀐 뒤에** 변경 처리가 불린다.
|
||
그 안에서 뜨는 확인 창이 이름을 `SelectedFile` 에서 가져왔고, [저장하고 계속] 의 저장(`SaveWorkingCopy`)도 쓸 곳을 `SelectedFile` 에서 가져왔다.
|
||
[취소] 뒤 되돌리기는 콤보가 고르는 **도중에** 값을 되돌려, 콤보가 그 알림을 흘려 버렸다.
|
||
|
||
### 고친 것
|
||
|
||
| 자리 | 무엇 |
|
||
|---|---|
|
||
| `ConfirmDiscard` | 이름을 **지금 화면에 실린 파일**(`_lastLoadedFile`)에서 가져온다. Git 모드는 저장소 안 경로로 적는다 - 다른 폴더에 같은 이름 파일이 흔하다 |
|
||
| `SaveWorkingCopy` | 쓸 곳도 **실린 파일**로. 병합 중 충돌 파일이면 그 파일의 충돌을 푼다 |
|
||
| `OnSelectedFileChanged` | [취소] 의 되돌리기를 **콤보의 선택 처리가 끝난 다음 차례**에 한다 |
|
||
| 생성자 | 저장소가 이미 열려 있으면 그 저장소로 목록을 세운다(B-1 빈틈) |
|
||
|
||
### 확인한 것 (새로 만든 저장소, 저장소를 먼저 열고 파일 비교 탭을 나중에 처음 연다)
|
||
|
||
| 확인 | 결과 |
|
||
|---|---|
|
||
| 파일 비교 탭을 처음 연다, [새로고침] 안 누름 | 목록 **23개** |
|
||
| 고친 `Io/Unit12.cs` 에서 `Io/Unit20.cs` 를 고름 | "**Io/Unit12.cs** 에 저장하지 않은 편집이 있습니다. / 다른 파일을 엽니다: Io/Unit20.cs" |
|
||
| [취소] | 콤보 `Io/Unit12.cs`, 화면 `(merge base) Io/Unit12.cs`, 고친 내용 그대로 |
|
||
| [저장하지 않고 계속] | 콤보·화면 `Io/Unit20.cs`, git 충돌 23 그대로, `Io/Unit12.cs` 는 여전히 `UU` |
|
||
| `Io/Unit20.cs` 를 고치고 `Io/Unit12.cs` 로 옮기며 [저장하고 계속] | 확인 창이 **Io/Unit20.cs** 이름을 댐. git 충돌 22, **풀린 것은 `Io/Unit20.cs`**(`M`, 안의 형식 `Unit20`, 상대 줄 들어감), **`Io/Unit12.cs` 는 `UU` 그대로**(안의 형식 `Unit12`), 그다음 `Io/Unit12.cs` 가 열림 |
|
||
|
||
화면 확인 **18건 통과**. 고친 파일(`HomeViewModel`)은 검사 하네스가 쓰지 않아 앞의 785건이 그대로 유효하다. 빌드 경고 0 · 오류 0.
|
||
|
||
## B-3. 커밋 메시지가 엉뚱하게 남는다 (2026-09-14, 완료)
|
||
|
||
### 고치기 전에 다시 났다
|
||
|
||
새로 만든 저장소(충돌 23개)를 MergeHub 로 병합했다.
|
||
|
||
| 본 것 | 값 |
|
||
|---|---|
|
||
| 파일 비교 탭에서 `Common/Util/Unit15.cs` 를 연 뒤 `Io/Unit12.cs` · `Motion/Unit0.cs` 로 옮김 | 메시지 칸이 끝까지 "**Merge: Common/Util/Unit15.cs 충돌 해결**" — 처음 연 파일 이름이 굳음 |
|
||
| 충돌을 다 풀고 앱을 다시 켠 뒤 충돌 탭 [병합 커밋] | 첫 줄 "**Merge commit 'fc8b8f8…'**", 그 아래 **`#` 로 시작하는 줄 24개**(`#Conflicts:` 와 파일 목록). 부모 2개 |
|
||
|
||
### 무엇이 원인이었나
|
||
|
||
- 파일 비교 탭은 메시지 칸이 **비어 있을 때만** 채웠다. 첫 파일에서 한 번 채우면 그 뒤로는 사람이 적은 글과 구별이 안 돼 그대로 남았다. 병합이 끝나도 지우는 곳이 없었다.
|
||
- 메시지 없이 커밋하면 git 이 남긴 `MERGE_MSG` 를 **그대로** 썼다. git 명령은 커밋할 때 `#` 줄을 지우지만 MergeHub 는 파일을 통째로 읽어 넣었다.
|
||
또 MergeHub 는 브랜치를 커밋으로 풀어 병합하므로(libgit2) git 이 첫 줄을 해시로 적는다 — 이력에서 무엇을 합쳤는지 안 보였다.
|
||
|
||
### 고친 것
|
||
|
||
| 자리 | 무엇 |
|
||
|---|---|
|
||
| `MergeNames.BranchMergeMessage` | 기본 메시지 `Merge branch '상대' into 내 브랜치`. 파일 이름은 넣지 않는다 — 병합 커밋은 병합 전체를 담는다 |
|
||
| `MergeNames.CleanMergeMessage` | `MERGE_MSG` 에서 `#` 줄을 빼고 앞뒤 빈 줄을 정리. 첫 줄이 `Merge commit '해시'` 이고 상대 브랜치 이름을 알면 브랜치 모양으로 바꾼다. 남는 것이 없으면 기본 메시지 |
|
||
| `GitService.CommitMerge` | 메시지가 비면 `CleanMergeMessage` 를 거친 것을 쓴다 |
|
||
| `HomeViewModel` | 스스로 채운 글을 `_autoCommitMessage` 로 기억한다. 칸이 비었거나 **내가 채운 글 그대로**일 때만 다시 채우고, 사람이 고친 글은 건드리지 않는다. 병합이 끝나면(새로고침에서 병합 중이 아님 · 병합 아닌 파일을 열 때 · 커밋 성공) 내가 채운 글만 지운다 |
|
||
|
||
`MergeSaveTest` 에 메시지 경우를 넣어 **62건 통과**.
|
||
|
||
### 확인한 것 (새로 만든 저장소)
|
||
|
||
| 확인 | 결과 |
|
||
|---|---|
|
||
| 병합 뒤 `Io/Unit12.cs` 를 엶 | 칸 `Merge branch 'feature/theirs' into feature/mine` |
|
||
| `Motion/Unit0.cs` 로 옮김 | 같은 글. 파일 이름 없음 |
|
||
| 칸에 "냉각 로직 병합 - 사람이 적음" 을 쓰고 `Io/Unit20.cs` 로 옮김 | 적은 글 그대로 |
|
||
| 칸을 비우고 `Io/Unit12.cs` 로 옮김 | 기본 메시지가 다시 채워짐 |
|
||
| 충돌을 git 으로 다 풀고 **앱을 다시 켠 뒤** 충돌 탭 [병합 커밋] | 커밋 메시지 `Merge branch 'feature/theirs' into feature/mine` 한 줄, `#` 줄 **0개**, 부모 2개, MERGE_HEAD 사라짐 |
|
||
| 저장소를 새로 만들어 병합 → 파일을 열어 칸이 채워진 채로 충돌을 풀고 **앱을 끄지 않고** [병합 커밋] | 커밋 메시지 같은 한 줄, `#` 줄 0개, 부모 2개. **파일 비교 탭 메시지 칸이 비워짐** |
|
||
|
||
화면 확인 **7건 통과**. 빌드 경고 0 · 오류 0.
|
||
|
||
## B-4. MERGED 칸 줄 번호가 네 자리에서 잘린다 (2026-09-14, 완료)
|
||
|
||
### 고치기 전에 다시 났다
|
||
|
||
새로 만든 저장소(충돌 23개)를 병합하고 `Motion/Unit0.cs`(3,363줄)를 네 칸으로 열어 편집기에서 Ctrl+End 를 눌렀다.
|
||
|
||
| 칸 | 보인 번호 |
|
||
|---|---|
|
||
| LOCAL · BASE · REMOTE | `2949` · `2950` 온전 |
|
||
| **MERGED** | **`294` · `295` · `295`** — 2949 · 2950 · 2951 줄 |
|
||
|
||
### 무엇이 원인이었나
|
||
|
||
줄 여백 폭은 네 칸 모두 54px 로 같지만 **나눈 방법이 달랐다.**
|
||
|
||
| 칸 | 여백 나눔 | 번호 글자 자리 |
|
||
|---|---|---|
|
||
| BASE | 번호 54 (오른쪽 띄움 8) | 46px |
|
||
| LOCAL 원본 · REMOTE | 번호 34 (띄움 4) + [<]/[>] 20 | 30px |
|
||
| MERGED | 북마크 10 + 번호 24 (띄움 4) + [>] 20 | **20px** |
|
||
|
||
Consolas 12 는 한 글자 약 6.6px 라 20px 에는 세 자리만 들어간다. MERGED 칸만 북마크 막대 자리를 따로 떼어 번호가 가장 좁았다.
|
||
|
||
### 고친 것 (`Views/HomeView.xaml` 만)
|
||
|
||
여백을 네 칸 모두 **66px** 로 넓혔다. 칸마다 폭이 같아야 워드랩에서 접히는 자리가 같다(한 칸만 넓히면 행이 어긋난다).
|
||
|
||
| 자리 | 전 | 후 | 번호 글자 자리 |
|
||
|---|---|---|---|
|
||
| BASE 템플릿 | 54 | 66 | 58px |
|
||
| LOCAL 원본 · REMOTE 템플릿 | 34 + 20 | 46 + 20 | 42px |
|
||
| MERGED 템플릿 | 10 + 24 + 20 | **6 + 40 + 20** | **36px** — 다섯 자리(99,999줄)까지 |
|
||
| 위에 겹친 편집기 `LocalEditor` 왼쪽 여백 | 54 | 66 | — 아래층 글자와 x 가 같아야 캐럿이 맞는다 |
|
||
|
||
북마크 막대는 너비 4 · 좌우 띄움 1 이라 6px 로 모자람이 없다. 칸마다 코드 자리가 12px 줄었다.
|
||
|
||
### 확인한 것
|
||
|
||
| 확인 | 결과 |
|
||
|---|---|
|
||
| C-1 저장소 `Motion/Unit0.cs` 네 칸, Ctrl+End | MERGED `2949` · `2950` · `2951` 온전. 확대해 보니 [>] 단추와도 안 겹친다. 위 세 칸도 온전 |
|
||
| 만든 저장소(`Big7479.cs` 7,479줄, 2950 · 7470 줄 양쪽이 다르게 고침)를 MergeHub 로 병합, 네 칸, Ctrl+End | **네 칸 모두** `7462` ~ `7470` 온전. 충돌 2 |
|
||
| 7,479줄 두 파일을 `--merge` 인자로 열기(두 칸) | BASE · LOCAL `7440` ~ `7479` 온전 |
|
||
| 편집기와 아래층이 맞는가 | 7468 줄에서 Ctrl+Shift+→ 로 `public ` 을 고르니(UIA 로 읽은 선택 글도 `public `) 선택 영역이 아래층의 `public` 글자에 딱 겹친다 |
|
||
|
||
빌드 경고 0 · 오류 0. 고친 파일은 XAML 뿐이라 검사 하네스는 영향이 없다.
|
||
|
||
### 확인하다 본 것 두 가지 — 아래 "B-4 확인 중 발견 두 건" 에서 고쳤다
|
||
|
||
- 3분할에서 Ctrl+End 를 누르면 네 칸 모두 몇 줄만 보이고 **그 아래가 빈 채**로 그려진다. 고치기 전 화면에서도 같았다.
|
||
- 저장소 밖 파일을 `--merge --base --local --remote --merged` 로 넘기면 네 칸이 아니라 BASE · LOCAL 두 칸으로 열린다.
|
||
|
||
## B-4 확인 중 발견 두 건 (2026-09-14, 완료)
|
||
|
||
사용자가 "확인하다 본 것도 고쳐 달라" 고 했다.
|
||
|
||
### ① 멀리 건너뛰면 네 칸이 위로 밀려 아래가 빈다
|
||
|
||
**다시 났다.** 만든 저장소 `Big7479.cs`(7,479줄) 네 칸에서 Ctrl+End. 네 칸 모두 `7462` ~ `7470` 아홉 줄만 보이고 아래가 비었다. 휠을 한 칸 굴리면 멀쩡해졌다.
|
||
|
||
Ctrl+End 는 원래 "마지막 차이" 단축키(4차)라 마지막 충돌 줄로 가는 것은 설계대로다. 문제는 빈 화면이다.
|
||
|
||
**원인을 가른 과정.**
|
||
|
||
| 해 본 것 | 본 것 |
|
||
|---|---|
|
||
| UIA 로 MERGED 목록의 행 | 행은 만들어져 있다(7446 ~ 7471). 비어서 빈 것이 아니다 |
|
||
| 1px 굴렸다 되돌리는 코드를 넣음 | **그대로** — 이 길이 아니다(걷어냄) |
|
||
| 목록 칸과 편집기의 화면 좌표 | **MERGED 목록 칸이 편집기보다 279px(약 9줄) 위에 있었다.** 목록 위쪽은 머리글에 가리고 그만큼 아래가 빈다 |
|
||
|
||
표시층(색 · 줄 번호) 목록 넷은 편집기 글자에 맞추려고 **같은 세로 이동값**(`_decorationShift`)을 공유한다. 이 값은 배치가 끝날 때마다 "편집기의 N행 글자 위치" 와 "표시층 N행 위치" 를 견줘 정한다.
|
||
그런데 N 을 **처음 만들어진 목록 행**에서 골랐다. 멀리 건너뛴 직후 그 행은 화면 밖에 남은 행이고, TextBox 는 화면 밖 글자의 좌표를 **어림으로** 준다. 그 어림이 9줄쯤 틀려 네 칸이 한꺼번에 밀렸다. 휠을 굴리면 기준 행이 바뀌어 다시 맞았다.
|
||
|
||
**고친 것** (`Views/HomeView.xaml.cs`)
|
||
|
||
| 자리 | 무엇 |
|
||
|---|---|
|
||
| `AlignDecorationToEditor` | 기준 행을 **목록 칸 안에 위가 안 잘리고 보이는 첫 행**으로 고른다(`FirstVisibleContainer` - 행 판의 자식만 훑는다). 편집기 글자 좌표가 **편집기 화면 밖이면 맞추지 않고** 다음 배치 때 다시 본다 |
|
||
|
||
**확인** (`Big7479.cs` 네 칸)
|
||
|
||
| 동작 | MERGED 목록 칸 y − 편집기 y | 화면 |
|
||
|---|---|---|
|
||
| 처음 연 뒤 | −7px (고치기 전과 같은 값 - 칸 테두리 차) | 정상 |
|
||
| Ctrl+End | **−7px** (고치기 전 −279px) | 네 칸 `7453` ~ `7470` 끝까지 채워짐 |
|
||
| Ctrl+Home(첫 차이 2950) · 끝에서 Alt+↑ | 행 번호와 편집기 줄이 같은 눈높이 | 정상 |
|
||
|
||
### ② `git mergetool` 세 벌이 두 칸으로 열린다
|
||
|
||
**다시 났다.** 7,479줄 파일 셋을 `--merge --base --local --remote --merged` 로 넘기자 BASE · LOCAL 두 칸, "차이 없음".
|
||
|
||
**원인.** 켤 때 인자를 `DiffPair`(BASE, MERGED) 한 쌍으로만 넘겼다. 세 벌을 받는 길이 없었고, 네 칸(3-Way)은 `IsGitMode` 일 때만 켜졌다.
|
||
MERGED 는 git 이 충돌 표시를 넣어 둔 파일이라, 두 칸으로는 상대가 무엇을 바꿨는지 볼 수 없고 충돌 표시를 손으로 지워 가며 풀어야 했다.
|
||
|
||
**고친 것**
|
||
|
||
| 파일 | 무엇 |
|
||
|---|---|
|
||
| `Services/StartupArgs.cs` | `ThreeWayMerge` — `--merge` 에 LOCAL · REMOTE 가 다 있으면 `MergeToolRequest(Base, Local, Remote, Merged)`. BASE 는 없어도 되고(양쪽 새 파일), MERGED 가 없으면 LOCAL 에 쓴다 |
|
||
| `Services/GitWorkspace.cs` | 켤 때 세 벌이면 `RequestMergeTool` — 다른 요청처럼 화면이 생길 때까지 남겨 둔다 |
|
||
| `ViewModels/HomeViewModel.cs` | `OnMergeToolRequested` · `LoadMergeToolAsync` — 파일 비교 모드에서 네 칸으로 연다. MERGED 칸의 처음 글은 충돌 표시가 아니라 **LOCAL**. `IsThreeWay` 가 Git 이 아니면 mergetool 네 칸일 때 참. 파일 두 개 비교를 열면 접는다 |
|
||
| 같은 파일 | [저장] 은 안 고쳤어도 **충돌이 다 풀리면** 켜진다. MERGED 파일에 쓸 때 형식(인코딩 · 줄끝)은 **LOCAL 파일**에서 읽는다 - MERGED 는 충돌 표시 줄이 LF 라 섞여 보인다(A-1 과 같은 까닭) |
|
||
|
||
`ArgsTest` 에 10건을 넣어 **36건 통과**.
|
||
|
||
**확인** (지어낸 세 벌: 40줄 CRLF · BOM, 10줄은 양쪽이 다르게 고침, 30줄은 상대만 고침. MERGED 는 git 모양대로 충돌 표시를 넣고 LF)
|
||
|
||
| 확인 | 결과 |
|
||
|---|---|
|
||
| 인자로 켬 | **네 칸**. BASE `Weld_BASE_1234.cs`, REMOTE `REMOTE · Weld_REMOTE_1234.cs`, 범례 Conflict 1 · Theirs only 1, MERGED 는 내 쪽 글(`= 111; // 내 쪽`), [저장] 꺼짐 |
|
||
| 10줄 Take Theirs → [자동 병합] | "1줄 자동 병합 · 남은 충돌 없음", [저장] 켜짐 |
|
||
| [저장] 뒤 `Weld.cs` | BOM 있음 · CRLF 42 · 홀로 LF 0 · 충돌 표시 없음. 10줄 머지 주석, 11줄 `= 222; // 상대`, 31줄 머지 주석, 32줄 `= 3030; // 상대만 고침` |
|
||
|
||
위쪽 LOCAL(Mine) 칸 제목은 MERGED 칸과 같은 값을 쓴다(Git 모드에서는 둘 다 브랜치 이름). mergetool 에서는 결과 파일 이름 `Weld.cs` 가 보인다. 손대지 않았다.
|
||
|
||
빌드 경고 0 · 오류 0.
|
||
|
||
## B-5. 머지 주석은 소스에 넣고, 병합 뒤에는 그 줄을 무시한다 (2026-09-14, 완료)
|
||
|
||
사용자가 정한 것: 머지 주석은 지금처럼 소스에 넣는다. 비교하고 병합할 때 그 줄을 무시한다.
|
||
|
||
### 생성기에 넣은 경우 (`C1Repo`, D-1 에서도 쓴다)
|
||
|
||
| 이름 | 파일 | 내 쪽 | 상대 쪽 |
|
||
|---|---|---|---|
|
||
| K | `Ui/Views/Unit45.cs` | 13줄 코드 위에 `// [김승준] 2026-09-10` 한 줄 | 같은 자리에 `// [박지훈] 2026-09-11` 한 줄 |
|
||
| L | `Ui/Dialogs/Unit46.cs` | 13줄 끝에 ` // [김승준] 2026-09-10` | 같은 줄 끝에 ` // [박지훈] 2026-09-11` |
|
||
| M | `Common/Util/Unit47.cs` | 9줄 `= 111` 로 고치고 그 위에 내 주석 | `= 222` 로 고치고 그 위에 상대 주석 |
|
||
|
||
이제 병합하면 git 충돌은 23 → **26**개다.
|
||
|
||
### 고치기 전에 다시 났다
|
||
|
||
| 확인 | 결과 |
|
||
|---|---|
|
||
| MergeHub 로 병합 | git 충돌 **26**개, K · L · M 모두 충돌 |
|
||
| 두 칸 비교: 줄 끝 주석 1 · 주석 줄 1 만 다른 두 파일 | **남은 차이 2줄** |
|
||
| 네 칸: K · L · M | 모두 충돌로 물음 |
|
||
|
||
(파일을 바꾸고 5초 뒤에 읽은 값이 한 파일씩 밀려 있었다 - M 이라고 찍은 화면이 K 였다. 그래서 파일별 "예전 모습" 은 아래 "설정을 끄면" 에서 다시 읽은 값을 쓴다.)
|
||
|
||
### 무엇이 원인이었나
|
||
|
||
- 비교 규칙(`CompareRules`)에 머지 주석을 가리는 규칙이 없었다. 줄 필터는 사용자가 정규식을 적어야 했고, 줄 끝에 붙은 주석은 줄을 통째로 걸러야 해서 코드 차이까지 가려졌다.
|
||
- git(libgit2)은 주석 줄도 글자로 본다. 양쪽이 같은 자리에 다른 주석을 달면 그 줄끼리 충돌한다. MergeHub 는 그대로 사람에게 물었다.
|
||
|
||
### 고친 것
|
||
|
||
| 파일 | 무엇 |
|
||
|---|---|
|
||
| `Services/MergeNote.cs` | `TailStart` · `WithoutTail` · `TailOf` — 줄 끝 머지 주석 조각을 찾는다. 주석이 둘 이어 붙어도 첫 주석부터. 주석 기호 앞이 공백이 아니면 글 속 기호로 본다(`"http://..."`). XAML 은 닫는 기호까지 있어야 한다 |
|
||
| `Services/CompareRules.cs` | `IgnoreMergeNotes` · `NotePath`. 켜면 주석만 있는 줄은 걸러지고(줄 필터 켜짐과 따로), 비교 키는 **줄 끝 주석을 뗀 코드**. 주석 줄끼리는 같은 키라 이름 · 날짜가 달라도 짝지어지고, 빈 줄과는 짝지어지지 않는다. 상태바 "무시: 머지 주석" |
|
||
| `Services/NoteRows.cs` (새) | 정렬기가 **주석 줄과 코드 줄을 한 행에 짝지으면** 주석 행 · 코드 행으로 나누고, 떼어 낸 코드 조각은 칸이 안 겹치는 다음 행과 합친다. M 에서 BASE 코드 줄이 양쪽 주석 줄과 한 행이 되어 "BASE 와 다름" 이 하나 더 세지고, 코드 충돌이 BASE 없이 보이던 것을 막는다 |
|
||
| `Services/NoteConflict.cs` (새) | git 이 워킹트리에 남긴 충돌 표시를 읽어, **모든 덩어리가** 주석을 빼면 같은 코드일 때만 푼다. 양쪽 주석을 다 남긴다 - 줄 위 주석은 내 쪽 다음 상대 쪽(같은 줄은 한 번), 줄 끝 주석은 이어 붙인다. `|||||||` (diff3) 모양도 읽는다 |
|
||
| `Services/GitService.cs` · `IGitService.cs` | `ResolveNoteOnlyConflicts` — 양쪽 다 있는 글 파일 충돌에만. 쓰기는 직접 편집 해결과 같은 길(내 쪽 형식 따름, A-1) |
|
||
| `ViewModels/GitViewModel.cs` | [Merge] 뒤 충돌이 있으면 자동으로 풀고, 알림 · 로그에 몇 건 풀었는지와 남은 충돌 수 |
|
||
| `Services/FolderComparer.cs` | 켜면 주석 줄을 빼고 줄 끝 주석을 뗀 글이 같으면 **같음**. 뗀 것이 없거나 줄끝(`\r`)이 다르면 가리지 않는다 |
|
||
| `ViewModels/HomeViewModel.cs` · `FolderViewModel.cs` | 설정을 규칙에 넘기고 파일 경로로 주석 기호를 고른다. 두 칸 · 네 칸 정렬 뒤 `NoteRows` |
|
||
| `MergeHubSettings.cs` | [머지 주석] "비교·병합에서 머지 주석 무시 (사람이 '// [이름] 2026-01-01' 모양으로 쓴 주석도 무시됨)", 기본 켬 |
|
||
|
||
검사: NoteTest 29 → **64**, RulesTest 21 → **36**, FolderTest 51 → **57**. 검사 모음 21개를 모두 새로 빌드해 **865건 통과**.
|
||
|
||
### 확인한 것 (새로 만든 저장소)
|
||
|
||
| 확인 | 결과 |
|
||
|---|---|
|
||
| MergeHub 로 병합 | git 충돌 **24**개. 알림 "충돌 24건 (머지 주석만 다른 2건은 자동으로 풀림)", 로그에 파일별 "머지 주석만 다른 충돌 1곳 자동 해결" |
|
||
| K 스테이징된 글 | 13줄 `// [김승준] 2026-09-10`, 14줄 `// [박지훈] 2026-09-11`, 15줄 코드. BOM · CRLF 342 · 홀로 LF 0 · 충돌 표시 0. 워킹트리 = 인덱스 |
|
||
| L 스테이징된 글 | 13줄 `Log("step 5"); // [김승준] 2026-09-10 // [박지훈] 2026-09-11`. BOM · CRLF 242 · 홀로 LF 0 |
|
||
| M (코드도 다름) | 충돌로 남음(`UU`). 네 칸: 주석 행은 따로 **무시 1**, 코드 행은 BASE `101` · 내 쪽 `111` · 상대 `222` 가 한 행. **남은 충돌 1 · 남은 차이 1** |
|
||
| 두 칸 비교: 줄 끝 주석 1 · 주석 줄 1 | **남은 차이 0줄** |
|
||
|
||
### 설정을 끄면 (설정 파일을 백업해 끄고 확인한 뒤 원래 바이트로 되돌림 - 해시 같음)
|
||
|
||
| 확인 | 결과 |
|
||
|---|---|
|
||
| 병합 | git 충돌 **26**개, K · L · M 모두 남음 |
|
||
| 네 칸 K · L · M | 남은 충돌 1 · 1 · **2** (M 은 주석 줄끼리도 충돌) |
|
||
| 두 칸 비교 | 남은 차이 **2줄** |
|
||
|
||
빌드 경고 0 · 오류 0.
|
||
|
||
### 남긴 것
|
||
|
||
- 자동 풀기는 Git 탭 [Merge] 에만 걸었다. pull · rebase · cherry-pick 이 낸 충돌에는 아직 걸지 않았다.
|
||
- MERGED 칸에서 반영하며 새로 넣은 주석 줄은, 다시 정렬하면(타이핑 멈춤 · 규칙 변경) "무시" 색이 된다. 차이 수에는 원래도 세지 않던 줄이다.
|
||
|
||
## B-6. 충돌 탭에서 하나씩 누를 때마다 오래 기다린다 (2026-09-14, 완료)
|
||
|
||
### 고치기 전에 다시 났다
|
||
|
||
새로 만든 저장소를 병합(충돌 24)하고, 같은 줄 충돌 15개(`Vision/Unit10.cs` ~ `Motion/Unit24.cs`)를 충돌 탭에서 **하나씩 골라 Accept Mine**.
|
||
|
||
| 본 것 | 값 |
|
||
|---|---|
|
||
| 15개 합계 | **22.0초** |
|
||
| 한 개 평균 | **1.47초** (자동 조작의 고르기 0.3초 포함) |
|
||
| 여러 개 고르기 | 안 됨 - 목록이 한 개만 고른다 |
|
||
|
||
### 무엇이 원인이었나
|
||
|
||
- 목록이 `SelectionMode` 기본값(한 개)이었다.
|
||
- Accept 한 번이 작업 하나(`RunAndRefreshAsync`)라, 끝날 때마다 저장소 전체를 다시 읽었다.
|
||
|
||
### 고친 것
|
||
|
||
| 파일 | 무엇 |
|
||
|---|---|
|
||
| `Views/GitView.xaml` | 충돌 목록 `SelectionMode="Extended"`(Ctrl · Shift · Ctrl+A). 단추 옆에 "N개 고름 - Accept 는 고른 파일 모두에 한 번에 적용됩니다". 안내 글에 여러 개 고르기 |
|
||
| `Views/GitView.xaml.cs` | `SelectionChanged` 에서 고른 목록을 넘긴다(SelectedItems 는 바인딩이 안 된다) |
|
||
| `ViewModels/GitViewModel.cs` | `SetSelectedConflicts` · `ConflictTargets`. Accept Mine/Theirs/Both 가 고른 파일 모두를 **작업 하나**로 풀고 다시 읽기는 끝에 한 번. 한 파일이 실패해도 나머지는 풀고, 실패는 로그 · 알림. 로그 "충돌 N개를 Mine 로 한 번에 풀었습니다 (… ms)". Accept Both 확인 창은 "N개 파일 (첫 파일 외 N-1개)" |
|
||
|
||
### 확인한 것 (같은 저장소 · 같은 15개)
|
||
|
||
| 확인 | 결과 |
|
||
|---|---|
|
||
| 15개를 골라 Accept Mine 한 번 | "15개 고름" 표시. git 반영 **0.8초**, 목록이 9개로 다시 설 때까지 **1.2초** (고치기 전 22.0초) |
|
||
| 로그 | "충돌 15개를 Mine 로 한 번에 풀었습니다 (224ms · 저장소 다시 읽기는 끝에 한 번)" |
|
||
| 결과 | 충돌 24 → 9. 15개 모두 내 쪽(HEAD) 글과 같음 |
|
||
|
||
(자동 조작은 목록이 화면에 보이는 항목만 만들어 두는 탓에 처음에는 10개만 골랐다. 목록 항목을 모두 불러오게 고쳐 15개로 다시 쟀다.)
|
||
|
||
빌드 경고 0 · 오류 0.
|
||
|
||
## E-1. C# 코드 스타일 맞춤 (2026-09-14, 추가 요청, 완료)
|
||
|
||
사용자 요청: Visual Studio [텍스트 편집기 > C# > 코드 스타일 > 서식 > 줄 바꿈] 옵션 그림과 같은 스타일(AURORA)이 아니면 diff 가 많이 뜬다. C# 이면 그 스타일로 자동으로 맞춰 달라. AURORA 와 Default 를 고를 수 있게.
|
||
|
||
### 그림에서 읽은 AURORA
|
||
|
||
| 옵션 | 켬 |
|
||
|---|---|
|
||
| 여는 중괄호 새 줄: 형식 · 메서드와 로컬 함수 | ✓ |
|
||
| 여는 중괄호 새 줄: 속성·인덱서·이벤트 · 접근자 · 익명 메서드 · 제어 블록 · 익명 형식 · 개체/컬렉션/배열 초기화 · 람다 | 끔(같은 줄) |
|
||
| "else" · "catch" · "finally" 새 줄 | 끔(`} else {`) |
|
||
| 개체 초기화 멤버 · 익명 형식 멤버 · 쿼리 절 새 줄 | ✓ (손대지 않는다) |
|
||
|
||
Default 는 Visual Studio 기본 - 모두 새 줄.
|
||
|
||
### 만든 것
|
||
|
||
| 파일 | 무엇 |
|
||
|---|---|
|
||
| `Services/CSharpStyle.cs` (새) | 중괄호 · else/catch/finally 의 **줄 위치만** 옮긴다. 주석 · 문자열 · 보간 문자열 · 전처리 줄은 가려서 본다. 중괄호 앞 머리를 거꾸로 읽어 갈래(형식 · 메서드 · 속성 · 접근자 · 익명 메서드 · 제어 · 익명 형식 · 초기화 · 람다)를 정하고 옵션표대로 올리거나 내린다. 앞 줄 끝에 주석이 있으면 올리지 않는다. 판단할 수 없는 블록은 그대로. 바꿀 것이 없으면 받은 글 그대로 |
|
||
| `ViewModels/HomeViewModel.cs` | 비교 규칙 칸에 [C# 스타일 끔 / C# AURORA / C# Default]. 고르면 원본부터 다시 읽어 BASE · LOCAL · REMOTE 를 맞춘다(Git 두 칸 · 네 칸 · 파일 두 개 · mergetool). 편집 칸 글이 바뀌면 저장할 것이 있는 상태, 저장하면 그 스타일로 파일에 쓴다. **스타일만 바뀐 편집은 파일을 바꿀 때 묻지 않는다**(묻게 두면 파일마다 창이 뜬다). 상태바 요약에 스타일 |
|
||
| `Services/FolderComparer.cs` · `FolderViewModel.cs` | 스타일만 다른 C# 파일은 **같음**. 줄끝이 다르면 가리지 않는다 |
|
||
| `MergeHubSettings.cs` | [비교 옵션] "C# 코드 스타일 맞춤", 기본 끔 |
|
||
|
||
검사: StyleTest(새) **78건** - 두 스타일 사이 왕복, 다시 맞춰도 그대로, CRLF, 주석 · 문자열 · 전처리 속 중괄호, 갈래 판단 36가지, 옵션표, 1,600줄대 글 속도. FolderTest 57 → **62**. 검사 모음 22개 **948건 통과**.
|
||
|
||
### 확인한 것
|
||
|
||
만든 시연 재료(`demo\style\Default.cs` 132줄, `Aurora.cs` 는 같은 코드를 AURORA 로 맞추고 값 한 줄 `RetryCount` 3 → 5).
|
||
|
||
| 확인 | 남은 차이 | 저장 |
|
||
|---|---|---|
|
||
| 스타일 끔 | **39줄** | 꺼짐 |
|
||
| C# AURORA | **1줄** (RetryCount) | 꺼짐 - 편집 칸이 이미 AURORA |
|
||
| C# Default | 1줄 | **켜짐** - 편집 칸이 Default 로 바뀜 |
|
||
| 다시 끔 | 39줄 | 꺼짐, **확인 창 안 뜸** |
|
||
|
||
시연 저장소(`D:\MergeHubDemo\repo`)를 MergeHub 로 병합 → `Line/Style.cs`(내 쪽 AURORA + 값 4, 상대 Default + 값 6) 네 칸: 끔 **남은 충돌 1 · 차이 39줄** → AURORA **충돌 1 · 차이 1줄**.
|
||
|
||
설정 화면에 저장된 값은 `"CSharpBraceStyle": 1`. 확인 뒤 설정 파일은 원래 바이트로 되돌렸다.
|
||
|
||
### 남긴 것
|
||
|
||
- 들여쓰기 · 공백 · 그 밖의 서식은 맞추지 않는다. 중괄호 줄 위치만.
|
||
- git 병합(libgit2) 자체는 스타일을 모른다. 스타일만 다른 충돌도 git 은 충돌로 내고, 네 칸에서 맞춰 보여 줄 뿐 자동으로 풀지는 않는다.
|
||
|
||
## E-2. 발표 시연 재료 (2026-09-14, 추가 요청, 완료)
|
||
|
||
`demo\make-demo.ps1` 한 번이면 `demo\plain` · `style` · `mergetool` · `big` 과 병합 저장소 `D:\MergeHubDemo\repo` 를 새로 만든다(`tools\DemoMaker`, 지어낸 코드). 내용과 쓰는 법은 `demo\README.md`.
|
||
|
||
시연 저장소를 git 으로 병합하면 충돌 8개(같은 줄 · 머지 주석 줄 위 · 줄 끝 · 스타일 · 양쪽 새 파일 · 한쪽 삭제 · CP949 · XAML), `Gripper.cs` · `Heater.cs` 는 자동 병합. MergeHub 로 병합하면 머지 주석만 다른 2개를 스스로 풀어 6개.
|
||
|
||
처음 만든 생성기는 CP949 파일을 UTF-8 로 읽어 충돌이 안 났고, `Gripper.cs` 는 맞붙은 줄을 고쳐 충돌이 났다. 둘 다 고쳐 다시 만들었다. `make-demo.ps1` 은 PowerShell 5.1 이 한글을 읽도록 BOM 을 붙였다.
|