diff --git a/doc/history/2026-09-17-9차.md b/doc/history/2026-09-17-9차.md index acad5de..bf54a54 100644 --- a/doc/history/2026-09-17-9차.md +++ b/doc/history/2026-09-17-9차.md @@ -68,3 +68,40 @@ 이번에 바꾼 것이 아니라 전부터 그랬다. 고치지 않고 적어 둔다. - A-2(한 번은 다른 브랜치 커밋이 기준)는 이번에 원인(목록 맨 위로 채움)이 드러나 함께 고쳐졌다. 지시서대로 A-2 에서 같은 순서를 다섯 번 넘게 되풀이해 확인한다. - 자동 조작 쪽: 첫 확인 스크립트는 Git 탭에서 HEAD 를 골라 파일 비교 탭을 봤는데 위의 까닭으로 안 바뀌어, 파일 비교 탭 이력에서 고르게 고쳤다. + +## A-2. 한 번은 기준이 HEAD 도 가져온 커밋도 아닌 다른 브랜치 커밋이었다 (2026-09-17, 완료) + +### 원인 (A-1 에서 드러남) + +- 8차 E-1 N1-덧의 `f310f49`(다른 브랜치의 `뒤이은 수정`)는 **목록 맨 위**였다. 새로고침이 이력 목록을 갈아 끼울 때 Git 탭 목록이 선택을 놓치면 + 고른 커밋이 비고, 비면 `Commits[0]` 으로 채웠다. [모든 브랜치] 를 켜면 맨 위는 가장 최근의 다른 브랜치 커밋이다. +- 놓치는지는 그때 화면 상태에 따라 달라 "같은 순서로 다시 재니 안 났다". 9차 A-1 의 고치기 전 확인에서 **다시 났다**(기준 `ace42e3 "뒤이은 수정"`). +- A-1 에서 해시로 되찾고 없으면 HEAD 로 채우게 고쳤다(`CommitSelection`). + +### 고치며 새로 걸린 것 → 함께 고침 + +- A-1 대로 해시만 지키자 **cherry-pick [커밋] 뒤에도 선택이 옛 HEAD(`내 쪽 작업`)에 남아** 파일 비교가 방금 커밋한 줄을 **"남은 차이 6줄"** 이라고 했다 + (되풀이 확인 첫 회차에서 남). 예전에는 선택을 놓치면 맨 위(새 커밋)로 가서 우연히 "차이 없음" 이었다. +- `CommitSelection.Choose(…, previousHeadSha)` — **HEAD 를 보고 있다가 HEAD 가 움직이면**(커밋 · 체크아웃) 새 HEAD 를 따라간다. + 다른 커밋을 보고 있었으면 그대로 둔다. `GitWorkspace` 가 지난 새로고침의 HEAD(`_lastHeadSha`)를 들고 있다. +- 하네스 `MergeSaveTest` 5건 추가(206건): HEAD 를 보다 커밋하면 새 HEAD · 다른 커밋을 보고 있었으면 그대로 · HEAD 가 안 움직이면 그대로 · 처음 열기 · 새 HEAD 가 목록에 없음. + +### 확인한 것 — 8차 E-1 순서를 여섯 번 (`d9-a2.ps1`) + +회차마다 저장소를 새로 만들고, 파일 비교 탭을 미리 열지 않고, [모든 브랜치] 를 켜고 이력에서 `상대 쪽 작업` → [Cherry-pick] → +`Weld/Unit1.cs` 만 남기고 파일 단위 Accept Theirs → [3분할로 열기] · Take Theirs · [Save] → 충돌 탭 [커밋]. 새로고침이 일어나는 곳마다 Git 탭 이력의 선택 줄과 파일 비교 탭 Selected Commit 을 적었다. + +| 지점 | 기대(규칙) | 6회 결과 | +|---|---|---| +| [Cherry-pick] 뒤(충돌 24) | 고른 커밋 `상대 쪽 작업` | 6/6 | +| 파일 단위 Accept 뒤(충돌 1) | 고른 커밋 | 6/6 | +| 마지막 [Save] 뒤(충돌 0) | HEAD `내 쪽 작업` · 제목 "가져오기 전 내 쪽" · 요약 "내 쪽 대비 가져온 변경 6줄" | 6/6 (Git 탭 · 파일 비교 탭 둘 다) | +| [커밋] 뒤 | 새 HEAD(cherry-pick 커밋) · "차이 없음" · Working Tree Clean | 6/6 (첫 회차는 고친 뒤 되풀이 스크립트의 1회) | + +- 다른 브랜치 커밋(`뒤이은 수정`)이 기준으로 잡힌 회차 0. +- 하네스 22벌 1,117건 통과(+5) · 빌드 경고 0 · 오류 0. + +### 남긴 것 + +- 자동 조작 쪽: 되풀이 스크립트가 처음에 한 회차만 돌았다. PowerShell 변수 이름은 대소문자를 가리지 않아 안쪽 반복 횟수 `$rounds` 가 회차 수 `$Rounds` 를 덮었다. 이름을 바꿔 다시 돌렸다. +- Git 탭에서 고른 커밋을 파일 비교 탭이 새로고침 전까지 안 따라오는 것(A-1 에 적음)은 그대로다. diff --git a/doc/history/changelog.md b/doc/history/changelog.md index bd3b1c3..7711cbd 100644 --- a/doc/history/changelog.md +++ b/doc/history/changelog.md @@ -8,6 +8,7 @@ | 날짜 | 항목 | 커밋 | |---|---|---| +| 2026-09-17 | **9차 A-2** — 8차 E-1 에서 한 번 다른 브랜치 커밋이 기준으로 잡힌 것은 A-1 과 뿌리가 같았다(선택을 놓치면 목록 맨 위). 해시만 지키자 cherry-pick [커밋] 뒤 선택이 옛 HEAD 에 남아 방금 커밋한 줄이 "남은 차이 6줄" 로 보여, **HEAD 를 보고 있었으면 커밋 · 체크아웃 뒤 새 HEAD 를 따라가게** 함께 고침. 같은 순서 여섯 번 모두 규칙대로. 하네스 +5 | 아래 | | 2026-09-17 | **9차 A-1** — cherry-pick 마지막 [Save] 뒤 두 칸의 기준이 고른 커밋 · 다른 브랜치 커밋 · HEAD 중 하나로 그때그때 달랐는데 요약은 늘 "내 쪽 대비" 라고 했다. 새로고침이 이력 목록을 갈아 끼우며 선택을 놓치면 **목록 맨 위**로 채웠기 때문([모든 브랜치] 에서 맨 위는 HEAD 가 아님). 사용자 결정대로 작업 중 충돌이 막 0 이 되면 기준을 HEAD 로 옮기고, 그 밖에는 해시로 되찾고 없으면 HEAD. 요약은 기준이 HEAD 일 때만 작업 글. 하네스 +18 | 아래 | | 2026-09-17 | **9차 지시서** — 8차 전부 끝. E-1 에서 새로 걸린 N1(A-1 cherry-pick 마지막 저장 뒤 요약은 "내 쪽 대비" 인데 두 칸의 기준은 이력에서 고른 커밋 — 먼저 정함 · A-2 한 번은 다른 브랜치 커밋이 기준 — 새로고침 뒤 선택 복원, 원인부터)과 전에 남긴 것(B-1 MERGED 주석 줄 "무시" 색 · B-2 폴더 트리 1초) · C-1 웹 문서(F-1 · 숫자) · D-1 cherry-pick · revert 를 마지막 파일까지 네 칸으로. 코드 변경 없음 | 아래 | | 2026-09-17 | **8차 E-1** — 다섯 길(병합 · pull · rebase · cherry-pick · revert)을 새 저장소에서 **파일 비교 탭을 미리 열지 않고** MergeHub 만으로 끝까지(제품 코드 변경 없음). 다섯 길 모두 커밋(rebase 끝)까지 · 줄끝 · BOM 바뀐 파일 0 · 충돌 표식 0 · class 이름 검사 0. 7차 A-1 · A-2 · B-1, 8차 A-1 · B-1 · B-2 다시 안 남, F-1 병합 · pull 본문 26개. 새로 걸린 것: N1 cherry-pick 요약 "내 쪽 대비 가져온 변경" 이 실제 기준(이력에서 고른 커밋)과 다름 · 한 번 다른 브랜치 커밋이 기준으로 잡힘(재현 안 됨) → 9차. 8차 전부 끝 | 아래 | diff --git a/doc/진행상황.md b/doc/진행상황.md index 4e5f277..d87edf4 100644 --- a/doc/진행상황.md +++ b/doc/진행상황.md @@ -251,7 +251,7 @@ | 상태 | 항목 | |---|---| | **완료** | A-1 cherry-pick 마지막 저장 뒤 "내 쪽 대비" 라고 하지만 실제 기준은 이력에서 고른 커밋이다 — 사용자가 "HEAD 로 옮긴다" 를 고름(2026-09-17). 새로고침이 고른 커밋을 놓치면 목록 맨 위(모든 브랜치에서는 다른 브랜치 커밋)로 채우던 것을 해시로 되찾고 없으면 HEAD, 작업 중 충돌이 막 0 이 되면 HEAD 로. 요약은 기준이 HEAD 일 때만 작업 글. cherry-pick · revert 마지막 Save 뒤 제목 · 요약 · 기준이 같음(6줄 = git 대조) | -| 대기 | A-2 한 번은 기준이 다른 브랜치 커밋이었다 — 새로고침 뒤 이력 선택 복원 (원인부터) | +| **완료** | A-2 한 번은 기준이 다른 브랜치 커밋이었다 — 원인은 A-1 과 같음(선택을 놓치면 목록 맨 위로 채움). 해시만 지키자 cherry-pick [커밋] 뒤 옛 HEAD 에 남아 "남은 차이 6줄" 이 떠서, HEAD 를 보고 있었으면 새 HEAD 를 따라가게 함께 고침. 8차 E-1 순서 여섯 번 모두 시작 · 파일 단위 = 고른 커밋, Save 뒤 = HEAD, 커밋 뒤 = 새 HEAD | ### B. 전에 남겨 둔 작은 것 diff --git a/src/MergeHub/Services/CommitSelection.cs b/src/MergeHub/Services/CommitSelection.cs index 1574b92..6143779 100644 --- a/src/MergeHub/Services/CommitSelection.cs +++ b/src/MergeHub/Services/CommitSelection.cs @@ -14,6 +14,7 @@ namespace MergeHub.Services /// 정한 것(2026-09-17 사용자) - 작업(병합 · rebase · cherry-pick · revert) 중 충돌이 0 이 되는 순간 기준을 HEAD 로 옮긴다. /// 병합 · rebase 와 같아지고 "내 쪽 대비 가져온 변경 N줄" 이 사실이 된다(커밋하면 무엇이 바뀌는지). /// 그 밖에는 사람이 고른 커밋을 해시로 되찾고, 없으면 맨 위가 아니라 HEAD 로 채운다. + /// 다만 HEAD 를 보고 있다가 HEAD 가 움직이면(커밋 · 체크아웃) 새 HEAD 를 따라간다(9차 A-2). /// public static class CommitSelection { @@ -21,13 +22,21 @@ namespace MergeHub.Services /// 새로고침 전에 고른 커밋. 없으면 null. /// 지금 HEAD. 목록에 없을 수 있다(이력 쪽수 밖). /// 이번 새로고침에서 "작업 중 · 충돌 0" 이 막 된 것인지(). - public static int Choose(IReadOnlyList shas, string? previousSha, string? headSha, bool enteredResolvedOperation) + /// + /// 지난 새로고침 때의 HEAD. HEAD 를 보고 있다가 HEAD 가 움직이면(커밋 · 체크아웃) 새 HEAD 를 따라간다(9차 A-2). + /// 해시만 지키면 cherry-pick [커밋] 뒤에도 옛 HEAD 에 남아, 파일 비교가 방금 커밋한 줄을 "남은 차이 6줄" 이라고 했다. + /// + public static int Choose(IReadOnlyList shas, string? previousSha, string? headSha, bool enteredResolvedOperation, + string? previousHeadSha = null) { if (shas is null || shas.Count == 0) return -1; var head = IndexOf(shas, headSha); if (enteredResolvedOperation && head >= 0) return head; + var wasLookingAtHead = SameSha(previousSha, previousHeadSha); + if (wasLookingAtHead && head >= 0 && !SameSha(previousHeadSha, headSha)) return head; + var kept = IndexOf(shas, previousSha); if (kept >= 0) return kept; @@ -44,6 +53,9 @@ namespace MergeHub.Services /// 작업이 진행 중이고 남은 충돌이 없는가. public static bool IsResolvedOperation(bool inProgress, int conflictCount) => inProgress && conflictCount == 0; + private static bool SameSha(string? a, string? b) => + !string.IsNullOrEmpty(a) && string.Equals(a, b, StringComparison.OrdinalIgnoreCase); + private static int IndexOf(IReadOnlyList shas, string? sha) { if (string.IsNullOrEmpty(sha)) return -1; diff --git a/src/MergeHub/Services/GitWorkspace.cs b/src/MergeHub/Services/GitWorkspace.cs index ef5b85c..729f0d6 100644 --- a/src/MergeHub/Services/GitWorkspace.cs +++ b/src/MergeHub/Services/GitWorkspace.cs @@ -224,6 +224,9 @@ namespace MergeHub.Services /// 지난 새로고침 때 "작업 중 · 충돌 0" 이었는지. 그 상태가 막 된 순간에만 선택을 HEAD 로 옮긴다(9차 A-1). private bool _wasResolvedOperation; + + /// 지난 새로고침 때의 HEAD(전체 해시). HEAD 를 보고 있다가 HEAD 가 움직이면 새 HEAD 를 따라가려고 둔다(9차 A-2). + private string? _lastHeadSha; [ObservableProperty] private BranchInfo? _selectedBranch; [ObservableProperty] private string _log = string.Empty; @@ -427,7 +430,9 @@ namespace MergeHub.Services // 두 칸의 기준이 다른 브랜치 커밋이 되기도 했다. 해시로 되찾고, 없으면 HEAD. 작업 중 충돌이 막 0 이 되면 HEAD 로(9차 A-1). var entered = CommitSelection.EntersResolvedOperation(_wasResolvedOperation, operation.IsInProgress, conflicts.Count); _wasResolvedOperation = resolvedOperation; - var pick = CommitSelection.Choose(commits.Select(c => c.Sha).ToList(), previousSha, info.HeadSha, entered); + // HEAD 를 보고 있었으면 커밋 · 체크아웃 뒤 새 HEAD 를 따라간다 - 옛 HEAD 에 남으면 방금 커밋한 줄이 "남은 차이" 로 보였다(9차 A-2). + var pick = CommitSelection.Choose(commits.Select(c => c.Sha).ToList(), previousSha, info.HeadSha, entered, _lastHeadSha); + _lastHeadSha = info.HeadSha; SelectedCommit = pick < 0 ? null : Commits[pick]; }); }