6차 B-1: 병합 뒤 목록이 바로 서고 충돌 목록에서 3분할로 연다

병합하고 파일 비교 탭으로 오면 목록이 병합 전 커밋 파일(304개) 그대로
였고, [새로고침] 을 한 번 더 눌러야 충돌 23개가 나왔다. 충돌 탭 목록을
두 번 눌러도 아무 일이 없었고, 안내 글은 없는 "홈 탭" 을 가리켰다.

원인: 새로고침은 게이트를 쥔 채 화면 목록을 갈아 끼우고, 끝나자마자
Git 탭이 게이트를 다시 잡는다. 파일 비교 화면이 그 틈에 목록을 세우려다
"다른 작업 진행 중" 으로 건너뛰었다.

- OnWorkspaceRefreshed: 게이트가 풀릴 때까지 기다렸다 목록을 세운다
- GitWorkspace.RequestOpenConflict: 충돌 파일을 파일 비교 탭에서 열어
  달라는 요청을 남겨 두고 탭을 옮긴다(폴더 비교의 넘겨주기와 같은 방식)
- OpenConflictAsync: 목록에 설 때까지 기다렸다 고른다. 이 요청 때문에
  화면이 새로 만들어졌으면 기본 저장소를 새로 열지 않는다
- 충돌 목록: 두 번 누르기(MouseDoubleClick), Enter, [3분할로 열기]
- 안내 글과 병합 알림의 "홈 탭" 을 실제 탭 이름으로

화면 확인 17건 통과(병합 뒤 목록 23개, 두 번 누르기와 단추와 Enter 로
네 칸, 파일 비교 탭을 안 연 새 앱에서도 열림, 병합 취소 뒤 Git 탭
새로고침만으로 목록 304개). 경고 0, 오류 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
김승준 2026-09-14 11:36:16 +09:00
parent 04b8ef8a60
commit 9b597fc50f
9 changed files with 188 additions and 5 deletions

View File

@ -317,3 +317,57 @@ CP949 로 쓰이고 양쪽 한글이 다 읽히는지까지 본다.
A 의 다섯 가지(줄끝 · 반쯤 된 실패 · 상대 칸 · 지운 파일 · Both)가 모두 끝났다. 끝까지 풀어 커밋까지 가는 확인은 D-1 에서 A 의 다섯 가지(줄끝 · 반쯤 된 실패 · 상대 칸 · 지운 파일 · Both)가 모두 끝났다. 끝까지 풀어 커밋까지 가는 확인은 D-1 에서
처음부터 다시 한다. A 를 하다 B-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.

View File

@ -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-5** — 파일 단위 Accept Both 가 두 벌을 글로 풀어 이어 붙이고 내 쪽 형식으로 쓴다(가운데 BOM 사라짐). 누르기 전에 무엇이 일어나는지 묻는다. A 묶음 끝 | 아래 |
| 2026-09-14 | **6차 A-4** — 한쪽이 지운 충돌(UD·DU)은 줄 단위 3분할 대신 남은 파일을 두 칸으로 보이고 [파일 남기기] · [파일 지우기] 로 푼다. 거꾸로 세던 범례(Theirs only 89)가 없어짐 | 아래 | | 2026-09-14 | **6차 A-4** — 한쪽이 지운 충돌(UD·DU)은 줄 단위 3분할 대신 남은 파일을 두 칸으로 보이고 [파일 남기기] · [파일 지우기] 로 푼다. 거꾸로 세던 범례(Theirs only 89)가 없어짐 | 아래 |
| 2026-09-14 | **6차 A-3** — 병합 중 충돌 파일을 열면 콤보를 안 건드려도 네 칸이 뜬다. REMOTE 제목에 상대 브랜치 이름(MERGE_HEAD → 병합 메시지 → 짧은 해시) | 아래 | | 2026-09-14 | **6차 A-3** — 병합 중 충돌 파일을 열면 콤보를 안 건드려도 네 칸이 뜬다. REMOTE 제목에 상대 브랜치 이름(MERGE_HEAD → 병합 메시지 → 짧은 해시) | 아래 |

View File

@ -103,7 +103,7 @@
| 상태 | 항목 | | 상태 | 항목 |
|---|---| |---|---|
| 대기 | B-1 충돌 목록에서 3분할 화면까지 가는 길이 끊겨 있다 | | **완료** | B-1 충돌 목록에서 3분할 화면까지 가는 길이 끊겨 있다 — 병합 뒤 목록이 바로 서고, 두 번 누르기·Enter·[3분할로 열기] |
| 대기 | B-2 확인 창이 엉뚱한 파일 이름을 댄다 | | 대기 | B-2 확인 창이 엉뚱한 파일 이름을 댄다 |
| 대기 | B-3 커밋 메시지가 엉뚱하게 남는다 | | 대기 | B-3 커밋 메시지가 엉뚱하게 남는다 |
| 대기 | B-4 MERGED 칸 줄 번호가 네 자리에서 잘린다 | | 대기 | B-4 MERGED 칸 줄 번호가 네 자리에서 잘린다 |

View File

@ -523,6 +523,40 @@ namespace MergeHub.Services
PlainCompareRequested.Invoke(pending.Left, pending.Right); PlainCompareRequested.Invoke(pending.Left, pending.Right);
} }
// ================= 충돌 파일을 파일 비교 탭에서 열기 (6차 B-1) =================
/// <summary>
/// "이 충돌 파일을 3분할로 열어 달라" - Git 탭의 충돌 목록이 보내고 파일 비교 탭이 받는다.
///
/// 예전에는 충돌 목록을 두 번 눌러도 아무 일이 없었다. 줄마다 고르려면 파일 비교 탭으로 가서
/// [새로고침] 을 누르고 콤보에서 같은 파일을 다시 찾아야 했다.
/// </summary>
public event Action<string>? OpenConflictRequested;
/// <summary>아직 아무도 받지 않은 요청. 파일 비교 화면은 탭을 처음 열 때 만들어지므로 남겨 둔다.</summary>
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);
}
/// <summary>남아 있는 요청을 넘긴다. 파일 비교 화면이 새로 만들어지면 스스로 한 번 불러 준다.</summary>
public void DeliverPendingOpenConflict()
{
if (_pendingOpenConflict is not { } path) return;
if (OpenConflictRequested is null) return;
_pendingOpenConflict = null;
OpenConflictRequested.Invoke(path);
}
// ================= 켤 때 넘어온 인자 ================= // ================= 켤 때 넘어온 인자 =================
/// <summary>폴더 비교 화면이 아직 없을 때 남겨 두는 요청. 파일 비교 쪽과 같은 방식이다.</summary> /// <summary>폴더 비교 화면이 아직 없을 때 남겨 두는 요청. 파일 비교 쪽과 같은 방식이다.</summary>

View File

@ -148,7 +148,7 @@ namespace MergeHub.ViewModels
else Summary = "오른쪽 폴더를 고르고 비교를 누르세요."; else Summary = "오른쪽 폴더를 고르고 비교를 누르세요.";
} }
/// <summary>홈 탭에서 이 두 파일을 비교해 달라고 알린다.</summary> /// <summary>파일 비교 탭에서 이 두 파일을 비교해 달라고 알린다.</summary>
public event EventHandler<(string Left, string Right)>? OpenInDiffRequested; public event EventHandler<(string Left, string Right)>? OpenInDiffRequested;
public ObservableCollection<FolderNode> Nodes { get; } = new ObservableCollection<FolderNode>(); public ObservableCollection<FolderNode> Nodes { get; } = new ObservableCollection<FolderNode>();

View File

@ -237,7 +237,7 @@ namespace MergeHub.ViewModels
CommitMessage = $"Merge branch '{branch}' into {_workspace.CurrentBranch}"; CommitMessage = $"Merge branch '{branch}' into {_workspace.CurrentBranch}";
if (result.HasConflicts) if (result.HasConflicts)
MessageUtil.ShowWarningToast($"충돌 {result.Conflicts.Count}건 - 홈 탭에서 해결하세요."); MessageUtil.ShowWarningToast($"충돌 {result.Conflicts.Count}건 - 충돌 탭에서 파일을 두 번 누르면 파일 비교 탭에서 세 칸으로 풉니다.");
else else
MessageUtil.ShowToast(result.Message); MessageUtil.ShowToast(result.Message);
}); });
@ -285,6 +285,25 @@ namespace MergeHub.ViewModels
return go ? ResolveAsync(ConflictResolution.Both) : Task.CompletedTask; return go ? ResolveAsync(ConflictResolution.Both) : Task.CompletedTask;
} }
/// <summary>
/// 고른 충돌 파일을 파일 비교 탭의 3분할로 연다(6차 B-1). 목록을 두 번 누르거나 Enter 로도 온다.
/// 켜짐/꺼짐을 따로 두지 않는다 - 고른 것이 없으면 아무 일도 안 한다. 이진 파일은 줄 단위로 볼 수 없어 알린다.
/// </summary>
[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) => private Task ResolveAsync(ConflictResolution resolution) =>
_workspace.RunAndRefreshAsync($"충돌 해결({resolution})", _workspace.RunAndRefreshAsync($"충돌 해결({resolution})",
() => _workspace.Git.ResolveConflict(SelectedConflict!.Path, resolution)); () => _workspace.Git.ResolveConflict(SelectedConflict!.Path, resolution));

View File

@ -36,6 +36,7 @@ namespace MergeHub.ViewModels
_workspace.Refreshed += OnWorkspaceRefreshed; _workspace.Refreshed += OnWorkspaceRefreshed;
_workspace.StateChanged += RaiseCanExecuteChanged; _workspace.StateChanged += RaiseCanExecuteChanged;
_workspace.PlainCompareRequested += OnPlainCompareRequested; _workspace.PlainCompareRequested += OnPlainCompareRequested;
_workspace.OpenConflictRequested += OnOpenConflictRequested;
_workspace.Recent.Changed += (_, _) => _workspace.OnUi(RefreshRecent); _workspace.Recent.Changed += (_, _) => _workspace.OnUi(RefreshRecent);
_workspace.QueueChanged += (_, _) => RefreshQueue(); _workspace.QueueChanged += (_, _) => RefreshQueue();
@ -50,6 +51,16 @@ namespace MergeHub.ViewModels
IsGitMode = _workspace.IsRepositoryOpen; IsGitMode = _workspace.IsRepositoryOpen;
RefreshRemoteBranches(); RefreshRemoteBranches();
// Git 탭의 충돌 목록에서 파일을 두 번 눌러 이 화면이 처음 만들어진 경우(6차 B-1).
// 저장소는 Git 탭이 이미 열었다. 아래의 기본 저장소 열기로 가면 다른 저장소를 열 수 있다 -
// 지금 저장소로 목록만 세우고 그 파일을 연다.
if (_workspace.HasPendingOpenConflict)
{
OnWorkspaceRefreshed();
_workspace.DeliverPendingOpenConflict();
return;
}
// 폴더 비교에서 파일을 골라 이 화면이 만들어진 경우라면 그 파일을 먼저 연다. // 폴더 비교에서 파일을 골라 이 화면이 만들어진 경우라면 그 파일을 먼저 연다.
// 이 화면은 탭을 처음 열 때 만들어지므로, 요청이 우리보다 먼저 도착해 있을 수 있다. // 이 화면은 탭을 처음 열 때 만들어지므로, 요청이 우리보다 먼저 도착해 있을 수 있다.
if (_workspace.HasPendingPlainCompare) if (_workspace.HasPendingPlainCompare)
@ -868,10 +879,57 @@ namespace MergeHub.ViewModels
OnPropertyChanged(nameof(SelectedCommit)); OnPropertyChanged(nameof(SelectedCommit));
RaiseCommitDetail(); RaiseCommitDetail();
_ = LoadCommitAsync(); _ = RebuildListAfterRefreshAsync();
// 여기서 다시 세우는 것은 사용자가 고른 것이 아니다. LoadCommitAsync 안에서 묻지 않게 한다. // 여기서 다시 세우는 것은 사용자가 고른 것이 아니다. LoadCommitAsync 안에서 묻지 않게 한다.
} }
private void OnOpenConflictRequested(string path) => _ = OpenConflictAsync(path);
/// <summary>
/// Git 탭 충돌 목록에서 고른 파일을 연다(6차 B-1). 충돌 파일이므로 A-3 대로 세 칸으로 열린다.
///
/// 목록에 그 파일이 아직 없을 수 있다 - 병합 직후라 새로고침 뒤 목록 세우기가 아직 안 끝났거나,
/// 이 화면이 방금 만들어졌다. 게이트가 풀릴 때까지 기다렸다 목록을 세우고 나서 고른다.
/// 고르는 일은 <see cref="SelectedFile"/> 에 맡긴다 - 저장 안 한 편집이 있으면 거기서 묻는다.
/// </summary>
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;
}
/// <summary>
/// 새로고침 뒤 파일 목록을 <b>반드시</b> 다시 세운다(6차 B-1).
///
/// 새로고침은 저장소 상태를 읽는 동안(게이트를 쥔 채) 화면 목록을 갈아 끼우고, 끝나자마자 Git 탭이 제 일로
/// 게이트를 다시 잡는다. 그 틈에 목록 세우기를 부르면 "다른 작업 진행 중" 으로 건너뛰었다.
/// 병합하고 파일 비교 탭으로 오면 목록이 병합 전 커밋 파일(304개) 그대로였고, [새로고침] 을 한 번 더 눌러야 충돌 23개가 나왔다.
/// 게이트가 풀릴 때까지 기다렸다가 세우고, 또 막히면 몇 번 더 해 본다.
/// </summary>
private async Task RebuildListAfterRefreshAsync()
{
for (var attempt = 0; attempt < 5; attempt++)
{
await _workspace.WhenIdleAsync();
if (await LoadCommitAsync()) return;
}
}
/// <summary>선택 커밋이 담고 있는 파일 목록을 채우고, 가능하면 이전 선택을 유지한다.</summary> /// <summary>선택 커밋이 담고 있는 파일 목록을 채우고, 가능하면 이전 선택을 유지한다.</summary>
/// <returns> /// <returns>
/// 목록을 다시 세웠거나 세울 것이 없으면 true. <b>게이트에 막혀 건너뛰었으면 false</b> - /// 목록을 다시 세웠거나 세울 것이 없으면 true. <b>게이트에 막혀 건너뛰었으면 false</b> -

View File

@ -240,11 +240,18 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Grid.Row="0" Style="{StaticResource Muted}" Margin="0,0,0,6" TextWrapping="Wrap" <TextBlock Grid.Row="0" Style="{StaticResource Muted}" Margin="0,0,0,6" TextWrapping="Wrap"
Text="파일 전체를 한쪽으로 채택하려면 아래 버튼을 쓰고, 줄 단위로 고르려면 홈 탭의 3분할 화면에서 처리하세요."/> Text="파일 전체를 한쪽으로 채택하려면 아래 버튼을 쓰고, 줄마다 고르려면 목록을 두 번 누르거나 [3분할로 열기] 를 누르세요. 파일 비교 탭에서 세 칸으로 열립니다."/>
<!-- 두 번 누르거나 Enter 로 파일 비교 탭의 3분할에서 연다(6차 B-1).
두 번 누르기는 MouseBinding 이 아니라 목록의 MouseDoubleClick 으로 받는다 -
항목이 마우스 누름을 먼저 처리해 버려 MouseBinding 은 항목 위에서 안 온다. -->
<ListBox Grid.Row="1" ItemsSource="{Binding Workspace.Conflicts}" <ListBox Grid.Row="1" ItemsSource="{Binding Workspace.Conflicts}"
SelectedItem="{Binding SelectedConflict}" HorizontalContentAlignment="Stretch" SelectedItem="{Binding SelectedConflict}" HorizontalContentAlignment="Stretch"
MouseDoubleClick="Conflicts_MouseDoubleClick"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"> ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding OpenConflictInCompareCommand}"/>
</ListBox.InputBindings>
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
@ -271,6 +278,8 @@
</ListBox> </ListBox>
<WrapPanel Grid.Row="2" Margin="0,6,0,0"> <WrapPanel Grid.Row="2" Margin="0,6,0,0">
<Button Content="3분할로 열기" Command="{Binding OpenConflictInCompareCommand}" MinWidth="100"
ToolTip="고른 파일을 파일 비교 탭에서 세 칸으로 엽니다. 목록을 두 번 누르거나 Enter 를 눌러도 됩니다"/>
<Button Content="Accept Mine" Command="{Binding AcceptMineCommand}" MinWidth="100" Style="{StaticResource FilledButton}" <Button Content="Accept Mine" Command="{Binding AcceptMineCommand}" MinWidth="100" Style="{StaticResource FilledButton}"
Background="{DynamicResource Ubisam.Brush.Success}" Background="{DynamicResource Ubisam.Brush.Success}"
Foreground="{DynamicResource Ubisam.Brush.Accent.OnFill}"/> Foreground="{DynamicResource Ubisam.Brush.Accent.OnFill}"/>

View File

@ -36,6 +36,14 @@ namespace MergeHub.Views
} }
// PasswordBox 는 보안상 Password 를 바인딩할 수 없어 코드 비하인드로 전달한다. // PasswordBox 는 보안상 Password 를 바인딩할 수 없어 코드 비하인드로 전달한다.
/// <summary>충돌 목록을 두 번 누르면 파일 비교 탭의 3분할에서 연다(6차 B-1). 항목 위가 아니면 무시한다.</summary>
private void Conflicts_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (e.OriginalSource is not System.Windows.DependencyObject hit) return;
if (System.Windows.Controls.ItemsControl.ContainerFromElement((System.Windows.Controls.ItemsControl)sender, hit) is null) return;
if (DataContext is MergeHub.ViewModels.GitViewModel vm && vm.OpenConflictInCompareCommand.CanExecute(null))
vm.OpenConflictInCompareCommand.Execute(null);
}
private void Password_Changed(object sender, RoutedEventArgs e) private void Password_Changed(object sender, RoutedEventArgs e)
{ {
if (sender is PasswordBox box && ViewModel is { } vm) vm.Workspace.Password = box.Password; if (sender is PasswordBox box && ViewModel is { } vm) vm.Workspace.Password = box.Password;