diff --git a/doc/history/2026-09-14-7차.md b/doc/history/2026-09-14-7차.md
index 22e4bbf..caa622a 100644
--- a/doc/history/2026-09-14-7차.md
+++ b/doc/history/2026-09-14-7차.md
@@ -86,6 +86,43 @@ MergeSaveTest 70건 통과. 빌드 경고 0 · 오류 0. 설정 파일은 바이
MergeSaveTest 82건 통과. 빌드 경고 0 · 오류 0. 설정 파일은 바이트 그대로 되돌림.
콤보로 브랜치를 고르는 3-Way 는 병합이 아닐 때만 쓰는 길이라 화면으로는 따로 안 봤다 — 문구가 그대로인 것은 하네스로 확인했다.
+## B-1. 병합 커밋 뒤에도 `.git/MERGEHUB_CONFLICTS` 가 남는다 (2026-09-14, 완료)
+
+### 고치기 전에 다시 났다 (새로 만든 저장소, MergeHub 로 병합)
+
+| 때 | MERGE_HEAD | MERGEHUB_CONFLICTS |
+|---|---|---|
+| 병합 전 | 없음 | 없음 |
+| MergeHub [Merge] 뒤 | 있음 | 있음 |
+| [작업 취소] 뒤 | 없음 | **남음** |
+| 다시 병합 → 터미널에서 풀고 커밋 → 앱 [새로고침] | 없음 | **남음** |
+| (6차 D-1) MergeHub [병합 커밋] 뒤 | 없음 | **남음** |
+
+### 원인
+
+`GitService.RememberMergeConflicts` 가 병합 때 적기만 하고, 병합 커밋 · 작업 취소 · 상태 다시 읽기 어디에도 치우는 코드가 없었다.
+첫 줄의 MERGE_HEAD 와 대조하므로(`GetMergeConflictTotal`) 다음 병합의 수가 틀리지는 않았다 - 남의 `.git` 에 우리 파일이 남는 것만 문제였다.
+
+### 고친 것
+
+| 파일 | 무엇 |
+|---|---|
+| `Services/MergeConflictRecord.cs` (새 파일) | `IsStale(MERGE_HEAD, 첫 줄)` — 병합 중이 아니거나, 빈 파일이거나, 첫 줄이 지금 MERGE_HEAD 와 다르면 쓸모없음 |
+| `Services/GitService.cs` | `ForgetStaleMergeConflicts()` — 쓸모없으면 지운다. 잠김 등으로 못 지워도 다른 일을 막지 않는다. `CommitMerge` 뒤 · `AbortOperation`(작업 취소) 뒤에 부른다 |
+| `Services/IGitService.cs` | 선언 |
+| `Services/GitWorkspace.cs` | `ReadState` 처음에 부른다 — 새로고침 · 저장소 열기 · 모든 Git 작업 뒤. 터미널에서 끝낸 병합도 여기서 치운다 |
+| 하네스 `MergeSaveTest` | `IsStale` 검사 6건(병합 아님 · 빈 MERGE_HEAD · 같은 병합은 둠 · 다른 병합 · 대소문자와 공백 · 빈 파일) |
+
+### 확인한 것 (세 경우 모두 저장소를 새로 만들어)
+
+| 경우 | 병합 중 | 끝난 뒤 |
+|---|---|---|
+| ① [Merge] → [작업 취소] | 파일 있음 · 진행 줄 "남은 충돌 24개 · 푼 것 2개 (2/26)" | MERGE_HEAD 없음 · **파일 없음** |
+| ② [Merge] → git 으로 풀기만 → [새로고침] → [병합 커밋] | 다 풀고 새로고침해도 **파일 그대로** · "충돌 정리 끝 · 푼 것 26개" (병합 중에는 안 지운다) | 커밋 `5f9d5d9`(부모 둘) · **파일 없음** |
+| ③ [Merge] → 터미널에서 풀고 커밋 → [새로고침] | 새로고침 전에는 파일 남음 | 새로고침 뒤 **파일 없음** |
+
+MergeSaveTest 88건 통과. 빌드 경고 0 · 오류 0. 설정 파일은 바이트 그대로 되돌림.
+
## E-2. 폴더 비교 탭에 폴더를 하나씩 끌어다 놓으면 첫 칸 → 둘째 칸 차례로 (2026-09-14, 추가 요청, 완료)
사용자: "폴더비교 탭 첫번째 드래그 첫번째 폴더 경로에 들어가고 두번째는 드래그앤드롭은 두번째로 들어가게해줘 경로텍스트 박스에 안넣어도 그냥 화면에다 넣어둬 기능 동작하도록".
diff --git a/doc/history/changelog.md b/doc/history/changelog.md
index 095d92b..38de13d 100644
--- a/doc/history/changelog.md
+++ b/doc/history/changelog.md
@@ -8,6 +8,7 @@
| 날짜 | 항목 | 커밋 |
|---|---|---|
+| 2026-09-14 | **7차 B-1** — 병합 때 적는 처음 충돌 목록(`.git/MERGEHUB_CONFLICTS`)이 병합 커밋 · 작업 취소 · 터미널에서 끝낸 병합 뒤에도 남았다. MERGE_HEAD 가 없거나 다른 병합의 것이면 병합 커밋 · 작업 취소 직후와 상태를 다시 읽을 때마다 지운다. 병합 중에는 그대로라 "푼 것 M개" 는 이어진다 | 아래 |
| 2026-09-14 | **7차 A-2** — 병합 충돌 네 칸에서 Take Theirs 로그가 "10행 (3-Way 안 씀) 반영" 이고 Take Mine 은 로그가 없었다. 이제 "10행 Theirs(feature/theirs) 반영" · "10행 Mine(feature/mine) 으로 정함". 충돌 네 칸 머리는 3-Way 콤보 대신 "병합 충돌 · REMOTE = 상대". 브랜치 3-Way 문구는 그대로 | 아래 |
| 2026-09-14 | **7차 E-2** — 폴더 비교 화면 아무 데나 폴더를 하나 놓으면 늘 첫 칸에 들어가 두 번째 폴더가 첫 폴더를 덮어썼다. 놓는 차례대로 첫 칸 → 둘째 칸(양쪽이 차면 바로 비교), 경로 칸 위에 놓은 것도 차례에 친다. 탐색기에서 끌어다 놓아 확인 | 아래 |
| 2026-09-14 | **7차 E-3** — 켤 때 사용자 이름 창을 Alt+F4 로 닫으면 이름 없이 본 창을 쓸 수 있었고 창도 끌려 움직였다. 저장 전에는 닫히지 않고 움직이지 않게(플랫폼 `UserNameDialog`, 확인 받고 `D:\UbisamPlatform\bin` 배포). MergeHub 코드 변경 없음 | 아래 |
diff --git a/doc/진행상황.md b/doc/진행상황.md
index 534b1a7..bff0fcc 100644
--- a/doc/진행상황.md
+++ b/doc/진행상황.md
@@ -157,7 +157,7 @@
| 상태 | 항목 |
|---|---|
-| 대기 | B-1 병합 커밋 뒤에도 `.git/MERGEHUB_CONFLICTS` 가 남는다 |
+| **완료** | B-1 병합 커밋 뒤에도 `.git/MERGEHUB_CONFLICTS` 가 남는다 — 병합 커밋 · 작업 취소 직후와 새로고침마다 쓸모없어진 목록을 지움(터미널에서 끝낸 병합 포함). 병합 중에는 그대로 |
### C. 병합 말고 다른 충돌 길
diff --git a/src/MergeHub/Services/GitService.cs b/src/MergeHub/Services/GitService.cs
index d5bca24..ff41215 100644
--- a/src/MergeHub/Services/GitService.cs
+++ b/src/MergeHub/Services/GitService.cs
@@ -550,6 +550,7 @@ namespace MergeHub.Services
// libgit2 의 reset(HARD) 이 MERGE_HEAD / CHERRY_PICK_HEAD / REVERT_HEAD 까지 정리한다.
Repo.Reset(ResetMode.Hard, Repo.Head.Tip);
+ ForgetStaleMergeConflicts(); // MERGE_HEAD 가 없어졌으니 처음 충돌 목록도 치운다(7차 B-1)
Report("작업 취소 (reset --hard HEAD)");
}
@@ -880,6 +881,28 @@ namespace MergeHub.Services
return lines.Skip(1).Count(l => l.Trim().Length > 0);
}
+ ///
+ /// 쓸모없어진 충돌 목록 파일을 지운다(7차 B-1). 지웠으면 true.
+ /// 병합 커밋 · 작업 취소 직후와 상태를 다시 읽을 때마다 부른다 - 터미널에서 병합을 끝낸 경우도 다음 새로고침에 치운다.
+ /// 못 지워도(잠김 등) 다른 일을 막지 않는다. 다음에 다시 시도한다.
+ ///
+ public bool ForgetStaleMergeConflicts()
+ {
+ var path = Path.Combine(Repo.Info.Path, MergeConflictListFile);
+ if (!File.Exists(path)) return false;
+
+ try
+ {
+ var first = File.ReadAllLines(path).FirstOrDefault();
+ if (!MergeConflictRecord.IsStale(ReadMergeHeadSha(), first)) return false;
+
+ File.Delete(path);
+ return true;
+ }
+ catch (IOException) { return false; }
+ catch (UnauthorizedAccessException) { return false; }
+ }
+
public ThreeWayContent GetThreeWayContent(string path)
{
var normalized = NormalizePath(path);
@@ -1019,6 +1042,7 @@ namespace MergeHub.Services
var signature = BuildSignature();
var commit = Repo.Commit(message, signature, signature);
+ ForgetStaleMergeConflicts(); // 병합이 끝났으니 처음 충돌 목록도 치운다(7차 B-1)
Report($"병합 커밋: {Shorten(commit.Sha)} {commit.MessageShort}");
return commit.Sha;
}
diff --git a/src/MergeHub/Services/GitWorkspace.cs b/src/MergeHub/Services/GitWorkspace.cs
index f81c339..5e28b2b 100644
--- a/src/MergeHub/Services/GitWorkspace.cs
+++ b/src/MergeHub/Services/GitWorkspace.cs
@@ -335,6 +335,9 @@ namespace MergeHub.Services
{
if (!Git.IsOpen) return;
+ // 병합이 끝났는데 남은 처음 충돌 목록을 치운다 - MergeHub 밖(터미널)에서 끝낸 병합도 여기서 걸린다(7차 B-1).
+ Git.ForgetStaleMergeConflicts();
+
var info = Git.GetRepositoryInfo();
var branches = Git.GetBranches();
var commits = Git.GetCommits(maxCount: Settings.CommitPageSize <= 0 ? 200 : Settings.CommitPageSize);
diff --git a/src/MergeHub/Services/IGitService.cs b/src/MergeHub/Services/IGitService.cs
index cfb3649..fa93730 100644
--- a/src/MergeHub/Services/IGitService.cs
+++ b/src/MergeHub/Services/IGitService.cs
@@ -82,6 +82,9 @@ namespace MergeHub.Services
/// 이번 병합에서 처음 난 충돌 파일 수. MergeHub 가 병합할 때 적어 둔 것, 모르면 null(6차 C-1).
int? GetMergeConflictTotal();
+ /// 쓸모없어진 처음 충돌 목록(.git/MERGEHUB_CONFLICTS)을 지운다. 지웠으면 true(7차 B-1).
+ bool ForgetStaleMergeConflicts();
+
/// 충돌 파일의 Base/Mine/Theirs 3분할 본문.
ThreeWayContent GetThreeWayContent(string path);
diff --git a/src/MergeHub/Services/MergeConflictRecord.cs b/src/MergeHub/Services/MergeConflictRecord.cs
new file mode 100644
index 0000000..1515862
--- /dev/null
+++ b/src/MergeHub/Services/MergeConflictRecord.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace MergeHub.Services
+{
+ ///
+ /// 병합 때 처음 충돌 목록을 적어 두는 .git/MERGEHUB_CONFLICTS 를 치울지 가른다(7차 B-1).
+ ///
+ /// 이 파일은 "푼 것 M개 (M/전체)" 를 세려고 둔다(6차 C-1). 첫 줄이 그때의 MERGE_HEAD 라 다른 병합과 섞이지는 않지만,
+ /// 지우는 곳이 없어 병합 커밋 · 작업 취소 · 터미널에서 끝낸 병합 뒤에도 남의 .git 에 남았다.
+ /// 판단만 여기 두어 저장소 없이 검사한다. 지우는 것은 가 한다.
+ ///
+ public static class MergeConflictRecord
+ {
+ ///
+ /// 적어 둔 것이 이제 쓸모없는지. 병합 중이 아니면(MERGE_HEAD 없음) 쓸모없다.
+ /// 병합 중이어도 첫 줄이 지금 MERGE_HEAD 와 다르면 다른 병합의 것이라 쓸모없다.
+ ///
+ /// 지금 MERGE_HEAD. 병합 중이 아니면 null.
+ /// 적어 둔 파일의 첫 줄. 빈 파일이면 null.
+ public static bool IsStale(string? mergeHeadSha, string? recordFirstLine)
+ {
+ if (string.IsNullOrWhiteSpace(mergeHeadSha)) return true;
+ if (string.IsNullOrWhiteSpace(recordFirstLine)) return true;
+ return !string.Equals(recordFirstLine!.Trim(), mergeHeadSha!.Trim(), StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}