ksj09406 df9202c6d5 11차 F-12: C# 코드 스타일 맞춤 기본값을 AURORA 로
사용자 결정(2026-09-18): 제품 기본값 AURORA · C# 파일만 · 저장 동작은 지금 그대로.

- MergeHubSettings.CSharpBraceStyle 기본값 None → Aurora (한 줄, 까닭 주석)
- C/C++ · XAML 은 고칠 것 없음 - CSharpStyle.Applies 가 확장자 .cs 만 본다
- 하네스 StyleTest +12 (78 → 90): 기본값 · 확장자별 적용 여부. 먼저 넣어 기본값 검사만 실패하는 것을 보고 시작
- 설정 파일 없는 첫 실행: 상태바 'C# AURORA' · C# 짝 차이 없음 · C++ 짝 남은 차이 4줄 · 새로 만든 설정에 1
- 웹 문서 매뉴얼 · 설정 표 · 시연 대본의 '기본은 끔' 을 고침
- 22벌 1,159건 통과 · 빌드 경고 0 오류 0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 14:02:35 +09:00

305 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 작업 지시 — MergeHub 11차 (변경분 추출을 믿고 들고 가게 · 베타 전에 깨지는 것 · 남은 기획 범위)
대상: `D:\Sorce\MergeTool\MergeHub` (플러그인) · `D:\Sorce\MergeTool\UbisamPlatform` (셸)
전제: 1~10차 지시서는 모두 끝났다([../진행상황.md](../진행상황.md)).
**이번 지시서가 나온 곳** — 10차 E-1 에서 다섯 길(병합 · pull · rebase · cherry-pick · revert)을 다시 끝까지 했고 **새로 걸린 것은 없었다**.
그래서 11차는 "고칠 버그" 가 아니라 **기획안 v1.2 에서 아직 "일부" 인 것**과 **사내 베타(기획 4단계, 09.29 ~ 10.02) 전에 막아야 할 것**에서 나왔다.
근거는 코드를 읽고 적은 것이다 — **실제로 해 보기 전의 짐작**이므로 항목마다 먼저 다시 내고 시작한다.
| 어디서 | 무엇을 봤나 | 근거 |
|---|---|---|
| 변경분 추출 (기획안 "일부") | 지운 파일은 개수만 알리고 목록이 어디에도 안 남는다(코드 주석은 "목록에만 남긴다") · 어느 범위를 뽑았는지 폴더에 안 남는다 · 이미 파일이 있는 폴더에 묻지 않고 덮어쓰고 옛 파일이 섞인다 · ZIP 없음 | `GitService.ExportChangedFiles` · `GitViewModel.ExportChangesAsync` |
| 배포 폴더 | 매뉴얼이 가리키는 `bin\Release\net48` 의 `MergeHub.dll` 이 **09-07 14:11**, 셸이 09-07 13:41 — 10일 동안 Debug 로만 빌드했다. 폴더 크기 96MB(LiveCharts · Skia · OpenTK 등 플랫폼 런타임 포함) | 파일 시각 · `README.txt` |
| 설정 · 로그 위치 | 플랫폼이 `D:\UbisamConfig` 를 고정으로 쓴다. D 드라이브가 없는 PC 에서 어떻게 되는지 모른다 | `UbisamAppBuilder.ConfigRoot` · `LogService` · `LockConfirmDialog` |
| 커밋 메시지 수정 (기획안 "일부") | 마지막 커밋만 [커밋 수정](amend). 기획안은 "커밋 우클릭 → 이름 변경". **있는 [커밋 수정] 에도 걸리는 것이 보인다** — [마지막 메시지] 가 HEAD 가 아니라 이력 목록 맨 위 메시지를 가져옴([모든 브랜치] 면 다른 브랜치 커밋) · 스테이징해 둔 파일이 말없이 같이 들어감 · 원래 작성자 · 작성 시각을 지금 사람으로 바꿈(git 은 남김) · 병합 · rebase 중에도 켜짐 · 확인 창 · 되돌릴 옛 해시 없음 · 하네스에 amend 검사 0건 | `GitViewModel.AmendAsync` · `LoadLastMessage` · `GitService.Commit` |
| 10차 E-1 시험 도구 | 줄끝 검사 스크립트가 부모 하나인 커밋에서 오류를 찍음 · 요약 읽는 함수가 "차이 없음" 을 못 읽음 | [../history/2026-09-17-10차.md](../history/2026-09-17-10차.md) E-1 "남긴 것" |
---
## 공통 규칙
- **A → B → C → D → E → F 순서로 진행한다.** A 는 사용자가 물어 본 것이라 먼저다. B 는 베타 날짜가 있어 C 보다 앞이다. F(2026-09-18 추가 요청)는 앞의 것을 끝낸 뒤 하나씩 올린다 — 급한 것이 있으면 사용자가 순서를 바꿔 말한다.
- **항목 하나를 끝낼 때마다** 빌드하고 실제로 실행해 화면을 확인한 뒤 보고한다.
- **항목 하나가 끝나면 기록을 세 군데에 남기고 로컬 커밋한다**(4차 D-2 규칙).
1. **상세**는 `doc/history/YYYY-MM-DD-11차.md` — 원인 · 판단 · 확인한 결과
2. [../진행상황.md](../진행상황.md) 은 **대기 → 완료로 상태만**
3. [../history/changelog.md](../history/changelog.md) 에 한 줄 — 날짜 · 무엇을 왜 · 커밋
- **푸쉬하지 않는다.**
- **실제 소스를 열지 않는다**(2026-09-11 결정). 시험 재료는 `C1Repo`(병합 저장소) · `FolderPerf/Corpus.cs`(폴더 묶음) · 직접 만든 파일로 만든다.
고치기 전에 문제가 **다시 나는 것부터 보이고**, 고친 뒤 안 나는 것을 보인다. 다시 나지 않으면 **짐작으로 고치지 않고** 되풀이한 횟수와 짚은 곳을 적어 보고한다.
- 기존 검사 하네스(22벌 1,147건)는 계속 통과해야 한다. 로직을 건드리면 **테스트를 먼저 늘린다.**
- 빌드 경고 0 · 오류 0 을 유지한다. 앱이 실행 중이면 `UbisamBase.Shell` 을 먼저 종료한다.
- 사용자 설정 파일(`D:\UbisamConfig\Settings\MergeHubSettings.json`)은 시험 전에 백업하고, 끝나면 **바이트 그대로** 되돌린다.
창 기억(`ShellWindowPlacement.json`)은 시험 뒤 "기억 없음" 으로 둔다.
- **자동 조작으로 확인할 때는 사람이 갈 수 있는 길만 쓴다.** 이름 창은 [저장], 접힌 영역은 [펴기], 이력 줄은 마우스로(10차 B-1 은 누르기 전에 묻는 길).
- **문서 글을 PowerShell 로 넣을 때는 작은따옴표 문자열만 쓴다**(8차). **PowerShell 변수 이름은 대소문자를 가리지 않는다** — 매개변수와 겹치는 이름을 쓰지 않는다(9차 A-2).
- **앱 안에 임시 측정 코드를 넣었다면** 커밋하지 않고 잰 뒤 되돌리고 다시 빌드한다(9차 B-2).
- **레지스트리 HKLM · 셸 확장 · 보안(백신) 설정은 건드리지 않는다.** 시스템 설정을 바꿔야만 재볼 수 있는 것은 재지 않고 적는다.
- 플랫폼을 고치면 다른 소비 프로그램에 미치는 영향을 함께 적고 `UbisamPlatform/doc/history/changelog.md` 에 남긴다. **배포는 사용자 확인 뒤.**
---
## A. 변경분 추출을 믿고 들고 가게
기획안: "장비에 소스를 통째로 넣지 못하는 현장에서 **바뀐 파일만** 들고 간다." 들고 간 폴더만 보고 **무엇이 바뀌고 무엇을 지워야 하는지** 알 수 있어야 쓸 수 있다.
### A-1. 먼저 뽑아 보고 적는다 (고치지 않음)
- `C1Repo` 로 저장소를 새로 만들고 Git 탭 → 이력에서 사람 순서로 뽑는다. 경우마다 뽑은 폴더를 `git diff --name-status` · `git show <커밋>:<경로>` 바이트와 견준다.
| # | 경우 | 볼 것 |
|---|---|---|
| 1 | 커밋 하나(시작점 없음) | 뽑은 파일 수 = `git diff --name-status 부모 커밋` 의 A · M 수, 바이트 같음 |
| 2 | [여기부터] 로 범위 | 범위 안 중간 커밋에서만 바뀌고 되돌려진 파일이 들어가는지 |
| 3 | 지운 파일이 있는 범위 | 알림 "삭제되어 건너뜀 N개" 뒤 **어느 파일인지 알 길이 있는지** |
| 4 | 이름 바뀐 파일 | 새 경로로 뽑히는지 · 옛 경로를 알 길이 있는지 |
| 5 | 병합 커밋 하나 | 첫 부모와만 견주는지(상대 브랜치에서 들어온 파일이 모두 나오는지) |
| 6 | 시작점이 고른 커밋보다 **뒤** | 무엇이 뽑히는지 · 경고가 있는지 |
| 7 | 이진 파일 · 한글 경로 · 깊은 폴더 | 바이트 · 경로 그대로인지 |
| 8 | 이미 파일이 있는 폴더에 다시 뽑기 | 덮어쓰기 · 옛 파일이 남아 섞이는지 · 묻는지 |
| 9 | 뽑는 중 대상 파일이 읽기 전용 · 열려 있음 | 오류 문구 · 반쯤 뽑힌 채 남는지 |
- **판정 기준**: 9가지 경우의 결과를 값(파일 수 · 해시 · 알림 문구)으로 표에 적는다. 걸린 것은 A-2 ~ A-4 에 붙이거나 새로 적는다.
### A-2. 뽑은 폴더에 "무엇을 뽑았는지" 를 남긴다 — **먼저 정한다**
- A-1 의 3 · 4 · 6 결과를 보인 뒤 **사용자에게 형식을 정한다.**
1. 뽑은 폴더 안에 안내 파일 하나(예: `_변경분.txt`) — 범위(커밋 해시 · 제목 · 날짜) · 뽑은 사람 · 뽑은 파일 목록(추가 / 수정 / 이름 바뀜 옛 경로) · **지워야 할 파일 목록**
2. 폴더 밖(옆)에 같은 내용 — 장비에 폴더째 넣을 때 안내 파일이 섞이지 않게
3. 파일은 만들지 않고 화면(Git 탭 로그 · 결과 창)에만
- 결과 내보내기(3차)와 보고서의 머리글(만든 사람 · 만든 때, 4차 A-2)과 같은 꼴을 쓴다.
- **판정 기준**: 정한 대로 뽑은 결과만 보고 지워야 할 파일 · 이름 바뀐 옛 경로 · 범위를 알 수 있다. A-1 의 1 · 2 · 7 바이트 결과가 고치기 전과 같다. 하네스로 "지운 파일 · 이름 바뀜 · 병합 커밋" 목록을 만든다.
### A-3. 이미 파일이 있는 폴더에 뽑을 때 — **먼저 정한다**
- A-1 의 8 결과를 보인 뒤 정한다.
1. 비어 있지 않으면 **묻는다**(덮어쓰기 · 취소)
2. 고른 폴더 아래에 **새 폴더를 만든다**(예: `변경분_ace42e3..f310f49_2026-09-17`)
3. 그대로 둔다(한계로 매뉴얼에 적음)
- **판정 기준**: 정한 대로 옛 파일과 새 파일이 말없이 섞이지 않는다. [취소] 뒤 파일이 하나도 바뀌지 않았다.
### A-4. ZIP 으로 뽑기 — **할지 먼저 정한다**
- 폴더로만 뽑는다(기획안 "폴더·ZIP"). 필요한지 사용자에게 묻는다. 한다면 A-2 안내 파일이 ZIP 안에 들어가고, 풀었을 때 바이트 · 경로가 폴더로 뽑은 것과 같아야 한다.
- **판정 기준**: 안 하기로 하면 기획안 · 웹 문서에 "폴더로만" 을 까닭과 함께 적는다. 하면 폴더 결과와 ZIP 을 푼 결과의 해시 목록이 같다.
---
## B. 사내 베타 전에 깨지는 것
기획 4단계(09.29 ~ 10.02) 에 다른 사람 PC 에서 처음 켠다. 지금까지는 **이 PC · 이 경로 · 이 설정**에서만 켰다.
### B-1. 배포 폴더가 10일 전 것이다 — 다시 만들고 무엇이 들어가야 하는지 정한다
- 매뉴얼("개발 PC 에서는 `src\MergeHub\bin\Release\net48\UbisamBase.Shell.exe`")이 가리키는 Release 출력의 `MergeHub.dll` 이 **2026-09-07** 이다. 먼저 **지금 Release 로 켜면 무엇이 다른지**(예: 6차 이후 기능이 없는지)를 한 번 확인해 적는다.
- Release 로 다시 빌드하고, 배포 묶음에 **꼭 필요한 파일**을 가린다: 셸 · 플러그인 · LibGit2Sharp 네이티브(`lib\win32` · `win-x64`) · CommunityToolkit · 설정 기본값 · `Lang`. LiveCharts · Skia · OpenTK 가 MergeHub 에 필요한지는 **지워 보고 켜서** 가린다(플랫폼이 쓰는지 같이 본다).
- 빈 폴더(예: `%TEMP%\mergehub-beta`)에 묶음만 복사해 켜고, 다섯 탭 · 병합 한 길(C1Repo)을 끝까지 한다.
- **판정 기준**: 배포 묶음 목록(파일 · 크기 · 왜 필요한지)과 총 크기를 적는다. 빈 폴더에서 켜 병합 커밋까지 된다. 매뉴얼의 실행 위치 문장이 사실과 같다. 플랫폼 배포 폴더를 바꿔야 하면 사용자 확인 뒤.
### B-2. D 드라이브가 없는 PC · 설정 파일이 없는 첫 실행
- 플랫폼이 설정 · 로그 · 화면 잠금 기록을 `D:\UbisamConfig` 에 고정으로 쓴다. **D 드라이브를 없애는 시험은 하지 않는다**(디스크 설정). 대신 코드로 "D:\ 가 없을 때 어느 줄에서 무엇이 되는지" 를 따라가 적고, 설정 파일 · 폴더가 없는 첫 실행은 **백업 뒤 폴더 이름을 잠시 바꿔** 재 본다(끝나면 바이트 그대로 되돌림).
- 첫 실행에서 볼 것: 이름 창 · 기본 설정 값 · 창 위치 · 로그 폴더 생성 · 오류 창 여부.
- 방향: D 드라이브가 없을 때 켜지지 않는다면 **플랫폼 문제**다. 고치는 길(예: 없으면 사용자 폴더로)을 다른 소비 프로그램 영향과 함께 **사용자에게 보이고 정한다.**
- **판정 기준**: 첫 실행이 오류 창 없이 켜지고 기본값이 매뉴얼 설정 표와 같다. D 드라이브 없는 경우의 결과를 코드 근거로 적었다(고쳤다면 플랫폼 changelog).
---
## C. 커밋 메시지 수정 — 기획 범위에서 "일부" 로 남은 것
기획안 1차 범위 "커밋 메시지 수정 — 커밋 우클릭 → 이름 변경. 명령어와 옵션을 외울 필요 없음". 기획안 v1.2 · 웹 문서에는 "마지막 커밋만 [커밋 수정](amend)" 으로 **일부**라고 적었다.
그런데 코드를 다시 읽으니 **있는 [커밋 수정] 도 믿고 쓰기 어려운 곳**이 있다. 새 기능(C-2)을 정하기 전에 있는 길부터 바로잡는다.
지금 모양 (Git 탭 → 변경 · 커밋, 아래 줄 `커밋 메시지 [칸] [마지막 메시지] [커밋] [커밋 수정]`):
- [커밋 수정] = `GitService.Commit(message, amend: true)` → LibGit2Sharp `Repo.Commit(message, 지금 사람, 지금 사람, AmendPreviousCommit)`. 켜지는 조건은 **저장소가 열려 있고 메시지 칸이 비어 있지 않음** 뿐이다.
- [마지막 메시지] = `_workspace.Commits[0].Message` — **이력 목록의 맨 위** 커밋 메시지.
- 이력 탭에서 고른 커밋과는 상관없이 늘 HEAD 를 고친다. 우클릭 메뉴는 없다.
- 하네스(`GitOpTest` 등)에 amend 를 재는 검사가 없다.
### C-1. 있는 [커밋 수정] 을 다시 내 보고 바로잡는다 (원인부터)
- `C1Repo` 로 저장소를 새로 만들어 사람 순서로 한다. 경우마다 고치기 **전** 결과를 `git log -1 --format="%H %an %ae %ad %cn %s"` · `git show --stat` · `git status` 로 적는다.
| # | 짚이는 것 | 다시 내는 순서 | git 은 어떻게 하나 |
|---|---|---|---|
| 1 | **[마지막 메시지] 가 다른 커밋 메시지를 가져온다** | 이력 [모든 브랜치] 켬(9차 A-2: 맨 위가 다른 브랜치 커밋) → 변경 · 커밋 [마지막 메시지] → 칸의 글이 HEAD 메시지인지 → 그대로 [커밋 수정] 하면 HEAD 가 **다른 커밋의 메시지**로 바뀌는지 | `git commit --amend` 는 HEAD 메시지를 편집기에 띄운다 |
| 2 | **스테이징해 둔 파일이 같이 들어간다** | 파일 하나 고쳐 [스테이징] → 메시지만 고칠 생각으로 [마지막 메시지] → [커밋 수정] → 그 파일이 HEAD 에 들어갔는지 · 알림에 그런 말이 있는지 | 같이 들어간다(`--only` 없이). 다만 편집기 화면에 바뀔 파일 목록이 보인다 |
| 3 | **원래 작성자 · 작성 시각이 바뀐다** | 8차 C-2 처럼 작성자 `박지훈` 인 커밋을 cherry-pick 해 HEAD 로 만든 뒤 [커밋 수정] → `%an` · `%ad` | 작성자 · 작성 시각은 **그대로**, 커밋한 사람 · 시각만 바뀐다(`--reset-author` 를 줄 때만 바뀜) |
| 4 | **작업 중에도 켜진다** | 병합 충돌 중 · rebase 중 · cherry-pick / revert 중에 메시지 칸에 글을 넣고 [커밋 수정] 이 켜지는지 · 누르면 무엇이 되는지(부모 수 · `MERGE_HEAD` · `rebase-merge` 가 남는지) | 병합 중에는 막는다("You are in the middle of a merge -- cannot amend") · rebase 중에는 멈춘 커밋을 고치는 뜻이 된다 · cherry-pick / revert 중은 재어 적는다 |
| 5 | **병합 커밋을 고치면** | 병합 커밋이 HEAD 일 때 [커밋 수정] → 부모 둘이 남는지 · 8차 F-1 본문(충돌 해결 내역)이 사라지는지 | 부모는 그대로 |
| 6 | **이미 올린 커밋** | pull 길(10차 E-1)의 원격을 흉내 낸 bare 저장소로 push 한 뒤 [커밋 수정] → 막거나 알리는지 · 그 뒤 Push 가 어떻게 되는지 | 막지 않는다. 다음 push 가 거부된다 |
| 7 | **되돌리는 길** | 수정 뒤 알림 · 로그에 **고치기 전 해시**가 남는지 | reflog 에 남는다 |
| 8 | **메시지 칸 · 첫 줄** | 여러 줄 메시지 · 빈 줄 · `#` 로 시작하는 줄이 그대로 들어가는지(6차 B-3 은 병합 메시지의 `#` 줄을 뺐다) | 편집기를 거치면 `#` 줄이 빠진다 |
- 원인이 확인된 것만 고친다. 고칠 방향(사용자에게 보이고 정할 것은 **정함** 으로 표시):
- 1 — [마지막 메시지] 는 **HEAD 커밋**의 메시지를 읽는다(목록 순서와 무관).
- 2 — **정함**: [커밋 수정] 을 누를 때 스테이징된 파일이 있으면 "파일 N개도 이 커밋에 들어갑니다" 를 묻는다 / 메시지만 고치는 길을 따로 둔다 / 그대로 두고 매뉴얼에 적는다.
- 3 — 작성자 · 작성 시각은 원래 커밋 것을 쓴다(git 과 같게). 커밋한 사람만 지금 사람.
- 4 — merge · rebase · cherry-pick · revert 진행 중에는 [커밋 수정] 을 끈다(단추 설명에 까닭).
- 5 — 병합 커밋 본문이 사라진다면 [마지막 메시지] 가 본문까지 가져오는지 보고 적는다.
- 6 — **정함**: 원격 추적 브랜치에 이미 있는 커밋이면 묻는다 / 막는다 / 매뉴얼에만 적는다.
- 7 — 알림 · 로그에 "커밋 수정 완료 (옛 abc1234 → 새 def5678)".
- **판정 기준**: 여덟 가지 모두 고치기 전 · 뒤 결과를 값으로 적는다. 고친 것은 하네스에 먼저 넣는다 — HEAD 메시지 읽기([모든 브랜치] 목록에서도) · 작성자 · 작성 시각 보존 · 작업 중 꺼짐 · 병합 커밋 부모 둘 유지. 파일 비교 탭에 저장 안 한 편집이 있을 때 먼저 묻는 것(5차 A-2)이 그대로다.
### C-2. 마지막 커밋이 아닌 커밋의 메시지 수정 — **할지 먼저 정한다**
- C-1 을 끝낸 뒤 정한다. 기획안은 "커밋 우클릭 → 이름 변경" 이고, 이력 탭에서 고른 커밋의 메시지를 바꾸는 일이다.
- HEAD 가 아니면 **그 뒤 커밋을 모두 다시 쓴다**(해시가 모두 바뀜 · `git rebase -i` 의 reword). 사용자에게 보일 것:
- 되는 범위: 현재 브랜치의 조상 커밋 · 원격에 없는 커밋 · 그 사이에 병합 커밋이 없을 때
- 막을 것: 원격에 올라간 커밋 · 사이에 병합 커밋 · 작업 중(merge · rebase · cherry-pick · revert) · 워킹트리에 커밋 안 한 변경 · 파일 비교 탭에 저장 안 한 편집 · 다른 브랜치에만 있는 커밋([모든 브랜치])
- 지켜야 할 것: 다시 쓴 커밋들의 트리 · 작성자 · 작성 시각 그대로, 메시지는 고른 커밋만
- 되돌리는 길: 바꾸기 전 HEAD 해시를 알림 · 로그에 남긴다
- 화면: 이력 줄 우클릭 메뉴 또는 이력 탭 단추 — 10차 B-1 대로 두 탭이 같은 커밋을 보는지
- **판정 기준**: 안 하기로 하면 기획안 · 웹 문서에 "마지막 커밋만" 을 까닭과 함께 적고 닫는다.
하면 하네스로 "세 커밋 앞 커밋 메시지 바꾸기 → 그 뒤 커밋 수 같음 · 커밋마다 트리 해시 · 작성자 · 작성 시각 같음 · 고른 커밋 메시지만 바뀜" 을 보이고, 막을 경우 여섯 가지가 실제로 막힌다. 바꾼 뒤 두 탭의 고른 커밋이 새 해시를 따라간다(9차 A-1 · A-2 규칙).
---
## D. 시험 도구 정리 (제품 아님)
### D-1. 10차 E-1 에서 남긴 둘
- 줄끝 검사 스크립트(`d1-verify.ps1`)가 부모 하나인 커밋(rebase · cherry-pick · revert)에서 둘째 부모가 없어 `git merge-base` 오류를 찍는다 — 부모 하나면 첫 부모와만 견준다고 적고 오류 없이.
- 파일 비교 탭 요약을 읽는 함수(`FileState`)가 "차이 없음" 을 못 읽어 커밋 뒤 요약이 빈칸으로 적힌다.
- **판정 기준**: 10차 E-1 의 rebase 한 길을 다시 돌려 오류 줄 0 · 커밋 뒤 요약 "차이 없음" 이 적힌다. 제품 코드는 바꾸지 않는다.
---
## E. 확인 · 문서
### E-1. 바꾼 길을 다시 끝까지
- F 에서 화면 · 저장을 건드렸으면 그 길도 함께 본다. A 를 고쳤으면 A-1 의 9가지를 다시 돌려 표를 채운다. C-1 을 고쳤으면 C-1 의 여덟 가지를 다시, C-2 를 했으면 메시지를 바꿔 이력을 다시 쓴 저장소로 병합 한 길을 끝까지.
- 제품의 병합 · 저장 코드를 건드렸다면 10차 E-1 처럼 **다섯 길(+ pull)** 을 다시 한다. 안 건드렸다면 병합 한 길만(회귀).
- **판정 기준**: 줄끝 · BOM 바뀐 파일 0 · 충돌 표식 0 · class 이름 다른 Unit 파일 0 · 누른 수 = 줄 고른 로그. 10차 B-1(두 탭 같은 커밋)이 다시 나지 않는다.
### E-2. 웹 문서 · 기획안을 11차에 맞게
- **MergeHub 문서**(`doc/web/index.html`, 아티팩트 "MergeHub 문서"): 매뉴얼 변경분 추출 · 실행 위치(B-1) · 설정 위치(B-2) · F 에서 바뀐 화면(칸 이름 · 감추기 · 글자 크기 · 백업 저장 · 보내기) · 알아 둘 한계 · 남은 일 · 지금까지 만든 것. 발행본을 먼저 읽고 합쳐 올린다.
- **기획안 v1.2**(잠금 아티팩트 "UbiSam MergeTool 개발 기획안"): 3.2 기능 목록의 변경분 추출 · 커밋 메시지 수정 상태, 4단계 진행. 원문은 로컬에서 고치고 A4 쪽마다 원본과 같은 높이인지 그림으로 견준다.
**잠금은 사용자가 비밀번호로 한다** — 잠그지 않은 파일은 올리지 않는다(공유 링크가 열린 사내 문서).
- **판정 기준**: 문서의 단추 이름 · 흐름이 지금 화면과 같다(grep 대조). 태그 짝 맞음. 기획안 새 잠금 파일을 풀면 고친 원문과 바이트가 같다.
---
## F. 추가 요청 (2026-09-18, 사용자)
열한 가지를 한꺼번에 받았다. **다 하는 것이 아니라 하나씩 올린다** — F-1 · F-2 · F-3 은 화면과 저장에 바로 닿고, F-4 ~ F-7 은 보이는 양을 늘리는 한 묶음, F-8 ~ F-11 은 저장 · 확인이다.
아래 "지금은" 은 코드를 읽고 적은 것이다(2026-09-18). 항목마다 **화면에서 먼저 다시 보이고** 시작한다.
### F-1. LOCAL · REMOTE · MERGED 이름을 맞추고, 그 칸이 **어느 파일인지** 보이게
- 지금은: 칸 이름이 `BaseTitle => "BASE"` · `LocalTitle => IsThreeWay ? "MERGED" : "LOCAL"`(`HomeViewModel.cs:2393`)뿐이고, "한글 풀이를 붙이지 않는다" 는 판단이 바로 위 주석(`:2389`)에 적혀 있다.
파일 경로는 **맨 위 한 줄에 한 번만** 나온다(4차 C-1 에서 세 곳 → 한 곳으로 줄였다). BASE 칸에만 `(merge base) 경로`, REMOTE 칸에는 브랜치 · 커밋이 붙는다.
- 그래서 `git mergetool` 로 열었을 때처럼 **네 칸이 서로 다른 파일**(임시 `--base` · `--local` · `--remote` · `--merged`)을 볼 때 어느 칸이 어느 파일인지 화면에 없다.
- 방향: 칸 머리마다 한 줄로 **무엇 · 어느 파일 · 어느 커밋/브랜치**. 예) `MERGED · Weld/Unit1.cs (저장할 파일)` · `REMOTE(Theirs) · feature/theirs · Weld/Unit1.cs`.
긴 경로는 가운데를 줄이고 전체는 마우스를 올리면 보이게. 로그 · 보고서 · 매뉴얼에서 쓰는 말도 같은 말로 맞춘다.
- **정할 것**: 이름을 표준 용어 그대로 둘지(`LOCAL(Mine)`), 한글 풀이를 함께 붙일지(`LOCAL(내 쪽)`). 4차 · 6차에 "표준 용어 그대로" 로 정한 적이 있어 뒤집는 결정이다.
- **판정 기준**: 두 칸 · 네 칸 · `git mergetool` · `--merge` 네 길에서 칸마다 파일 이름이 보이고, 그 파일이 실제로 읽은 · 저장할 파일과 같다(8차 A-1 의 "화면에 실린 파일" 과 같은 값). 좁은 폭에서도 줄이 늘어나지 않는다.
### F-2. **메서드 단위**로 골라 반영 (지금은 줄 · 덩어리 · 전체)
- 지금은: 반영 단위가 줄(`[>]`) · 이어진 차이 덩어리(`이 덩어리 반영`) · 전체뿐이다. 덩어리를 반영하면 머지 주석은 덩어리 첫 줄 위에 한 줄 붙는다.
- 요청: **메서드를 통째로** 고르고, 머지 주석은 **메서드 이름 위에 한 줄만**.
- 방향: Roslyn 없이 `CodeTokenizer` 로 중괄호 짝과 시작 줄을 찾아 메서드 범위를 잡는다. 문자열 · 주석 · 전처리기(`#if`) 안의 중괄호에 속지 않아야 한다.
캐럿이 놓인 메서드를 잡아 [이 메서드 반영]. 범위를 잡지 못하면 단추를 끄고 까닭을 알린다(틀린 범위를 반영하는 것보다 낫다).
- **정할 것**: C# 만 먼저 할지(XAML · C/C++ 은 뒤로), 속성 · 로컬 함수 · 생성자도 "메서드" 로 볼지.
- **판정 기준**: 시험 파일에서 메서드 20개(한 줄 메서드 · 중첩 중괄호 · 문자열 안 중괄호 · `#if` 로 갈린 메서드 · 주석 안 중괄호 포함)의 범위를 정확히 잡는다(하네스). 반영한 뒤 저장 결과가 줄 단위로 같은 범위를 반영한 것과 **바이트가 같다**. 머지 주석은 메서드 이름 위 한 줄.
### F-3. 탐색기에서 **폴더 두 개**를 골라 [보내기] 로 바로 비교
- 지금은: 보내기 등록은 `%APPDATA%\Microsoft\Windows\SendTo` 에 바로가기 하나를 두는 것이 전부다(`ShellIntegration.cs:18`, 관리자 권한 · 레지스트리 없음).
명령줄 쪽은 폴더 두 개를 받으면 폴더 비교로 연다(`StartupArgs.cs:118~122`). 그런데 탐색기가 **여러 개를 고른 보내기**를 어떻게 넘기는지(한 번에 인자 둘 / 프로세스를 두 번)는 재 보지 않았다.
- 확인할 것: 파일 2개 · **폴더 2개** · 폴더 1개 · 3개 이상 · 경로에 공백 · 한글 · 아주 긴 경로. 프로세스가 두 번 뜨면 **하나로 모으는 길**(이미 떠 있는 창으로 보내기)을 정한다.
- **설치 파일 없이 되는지**도 여기서 답한다 — 지금 구조(바로가기 하나 · HKCU/HKLM 안 씀)면 설치 관리자가 필요 없다. 다만 배포 묶음을 옮기면 바로가기 경로가 어긋나므로 [탐색기 보내기 등록] 을 다시 눌러야 하는지, 켤 때 저절로 고칠지 정한다(B-1 배포 묶음과 함께).
- **판정 기준**: 폴더 두 개를 골라 보내기 → 창 하나에 왼쪽 · 오른쪽 폴더가 들어가 비교까지 간다. 위 일곱 경우의 결과를 값으로 적는다. 레지스트리를 쓰지 않는다.
### F-4. 파일 비교 탭의 **툴박스(ACTIONS)를 감추는 단추**
- 지금은: ACTIONS 칸은 접어도 **폭이 그대로**다(4차 C-5 결정, `HomeView.xaml:864`). 접기는 "한 번 정하는 것"(저장 형식 · 비교 규칙)에만 걸려 있다.
- 방향: 칸 전체를 감춰 코드 폭을 넓히는 단추(또는 `F9` 같은 키). 감춘 상태를 설정에 기억. 감춘 채로도 저장 · 반영 키는 살아 있어야 한다.
- **판정 기준**: 감추면 코드 칸 폭이 그만큼 늘고, 다시 펴면 원래대로. 앱을 껐다 켜도 상태가 남는다. 감춘 상태에서 `Ctrl+S` · `Alt+←/→` 가 그대로 듣는다.
### F-5. **글자 크기 조절**
- 지금은: 코드 칸은 `Consolas 12` 로 XAML 에 박혀 있고(`HomeView.xaml:163` 등), 설정에 글자 크기 항목이 없다.
- 방향: 설정 값 하나 + `Ctrl` + 휠 · `Ctrl` + `+`/`-` 로. 코드 칸 · 줄 번호 · 미니맵이 같이 커지고 줄 높이도 따라야 한다.
- **판정 기준**: 8pt ~ 24pt 에서 줄 번호 · 여백 단추 · 미니맵 자리가 어긋나지 않는다. 바꾼 크기가 앱을 껐다 켜도 남는다. 네 칸에서 세 칸의 줄 높이가 서로 맞는다.
### F-6. **글자를 되도록 많이 보이게**
- 지금은: 6차에서 셸 상 · 하단 띠를 줄여 코드가 3줄 더 보이게 했다. 그 뒤로는 더 재지 않았다.
- 방향: 한 단추(또는 키)로 **넓게 보기** — 색 구분 범례 줄 접기 · ACTIONS 감추기(F-4) · 위 줄 한 줄로 · 줄 간격 좁히기.
- **판정 기준**: 1920 × 1080 에서 지금 몇 줄이 보이는지 재고, 넓게 보기로 **몇 줄이 늘었는지** 값으로 적는다(예: 34줄 → 44줄). 글자 크기(F-5)와 섞어 써도 깨지지 않는다.
### F-7. **어느 모니터에서든** 제대로 뜨게
- 지금은: 창 자리 기억은 플랫폼이 한다(`ShellWindow.xaml.cs:475` `PreparePlacement` · 모니터 작업 영역과 배율로 최소 크기를 다시 계산). 8차 C-3 에서 첫 `Win`+`↓` 와 확인 창 가운데 맞춤을 고쳤다.
- **먼저 증상부터 적는다** — 어느 모니터에서 무엇이 잘못되는지(창이 화면 밖 · 너무 작음 · 글자만 크게 · 두 번째 모니터에서 안 뜸 · 배율 150% 에서 잘림). 재현되지 않으면 고치지 않는다.
- 플랫폼을 고치면 다른 소비 프로그램에 영향이 있다 — 영향과 함께 **사용자 확인 뒤** 배포.
- **판정 기준**: 증상과 조건(모니터 수 · 해상도 · 배율 · 주 모니터 위치)을 적는다. 고쳤다면 같은 조건에서 창이 보이는 자리에 뜨고, 기억한 자리가 사라진 모니터면 주 모니터로 온다.
### F-8. **저장한 뒤에도 되돌리기**가 되게
- 지금은: 저장하는 네 길이 모두 되돌리기 스택을 비운다 — `SaveResolvedAsync:977` · `SaveAndCommitAsync:896` · `SavePlainAsync:2686` · `SaveWorkingCopy:2836`. 저장한 글이 새 기준(`_originalLocalText`)이 되기 때문이다.
- 방향: 스택을 비우는 대신 **저장 지점을 표시**한다. 그 아래로 되돌리면 다시 "저장할 것 있음" 이 되고, 다시 저장 지점으로 오면 없어진다.
- **정할 것**: 병합 중 [Save] 까지 되돌릴지. 이 저장은 git 인덱스에 "해결" 로 등록하므로, 되돌리면 인덱스를 어떻게 할지 함께 정해야 한다(되돌리면 다시 충돌로 / 화면만 되돌리고 인덱스는 그대로 · 알림).
- **판정 기준**: 저장 → Undo → 화면이 저장 전으로, 상태가 "저장할 것 있음". Redo 로 다시 저장 후로. [전체 되돌리기] 의 기준이 정한 대로 움직인다. 하네스로 "저장 표시가 있는 스택" 을 먼저 만든다.
### F-9. **백업본으로 저장** — 원본을 건드릴지 고르게
- 지금은: 저장은 늘 원본 파일을 덮어쓴다. `.bak` 도, 다른 이름으로 저장도 없다.
- 방향: [저장] 옆에 **[다른 이름으로 저장]**, 또는 설정 "저장할 때 원본을 `파일명.bak` 으로 남기기". 남긴 백업의 이름 규칙(날짜 · 번호)과 지우는 시점을 정한다.
- **정할 것**: 병합 중 · Git 저장소에서도 같게 할지. 병합 중 [Save] 는 git 인덱스에 등록하는 일이라 "원본을 건드리지 않음" 과 어울리지 않는다 — Git 아닐 때만 둘지 정한다.
- **판정 기준**: 켠 채로 저장하면 원본과 백업이 둘 다 남고, 백업은 **저장 전 바이트 그대로**(해시 대조). 끄면 지금과 같다. 폴더 비교 · 변경분 추출이 백업 파일을 결과에 섞지 않는다.
### F-10. 덩어리(블록)가 **어디로 갔는지 UI 로 잇기**
- 지금은: 자리만 옮긴 덩어리를 찾아 "자리 옮김" 색으로 칠한다(`MoveDetector`). 하지만 **어디에서 어디로** 갔는지는 색만으로 알기 어렵다. 줄 위치가 많이 벌어지거나 순서가 바뀌면 두 칸을 눈으로 따라가야 한다.
- 방향: 두 칸 사이에 **잇는 선**(또는 같은 번호표)을 그려 짝을 보이게. 미니맵에도 같은 표시. 고른 덩어리의 짝으로 한 번에 건너뛰는 단추.
- **판정 기준**: 메서드 세 개의 순서를 바꾼 시험 파일에서 세 짝이 모두 선으로 이어지고, 잇는 선이 잘못 짝지은 것 0. 스크롤 · 접기 · 글자 크기(F-5)를 바꿔도 선이 제자리. 차이 수 · 저장 내용은 그대로.
### F-11. Git 계정 · URL · 암호가 **한 번 쓰면 저장되는지** 확인
- 지금은: 서버 주소와 계정은 설정 파일에 저장된다(`MergeHubSettings.cs:40` `GiteaUrl` · `:44` `GiteaUserName`). **암호 · 토큰은 저장되지 않는다** — 화면의 PasswordBox 에서 받아 메모리(`GitWorkspace.Password:154`)에만 두고, Fetch · Pull · Push 직전에 붙인다(`ApplyCredentials:519`).
그래서 앱을 껐다 켜면 토큰을 다시 넣어야 한다.
- 먼저 화면으로 확인해 적는다: 껐다 켠 뒤 주소 · 계정이 남는지, 암호 칸이 비는지, 그 상태로 Fetch 를 누르면 무엇이라 하는지.
- **정할 것**: 토큰을 저장할지. 저장한다면 **평문은 안 된다** — 윈도 자격 증명 관리자(DPAPI) 에 사용자 계정으로 묶어 넣는다. 저장하지 않기로 하면 매뉴얼에 그렇게 적는다.
- **판정 기준**: 확인한 결과를 적는다. 저장하기로 하면 설정 파일 어디에도 토큰 글자가 없고(파일 grep 0), 껐다 켜도 Push 가 다시 묻지 않는다. 지우는 단추가 있다.
### F-12. C# 코드 스타일 맞춤의 **기본값을 AURORA 로** (2026-09-18, 사용자 결정 · 완료)
- 사용자: "기본값은 원본형태를 따르고 C#인 경우에 AURORA 스타일로 하자" → 물어 정한 것: **제품 기본값 AURORA · C# 파일만 · 저장 동작은 지금 그대로**(켜져 있으면 저장도 그 스타일로 쓴다).
- 지금은: `MergeHubSettings.CSharpBraceStyle` 기본값이 `None`(끔). C/C++ · XAML 은 `CSharpStyle.Applies` 가 확장자 `.cs` 만 보므로 **어느 값이어도 원본 그대로**다 — 따로 고칠 것이 없다.
- 2026-09-17 에 "비교할 때만 맞추고 저장은 원본대로" 하는 선택지는 만들지 않기로 했다. 이번 결정도 그 위에 선다.
- **판정 기준**: 설정 파일이 없는 첫 실행에서 AURORA 로 시작한다. 중괄호 자리만 다른 C# 짝이 "차이 없음", 같은 모양의 C/C++ 짝은 차이가 그대로 남는다. 하네스에 기본값 · 확장자 검사를 넣는다. 매뉴얼 · 설정 표 · 시연 대본의 "기본은 끔" 을 고친다.
---
## 이번 지시서에서 하지 않는 것
- **실제 소스로 해 보기** — 실제 소스는 열지 않는다(2026-09-11 결정).
- **느린 디스크(HDD)에서 폴더 비교 재기** — 이 PC 에는 NVMe 뿐이다. 사내 베타 때 그 PC 에서 잰다(10차 D-1).
- **사내 베타 배포 자체 · 푸시** — 사내 Gitea 저장소와 대상 PC 가 정해지면 따로 지시한다. B 는 배포 **전** 확인까지.
- **D 드라이브를 없애거나 디스크 · 백신 · 레지스트리(HKLM) 설정을 바꿔 보는 시험** — 보안 · 시스템 설정이라 하지 않는다.
- **빈 cherry-pick 을 커밋으로 남기는 길** — 두지 않기로 했다(2026-09-16 사용자 결정).
- **비교할 때만 C# 스타일을 맞추는 선택지** — 만들지 않기로 했다(2026-09-17 사용자 결정).
- **MergeHub 밖에서 푼 충돌을 병합 본문에 적기** — 알 길이 없다(8차 F-1).
- 셸 창 반쪽 스냅 · 1px 테두리 — 8차 C-3 결정 그대로.
- 로그인·권한 · Gitea PR 코멘트 · 멀티 레포 · Roslyn · 같은 줄 접기(4차 B-2) · 외부 편집기(4차 B-4) · 창 여러 개 — 미룬 그대로 둔다.
---
## 보고 형식
항목마다 이렇게 적는다.
- 고치기 전에 문제가 **다시 나는 것**을 어떻게 보였는지 (만든 저장소 · 묶음의 어느 파일로, 몇 번 되풀이해서)
- 무엇이 원인이었는지 한두 줄 (코드 · 로그 근거)
- 사용자에게 정해 달라고 한 것과 정한 것
- 어떤 파일을 어떻게 고쳤는지
- 실제로 실행해 확인한 결과 (파일 수 · 해시 · 크기 · 알림 문구 · ms 처럼 **눈이 아닌 값**으로)
- 커밋 해시