만든 저장소(파일 303개, 충돌 22개)로 MergeHub 만으로 병합해서 커밋까지 갔다. 지시서대로 고치지 않고 적기만 했다. 제품 코드는 바꾸지 않았다. 맞게 된 것: 병합 결과가 git 과 똑같다(UU 20 AA 1 UD 1 M 7). 3분할에서 충돌 6개를 6 -> 0 으로 줄였고, 파일 단위 Accept 는 고른 쪽과 바이트까지 같다. 부모 둘인 병합 커밋이 생겼다. 걸린 것 23가지. 심각한 다섯: - 3분할로 푼 파일을 저장하면 CRLF 가 LF 로 바뀐다(커밋에 CR 0개) - Save & Commit 이 실패라는데 파일은 이미 써지고 스테이징됐다 - 충돌 파일을 고르면 상대(Theirs) 칸이 안 나온다 - 상대가 지운 파일을 3분할이 다룰 줄 모른다 - 양쪽 새 파일에 Accept Both 를 누르면 BOM 이 가운데 하나 더 들어간다 기대값은 사본에서 git 으로 먼저 받아 대조했다. 실제 소스는 열지 않았다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
26 KiB
5차 — 실제 소스에 맞추기 (2026-09-11 ~)
지시서: 작업지시/5차.md
A-1. CP949 파일 67개를 잘못 읽으면 한글이 깨진다 — 완료 (2026-09-11)
지시서가 먼저 확인만 하고 틀리면 고치라고 했다. 확인부터 했다.
잰 것 — 실제 저장소 2,091개 전부
TextFile.Read 로 실제 파일을 한 개씩 읽어 보는 도구를 만들어 돌렸다(원본은 읽기만 하고,
저장 왕복은 사본에만 했다).
| 실제 인코딩 | 개수 |
|---|---|
| UTF-8 (BOM) | 1,441 |
| UTF-8 (BOM 없음) | 353 |
| CP949 | 67 |
| ASCII 만 | 230 |
| MergeHub 로 읽었을 때 | 결과 |
|---|---|
| 한글이 깨진 파일 | 0개 |
| 사본에 그대로 저장했을 때 바이트가 달라진 파일 (한글 파일 1,861개) | 0개 |
판정 기준을 이미 만족하고 있었다. 판별 규칙(BOM → 엄격한 UTF-8 → 실패하면 시스템 ANSI)이 실제 소스에서 맞게 돈다. 고칠 것이 없었다.
그래도 고친 것 둘
① 판별 코드가 두 군데에 복사돼 있었다.
TextFile.Detect 와 GitService.DetectEncoding 이 글자 하나까지 같은 코드였다.
TextFile 주석에는 "판별 규칙은 GitService 와 같다 - 같은 파일을 어느 경로로 열든 결과가 같아야 한다"
고 적혀 있었는데, 지키는 방법이 복사뿐이었다. 한쪽만 고치면 조용히 어긋나고,
그러면 같은 파일이 워킹트리에서는 멀쩡한데 커밋에서 열면 깨져 보인다.
어느 쪽이 맞는지 알 방법도 없다. TextFile.DetectEncoding 하나로 모으고 GitService 는 그것을 부른다.
② 같은 인코딩을 두 이름으로 부르고 있었다.
상태바는 KS_C_5601-1987(코드페이지 949 의 공식 이름), 저장 형식 목록은 CP949.
실제 CP949 파일을 열어 보다가 눈에 띄었다 - "지금 이 파일이 그 CP949 라는 건가" 를 되묻게 된다.
FriendlyName 으로 949 는 언제나 CP949 라고 적는다. 두 자리 모두 고쳤다.
확인한 것
| 확인 | 결과 |
|---|---|
실제 CP949 파일(AXA.cs 사본)을 열었을 때 |
//========== 보드 및 모듈 정보 확인 함수 ... 그대로 읽힌다 |
| 깨진 글자(U+FFFD) | 없다 |
| 상태바 인코딩 | CP949 (전에는 KS_C_5601-1987) |
| 고친 뒤 실제 저장소 2,091개 재확인 | 깨짐 0개 · 왕복 어긋남 0개 (고치기 전과 같다) |
검사를 새로 만들었다(EncTest 16건). 실제 저장소가 없어도 도는 것이 요점이다 -
BOM · BOM 없는 UTF-8 · CP949 · ASCII 를 만들어 놓고 읽기 · 왕복 저장 · 형식 바꿔 저장까지 본다.
규칙을 여기 못박아 두면 다음에 누가 고쳐도 바로 걸린다.
전체 587건 통과(하네스 18벌) · 빌드 경고 0 · 오류 0. 실제 저장소는 건드리지 않았다 - 작업 전후 모두 변경 1건(7월부터 있던 것)으로 같다.
A-2. 커밋·체크아웃 뒤에 저장 안 한 편집이 조용히 사라진다 (2026-09-11, 완료)
어디서 사라지고 있었나
4차 A-1 에서 편집이 말없이 사라지던 길 여덟 군데를 막았지만, Git 탭은 그대로 두었다.
RefreshCommits → LoadCommitAsync 는 프로그램이 목록을 다시 세우는 한가운데라
거기서 확인 창을 띄우면 무엇에 답하는 창인지 알 수 없고, 취소해도 목록은 이미 새로 세워진 뒤였다.
그래서 막는 자리를 옮겼다. 목록을 다시 세우는 자리가 아니라 Git 작업을 시작하는 자리에서 묻는다. 그 순간에는 아직 아무것도 바뀌지 않았으므로 취소가 뜻을 가진다.
GitWorkspace.RunAndRefreshAsync 는 Git 탭의 작업 서른 가지가 모두 지나가는 문이다.
여기에 ConfirmDiscardEdits 갈고리를 달고, 파일 비교 화면이 그 갈고리를 채운다.
public async Task<bool> RunAndRefreshAsync(string operation, Action action)
{
if (GitOperationKinds.TouchesFiles(operation) && ConfirmDiscardEdits is { } ask && !ask(operation)) return false;
var ok = await RunAsync(operation, action);
if (ok) await RefreshAsync();
return ok;
}
처음 고른 답이 틀렸다 — 가려서 묻기
처음에는 작업을 가려서 물으려 했다. fetch · 태그 생성 · 전체 스테이징처럼 파일 내용을 안 건드리는 열두 가지는 묻지 않는 쪽이 낫다고 봤다. 쓸데없이 뜨는 창만큼 사람을 지치게 하는 것이 없기 때문이다.
첫 시도는 호출 자리마다 touchesFiles: false 를 붙이는 것이었는데,
줄을 훑어 고치는 스크립트가 엉뚱한 닫는 괄호(예: throw new InvalidOperationException(...))에 붙였다.
되돌리고, 판단을 이름표 하나(GitOperationKinds)로 모았다. 부르는 자리는 손대지 않는다.
그런데 화면에서 해 보니 안 묻고 지나간 뒤에도 편집이 사라졌다. 파일은 그대로인데도 저장소 상태를 다시 읽으면서 본문을 새로 그리기 때문이다.
다시 그리지 않게 막는 쪽으로도 해 봤다.
if (HasChanges && !threeWay && string.Equals(localText, _originalLocalText, StringComparison.Ordinal))
return;
이것도 안 들었다. 다시 읽는 김에 고른 파일까지 바뀌는 경우가 있어 "안 바뀌었다" 를 정확히 가릴 조건을 세울 수가 없었다. 이 코드는 지웠다.
골라잡은 답 — 전부 묻는다
잃지 않는 쪽으로 갔다. TouchesFiles 는 이제 언제나 true 다.
- 확인 창은 고치던 것이 있을 때만 뜬다. 평소에는 한 번도 보이지 않는다.
- 틀리더라도 한 번 더 묻는 것으로 끝나지, 작업을 잃지는 않는다. 가려서 묻다 틀리면 사람이 고친 것을 잃는다. 두 실수의 값이 다르다.
다만 무엇이 안전한지 알아낸 것은 버리지 않았다. 열두 가지 이름을
GitOperationKinds.KnownSafeCandidates 에 남겨 두고, 왜 지금 쓰지 않는지를 클래스 주석에 적었다.
"다시 그려도 편집이 살아남는" 구조가 되면 거기서부터 다시 시작한다.
덤으로 잡은 것 — 4차 A-1 이 새던 자리 셋
전부 묻게 만들고 나니 Git 탭을 그냥 열기만 해도 확인 창이 떴다. 사람이 한 일이 아닌데 4차 A-1 의 확인 창이 불려 나오고 있었다. 셋을 잡았다.
| 새던 자리 | 왜 떴나 | 어떻게 막았나 |
|---|---|---|
OnSelectedNodeChanged |
커밋 목록을 다시 세우면 잠깐 null 이 밀려 들어온다 |
value is not null && !sameCommit 일 때만 묻는다 |
OnRemoteBranchChanged |
RefreshRemoteBranches 가 목록을 갈아 끼우며 값을 바꾼다 |
_syncingBranches 동안은 묻지 않는다 |
OnSelectedFileChanged |
Files.Clear() 가 가드 밖에 있었다 |
_suspendGuard 안으로 옮겼다 |
목록을 다시 세우면 화면이 값을 되밀어 넣는다. 그것은 사람이 고른 것이 아니다. 4차 A-1 을 넣을 때 놓친 것인데, A-2 를 화면에서 시험하다 드러났다.
확인한 것 (실제 화면, 데모 저장소)
| 확인 | 결과 |
|---|---|
| LOCAL 을 고친 줄이 들어간다 | OK |
| 전체 스테이징이 묻는다 | OK |
취소하면 스테이징도 안 한다 (git diff --cached 비었다) |
OK |
| 취소하면 고친 줄이 그대로다 | OK |
| 체크아웃이 묻는다 | OK |
취소하면 브랜치가 그대로다 (master) |
OK |
| 체크아웃을 취소해도 고친 줄이 그대로다 | OK |
| "저장하고 계속" 을 고르면 고친 내용이 파일에 남는다 | OK |
| 고친 것이 없으면 안 묻는다 | OK |
열 가지 전부 통과. 화면 시험은 UI Automation 으로 몰았다(a2screen.ps1).
확인 창이 따로 뜨는 창이 아니라 본 창 안에 덮어 그리는 판이라, 바깥 창 목록을 뒤지면 못 찾는다.
처음 두 번은 이것 때문에 "안 묻는다" 고 잘못 읽었다 — 시험 도구가 틀려서 제품을 의심했다.
검사를 GitOpTest 36건으로 고쳤다. 전체 623건 통과(하네스 19벌) · 빌드 경고 0 · 오류 0.
실제 저장소는 건드리지 않았다 — 데모 저장소(mergehub-demo) 사본으로만 했다.
B-1. XAML 색칠이 사실상 없다 (2026-09-11, 완료)
무엇이 문제였나
실제 소스에 XAML 이 400개 · 42,477줄이다. C# 다음으로 많다.
그런데 지금까지 XAML 은 LanguageProfile.Markup 으로 열렸고, 그것이 아는 것은 주석과 따옴표뿐이었다.
태그 이름 · 속성 이름 · 속성 값이 전부 같은 색이라 사실상 평문이었다.
왜 기존 토크나이저에 못 얹었나
CodeTokenizer 는 낱말을 읽어 예약어 표와 맞춰 보는 방식이다. 마크업은 규칙이 다르다.
같은 Content 가 꺾쇠 안에서는 속성 이름이고 밖에서는 그냥 글자다.
낱말이 무엇이냐가 아니라 어디에 있느냐로 갈린다. 그래서 마크업만 따로 뒀다(MarkupTokenizer).
줄을 넘어가는 상태를 넓혀야 했다
토크나이저는 한 줄씩 따로 훑는다. 앞 줄에서 넘어오는 것은 여태 bool inBlockComment 하나였다.
그런데 XAML 은 여는 태그가 여러 줄에 걸치는 것이 예외가 아니라 보통이다.
시험에 쓴 실제 파일(UCManualRCCOperation.xaml)은 첫 태그 하나가 14줄이다.
<Button Grid.Row="1"
Content="비교"
Click="OnCompare" />
둘째·셋째 줄은 < 로 시작하지 않는다. "지금 꺾쇠 안이다" 를 넘겨주지 않으면
속성 이름이 그냥 글자로 보인다 - XAML 은 이런 줄이 대부분이라 색칠이 없는 것과 같다.
그래서 bool 을 깃발 묶음(CarryOver)으로 넓혔다. None · BlockComment · Tag 셋이다.
부르는 자리 다섯(검사기 · 폴더 비교 · 화면 VM · 화면 컨트롤 · 행 모델)을 모두 바꿨다.
무엇을 무슨 색으로 보는가
| 무엇 | 색 | 까닭 |
|---|---|---|
| 태그 이름 | 예약어 (파랑) | 구조를 이루는 것이라 가장 먼저 눈에 들어와야 한다 |
| 속성 이름 | 메서드 (황토/연노랑) | 태그와 값 사이에서 따로 보여야 한다 |
| 속성 값 | 문자열 (빨강/주황) | 다른 언어의 문자열과 같은 색 - 같은 것이다 |
앞가지(x: · local:) |
타입 예약어 (청록) | XAML 에서 "어느 네임스페이스냐"는 정보다. 이름에 묻으면 x:Name 과 Name 이 같아 보인다 |
< > / : |
구두점 (회색) | 뒤로 물러나야 한다 |
= |
연산자 (회색) | 위와 같다 |
<?xml ?> · <!DOCTYPE> · <![CDATA[ |
지시문 (회색) | 파일마다 한두 줄이고 고칠 일이 없다. 안을 더 쪼개지 않는다 |
| 태그 밖의 본문 | 칠하지 않는다 | 물려받은 색 그대로 두어야 본문이 눈에 띈다 |
색을 새로 만들지 않고 이미 있는 갈래에 얹었다. 팔레트를 늘리면 밝은/어두운 두 벌을 다 손봐야 하고, 같은 화면에서 C# 과 XAML 이 서로 다른 색 체계를 쓰면 오히려 읽기 어렵다.
못 하는 것 (일부러)
속성 값이 줄을 넘어가면(닫는 따옴표가 다음 줄에 있으면) 그 줄까지만 문자열로 보고 다음 줄은 속성 자리로 돌아간다. XAML 에서 드물고, 틀려도 색만 어긋난다. 깃발을 하나 더 늘릴 만한 값이 아니라고 봤다.
하루를 날릴 뻔한 것 - 빈 화면
붙이고 나니 파일 비교 화면이 통째로 비어서 떴다. 단추 하나 없이 하얀 판이었다. 빌드는 경고 0 · 오류 0 이고 로그에도 아무것도 안 남았다.
까닭은 한 줄이었다. 의존 속성의 기본값 타입이 속성 타입과 달랐다.
DependencyProperty.Register(nameof(InBlockComment), typeof(CarryOver), typeof(CodeTextBlock),
new FrameworkPropertyMetadata(false, OnVisualInputChanged)); // ← false 는 bool 이다
타입을 bool 에서 CarryOver 로 바꾸면서 기본값을 같이 안 고쳤다.
이러면 이 컨트롤을 처음 쓰는 순간 정적 초기화에서 터지고, 화면 전체가 안 뜬다.
빌드는 통과한다 - object 를 받는 자리라 컴파일러가 잡을 수 없다.
찾은 방법은 고치기 전 상태로 되돌려 보는 것이었다(git stash). 되돌리니 화면이 나왔고,
그래서 내 변경이 원인인 줄 알았다. 그 다음에 파일을 바이트로 들여다보다 false 를 발견했다.
같은 주의를 코드 옆에 적어 두었다.
확인한 것
실제 저장소의 XAML 사본(UCManualRCCOperation.xaml · 638줄)을 열어 눈으로 봤다.
| 확인 | 결과 |
|---|---|
14줄짜리 여는 태그의 이어지는 줄 속성(d:DesignHeight · x:Name) |
속성 색으로 나온다 |
태그 이름(Setter · ControlTemplate · Border) |
파랑 |
앞가지(view: · x: · d:) |
청록 |
속성 값("White" · "#1A22...") |
빨강 |
주석 줄(<!--<Setter .../>-->) |
초록 한 덩어리 |
| C# 파일 색칠 | 달라지지 않았다 (문서 주석 초록 · public 굵은 자홍 · 타입 청록) |
검사를 새로 만들었다(MarkupTest 65건). 여러 줄 태그 · 주석 이어짐 · 앞가지 · CDATA 와 함께,
C# · C++ · JSON 색칠이 안 망가졌는지도 같이 본다. 토큰이 겹치거나 거꾸로 가지 않는지도 본다.
전체 688건 통과(하네스 20벌) · 빌드 경고 0 · 오류 0. 실제 저장소는 읽기만 했다 - 사본을 떠서 시험했다.
B-2. 폴더 비교가 큰 저장소에서 오래 걸린다 (2026-09-11, 완료)
먼저 잴 것을 만들었다
실제 소스를 더 열지 않기로 했으므로(2026-09-11 결정) 시험용 묶음을 만들었다(FolderPerf/Corpus.cs).
2026-09-10 에 재 둔 실제 저장소의 모양을 흉내 냈다.
| 맞춘 것 | 값 |
|---|---|
| 파일 수 | 4,500개 (양쪽 합쳐 8,922개 · 136MB) |
| 갈래 | C# 과 XAML 을 4:1 |
| 줄 수 분포 | 대부분 30 |
| 폴더 깊이 | 뿌리 5개 아래 2~3단 |
| 같고 다름 | 같음 4,063 · 다름 359(주석만 294) · 한쪽만 78 |
씨앗을 고정했다. 몇 번을 돌려도 똑같은 묶음이 나온다. 실제 폴더는 백신·인덱서가 건드려서 잴 때마다 값이 흔들리는데, 만든 묶음은 그렇지 않다 - 고치기 전후를 견주기에는 오히려 낫다.
재 보니 짚이는 데가 둘이었다
① 파일 하나마다 같은 것을 세 번 물었다.
Directory.GetFiles(dir) // 이름만 준다
File.Exists(leftPath) // 다시 묻는다
new FileInfo(leftPath).Length // 또 묻는다
Directory.GetFiles 는 이름만 준다. 그래서 파일마다 File.Exists 한 번과 FileInfo 한 번을
양쪽에 다시 물었다. 4,500개면 그것만 만 팔천 번이다.
DirectoryInfo.GetFiles() 는 목록을 얻을 때 크기와 수정 시각이 딸려 온다. 한 번으로 끝난다.
| 폴더 훑기만 따로 재면 | |
|---|---|
| 예전 방식 | 974ms |
| DirectoryInfo | 89ms |
② 파일을 여는 동안 아무것도 못 했다.
훑으면서 그 자리에서 파일을 열어 견줬다. 그러면 한 파일을 읽는 동안 다음 폴더로 못 간다. 디스크를 기다리는 시간이 통째로 줄줄이 쌓인다.
어떻게 고쳤나 - 세 걸음으로 나눴다
- 훑기 — 폴더를 돌며 트리를 세운다. 파일은 "아직 안 가린 것"으로 모아 둔다.
- 가리기 — 모아 둔 것을 여러 갈래로 나눠 가린다. 파일을 여는 자리는 여기뿐이다.
- 세기 — 트리 차례대로 폴더 상태를 정하고 개수를 센다.
결과 차례는 트리가 정한다. 몇 갈래로 돌리든 같은 순서로 나온다 - 나눠 돌린 것이 화면에 티가 나면 안 된다. 세는 일만은 반드시 한 갈래로 한다.
파일이 64개 아래면 나누지 않는다. 그 아래는 갈래를 세우는 값이 더 든다.
얼마나 빨라졌나
같은 묶음을 한 프로그램 안에서 나란히 돌려 견줬다. 따로 재면 디스크 캐시 상태가 달라 못 견준다.
| 고치기 전 | 고친 뒤 | ||
|---|---|---|---|
| 전체 비교 | 3,077ms | 901ms | 3.4배 |
| 빠른 비교 | 1,832ms | 392ms | 4.7배 |
| 앱에서 사람이 기다리는 시간 | 2,078ms | 1,086ms | 1.9배 |
결과는 글자 하나까지 같다. 검사가 전후 요약을 맞춰 본다.
앱 쪽이 덜 줄어든 까닭은 비교가 끝난 뒤 화면 트리를 세우는 데 따로 시간이 들기 때문이다. 4,500개짜리 노드를 만드는 일이라 비교와는 다른 문제다. 여기는 손대지 않았다 - 지금도 1초 남짓이고, 건드리면 트리 동작이 바뀔 위험이 있다. 더 줄일 자리로 적어 둔다.
기본 동작은 바꾸지 않았다
빠른 비교(크기·수정 시각만 보기)는 틀릴 수 있어 그대로 사용자가 고르는 것으로 남겼다. 빨라졌다고 기본으로 켜면, 내용이 바뀌었는데 크기와 시각이 우연히 같은 파일을 놓친다.
확인한 것
- 전후 요약이 글자 하나까지 같다(전체 비교 · 빠른 비교 둘 다).
- 검사를 열 개 늘렸다(
FolderTest41 → 51). 파일 200개짜리로 갈래를 나누는 길을 지나게 하고, 열 번을 돌려도 개수와 차례가 똑같은지, 많은 파일에서도 취소가 듣는지를 본다. - 앱을 띄워 실제로 비교해 봤다. 세 번 모두 같은 요약이 나온다.
전체 698건 통과(하네스 20벌) · 빌드 경고 0 · 오류 0. 실제 저장소는 열지 않았다.
C-1. 병합을 한 번 끝까지 해 본다 (2026-09-14, 완료)
지시서대로 고치지 않고 적기만 했다. 제품 코드는 한 줄도 안 바꿨다.
무엇으로 했나
실제 소스는 열지 않기로 했으므로(2026-09-11) 병합할 저장소를 만들었다. 데모는 파일이 다섯 개라 "긴 파일 여러 개에서 충돌이 여기저기 난" 상황을 한 번도 겪어 보지 못했다.
| 모양 | 값 |
|---|---|
| 파일 | 303개 (C# 240 · XAML 60 · 그 밖 3) |
| 큰 파일 | 3,000~7,000줄짜리 5개 |
| 브랜치 | feature/mine 에 서서 feature/theirs 를 MergeHub 로 병합 |
현장에서 나올 법한 충돌을 일부러 넣었다.
| 넣은 것 | 파일 | |
|---|---|---|
| A | 같은 줄을 양쪽이 다르게 고침 | 15 |
| B | 같은 파일, 다른 자리를 고침 (자동 병합되어야 함) | 7 |
| C | 양쪽이 똑같이 고침 (충돌 아님) | 3 |
| D | 큰 파일에 충돌 덩어리 6개씩 흩어 놓음 | 2 |
| E | XAML 여러 줄 태그 안에서 충돌 | 1 |
| F | 내가 고쳤는데 상대가 지움 | 1 |
| G | 양쪽이 같은 이름으로 새 파일을 만듦 | 1 |
| H | CP949 한글 파일에서 충돌 | 1 |
| I | 상대가 줄바꿈을 CRLF 에서 LF 로 바꾼 파일 | 1 |
기대값은 사본에서 git 으로 먼저 받았다. git merge 결과 UU 20 · AA 1 · UD 1 · M 7, 충돌 22개.
끝까지 갔다
- MergeHub 의 병합 결과가 git 과 똑같다 (UU 20 · AA 1 · UD 1 · M 7).
- 큰 파일은 3분할로 열어 [다음 충돌] 과 Take Mine/Theirs 로 6 → 0 까지 줄였다. 고른 대로 들어갔다.
- 나머지는 충돌 탭의 파일 단위 Accept 로 풀었다. 고른 쪽 파일과 바이트까지 같다.
- 22개를 다 풀자 [병합 커밋] 이 켜졌고, 부모 둘인 병합 커밋이 생겼다. 워킹트리 Clean · 충돌 표시 남은 파일 0.
- 상대가 지운 파일은 Accept Theirs 로 지워졌다. 자동 병합 파일은 양쪽 고친 것이 다 들어갔다.
- CP949 파일은 네 칸 모두 한글이 바르게 나오고, XAML 은 네 칸 모두 B-1 색칠이 된다.
판정 기준을 만족했다 — 만든 저장소에서 MergeHub 만으로 충돌을 풀어 커밋까지 끝냈다. 다만 가는 길에 걸린 것이 스물세 가지다(적을 때는 스물일곱이었는데 같은 뿌리를 합쳤다). 아래가 6차의 재료다.
걸린 것 — 파일이나 이력을 망가뜨리는 것
| # | 무엇 | 근거 |
|---|---|---|
| 1 | 3분할로 푼 파일을 저장하면 CRLF 가 LF 로 바뀐다. git 에는 파일 전체가 바뀐 것으로 나온다. 상태바는 CRLF, 저장 형식은 "원본 유지"였다. CP949 파일도 같다 | 커밋에 CR 0개 (바탕은 줄마다 CRLF). 105,025B → 101,810B. 줄끝 무시하면 12줄 차이인데 3,367줄이 바뀐 것으로 나온다 |
| 2 | Save & Commit 이 "실패"라고 하는데 파일은 이미 써지고 스테이징됐다. 남은 충돌이 있으면 커밋에서 막히지만 그 앞 단계는 이미 끝난 뒤다 | 충돌 22 → 21. 화면은 "실패: 아직 해결되지 않은 충돌이 21건" |
| 3 | 충돌 파일을 고르면 상대(Theirs) 칸이 안 나온다. BASE 와 내 것 두 칸뿐이다. 3-Way 콤보에서 브랜치를 골라야 세 칸이 된다. 상대가 무엇을 바꿨는지 모른 채 풀게 된다 | 충돌 갈래가 지역 변수 threeWay 만 켜고 IsThreeWay 는 안 켠다 |
| 4 | 상대가 지운 파일을 열면 REMOTE 칸이 비고, 범례는 "Theirs only 89" 라고 한다. 실은 내 쪽에만 있는 줄이다. 진짜 물음은 "남길까 지울까" 인데 3분할에는 지우는 길도, 상대가 지웠다는 말도 없다 | 충돌 탭에만 "상대쪽 삭제" |
| 5 | 양쪽 새 파일에 Accept Both 를 누르면 바이트째 이어 붙인다. 가운데 BOM 이 하나 더 들어가고 같은 class 가 두 번 나와 빌드가 안 된다 | 결과 파일 안에 EF BB BF 두 번 |
걸린 것 — 헷갈리거나 막히는 것
| # | 무엇 |
|---|---|
| 6 | "실패" 한 Save & Commit 뒤에도 화면은 저장 안 한 편집으로 여긴다. 다른 파일을 고르거나 Git 탭에서 Accept 를 누를 때마다 확인 창이 다시 뜬다 (2번의 다른 얼굴) |
| 7 | 그 확인 창이 엉뚱한 파일 이름을 댄다. 고친 것은 Unit0.cs 인데 "ToBeDeleted.cs 에 저장하지 않은 편집". 새로 고른 파일 이름을 가져다 쓴다 |
| 8 | Git 모드에는 커밋 없이 저장하는 길이 없다. Ctrl+S 도 Save & Commit 이다. 병합 중에 파일 하나씩 풀어 둘 수가 없다 |
| 9 | 병합 뒤 파일 비교 탭의 파일 목록이 비어 있다. 그 탭의 [새로고침] 을 눌러야 나온다. 앱을 새로 켜도 같다 |
| 10 | 충돌 탭 목록을 두 번 눌러도 3분할로 안 넘어간다 |
| 11 | 안내 글이 "홈 탭에서 해결하세요" 라고 한다. 홈 탭은 없다 |
| 12 | 3분할의 MERGED 칸 줄 번호가 네 자리에서 잘린다. 2950~2959 가 전부 "295" |
| 13 | 커밋 메시지가 처음 연 충돌 파일 것으로 굳는다. 앱을 껐다 켜도, 병합 커밋 뒤에도 "Merge: Common/Util/Unit15.cs 충돌 해결" |
| 14 | 병합 커밋 메시지가 브랜치 이름이 아니라 "Merge commit '1742d8f...'" 이고, git 의 주석 줄 "#Conflicts:" 와 파일 목록이 그대로 남는다 |
| 15 | Take Theirs 한 자리마다 머지 주석 "// [김승준] 2026-09-14" 가 소스에 들어간다. 병합 결과에 사람이 안 고른 줄이 생긴다 (설정으로 켠 기능 - 병합에서도 켜져야 하는지 따질 일) |
| 16 | 충돌 탭에서 Accept 할 때마다 저장소 전체를 다시 읽는다. 한 개 약 1.7초. 22개면 기다리는 것만 40초쯤. 여러 개를 골라 한 번에 풀 수 없다 |
| 17 | 상태바 인코딩·줄바꿈이 결과가 아니라 충돌 표시가 들어간 워킹트리 파일을 읽는다. 양쪽 다 CRLF 인 파일도 "혼합" 으로 나온다 |
| 18 | 상태바 "남은 차이 6줄" 은 충돌을 다 정해도 그대로다. "남은 충돌" 은 오른쪽 칸에만 나온다 |
| 19 | 파일 콤보에서 다른 파일을 고르는 한가운데서 확인 창이 뜬다. 고른 파일이 바뀌었는지 아닌지 불분명하다 |
| 20 | 병합 커밋 뒤 파일 비교 탭이 어정쩡하게 남는다. 목록은 비고, 3분할 틀에 병합과 상관없는 파일이 BASE 칸에만 뜬다 |
걸린 것 — 사소한 것
| # | 무엇 |
|---|---|
| 21 | 병합 로그에 "충돌 22건 - 해결이 필요합니다." 가 두 번 찍힌다 |
| 22 | 충돌 탭 목록의 종류 표시가 들쭉날쭉하다. 상대쪽 삭제만 표시되고 양쪽 새 파일은 표시가 없다. 몇 개 풀었는지(0/22)도 없다 |
| 23 | 로그가 CP949 를 "ks_c_5601-1987" 로 적는다. 5차 A-1 에서 모은 이름이 이 자리에는 안 갔다 |
(번호를 6차에서 쓰기 좋게 심각한 것부터 다시 매겼다. 적은 순서와는 다르다.)
6차로 넘길 때 보는 순서
- 1·2·6·8 은 한 뿌리다. 병합 중에 "파일 하나를 풀어서 저장한다" 는 동작이 없고, 커밋까지 한 번에 묶여 있다. 저장만 하는 길을 따로 두고, 그 길이 저장 형식(인코딩·줄바꿈)을 따르게 하면 네 가지가 같이 풀린다.
- 3·9·10·11 도 한 뿌리다. 충돌 목록에서 3분할 화면까지 가는 길이 끊겨 있다. 충돌 파일을 고르면 세 칸이 바로 뜨고, 충돌 탭에서 한 번에 넘어가게 하면 된다.
- 4·5 는 파일 단위 충돌(지움 · 양쪽 새 파일)을 줄 단위 화면이 다룰 줄 모르는 것이다.
시험 도구 쪽 사고 (제품 결함 아님)
자동 조작이 콤보 항목을 UIA 로 고르는 중에 확인 창이 떴고, 그 호출이 돌아오지 않은 채 확인 창이 입력을 안 받게 굳었다. 앱을 끄고 새로 띄웠다. 그 사이 파일에는 아무것도 안 써졌다 (크기 · 스테이징 · 남은 충돌 수로 확인). 사람이 마우스로 고를 때는 이렇게 굳지 않는다. 다만 19번은 사람에게도 헷갈리는 자리라 남겼다.
다시 해 보려면
시험 저장소는 C1Repo 로 만든다(씨앗 고정 · 몇 번을 돌려도 같다). 실제 소스를 열지 않는다.
만든 소스에는 진짜 코드의 지저분함이 없다 - 여기서 안 걸렸다고 실제에서 안 걸린다는 뜻은 아니다.
코드는 바꾸지 않았으므로 검사는 그대로 698건 통과(하네스 20벌) · 빌드 경고 0 · 오류 0.