diff --git a/doc/history/2026-09-14-6차.md b/doc/history/2026-09-14-6차.md index a082d74..9e3d171 100644 --- a/doc/history/2026-09-14-6차.md +++ b/doc/history/2026-09-14-6차.md @@ -317,3 +317,57 @@ CP949 로 쓰이고 양쪽 한글이 다 읽히는지까지 본다. 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. diff --git a/doc/history/changelog.md b/doc/history/changelog.md index faf0ff0..0b90182 100644 --- a/doc/history/changelog.md +++ b/doc/history/changelog.md @@ -8,6 +8,7 @@ | 날짜 | 항목 | 커밋 | |---|---|---| +| 2026-09-14 | **6차 B-1** — 새로고침 뒤 파일 목록을 게이트가 풀릴 때까지 기다렸다 세움(병합 뒤 304개 → 23개). 충돌 목록 두 번 누르기·Enter·[3분할로 열기] 로 파일 비교 탭 네 칸에서 연다. 없는 '홈 탭' 글을 고침 | 아래 | | 2026-09-14 | **6차 A-5** — 파일 단위 Accept Both 가 두 벌을 글로 풀어 이어 붙이고 내 쪽 형식으로 쓴다(가운데 BOM 사라짐). 누르기 전에 무엇이 일어나는지 묻는다. A 묶음 끝 | 아래 | | 2026-09-14 | **6차 A-4** — 한쪽이 지운 충돌(UD·DU)은 줄 단위 3분할 대신 남은 파일을 두 칸으로 보이고 [파일 남기기] · [파일 지우기] 로 푼다. 거꾸로 세던 범례(Theirs only 89)가 없어짐 | 아래 | | 2026-09-14 | **6차 A-3** — 병합 중 충돌 파일을 열면 콤보를 안 건드려도 네 칸이 뜬다. REMOTE 제목에 상대 브랜치 이름(MERGE_HEAD → 병합 메시지 → 짧은 해시) | 아래 | diff --git a/doc/진행상황.md b/doc/진행상황.md index f21e52e..7da58d8 100644 --- a/doc/진행상황.md +++ b/doc/진행상황.md @@ -103,7 +103,7 @@ | 상태 | 항목 | |---|---| -| 대기 | B-1 충돌 목록에서 3분할 화면까지 가는 길이 끊겨 있다 | +| **완료** | B-1 충돌 목록에서 3분할 화면까지 가는 길이 끊겨 있다 — 병합 뒤 목록이 바로 서고, 두 번 누르기·Enter·[3분할로 열기] | | 대기 | B-2 확인 창이 엉뚱한 파일 이름을 댄다 | | 대기 | B-3 커밋 메시지가 엉뚱하게 남는다 | | 대기 | B-4 MERGED 칸 줄 번호가 네 자리에서 잘린다 | diff --git a/src/MergeHub/Services/GitWorkspace.cs b/src/MergeHub/Services/GitWorkspace.cs index 5208c25..e251075 100644 --- a/src/MergeHub/Services/GitWorkspace.cs +++ b/src/MergeHub/Services/GitWorkspace.cs @@ -523,6 +523,40 @@ namespace MergeHub.Services PlainCompareRequested.Invoke(pending.Left, pending.Right); } + // ================= 충돌 파일을 파일 비교 탭에서 열기 (6차 B-1) ================= + + /// + /// "이 충돌 파일을 3분할로 열어 달라" - Git 탭의 충돌 목록이 보내고 파일 비교 탭이 받는다. + /// + /// 예전에는 충돌 목록을 두 번 눌러도 아무 일이 없었다. 줄마다 고르려면 파일 비교 탭으로 가서 + /// [새로고침] 을 누르고 콤보에서 같은 파일을 다시 찾아야 했다. + /// + public event Action? OpenConflictRequested; + + /// 아직 아무도 받지 않은 요청. 파일 비교 화면은 탭을 처음 열 때 만들어지므로 남겨 둔다. + private string? _pendingOpenConflict; + + public bool HasPendingOpenConflict => _pendingOpenConflict is not null; + + public void RequestOpenConflict(string path) + { + if (string.IsNullOrWhiteSpace(path)) return; + + _pendingOpenConflict = path; + OnUi(DeliverPendingOpenConflict); + ActivateWhenReady(MergeHub.ViewIds.Home); + } + + /// 남아 있는 요청을 넘긴다. 파일 비교 화면이 새로 만들어지면 스스로 한 번 불러 준다. + public void DeliverPendingOpenConflict() + { + if (_pendingOpenConflict is not { } path) return; + if (OpenConflictRequested is null) return; + + _pendingOpenConflict = null; + OpenConflictRequested.Invoke(path); + } + // ================= 켤 때 넘어온 인자 ================= /// 폴더 비교 화면이 아직 없을 때 남겨 두는 요청. 파일 비교 쪽과 같은 방식이다. diff --git a/src/MergeHub/ViewModels/FolderViewModel.cs b/src/MergeHub/ViewModels/FolderViewModel.cs index 6730795..7386ba1 100644 --- a/src/MergeHub/ViewModels/FolderViewModel.cs +++ b/src/MergeHub/ViewModels/FolderViewModel.cs @@ -148,7 +148,7 @@ namespace MergeHub.ViewModels else Summary = "오른쪽 폴더를 고르고 비교를 누르세요."; } - /// 홈 탭에서 이 두 파일을 비교해 달라고 알린다. + /// 파일 비교 탭에서 이 두 파일을 비교해 달라고 알린다. public event EventHandler<(string Left, string Right)>? OpenInDiffRequested; public ObservableCollection Nodes { get; } = new ObservableCollection(); diff --git a/src/MergeHub/ViewModels/GitViewModel.cs b/src/MergeHub/ViewModels/GitViewModel.cs index 32fd4d2..ea1e647 100644 --- a/src/MergeHub/ViewModels/GitViewModel.cs +++ b/src/MergeHub/ViewModels/GitViewModel.cs @@ -237,7 +237,7 @@ namespace MergeHub.ViewModels CommitMessage = $"Merge branch '{branch}' into {_workspace.CurrentBranch}"; if (result.HasConflicts) - MessageUtil.ShowWarningToast($"충돌 {result.Conflicts.Count}건 - 홈 탭에서 해결하세요."); + MessageUtil.ShowWarningToast($"충돌 {result.Conflicts.Count}건 - 충돌 탭에서 파일을 두 번 누르면 파일 비교 탭에서 세 칸으로 풉니다."); else MessageUtil.ShowToast(result.Message); }); @@ -285,6 +285,25 @@ namespace MergeHub.ViewModels return go ? ResolveAsync(ConflictResolution.Both) : Task.CompletedTask; } + /// + /// 고른 충돌 파일을 파일 비교 탭의 3분할로 연다(6차 B-1). 목록을 두 번 누르거나 Enter 로도 온다. + /// 켜짐/꺼짐을 따로 두지 않는다 - 고른 것이 없으면 아무 일도 안 한다. 이진 파일은 줄 단위로 볼 수 없어 알린다. + /// + [RelayCommand] + private void OpenConflictInCompare() + { + var conflict = SelectedConflict; + if (conflict is null) return; + + if (conflict.IsBinary) + { + MessageUtil.ShowWarningToast($"이진 파일이라 줄 단위로 볼 수 없습니다. 아래 Accept 단추로 한쪽을 고르세요: {conflict.Path}"); + return; + } + + _workspace.RequestOpenConflict(conflict.Path); + } + private Task ResolveAsync(ConflictResolution resolution) => _workspace.RunAndRefreshAsync($"충돌 해결({resolution})", () => _workspace.Git.ResolveConflict(SelectedConflict!.Path, resolution)); diff --git a/src/MergeHub/ViewModels/HomeViewModel.cs b/src/MergeHub/ViewModels/HomeViewModel.cs index a4d76f6..ba1bbc0 100644 --- a/src/MergeHub/ViewModels/HomeViewModel.cs +++ b/src/MergeHub/ViewModels/HomeViewModel.cs @@ -36,6 +36,7 @@ namespace MergeHub.ViewModels _workspace.Refreshed += OnWorkspaceRefreshed; _workspace.StateChanged += RaiseCanExecuteChanged; _workspace.PlainCompareRequested += OnPlainCompareRequested; + _workspace.OpenConflictRequested += OnOpenConflictRequested; _workspace.Recent.Changed += (_, _) => _workspace.OnUi(RefreshRecent); _workspace.QueueChanged += (_, _) => RefreshQueue(); @@ -50,6 +51,16 @@ namespace MergeHub.ViewModels IsGitMode = _workspace.IsRepositoryOpen; RefreshRemoteBranches(); + // Git 탭의 충돌 목록에서 파일을 두 번 눌러 이 화면이 처음 만들어진 경우(6차 B-1). + // 저장소는 Git 탭이 이미 열었다. 아래의 기본 저장소 열기로 가면 다른 저장소를 열 수 있다 - + // 지금 저장소로 목록만 세우고 그 파일을 연다. + if (_workspace.HasPendingOpenConflict) + { + OnWorkspaceRefreshed(); + _workspace.DeliverPendingOpenConflict(); + return; + } + // 폴더 비교에서 파일을 골라 이 화면이 만들어진 경우라면 그 파일을 먼저 연다. // 이 화면은 탭을 처음 열 때 만들어지므로, 요청이 우리보다 먼저 도착해 있을 수 있다. if (_workspace.HasPendingPlainCompare) @@ -868,10 +879,57 @@ namespace MergeHub.ViewModels OnPropertyChanged(nameof(SelectedCommit)); RaiseCommitDetail(); - _ = LoadCommitAsync(); + _ = RebuildListAfterRefreshAsync(); // 여기서 다시 세우는 것은 사용자가 고른 것이 아니다. LoadCommitAsync 안에서 묻지 않게 한다. } + private void OnOpenConflictRequested(string path) => _ = OpenConflictAsync(path); + + /// + /// Git 탭 충돌 목록에서 고른 파일을 연다(6차 B-1). 충돌 파일이므로 A-3 대로 세 칸으로 열린다. + /// + /// 목록에 그 파일이 아직 없을 수 있다 - 병합 직후라 새로고침 뒤 목록 세우기가 아직 안 끝났거나, + /// 이 화면이 방금 만들어졌다. 게이트가 풀릴 때까지 기다렸다 목록을 세우고 나서 고른다. + /// 고르는 일은 에 맡긴다 - 저장 안 한 편집이 있으면 거기서 묻는다. + /// + private async Task OpenConflictAsync(string path) + { + if (!_workspace.IsRepositoryOpen) return; + IsGitMode = true; + + for (var attempt = 0; attempt < 5 && !Files.Contains(path); attempt++) + { + await _workspace.WhenIdleAsync(); + await LoadCommitAsync(); + } + + if (!Files.Contains(path)) + { + MessageUtil.ShowWarningToast($"파일 목록에서 찾지 못했습니다: {path}"); + return; + } + + if (!string.Equals(SelectedFile, path, StringComparison.Ordinal)) + SelectedFile = path; + } + + /// + /// 새로고침 뒤 파일 목록을 반드시 다시 세운다(6차 B-1). + /// + /// 새로고침은 저장소 상태를 읽는 동안(게이트를 쥔 채) 화면 목록을 갈아 끼우고, 끝나자마자 Git 탭이 제 일로 + /// 게이트를 다시 잡는다. 그 틈에 목록 세우기를 부르면 "다른 작업 진행 중" 으로 건너뛰었다. + /// 병합하고 파일 비교 탭으로 오면 목록이 병합 전 커밋 파일(304개) 그대로였고, [새로고침] 을 한 번 더 눌러야 충돌 23개가 나왔다. + /// 게이트가 풀릴 때까지 기다렸다가 세우고, 또 막히면 몇 번 더 해 본다. + /// + private async Task RebuildListAfterRefreshAsync() + { + for (var attempt = 0; attempt < 5; attempt++) + { + await _workspace.WhenIdleAsync(); + if (await LoadCommitAsync()) return; + } + } + /// 선택 커밋이 담고 있는 파일 목록을 채우고, 가능하면 이전 선택을 유지한다. /// /// 목록을 다시 세웠거나 세울 것이 없으면 true. 게이트에 막혀 건너뛰었으면 false - diff --git a/src/MergeHub/Views/GitView.xaml b/src/MergeHub/Views/GitView.xaml index 6ef0d54..998d6ed 100644 --- a/src/MergeHub/Views/GitView.xaml +++ b/src/MergeHub/Views/GitView.xaml @@ -240,11 +240,18 @@ + Text="파일 전체를 한쪽으로 채택하려면 아래 버튼을 쓰고, 줄마다 고르려면 목록을 두 번 누르거나 [3분할로 열기] 를 누르세요. 파일 비교 탭에서 세 칸으로 열립니다."/> + + + + @@ -271,6 +278,8 @@ +