만든 저장소(파일 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>
훑기 - 가리기 - 세기로 나눴다. 훑으면서 그 자리에서 파일을 열어
견주던 것을, 트리를 먼저 세우고 가리는 일만 여러 갈래로 돌린다.
결과 차례는 트리가 정하므로 몇 갈래로 돌리든 같은 순서로 나온다.
세는 일만은 한 갈래로 한다. 파일이 64개 아래면 나누지 않는다 -
그 아래는 갈래를 세우는 값이 더 든다.
폴더를 훑는 방식도 바꿨다. Directory.GetFiles 는 이름만 주므로
파일마다 File.Exists 와 FileInfo 를 양쪽에 다시 물었다. 4,500개면
만 팔천 번이다. DirectoryInfo.GetFiles 는 크기와 수정 시각이
딸려 오므로 한 번으로 끝난다. 974ms 가 89ms 가 됐다.
재는 데 쓴 묶음은 우리가 만들었다(실제 소스를 열지 않는다).
2026-09-10 에 재 둔 모양을 흉내 냈고 씨앗을 고정해서 몇 번을
돌려도 똑같다.
전체 비교 3,077ms -> 901ms (3.4배)
빠른 비교 1,832ms -> 392ms (4.7배)
앱에서 기다리는 시간 2,078ms -> 1,086ms (1.9배)
결과는 글자 하나까지 같다. 앱 쪽이 덜 줄어든 것은 비교가 끝난 뒤
화면 트리를 세우는 데 따로 드는 시간 때문이고, 거기는 손대지 않았다.
빠른 비교를 기본으로 켜지도 않았다 - 틀릴 수 있는 방법이다.
FolderTest 41 -> 51 (갈래 나누는 길, 열 번 돌려 같은 결과, 취소).
전체 698건 통과(하네스 20벌). 경고 0 · 오류 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
실제 소스에 XAML 이 400개 42,477줄인데 여태 주석과 따옴표만 알아봐서
사실상 평문이었다. 마크업 전용 토크나이저를 따로 뒀다.
기존 토크나이저에 못 얹은 까닭은 규칙이 다르기 때문이다. 그쪽은 낱말을
읽어 예약어 표와 맞춰 보는데, 마크업은 같은 Content 가 꺾쇠 안에서는
속성 이름이고 밖에서는 그냥 글자다. 낱말이 무엇이냐가 아니라 어디에
있느냐로 갈린다.
줄 넘김 상태를 bool 하나에서 깃발 묶음(CarryOver)으로 넓혔다. XAML 은
여는 태그가 여러 줄에 걸치는 것이 보통이라 - 시험에 쓴 실제 파일은 첫
태그 하나가 14줄이다 - "지금 꺾쇠 안이다"를 안 넘기면 이어지는 줄의
속성이 그냥 글자로 보인다. 부르는 자리 다섯을 모두 바꿨다.
색은 새로 만들지 않고 이미 있는 갈래에 얹었다. 태그는 예약어 색,
속성 이름은 메서드 색, 값은 문자열 색, 앞가지는 타입 색이다. 태그
밖의 본문은 일부러 안 칠한다.
붙이는 중에 파일 비교 화면이 통째로 비어서 뜨는 일이 있었다. 의존
속성 타입을 bool 에서 CarryOver 로 바꾸면서 기본값 false 를 같이 안
고쳤다. 빌드도 통과하고 로그에도 안 남는다. 같은 주의를 코드 옆에
적어 뒀다.
실제 저장소 XAML 사본(638줄)으로 화면 확인. C# 색칠은 그대로.
MarkupTest 65건. 전체 688건 통과(하네스 20벌). 경고 0 · 오류 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
커밋·체크아웃 뒤에 저장 안 한 편집이 말없이 사라지던 것을 막았다.
막는 자리를 옮겼다. 목록을 다시 세우는 자리(RefreshCommits →
LoadCommitAsync)가 아니라 Git 작업이 시작되는 문(GitWorkspace.
RunAndRefreshAsync)에서 묻는다. 그 순간에는 아직 아무것도 바뀌지
않았으므로 취소가 뜻을 가진다.
작업을 가려서 묻는 쪽은 접었다. fetch·태그·스테이징처럼 파일을 안
건드리는 열두 가지는 안 물으려 했는데, 해 보니 그런 작업 뒤에도
편집이 사라졌다. 파일이 그대로여도 저장소를 다시 읽으면서 본문을
새로 그리기 때문이다. 다시 그리지 않게 막는 쪽도 해 봤지만 고른
파일까지 바뀌는 경우가 있어 조건을 세울 수 없었다. 잃지 않는 쪽으로
갔다 - 확인 창은 고치던 것이 있을 때만 뜨고, 틀려도 한 번 더 묻는
것으로 끝난다. 알아낸 열두 이름은 KnownSafeCandidates 에 남겼다.
덤으로 4차 A-1 이 새던 자리 셋을 막았다. 목록을 다시 세우면 화면이
값을 되밀어 넣는데, 그것을 사람이 고른 것으로 보고 확인 창을 띄우고
있었다. OnSelectedNodeChanged(null·같은 커밋), OnRemoteBranchChanged
(_syncingBranches), Files.Clear()(_suspendGuard 안으로).
화면 시험 10건 통과(데모 저장소). GitOpTest 36건. 전체 623건 통과
(하네스 19벌). 빌드 경고 0 · 오류 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
실제 저장소 2091개를 MergeHub 로 읽어 봤다. CP949 67개 포함해 깨짐 0, 저장 왕복 어긋남 0.
판별 코드가 TextFile 과 GitService 두 군데에 복사돼 있던 것을 한 곳으로 모았다.
상태바가 KS_C_5601-1987, 목록이 CP949 로 같은 것을 다르게 부르던 것도 맞췄다.