6차 C-1: 결과 로그 한 번, 충돌 목록에 종류와 푼 개수

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
김승준 2026-09-14 16:07:56 +09:00
parent d6ff7a65eb
commit 5856ca0436
10 changed files with 173 additions and 29 deletions

View File

@ -833,3 +833,47 @@ Allman 은 모두 새 줄(Visual Studio 기본값과 같다). 처음에는 Defau
- **A-2 · B-2** — 목록을 다시 세울 때 빈 목록으로 갈아엎지 않고, 실린 파일을 기준으로 다룬다.
코드 변경 없음. 흐름 ② 는 D-1(처음부터 다시 끝까지)에서 한 번 더 본다.
## C-1. 로그와 목록 다듬기 (2026-09-14, 완료)
### 고치기 전에 다시 났다 (새로 만든 저장소를 MergeHub 로 병합)
| 확인 | 결과 |
|---|---|
| 병합 로그 | "충돌 26건 - 해결이 필요합니다." **두 번**. 두 번째는 머지 주석 자동 해결 줄들 **뒤에** 다시 나와 순서도 헷갈림 |
| 충돌 탭 목록 표시 | 24줄 중 표시가 붙은 것은 `Recipe/ToBeDeleted.cs · 상대쪽 삭제` 하나. `Ui/Views/NewPanel.cs`(양쪽 새 파일) · `Recipe/ToBeDeletedByMe.cs`(내가 삭제)는 표시 없음 |
| 푼 개수 | 어디에도 없음 |
| CP949 로그 이름 | `충돌 해결(직접 편집, CP949 · CRLF): Common/Util/한글설명.cs` — **이미 CP949**. A-1 에서 로그 문장을 `TextFile.Describe` 로 바꾸며 없어졌다. 이 항목은 확인만 |
### 무엇이 원인이었나
- `GitService` 가 결과 문장을 로그에 적고(`Report` → `Progress`), 병합 · revert · cherry-pick · rebase · pull 명령이 **같은 문장을 한 번 더** 적었다.
- 목록 템플릿에는 "이진" 과 "상대쪽 삭제" 두 표시만 있었다.
- 병합에서 처음 몇 개가 충돌했는지 남기는 곳이 없었다.
### 고친 것
| 파일 | 무엇 |
|---|---|
| `ViewModels/GitViewModel.cs` | 병합 · revert · cherry-pick · rebase 시작 · rebase 계속 · pull 에서 결과 문장을 다시 적지 않는다 |
| `Services/GitService.cs` | 추적 브랜치가 없어 pull 을 못 할 때도 로그에 적는다(이 길만 서비스가 안 적었다). 병합에서 충돌이 나면 처음 충돌 목록을 `.git/MERGEHUB_CONFLICTS`(첫 줄 MERGE_HEAD)에 적고, `GetMergeConflictTotal` 로 읽는다. MERGE_HEAD 가 다르면 다른 병합의 것이라 쓰지 않는다 |
| `Models/GitModels.cs` | `ConflictInfo.KindLabel` — 양쪽 수정 · 상대 삭제 · 내가 삭제 · 양쪽 새 파일(이진이면 " · 이진"), `IsDeletion` |
| `Views/GitView.xaml` | 목록 줄마다 종류(한쪽 삭제는 경고 색, 이진은 위험 색). 목록 위에 진행 한 줄 |
| `Services/GitWorkspace.cs` | `ConflictProgressText` — "남은 충돌 N개 · 푼 것 M개 (M/전체)", 다 풀면 "충돌 정리 끝 · 푼 것 M개 - 병합 커밋을 할 수 있습니다". 처음 수를 모르면(MergeHub 밖에서 시작한 병합) 남은 수만 |
처음에는 푼 개수를 git 인덱스의 해결 기록(REUC)으로 셌다. 확인해 보니 libgit2 가 **저절로 병합된 파일 7개**도 거기 남겨 병합 직후 "푼 것 9개 (9/33)" 로 틀리게 나왔다. 그래서 병합할 때 처음 목록을 적어 두는 쪽으로 바꿨다.
### 확인한 것
| 확인 | 결과 |
|---|---|
| 병합 로그 | "해결이 필요합니다" **한 번**. 그 뒤로 자동 해결 두 줄과 "자동으로 풀었습니다. 남은 충돌 24건." |
| 기록 파일 | 경로 26개 |
| 목록 종류 | 양쪽 수정 21 · 상대 삭제 1(`Recipe/ToBeDeleted.cs`) · 내가 삭제 1(`Recipe/ToBeDeletedByMe.cs`) · 양쪽 새 파일 1(`Ui/Views/NewPanel.cs`) |
| 병합 직후 진행 줄 | **남은 충돌 24개 · 푼 것 2개 (2/26)** — 머지 주석만 다른 2개가 스스로 풀림 |
| 같은 줄 충돌 15개 Accept Mine | 남은 충돌 9개 · 푼 것 17개 (17/26) |
| 앱을 다시 켠 뒤 | 남은 충돌 9개 · 푼 것 17개 (17/26) — 이어짐 |
| 남은 9개 Accept Mine | 충돌 정리 끝 · 푼 것 26개 - 병합 커밋을 할 수 있습니다 |
| 병합 전 | 진행 줄 없음 |
FolderTest 62 · ReportTest 31 · GitOpTest 36 건 통과(`GitModels.cs` 를 함께 컴파일). 빌드 경고 0 · 오류 0.

View File

@ -8,6 +8,7 @@
| 날짜 | 항목 | 커밋 |
|---|---|---|
| 2026-09-14 | **6차 C-1** — 병합 · revert · cherry-pick · rebase · pull 결과가 로그에 두 번 찍히던 것을 한 번으로. 충돌 탭 목록에 종류(양쪽 수정 · 상대 삭제 · 내가 삭제 · 양쪽 새 파일)와 "남은 충돌 N개 · 푼 것 M개 (M/전체)". 처음 충돌 목록을 .git 에 적어 두어 앱을 다시 켜도 이어짐 | 아래 |
| 2026-09-14 | **6차 B-8 닫음** — 병합 커밋 뒤 파일 비교 탭이 어정쩡하게 남던 증상이 다시 나지 않음. Git 으로 풀고 커밋 · 탭에서 마지막 충돌을 풀고 커밋 두 흐름 모두 목록 304 · 두 칸 · 선택 커밋 = 병합 커밋. B-1 · A-3 에서 없어진 것으로 봄 | 아래 |
| 2026-09-14 | **6차 B-7** — 병합 중 상태바 인코딩 · 줄바꿈을 충돌 표시가 든 워킹트리가 아니라 내 쪽(ours) 파일로("UTF-8 · 혼합" → "UTF-8 (BOM) · CRLF"). 네 칸 요약을 "남은 충돌 N개 · 받을 변경 M줄" 로, 범례 수를 먼저 세어 한 박자 늦던 것도 고침 | 아래 |
| 2026-09-14 | **E-1 C# 코드 스타일 맞춤** — AURORA(형식 · 메서드만 새 줄, `} else {`) / Allman(모두 새 줄)로 중괄호 줄바꿈을 맞춘 뒤 비교. 두 칸 · 네 칸 · mergetool · 폴더 비교, 설정. 스타일만 바뀐 편집은 파일을 바꿀 때 묻지 않음. StyleTest 78건 | 아래 |

View File

@ -257,7 +257,7 @@ figcaption{font-size:.82rem;color:var(--ink-3);margin-top:6px}
<div class="facts">
<div><b>22벌 · 948건</b><span>검사 하네스 전부 통과</span></div>
<div><b>13 / 15</b><span>6차 항목 완료 (C-1 · D-1 남음)</span></div>
<div><b>14 / 15</b><span>6차 항목 완료 (D-1 남음)</span></div>
<div><b>68</b><span>MergeHub 로컬 커밋 (푸시 0)</span></div>
<div><b>26 → 24</b><span>만든 저장소 병합 충돌 (주석만 다른 2건 자동 해결)</span></div>
</div>
@ -843,7 +843,7 @@ Log(<span class="s">"step 5"</span>);</pre>
<li><span class="chip c-done">완료</span> B-6 충돌 여러 개 한 번에 · C# 스타일 맞춤 · 시연 재료</li>
<li><span class="chip c-done">완료</span> B-7 병합 중 상태바 값 — 내 쪽 파일 기준 · 남은 충돌 수</li>
<li><span class="chip c-done">완료</span> B-8 병합 커밋 뒤 탭 상태 — 다시 나지 않음</li>
<li><span class="chip c-wait">대기</span> C-1 로그 · 목록 다듬기</li>
<li><span class="chip c-done">완료</span> C-1 로그 · 목록 다듬기 — 충돌 종류 · 푼 개수</li>
</ul>
<ul>
<li><span class="chip c-wait">대기</span> D-1 새 저장소로 끝까지 — 줄끝이 바뀐 파일 0개가 기준</li>
@ -878,16 +878,16 @@ Log(<span class="s">"step 5"</span>);</pre>
<div class="page-head">
<div class="eyebrow">앞으로 남은 내용</div>
<h1>남은 일</h1>
<p class="lede">6차 지시서의 남은 두 항목과 일부러 미뤄 둔 것. 6차 규칙대로 C → D 순서로 간다.</p>
<p class="lede">6차 지시서의 마지막 항목(D-1)과 일부러 미뤄 둔 것.</p>
</div>
<div class="doc">
<h2>6차 — 병합을 끝까지 믿고 쓸 수 있게</h2>
<p>15개 항목 중 A 다섯 · B 여덟이 끝났다(B-8 은 다시 나지 않아 코드 변경 없이 닫음 ·B-6: 같은 줄 충돌 15개 22.0초 → 1.2초 · B-7: 병합 중 상태바가 내 쪽 파일 기준, 남은 충돌 수). 남은 것은 아래 순서다.</p>
<p>15개 항목 중 A 다섯 · B 여덟 · C-1 이 끝났다(C-1: 로그 한 번 · 충돌 종류 · 푼 개수 ·B-8 은 다시 나지 않아 코드 변경 없이 닫음 ·B-6: 같은 줄 충돌 15개 22.0초 → 1.2초 · B-7: 병합 중 상태바가 내 쪽 파일 기준, 남은 충돌 수). 남은 것은 아래 순서다.</p>
<div class="tbl wide"><table>
<tr><th>항목</th><th>상태</th><th>무엇이 문제인가</th><th>끝났다고 볼 기준</th></tr>
<tr><td><b>B-7</b> 상태바가 병합 중에 믿을 수 없는 값을 보인다</td><td><span class="chip c-done">완료</span></td><td>인코딩 · 줄바꿈을 충돌 표시가 들어간 워킹트리 파일에서 읽어, 양쪽 CRLF 인 파일도 “UTF-8 · 혼합”. “남은 차이 6줄” 이 충돌을 다 정해도 그대로.</td><td>병합 중 상태바 인코딩 · 줄바꿈이 ours 기준. 상태바에 남은 충돌 수가 나오고 풀면 준다.</td></tr>
<tr><td><b>B-8</b> 병합 커밋 뒤 파일 비교 탭이 어정쩡하게 남는다</td><td><span class="chip c-done">완료</span></td><td>목록이 비고, 병합과 상관없는 파일이 BASE 칸에만 뜨고 언어는 “평문”.</td><td>병합 커밋이 끝나면 병합 전과 같은 보통 상태(선택 커밋 = 방금 만든 병합 커밋).</td></tr>
<tr><td><b>C-1</b> 로그와 목록 다듬기</td><td><span class="chip c-wait">대기</span></td><td>“충돌 22건” 로그가 두 번. 충돌 종류 표시가 들쭉날쭉(상대쪽 삭제만). 풀린 개수 없음. 로그가 CP949 를 “ks_c_5601-1987” 로 적음.</td><td>로그 한 번. 종류(양쪽 수정 · 상대 삭제 · 내가 삭제 · 양쪽 새 파일)와 풀린 개수. 인코딩 이름이 상태바와 같음.</td></tr>
<tr><td><b>C-1</b> 로그와 목록 다듬기</td><td><span class="chip c-done">완료</span></td><td>“충돌 22건” 로그가 두 번. 충돌 종류 표시가 들쭉날쭉(상대쪽 삭제만). 풀린 개수 없음. 로그가 CP949 를 “ks_c_5601-1987” 로 적음.</td><td>로그 한 번. 종류(양쪽 수정 · 상대 삭제 · 내가 삭제 · 양쪽 새 파일)와 풀린 개수. 인코딩 이름이 상태바와 같음.</td></tr>
<tr><td><b>D-1</b> 병합을 처음부터 다시 끝까지</td><td><span class="chip c-wait">대기</span></td><td>A ~ C 를 고친 뒤 시험 저장소(C1Repo)를 새로 만들어 같은 병합을 다시 한다. 5차 C-1 과 같은 표로 23가지 중 무엇이 사라졌는지 적는다.</td><td>MergeHub 만으로 병합 커밋까지. 커밋된 파일 중 <b>줄끝이 바탕과 달라진 파일 0개</b>. A 다섯 가지가 다시 나지 않는다. 새로 걸린 것은 고치지 말고 적어 7차로.</td></tr>
</table></div>
@ -972,6 +972,7 @@ Log(<span class="s">"step 5"</span>);</pre>
<tr><td>B-6</td><td>충돌 탭에서 여러 파일을 골라 Accept 한 번, 저장소 다시 읽기는 끝에 한 번</td><td>같은 줄 충돌 15개 22.0초 → 1.2초</td></tr>
<tr><td>B-7</td><td>병합 중 상태바 인코딩 · 줄바꿈을 내 쪽(ours) 파일로, 요약에 "남은 충돌 N개 · 받을 변경 M줄"</td><td>“UTF-8 · 혼합” → “UTF-8 (BOM) · CRLF” · 충돌 6개 정하며 6 → 0</td></tr>
<tr><td>B-8</td><td>병합 커밋 뒤 파일 비교 탭 — 다시 나지 않아 닫음(B-1 · A-3 에서 없어짐)</td><td>두 흐름 모두 목록 304 · 두 칸 · 선택 커밋 = 병합 커밋</td></tr>
<tr><td>C-1</td><td>결과 로그 한 번 · 충돌 탭 목록에 종류 네 가지 · "남은 충돌 N개 · 푼 것 M개 (M/전체)"</td><td>병합 직후 2/26 → 15개 풀어 17/26 → 앱 다시 켜도 17/26</td></tr>
<tr><td>E-1</td><td>C# 코드 스타일 맞춤(AURORA / Allman) — 두 칸 · 네 칸 · mergetool · 폴더 비교 · 설정</td><td>시연 파일 남은 차이 39줄 → 1줄 · StyleTest 78건</td></tr>
<tr><td>E-2</td><td>시연 재료 <code>demo\make-demo.ps1</code> — 폴더 비교 · 스타일 · mergetool · 큰 파일 · 병합 저장소</td><td>시연 저장소 git 충돌 8 → MergeHub 6</td></tr>
</table></div>
@ -1127,7 +1128,7 @@ Log(<span class="s">"step 5"</span>);</pre>
<tr><th>단계</th><th>v1.1 계획</th><th>실제</th><th>상태</th></tr>
<tr><td>1 착수 · 설계</td><td>09.02 – 09.04 · 요구사항 · 구조 · WPF 골격</td><td>기획안 v1.1(09-03). 구조를 App · Core · Git · UI 분리 대신 <b>UbisamBase 셸 + 플러그인</b>으로</td><td><span class="chip c-done">완료</span></td></tr>
<tr><td>2 기반 구축</td><td>09.07 – 09.14 · Git 연동 · 커밋 그래프 · 편집기 · 비교 엔진</td><td>09-07 방향 전환 · 09-08 기능 덩어리 5개 · 09-09 3차 · 09-10 4차 · 09-11 5차. <b>MVP 8개가 이 단계 안에 모두 들어갔다</b></td><td><span class="chip c-done">완료</span></td></tr>
<tr><td>3 핵심 기능</td><td>09.15 – 09.23 · 3-Way · 부분 수용 · Undo · 우클릭 · Git 메뉴</td><td>기능은 앞당겨 끝났으므로 “병합을 끝까지 믿고 쓸 수 있게”(6차)로 쓴다. 09-14: A 5 · B 8 완료, C-1 · D-1 남음</td><td><span class="chip c-doing">진행</span></td></tr>
<tr><td>3 핵심 기능</td><td>09.15 – 09.23 · 3-Way · 부분 수용 · Undo · 우클릭 · Git 메뉴</td><td>기능은 앞당겨 끝났으므로 “병합을 끝까지 믿고 쓸 수 있게”(6차)로 쓴다. 09-14: A 5 · B 8 · C 1 완료, D-1 남음</td><td><span class="chip c-doing">진행</span></td></tr>
<tr><td>4 테스트 · 배포</td><td>09.29 – 10.02 · 성능 · 인코딩/줄바꿈 검증 · 베타 · 매뉴얼</td><td>성능 · 인코딩 · 줄바꿈 검증은 5차 · 6차에서 앞당겨 함. 매뉴얼 초안은 이 문서. 베타 배포는 대기</td><td><span class="chip c-wait">대기</span></td></tr>
</table></div>
<h4>일하는 방식 — v1.1 에 없던 것</h4>

View File

@ -125,7 +125,7 @@
| 상태 | 항목 |
|---|---|
| 대기 | C-1 로그와 목록 다듬기 |
| **완료** | C-1 로그와 목록 다듬기 — 결과 로그 한 번, 목록에 충돌 종류 네 가지, "남은 충돌 N개 · 푼 것 M개 (M/전체)". CP949 로그 이름은 이미 고쳐져 있었음 |
### D. 확인

View File

@ -71,7 +71,27 @@ namespace MergeHub.Models
bool HasBase,
bool HasOurs,
bool HasTheirs,
bool IsBinary);
bool IsBinary)
{
/// <summary>
/// 충돌 종류(6차 C-1). 충돌 탭 목록에 파일마다 붙인다 - 예전에는 "상대쪽 삭제" 만 있어서
/// 양쪽이 새로 만든 파일이나 내가 지운 파일은 겉보기로 보통 충돌과 구별이 안 됐다.
/// </summary>
public string KindLabel
{
get
{
var kind = !HasTheirs && HasOurs ? "상대 삭제"
: !HasOurs && HasTheirs ? "내가 삭제"
: !HasBase ? "양쪽 새 파일"
: "양쪽 수정";
return IsBinary ? kind + " · 이진" : kind;
}
}
/// <summary>한쪽이 지운 충돌인지. 목록에서 눈에 띄는 색으로 칠한다.</summary>
public bool IsDeletion => HasOurs != HasTheirs;
}
/// <summary>3-way 병합 뷰에 그대로 바인딩되는 3분할 본문.</summary>
public sealed record ThreeWayContent(

View File

@ -251,8 +251,12 @@ namespace MergeHub.Services
var tracked = Repo.Head.TrackedBranch;
if (tracked is null)
return new MergeResultInfo(MergeOutcome.Failed, null, Array.Empty<ConflictInfo>(),
$"현재 브랜치 '{Repo.Head.FriendlyName}' 에 추적 중인 원격 브랜치가 없습니다.");
{
// 다른 결과처럼 여기서 로그에 적는다 - 화면은 결과 메시지를 따로 적지 않는다(6차 C-1).
var message = $"현재 브랜치 '{Repo.Head.FriendlyName}' 에 추적 중인 원격 브랜치가 없습니다.";
Report(message);
return new MergeResultInfo(MergeOutcome.Failed, null, Array.Empty<ConflictInfo>(), message);
}
return Merge(tracked.FriendlyName, commitOnSuccess: false);
}
@ -516,6 +520,7 @@ namespace MergeHub.Services
};
var conflicts = outcome == MergeOutcome.Conflicts ? GetConflicts() : Array.Empty<ConflictInfo>();
if (outcome == MergeOutcome.Conflicts) RememberMergeConflicts(conflicts);
var message = outcome switch
{
@ -840,6 +845,41 @@ namespace MergeHub.Services
return list;
}
/// <summary>병합 때 충돌 파일 목록을 적어 두는 파일(.git 안). 첫 줄은 MERGE_HEAD, 나머지는 경로.</summary>
private const string MergeConflictListFile = "MERGEHUB_CONFLICTS";
/// <summary>
/// 이번 병합에서 처음 난 충돌 파일을 적어 둔다(6차 C-1). 몇 개를 풀었는지 세려고 둔다 - 앱을 다시 켜도 이어진다.
///
/// git 인덱스의 해결 기록(REUC)으로 세면 안 된다. libgit2 는 <b>저절로 병합된 파일</b>도 거기 남겨서,
/// 충돌 26개 중 머지 주석 2개를 풀었을 뿐인데 "푼 것 9개 (9/33)" 로 나왔다.
/// </summary>
private void RememberMergeConflicts(IReadOnlyList<ConflictInfo> conflicts)
{
var head = ReadMergeHeadSha();
if (string.IsNullOrEmpty(head)) return;
var lines = new List<string> { head! };
lines.AddRange(conflicts.Select(c => c.Path));
File.WriteAllLines(Path.Combine(Repo.Info.Path, MergeConflictListFile), lines, new UTF8Encoding(false));
}
/// <summary>
/// 이번 병합에서 처음 난 충돌 파일 수(6차 C-1). 적어 둔 것이 없거나 다른 병합의 것이면 null -
/// MergeHub 밖에서 시작한 병합은 처음 수를 모른다.
/// </summary>
public int? GetMergeConflictTotal()
{
var path = Path.Combine(Repo.Info.Path, MergeConflictListFile);
var head = ReadMergeHeadSha();
if (string.IsNullOrEmpty(head) || !File.Exists(path)) return null;
var lines = File.ReadAllLines(path);
if (lines.Length == 0 || !string.Equals(lines[0].Trim(), head, StringComparison.OrdinalIgnoreCase)) return null;
return lines.Skip(1).Count(l => l.Trim().Length > 0);
}
public ThreeWayContent GetThreeWayContent(string path)
{
var normalized = NormalizePath(path);

View File

@ -178,6 +178,31 @@ namespace MergeHub.Services
[ObservableProperty] private string _authorName = Environment.UserName;
[ObservableProperty] private bool _isMergeInProgress;
/// <summary>이번 병합에서 처음 난 충돌 파일 수(6차 C-1). 병합 중이 아니거나 모르면 null.</summary>
[ObservableProperty] private int? _mergeConflictTotal;
/// <summary>
/// 충돌 탭 목록 위의 진행 한 줄(6차 C-1). 예전에는 몇 개를 풀었는지 알 수 없었다.
/// 처음 충돌 수는 병합할 때 .git 에 적어 두므로 앱을 다시 켜도 이어진다.
/// MergeHub 밖에서 시작한 병합이라 처음 수를 모르면 남은 수만 적는다.
/// </summary>
public string ConflictProgressText
{
get
{
if (!IsMergeInProgress) return string.Empty;
var left = Conflicts.Count;
if (MergeConflictTotal is not int total || total < left)
return left == 0 ? "충돌 정리 끝 - 병합 커밋을 할 수 있습니다" : $"남은 충돌 {left}개";
var done = total - left;
return left == 0
? $"충돌 정리 끝 · 푼 것 {done}개 - 병합 커밋을 할 수 있습니다"
: $"남은 충돌 {left}개 · 푼 것 {done}개 ({done}/{total})";
}
}
[ObservableProperty] private bool _isRebaseInProgress;
[ObservableProperty] private string _pendingOperationText = string.Empty;
[ObservableProperty] private CommitInfo? _selectedCommit;
@ -322,6 +347,7 @@ namespace MergeHub.Services
var rebase = Git.GetRebaseStatus();
var remotes = Git.GetRemotes();
var mergeInProgress = Git.IsMergeInProgress;
var conflictTotal = mergeInProgress ? Git.GetMergeConflictTotal() : null;
var remote = remotes.FirstOrDefault(r => r.Name == RemoteName) ?? remotes.FirstOrDefault();
var head = branches.FirstOrDefault(b => b.IsCurrentHead);
@ -356,6 +382,8 @@ namespace MergeHub.Services
Replace(CommitNodes, nodes);
Replace(StatusFiles, status.Files);
Replace(Conflicts, conflicts);
MergeConflictTotal = conflictTotal;
OnPropertyChanged(nameof(ConflictProgressText));
Replace(Tags, tags);
Replace(Submodules, submodules);
Replace(Stashes, stashes);

View File

@ -79,6 +79,9 @@ namespace MergeHub.Services
IReadOnlyList<ConflictInfo> GetConflicts();
/// <summary>이번 병합에서 처음 난 충돌 파일 수. MergeHub 가 병합할 때 적어 둔 것, 모르면 null(6차 C-1).</summary>
int? GetMergeConflictTotal();
/// <summary>충돌 파일의 Base/Mine/Theirs 3분할 본문.</summary>
ThreeWayContent GetThreeWayContent(string path);

View File

@ -184,7 +184,7 @@ namespace MergeHub.ViewModels
_workspace.ApplyCredentials();
var result = _workspace.Git.Pull(_workspace.RemoteName);
_workspace.MarkFetched();
_workspace.OnUi(() => _workspace.AppendLog(result.Message));
// 결과 한 줄은 GitService 가 로그에 적는다(Pull 은 Merge 를 거친다). 여기서 또 적으면 두 번 찍힌다(6차 C-1).
});
[RelayCommand(CanExecute = nameof(IsOpen))]
@ -238,7 +238,8 @@ namespace MergeHub.ViewModels
_workspace.OnUi(() =>
{
_workspace.AppendLog(result.Message);
// result.Message("충돌 N건 - 해결이 필요합니다.")는 GitService 가 이미 로그에 적었다(Progress).
// 여기서 또 적으면 두 번 찍히고, 자동 해결 줄 뒤에 다시 나와 순서도 헷갈렸다(6차 C-1).
if (noteOnly.Count > 0)
_workspace.AppendLog($"머지 주석만 다른 충돌 {noteOnly.Count}개 파일은 자동으로 풀었습니다. 남은 충돌 {remaining}건.");
@ -394,15 +395,15 @@ namespace MergeHub.ViewModels
[RelayCommand(CanExecute = nameof(HasCommit))]
private Task RevertAsync() => _workspace.RunAndRefreshAsync("revert", () =>
{
var result = _workspace.Git.Revert(SelectedCommit!.Sha);
_workspace.OnUi(() => _workspace.AppendLog(result.Message));
// 결과 한 줄은 GitService 가 로그에 적는다. 여기서 또 적으면 두 번 찍힌다(6차 C-1).
_workspace.Git.Revert(SelectedCommit!.Sha);
});
[RelayCommand(CanExecute = nameof(HasCommit))]
private Task CherryPickAsync() => _workspace.RunAndRefreshAsync("cherry-pick", () =>
{
var result = _workspace.Git.CherryPick(SelectedCommit!.Sha);
_workspace.OnUi(() => _workspace.AppendLog(result.Message));
// 결과 한 줄은 GitService 가 로그에 적는다(6차 C-1).
_workspace.Git.CherryPick(SelectedCommit!.Sha);
});
[RelayCommand(CanExecute = nameof(HasCommit))]
@ -415,8 +416,8 @@ namespace MergeHub.ViewModels
[RelayCommand(CanExecute = nameof(CanRebase))]
private Task RebaseAsync() => _workspace.RunAndRefreshAsync("rebase", () =>
{
var result = _workspace.Git.RebaseStart(SelectedBranch!.FriendlyName);
_workspace.OnUi(() => _workspace.AppendLog(result.Message));
// 결과 한 줄은 GitService 가 로그에 적는다(6차 C-1).
_workspace.Git.RebaseStart(SelectedBranch!.FriendlyName);
});
private bool IsRebasing() => _workspace.IsRebaseInProgress;
@ -424,8 +425,8 @@ namespace MergeHub.ViewModels
[RelayCommand(CanExecute = nameof(IsRebasing))]
private Task RebaseContinueAsync() => _workspace.RunAndRefreshAsync("rebase continue", () =>
{
var result = _workspace.Git.RebaseContinue();
_workspace.OnUi(() => _workspace.AppendLog(result.Message));
// 결과 한 줄은 GitService 가 로그에 적는다(6차 C-1).
_workspace.Git.RebaseContinue();
});
[RelayCommand(CanExecute = nameof(IsRebasing))]

View File

@ -239,8 +239,13 @@
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Style="{StaticResource Muted}" Margin="0,0,0,6" TextWrapping="Wrap"
<!-- 진행 한 줄(6차 C-1): "남은 충돌 N개 · 푼 것 M개 (M/전체)". 병합이 아니면 비어 접힌다. -->
<StackPanel Grid.Row="0" Margin="0,0,0,6">
<TextBlock Text="{Binding Workspace.ConflictProgressText}" FontWeight="Bold" Margin="0,0,0,2"
Visibility="{Binding Workspace.IsMergeInProgress, Converter={StaticResource BoolToVis}}"/>
<TextBlock Style="{StaticResource Muted}" TextWrapping="Wrap"
Text="파일 전체를 한쪽으로 채택하려면 아래 버튼을 쓰고, 줄마다 고르려면 목록을 두 번 누르거나 [3분할로 열기] 를 누르세요. 파일 비교 탭에서 세 칸으로 열립니다. Ctrl · Shift 로 여러 파일을 고르면 Accept 가 한 번에 적용됩니다(Ctrl+A 전부)."/>
</StackPanel>
<!-- 두 번 누르거나 Enter 로 파일 비교 탭의 3분할에서 연다(6차 B-1).
두 번 누르기는 MouseBinding 이 아니라 목록의 MouseDoubleClick 으로 받는다 -
@ -258,17 +263,18 @@
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path}" TextTrimming="CharacterEllipsis"/>
<TextBlock Text=" (binary)" FontSize="11"
Foreground="{DynamicResource Ubisam.Brush.Danger}"
Visibility="{Binding IsBinary, Converter={StaticResource BoolToVis}}"/>
<TextBlock Text=" · 상대쪽 삭제" FontSize="11"
Foreground="{DynamicResource Ubisam.Brush.Warning}">
<!-- 충돌 종류(6차 C-1): 양쪽 수정 · 상대 삭제 · 내가 삭제 · 양쪽 새 파일(+ 이진).
한쪽이 지운 것은 정할 것이 "남길까 지울까" 라 경고 색으로 띄운다. -->
<TextBlock Text="{Binding KindLabel, StringFormat={} · {0}}" FontSize="11" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Muted}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasTheirs}" Value="False">
<Setter Property="Visibility" Value="Visible"/>
<DataTrigger Binding="{Binding IsDeletion}" Value="True">
<Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Warning}"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsBinary}" Value="True">
<Setter Property="Foreground" Value="{DynamicResource Ubisam.Brush.Danger}"/>
</DataTrigger>
</Style.Triggers>
</Style>