6차 B-5: 머지 주석은 비교에서 가리고 주석만 다른 충돌은 양쪽 주석을 남겨 자동으로 푼다
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
de097da2ff
commit
8dd9fa98a6
@ -584,3 +584,73 @@ MERGED 는 git 이 충돌 표시를 넣어 둔 파일이라, 두 칸으로는
|
||||
위쪽 LOCAL(Mine) 칸 제목은 MERGED 칸과 같은 값을 쓴다(Git 모드에서는 둘 다 브랜치 이름). mergetool 에서는 결과 파일 이름 `Weld.cs` 가 보인다. 손대지 않았다.
|
||||
|
||||
빌드 경고 0 · 오류 0.
|
||||
|
||||
## B-5. 머지 주석은 소스에 넣고, 병합 뒤에는 그 줄을 무시한다 (2026-09-14, 완료)
|
||||
|
||||
사용자가 정한 것: 머지 주석은 지금처럼 소스에 넣는다. 비교하고 병합할 때 그 줄을 무시한다.
|
||||
|
||||
### 생성기에 넣은 경우 (`C1Repo`, D-1 에서도 쓴다)
|
||||
|
||||
| 이름 | 파일 | 내 쪽 | 상대 쪽 |
|
||||
|---|---|---|---|
|
||||
| K | `Ui/Views/Unit45.cs` | 13줄 코드 위에 `// [김승준] 2026-09-10` 한 줄 | 같은 자리에 `// [박지훈] 2026-09-11` 한 줄 |
|
||||
| L | `Ui/Dialogs/Unit46.cs` | 13줄 끝에 ` // [김승준] 2026-09-10` | 같은 줄 끝에 ` // [박지훈] 2026-09-11` |
|
||||
| M | `Common/Util/Unit47.cs` | 9줄 `= 111` 로 고치고 그 위에 내 주석 | `= 222` 로 고치고 그 위에 상대 주석 |
|
||||
|
||||
이제 병합하면 git 충돌은 23 → **26**개다.
|
||||
|
||||
### 고치기 전에 다시 났다
|
||||
|
||||
| 확인 | 결과 |
|
||||
|---|---|
|
||||
| MergeHub 로 병합 | git 충돌 **26**개, K · L · M 모두 충돌 |
|
||||
| 두 칸 비교: 줄 끝 주석 1 · 주석 줄 1 만 다른 두 파일 | **남은 차이 2줄** |
|
||||
| 네 칸: K · L · M | 모두 충돌로 물음 |
|
||||
|
||||
(파일을 바꾸고 5초 뒤에 읽은 값이 한 파일씩 밀려 있었다 - M 이라고 찍은 화면이 K 였다. 그래서 파일별 "예전 모습" 은 아래 "설정을 끄면" 에서 다시 읽은 값을 쓴다.)
|
||||
|
||||
### 무엇이 원인이었나
|
||||
|
||||
- 비교 규칙(`CompareRules`)에 머지 주석을 가리는 규칙이 없었다. 줄 필터는 사용자가 정규식을 적어야 했고, 줄 끝에 붙은 주석은 줄을 통째로 걸러야 해서 코드 차이까지 가려졌다.
|
||||
- git(libgit2)은 주석 줄도 글자로 본다. 양쪽이 같은 자리에 다른 주석을 달면 그 줄끼리 충돌한다. MergeHub 는 그대로 사람에게 물었다.
|
||||
|
||||
### 고친 것
|
||||
|
||||
| 파일 | 무엇 |
|
||||
|---|---|
|
||||
| `Services/MergeNote.cs` | `TailStart` · `WithoutTail` · `TailOf` — 줄 끝 머지 주석 조각을 찾는다. 주석이 둘 이어 붙어도 첫 주석부터. 주석 기호 앞이 공백이 아니면 글 속 기호로 본다(`"http://..."`). XAML 은 닫는 기호까지 있어야 한다 |
|
||||
| `Services/CompareRules.cs` | `IgnoreMergeNotes` · `NotePath`. 켜면 주석만 있는 줄은 걸러지고(줄 필터 켜짐과 따로), 비교 키는 **줄 끝 주석을 뗀 코드**. 주석 줄끼리는 같은 키라 이름 · 날짜가 달라도 짝지어지고, 빈 줄과는 짝지어지지 않는다. 상태바 "무시: 머지 주석" |
|
||||
| `Services/NoteRows.cs` (새) | 정렬기가 **주석 줄과 코드 줄을 한 행에 짝지으면** 주석 행 · 코드 행으로 나누고, 떼어 낸 코드 조각은 칸이 안 겹치는 다음 행과 합친다. M 에서 BASE 코드 줄이 양쪽 주석 줄과 한 행이 되어 "BASE 와 다름" 이 하나 더 세지고, 코드 충돌이 BASE 없이 보이던 것을 막는다 |
|
||||
| `Services/NoteConflict.cs` (새) | git 이 워킹트리에 남긴 충돌 표시를 읽어, **모든 덩어리가** 주석을 빼면 같은 코드일 때만 푼다. 양쪽 주석을 다 남긴다 - 줄 위 주석은 내 쪽 다음 상대 쪽(같은 줄은 한 번), 줄 끝 주석은 이어 붙인다. `|||||||` (diff3) 모양도 읽는다 |
|
||||
| `Services/GitService.cs` · `IGitService.cs` | `ResolveNoteOnlyConflicts` — 양쪽 다 있는 글 파일 충돌에만. 쓰기는 직접 편집 해결과 같은 길(내 쪽 형식 따름, A-1) |
|
||||
| `ViewModels/GitViewModel.cs` | [Merge] 뒤 충돌이 있으면 자동으로 풀고, 알림 · 로그에 몇 건 풀었는지와 남은 충돌 수 |
|
||||
| `Services/FolderComparer.cs` | 켜면 주석 줄을 빼고 줄 끝 주석을 뗀 글이 같으면 **같음**. 뗀 것이 없거나 줄끝(`\r`)이 다르면 가리지 않는다 |
|
||||
| `ViewModels/HomeViewModel.cs` · `FolderViewModel.cs` | 설정을 규칙에 넘기고 파일 경로로 주석 기호를 고른다. 두 칸 · 네 칸 정렬 뒤 `NoteRows` |
|
||||
| `MergeHubSettings.cs` | [머지 주석] "비교·병합에서 머지 주석 무시 (사람이 '// [이름] 2026-01-01' 모양으로 쓴 주석도 무시됨)", 기본 켬 |
|
||||
|
||||
검사: NoteTest 29 → **64**, RulesTest 21 → **36**, FolderTest 51 → **57**. 검사 모음 21개를 모두 새로 빌드해 **865건 통과**.
|
||||
|
||||
### 확인한 것 (새로 만든 저장소)
|
||||
|
||||
| 확인 | 결과 |
|
||||
|---|---|
|
||||
| MergeHub 로 병합 | git 충돌 **24**개. 알림 "충돌 24건 (머지 주석만 다른 2건은 자동으로 풀림)", 로그에 파일별 "머지 주석만 다른 충돌 1곳 자동 해결" |
|
||||
| K 스테이징된 글 | 13줄 `// [김승준] 2026-09-10`, 14줄 `// [박지훈] 2026-09-11`, 15줄 코드. BOM · CRLF 342 · 홀로 LF 0 · 충돌 표시 0. 워킹트리 = 인덱스 |
|
||||
| L 스테이징된 글 | 13줄 `Log("step 5"); // [김승준] 2026-09-10 // [박지훈] 2026-09-11`. BOM · CRLF 242 · 홀로 LF 0 |
|
||||
| M (코드도 다름) | 충돌로 남음(`UU`). 네 칸: 주석 행은 따로 **무시 1**, 코드 행은 BASE `101` · 내 쪽 `111` · 상대 `222` 가 한 행. **남은 충돌 1 · 남은 차이 1** |
|
||||
| 두 칸 비교: 줄 끝 주석 1 · 주석 줄 1 | **남은 차이 0줄** |
|
||||
|
||||
### 설정을 끄면 (설정 파일을 백업해 끄고 확인한 뒤 원래 바이트로 되돌림 - 해시 같음)
|
||||
|
||||
| 확인 | 결과 |
|
||||
|---|---|
|
||||
| 병합 | git 충돌 **26**개, K · L · M 모두 남음 |
|
||||
| 네 칸 K · L · M | 남은 충돌 1 · 1 · **2** (M 은 주석 줄끼리도 충돌) |
|
||||
| 두 칸 비교 | 남은 차이 **2줄** |
|
||||
|
||||
빌드 경고 0 · 오류 0.
|
||||
|
||||
### 남긴 것
|
||||
|
||||
- 자동 풀기는 Git 탭 [Merge] 에만 걸었다. pull · rebase · cherry-pick 이 낸 충돌에는 아직 걸지 않았다.
|
||||
- MERGED 칸에서 반영하며 새로 넣은 주석 줄은, 다시 정렬하면(타이핑 멈춤 · 규칙 변경) "무시" 색이 된다. 차이 수에는 원래도 세지 않던 줄이다.
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
| 날짜 | 항목 | 커밋 |
|
||||
|---|---|---|
|
||||
| 2026-09-14 | **6차 B-5** — 머지 주석을 비교에서 무시(주석 줄은 거르고 줄 끝 주석은 떼고 견줌). 주석만 다른 git 충돌은 양쪽 주석을 남겨 자동으로 풂. 정렬이 주석 줄과 코드 줄을 짝지으면 떼어 놓음. 폴더 비교도. 설정 [머지 주석] 에서 끔 | 아래 |
|
||||
| 2026-09-14 | **6차 B-4 확인 중 발견 두 건** — "마지막 차이" 로 멀리 건너뛰면 표시층 보정이 화면 밖 행으로 재져 네 칸이 9줄 위로 밀리던 것을 고침. `git mergetool` 이 넘긴 세 벌을 두 칸이 아닌 네 칸으로 열고 결과를 MERGED 파일에 쓴다 | 아래 |
|
||||
| 2026-09-14 | **6차 B-4** — 3분할 MERGED 칸 줄 번호 자리가 20px 라 2949 가 "294" 로 잘리던 것을 고침. 네 칸 여백을 54 → 66px 로 맞춰 넓혀 다섯 자리까지 보인다 | 아래 |
|
||||
| 2026-09-14 | **6차 B-3** — 병합 커밋 기본 메시지를 `Merge branch '상대' into 내 브랜치` 로. 첫 파일 이름이 굳어 남던 것, 커밋에 `#Conflicts:` 줄 24개와 해시 첫 줄이 들어가던 것을 고침. 병합이 끝나면 스스로 채운 글만 지운다 | 아래 |
|
||||
|
||||
@ -108,7 +108,7 @@
|
||||
| **완료** | B-3 커밋 메시지가 엉뚱하게 남는다 — 기본값 `Merge branch '상대' into 내 브랜치`, `#` 줄 뺌, 병합이 끝나면 칸을 비움 |
|
||||
| **완료** | B-4 MERGED 칸 줄 번호가 네 자리에서 잘린다 — 네 칸 여백을 66px 로, MERGED 번호 자리 20 → 36px(다섯 자리) |
|
||||
| **완료** | B-4 확인 중 발견 두 건 — 멀리 건너뛰면 네 칸이 위로 밀려 아래가 빔 · `git mergetool` 세 벌이 두 칸으로 열림 |
|
||||
| 대기 | B-5 머지 주석은 소스에 넣고, 병합 뒤에는 그 줄을 무시한다 |
|
||||
| **완료** | B-5 머지 주석은 소스에 넣고, 병합 뒤에는 그 줄을 무시한다 — 비교(두 칸 · 네 칸 · 폴더)에서 가리고, 주석만 다른 git 충돌은 양쪽 주석을 남겨 자동으로 푼다. 설정에서 끔 |
|
||||
| 대기 | B-6 충돌 탭에서 하나씩 누를 때마다 오래 기다린다 |
|
||||
| 대기 | B-7 상태바가 병합 중에 믿을 수 없는 값을 보인다 |
|
||||
| 대기 | B-8 병합 커밋 뒤 파일 비교 탭이 어정쩡하게 남는다 |
|
||||
|
||||
@ -83,6 +83,14 @@ namespace MergeHub
|
||||
[DisplayName("반영한 줄에 주석 남기기")]
|
||||
public bool InsertMergeComment { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 비교하고 병합할 때 머지 주석을 무시할지(6차 B-5). 주석은 소스에 그대로 남는다 - 보는 방식만 바뀐다.
|
||||
/// 켜면 비교 화면(두 칸 · 네 칸 · 폴더)에서 차이로 세지 않고, git 병합에서 주석만 다른 충돌은 자동으로 푼다.
|
||||
/// </summary>
|
||||
[Category("머지 주석")]
|
||||
[DisplayName("비교·병합에서 머지 주석 무시 (사람이 '// [이름] 2026-01-01' 모양으로 쓴 주석도 무시됨)")]
|
||||
public bool IgnoreMergeNotes { get; set; } = true;
|
||||
|
||||
[Category("머지 주석")]
|
||||
[DisplayName("작성자 (비우면 git config user.name)")]
|
||||
public string MergeCommentAuthor { get; set; } = string.Empty;
|
||||
|
||||
@ -41,12 +41,30 @@ namespace MergeHub.Services
|
||||
/// <summary>줄 필터를 켤지. 식은 그대로 두고 잠시 끄고 싶을 때가 있다.</summary>
|
||||
public bool LineFilterEnabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 머지 주석(<c>// [이름] 날짜</c>)을 차이로 세지 않을지(6차 B-5).
|
||||
///
|
||||
/// 주석은 소스에 남기되 비교에서는 무시한다. 줄 전체가 주석이면 줄 필터처럼 걸러지고,
|
||||
/// 줄 끝에 붙은 주석은 <b>그 조각만 떼고</b> 견준다 - 코드가 같으면 같은 줄이다.
|
||||
/// 보는 방식일 뿐 파일의 글은 건드리지 않는다.
|
||||
/// </summary>
|
||||
public bool IgnoreMergeNotes { get; set; }
|
||||
|
||||
/// <summary>머지 주석 기호를 고를 파일 경로. 확장자만 본다.</summary>
|
||||
public string? NotePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 주석만 있는 줄의 비교용 키. 줄마다 이름·날짜가 달라도 서로 같은 줄로 짝지어진다.
|
||||
/// 빈 줄과 짝지어지지 않도록 소스에 나올 수 없는 글자로 만든다.
|
||||
/// </summary>
|
||||
public const string MergeNoteKey = "\u0000merge-note";
|
||||
|
||||
private Regex[]? _compiled;
|
||||
private string? _compiledFrom;
|
||||
|
||||
/// <summary>어떤 규칙도 켜져 있지 않으면 비교용 변환을 건너뛸 수 있다.</summary>
|
||||
public bool IsPlain =>
|
||||
Whitespace == WhitespaceMode.Exact && !IgnoreCase;
|
||||
Whitespace == WhitespaceMode.Exact && !IgnoreCase && !IgnoreMergeNotes;
|
||||
|
||||
/// <summary>
|
||||
/// 정렬에서 "같은 줄인가"를 따질 때 쓰는 문자열.
|
||||
@ -56,6 +74,14 @@ namespace MergeHub.Services
|
||||
{
|
||||
if (line.Length == 0) return line;
|
||||
|
||||
if (IgnoreMergeNotes)
|
||||
{
|
||||
if (MergeNote.LooksLikeNote(line, NotePath)) return MergeNoteKey;
|
||||
|
||||
var cut = MergeNote.TailStart(line, NotePath);
|
||||
if (cut > 0) line = line.Substring(0, cut);
|
||||
}
|
||||
|
||||
var text = Whitespace switch
|
||||
{
|
||||
WhitespaceMode.IgnoreAll => StripSpaces(line),
|
||||
@ -69,7 +95,12 @@ namespace MergeHub.Services
|
||||
/// <summary>줄 필터에 걸리는 줄인지. 걸리면 차이로 세지 않는다.</summary>
|
||||
public bool IsFiltered(string? line)
|
||||
{
|
||||
if (!LineFilterEnabled || line is null) return false;
|
||||
if (line is null) return false;
|
||||
|
||||
// 머지 주석만 있는 줄은 줄 필터 켜짐과 따로 본다 - 설정이 둘이다(6차 B-5).
|
||||
if (IgnoreMergeNotes && MergeNote.LooksLikeNote(line, NotePath)) return true;
|
||||
|
||||
if (!LineFilterEnabled) return false;
|
||||
|
||||
foreach (var rule in Compiled())
|
||||
{
|
||||
@ -88,6 +119,7 @@ namespace MergeHub.Services
|
||||
else if (Whitespace == WhitespaceMode.IgnoreAll) parts.Add("공백 전부");
|
||||
|
||||
if (IgnoreCase) parts.Add("대소문자");
|
||||
if (IgnoreMergeNotes) parts.Add("머지 주석");
|
||||
|
||||
var rules = Compiled().Length;
|
||||
if (rules > 0) parts.Add($"줄필터 {rules}");
|
||||
|
||||
@ -55,6 +55,12 @@ namespace MergeHub.Services
|
||||
/// <summary>하위 폴더까지 훑을지.</summary>
|
||||
public bool IncludeSubfolders { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 머지 주석(<c>// [이름] 날짜</c>)만 다른 파일을 같은 파일로 볼지(6차 B-5).
|
||||
/// 주석 줄을 빼고 줄 끝 주석을 뗀 글이 같으면 "같음" 이다. 줄끝 · 인코딩 차이는 가리지 않는다.
|
||||
/// </summary>
|
||||
public bool IgnoreMergeNotes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 수정 시각을 같다고 볼 오차. 파일 시스템마다 정밀도가 달라(FAT 는 2초) 정확히 같기를
|
||||
/// 기대하면 안 된다.
|
||||
@ -393,6 +399,9 @@ namespace MergeHub.Services
|
||||
{
|
||||
if (TextFile.IsBinary(leftPath) || TextFile.IsBinary(rightPath)) return FolderEntryStatus.Binary;
|
||||
|
||||
if (options.IgnoreMergeNotes && OnlyMergeNotesDiffer(leftPath, rightPath, size, options))
|
||||
return FolderEntryStatus.Same;
|
||||
|
||||
return OnlyCommentsDiffer(leftPath, rightPath, size, options)
|
||||
? FolderEntryStatus.CommentOnly
|
||||
: FolderEntryStatus.Different;
|
||||
@ -431,6 +440,69 @@ namespace MergeHub.Services
|
||||
return string.Equals(left, right, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 머지 주석만 다른가(6차 B-5). 주석만 있는 줄은 빼고, 줄 끝 주석은 떼고 견준다.
|
||||
///
|
||||
/// 어느 쪽에서도 주석을 뗀 것이 없으면 볼 것도 없다 - 바이트가 다른 까닭이 주석이 아니다.
|
||||
/// 줄끝(<c>\r</c>)은 그대로 남겨 견준다. 주석을 핑계로 줄끝 바꿈까지 "같음" 이 되면 안 된다.
|
||||
/// </summary>
|
||||
private static bool OnlyMergeNotesDiffer(string leftPath, string rightPath, long size,
|
||||
FolderCompareOptions options)
|
||||
{
|
||||
if (size > options.FullCompareLimitBytes) return false;
|
||||
|
||||
var left = WithoutMergeNotes(leftPath, out var leftCut);
|
||||
if (left is null) return false;
|
||||
|
||||
var right = WithoutMergeNotes(rightPath, out var rightCut);
|
||||
if (right is null) return false;
|
||||
|
||||
return (leftCut || rightCut) && string.Equals(left, right, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>머지 주석을 걷어낸 글. 읽지 못하면 null. 하나라도 걷어냈으면 <paramref name="cut"/> 이 참.</summary>
|
||||
private static string? WithoutMergeNotes(string path, out bool cut)
|
||||
{
|
||||
cut = false;
|
||||
|
||||
try
|
||||
{
|
||||
var text = TextFile.Read(path);
|
||||
var builder = new System.Text.StringBuilder(text.Length);
|
||||
|
||||
foreach (var raw in text.Split('\n'))
|
||||
{
|
||||
var hasCr = raw.EndsWith("\r", StringComparison.Ordinal);
|
||||
var line = hasCr ? raw.Substring(0, raw.Length - 1) : raw;
|
||||
|
||||
if (MergeNote.LooksLikeNote(line, path))
|
||||
{
|
||||
cut = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
var at = MergeNote.TailStart(line, path);
|
||||
if (at > 0)
|
||||
{
|
||||
line = line.Substring(0, at);
|
||||
cut = true;
|
||||
}
|
||||
|
||||
builder.Append(line).Append(hasCr ? "\r\n" : "\n");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>주석과 공백을 걷어낸 글. 읽지 못하면 null.</summary>
|
||||
private static string? CodeOnly(string path, LanguageProfile profile)
|
||||
{
|
||||
|
||||
@ -938,6 +938,33 @@ namespace MergeHub.Services
|
||||
Report($"충돌 해결(직접 편집, {written.Encoding} · {written.NewLine}): {normalized}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 머지 주석만 다른 충돌 파일을 풀고 스테이징한다(6차 B-5). 푼 파일 경로를 돌려준다.
|
||||
///
|
||||
/// git 은 주석 줄도 글자로 보고 충돌을 낸다. 워킹트리에 남긴 충돌 표시를 읽어 <b>모든 덩어리가</b>
|
||||
/// 주석을 빼면 같은 코드일 때만 푼다(<see cref="NoteConflict"/>). 양쪽 주석을 다 남긴다.
|
||||
/// 형식(인코딩 · 줄끝)은 직접 편집 해결과 같은 길로 내 쪽 파일을 따른다.
|
||||
/// 한쪽이 지운 파일 · 이진 파일은 보지 않는다.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> ResolveNoteOnlyConflicts()
|
||||
{
|
||||
var solved = new List<string>();
|
||||
|
||||
foreach (var conflict in GetConflicts())
|
||||
{
|
||||
if (conflict.IsBinary || !conflict.HasOurs || !conflict.HasTheirs) continue;
|
||||
|
||||
var marked = ReadWorkingFileText(conflict.Path);
|
||||
if (!NoteConflict.TryResolve(marked, conflict.Path, out var resolved, out var blocks)) continue;
|
||||
|
||||
ResolveConflictWithContent(conflict.Path, resolved);
|
||||
Report($"머지 주석만 다른 충돌 {blocks}곳 자동 해결: {conflict.Path}");
|
||||
solved.Add(conflict.Path);
|
||||
}
|
||||
|
||||
return solved;
|
||||
}
|
||||
|
||||
public string CommitMerge(string? message = null)
|
||||
{
|
||||
var remaining = GetConflicts();
|
||||
|
||||
@ -105,6 +105,9 @@ namespace MergeHub.Services
|
||||
void ResolveConflictWithContent(string path, string content,
|
||||
string? encodingName = null, string? newLineName = null);
|
||||
|
||||
/// <summary>머지 주석만 다른 충돌 파일을 풀고 스테이징한다(6차 B-5). 푼 파일 경로를 돌려준다.</summary>
|
||||
IReadOnlyList<string> ResolveNoteOnlyConflicts();
|
||||
|
||||
/// <summary>충돌을 모두 해결한 뒤 병합 커밋 생성. 메시지 생략 시 기본 메시지 사용.</summary>
|
||||
string CommitMerge(string? message = null);
|
||||
|
||||
|
||||
@ -81,6 +81,60 @@ namespace MergeHub.Services
|
||||
return NoteBody.IsMatch(body);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 줄 끝에 붙은 머지 주석 조각이 시작하는 자리(앞 공백 포함). 없으면 -1(6차 B-5).
|
||||
///
|
||||
/// <c>laser.Power = 150; // [김승준] 2026-09-10 설명</c> 에서 <c> // [김승준] ...</c> 가 시작하는 자리다.
|
||||
/// 주석이 두 개 이어 붙어 있어도(병합에서 양쪽 주석을 다 남긴 줄) <b>첫 번째부터</b> 끝까지를 조각으로 본다.
|
||||
/// 줄 전체가 주석이면 0 이다. 주석 기호 앞이 공백이나 줄 머리가 아니면 글 속 기호로 보고 넘어간다
|
||||
/// (<c>"http://..."</c> 의 <c>//</c>).
|
||||
/// </summary>
|
||||
public static int TailStart(string? line, string? path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(line)) return -1;
|
||||
|
||||
var (open, close) = CommentTokensFor(path);
|
||||
var text = line!;
|
||||
var from = 0;
|
||||
|
||||
while (from < text.Length)
|
||||
{
|
||||
var at = text.IndexOf(open, from, StringComparison.Ordinal);
|
||||
if (at < 0) return -1;
|
||||
|
||||
if (at == 0 || char.IsWhiteSpace(text[at - 1]))
|
||||
{
|
||||
var body = text.Substring(at + open.Length).TrimStart();
|
||||
var closes = close is null || text.TrimEnd().EndsWith(close, StringComparison.Ordinal);
|
||||
|
||||
if (closes && NoteBody.IsMatch(body))
|
||||
{
|
||||
var start = at;
|
||||
while (start > 0 && char.IsWhiteSpace(text[start - 1])) start--;
|
||||
return start;
|
||||
}
|
||||
}
|
||||
|
||||
from = at + open.Length;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>줄 끝 머지 주석을 뗀 코드. 주석이 없으면 그대로(6차 B-5).</summary>
|
||||
public static string WithoutTail(string line, string? path)
|
||||
{
|
||||
var at = TailStart(line, path);
|
||||
return at < 0 ? line : line.Substring(0, at);
|
||||
}
|
||||
|
||||
/// <summary>줄 끝 머지 주석 조각(앞뒤 공백 없이). 없으면 빈 글(6차 B-5).</summary>
|
||||
public static string TailOf(string line, string? path)
|
||||
{
|
||||
var at = TailStart(line, path);
|
||||
return at < 0 ? string.Empty : line.Substring(at).Trim();
|
||||
}
|
||||
|
||||
/// <summary>코드 줄 끝에 주석을 붙인다. 줄 끝 공백은 정리한다.</summary>
|
||||
public static string Append(string line, string note)
|
||||
{
|
||||
|
||||
166
src/MergeHub/Services/NoteConflict.cs
Normal file
166
src/MergeHub/Services/NoteConflict.cs
Normal file
@ -0,0 +1,166 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MergeHub.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// git 이 낸 충돌 가운데 <b>머지 주석만 다른 것</b>을 푼다(6차 B-5).
|
||||
///
|
||||
/// 머지 주석(<c>// [이름] 날짜</c>)은 소스에 남긴다 - 누가 언제 반영했는지가 이력이다.
|
||||
/// 그런데 git 은 이 규칙을 모른다. 양쪽이 같은 코드 줄 위에 서로 다른 주석을 달면 그 주석 줄끼리 충돌한다.
|
||||
/// 사람이 볼 것은 코드인데 주석 때문에 파일마다 물어보게 된다.
|
||||
///
|
||||
/// 그래서 git 이 워킹트리에 남긴 충돌 표시(<c><<<<<<<</c> · <c>=======</c> · <c>>>>>>>></c>)를 읽어
|
||||
/// <b>모든 덩어리가</b> "주석을 빼면 양쪽 코드가 같다" 일 때만 푼다. 한 덩어리라도 코드가 다르면 손대지 않는다 -
|
||||
/// 그 파일은 사람이 네 칸에서 코드 충돌만 보고 정한다(주석 차이는 비교 규칙이 가린다).
|
||||
///
|
||||
/// 풀 때는 <b>양쪽 주석을 모두 남긴다.</b> 이력은 지우지 않는다.
|
||||
/// · 코드 줄 위의 주석 줄: 내 쪽 줄 다음에 상대 쪽 줄. 똑같은 줄은 한 번만.
|
||||
/// · 줄 끝 주석: 내 쪽 줄 끝에 상대 주석을 이어 붙인다. 이미 들어 있으면 그대로.
|
||||
/// 결과 줄끝은 <c>\n</c> 이다. 파일에 쓰는 쪽(<see cref="TextFile.Encode"/>)이 원래 형식으로 되돌린다.
|
||||
/// </summary>
|
||||
public static class NoteConflict
|
||||
{
|
||||
/// <summary>코드와 줄 끝 주석 사이 간격. <see cref="MergeNote.Append"/> 와 같다.</summary>
|
||||
private const string Gap = " ";
|
||||
|
||||
/// <summary>
|
||||
/// 충돌 표시가 든 글을 푼다. 충돌 덩어리가 없거나, 코드가 다른 덩어리가 하나라도 있으면 false.
|
||||
/// </summary>
|
||||
/// <param name="blocks">푼 충돌 덩어리 수.</param>
|
||||
public static bool TryResolve(string? markedText, string? path, out string resolved, out int blocks)
|
||||
{
|
||||
resolved = markedText ?? string.Empty;
|
||||
blocks = 0;
|
||||
if (string.IsNullOrEmpty(markedText)) return false;
|
||||
|
||||
var text = markedText!.Replace("\r\n", "\n");
|
||||
var endsWithNewLine = text.EndsWith("\n", StringComparison.Ordinal);
|
||||
var lines = text.Split('\n');
|
||||
var count = endsWithNewLine ? lines.Length - 1 : lines.Length;
|
||||
|
||||
var output = new List<string>(count);
|
||||
var i = 0;
|
||||
|
||||
while (i < count)
|
||||
{
|
||||
if (!IsMarker(lines[i], "<<<<<<<"))
|
||||
{
|
||||
output.Add(lines[i]);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var ours = new List<string>();
|
||||
var theirs = new List<string>();
|
||||
var part = Part.Ours;
|
||||
var closed = false;
|
||||
|
||||
for (i++; i < count; i++)
|
||||
{
|
||||
var line = lines[i];
|
||||
|
||||
if (part == Part.Ours && IsMarker(line, "|||||||")) { part = Part.Base; continue; }
|
||||
if (part != Part.Theirs && IsMarker(line, "=======")) { part = Part.Theirs; continue; }
|
||||
if (part == Part.Theirs && IsMarker(line, ">>>>>>>")) { closed = true; i++; break; }
|
||||
|
||||
if (part == Part.Ours) ours.Add(line);
|
||||
else if (part == Part.Theirs) theirs.Add(line);
|
||||
}
|
||||
|
||||
if (!closed) return false;
|
||||
|
||||
var merged = MergeBlock(ours, theirs, path);
|
||||
if (merged is null) return false;
|
||||
|
||||
output.AddRange(merged);
|
||||
blocks++;
|
||||
}
|
||||
|
||||
if (blocks == 0) return false;
|
||||
|
||||
resolved = string.Join("\n", output) + (endsWithNewLine ? "\n" : string.Empty);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 충돌 한 덩어리(내 쪽 줄들 · 상대 쪽 줄들)를 푼다. 주석을 빼도 코드가 다르면 null.
|
||||
/// </summary>
|
||||
public static List<string>? MergeBlock(IReadOnlyList<string> ours, IReadOnlyList<string> theirs, string? path)
|
||||
{
|
||||
var oursCode = CodeRows(ours, path);
|
||||
var theirsCode = CodeRows(theirs, path);
|
||||
if (oursCode.Count != theirsCode.Count) return null;
|
||||
|
||||
for (var k = 0; k < oursCode.Count; k++)
|
||||
{
|
||||
if (!SameCode(ours[oursCode[k]], theirs[theirsCode[k]], path)) return null;
|
||||
}
|
||||
|
||||
var result = new List<string>(ours.Count + theirs.Count);
|
||||
int o = 0, t = 0;
|
||||
|
||||
for (var k = 0; k <= oursCode.Count; k++)
|
||||
{
|
||||
var oursEnd = k < oursCode.Count ? oursCode[k] : ours.Count;
|
||||
var theirsEnd = k < theirsCode.Count ? theirsCode[k] : theirs.Count;
|
||||
|
||||
// 이 코드 줄 앞의 주석 줄. 내 쪽 먼저, 상대 쪽은 내 쪽에 똑같은 줄이 없을 때만.
|
||||
var mine = new HashSet<string>(StringComparer.Ordinal);
|
||||
for (; o < oursEnd; o++)
|
||||
{
|
||||
result.Add(ours[o]);
|
||||
mine.Add(ours[o].Trim());
|
||||
}
|
||||
|
||||
for (; t < theirsEnd; t++)
|
||||
{
|
||||
if (!mine.Contains(theirs[t].Trim())) result.Add(theirs[t]);
|
||||
}
|
||||
|
||||
if (k == oursCode.Count) break;
|
||||
|
||||
result.Add(JoinTails(ours[o], theirs[t], path));
|
||||
o++;
|
||||
t++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private enum Part { Ours, Base, Theirs }
|
||||
|
||||
/// <summary>충돌 표시 줄인지. 표시 뒤에는 공백과 이름이 오거나 아무것도 없다.</summary>
|
||||
private static bool IsMarker(string line, string marker) =>
|
||||
line.StartsWith(marker, StringComparison.Ordinal) &&
|
||||
(line.Length == marker.Length || line[marker.Length] == ' ');
|
||||
|
||||
/// <summary>머지 주석만 있는 줄이 아닌 줄(코드 줄 · 빈 줄)의 자리.</summary>
|
||||
private static List<int> CodeRows(IReadOnlyList<string> lines, string? path)
|
||||
{
|
||||
var rows = new List<int>(lines.Count);
|
||||
for (var i = 0; i < lines.Count; i++)
|
||||
{
|
||||
if (!MergeNote.LooksLikeNote(lines[i], path)) rows.Add(i);
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
private static bool SameCode(string a, string b, string? path) =>
|
||||
string.Equals(MergeNote.WithoutTail(a, path).TrimEnd(), MergeNote.WithoutTail(b, path).TrimEnd(),
|
||||
StringComparison.Ordinal);
|
||||
|
||||
/// <summary>코드가 같은 두 줄의 줄 끝 주석을 합친다.</summary>
|
||||
private static string JoinTails(string mine, string theirs, string? path)
|
||||
{
|
||||
var mineTail = MergeNote.TailOf(mine, path);
|
||||
var theirsTail = MergeNote.TailOf(theirs, path);
|
||||
|
||||
if (theirsTail.Length == 0 || mineTail.IndexOf(theirsTail, StringComparison.Ordinal) >= 0) return mine;
|
||||
if (mineTail.Length == 0) return theirs;
|
||||
|
||||
return mine.TrimEnd() + Gap + theirsTail;
|
||||
}
|
||||
}
|
||||
}
|
||||
148
src/MergeHub/Services/NoteRows.cs
Normal file
148
src/MergeHub/Services/NoteRows.cs
Normal file
@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MergeHub.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// 정렬이 머지 주석 줄과 코드 줄을 <b>한 행에 짝지은 것</b>을 떼어 놓는다(6차 B-5).
|
||||
///
|
||||
/// 머지 주석을 무시하면 주석만 있는 줄은 모두 같은 비교 키(<see cref="CompareRules.MergeNoteKey"/>)가 된다.
|
||||
/// 그런데 정렬기는 같은 자리에서 서로 다른 줄을 "고친 줄" 로 짝짓는다. 내 쪽과 상대 쪽이 코드 줄 위에
|
||||
/// 주석을 넣고 코드도 고치면, BASE 의 코드 줄이 양쪽 <b>주석 줄</b>과 한 행이 되고
|
||||
/// 양쪽의 새 코드 줄은 BASE 없이 다음 행으로 밀린다. 그러면 주석 행이 "BASE 와 다름" 으로 세지고
|
||||
/// 코드 충돌은 BASE 가 빠진 채로 보인다(C-1 저장소 <c>Common/Util/Unit47.cs</c> 에서 "남은 차이 2줄").
|
||||
///
|
||||
/// 그래서 한 행에 주석 줄과 코드 줄이 섞여 있으면 <b>주석 행과 코드 행 둘로</b> 나눈다. 떼어 낸 코드 조각은
|
||||
/// 바로 다음 행과 칸이 겹치지 않으면 그 행과 합친다 - 정렬기가 주석에 붙이느라 떼어 놓은 짝이다.
|
||||
/// 칸이 겹치면 합치지 않는다(정렬기가 일부러 나눠 둔 것이다).
|
||||
/// </summary>
|
||||
public static class NoteRows
|
||||
{
|
||||
private enum Kind { None, Note, Code }
|
||||
|
||||
public static IReadOnlyList<FourWayRow> SplitFour(IReadOnlyList<FourWayRow> rows, Func<string, bool> isNote)
|
||||
{
|
||||
var output = new List<FourWayRow>(rows.Count);
|
||||
|
||||
for (var i = 0; i < rows.Count; i++)
|
||||
{
|
||||
var row = rows[i];
|
||||
var b = KindOf(row.BaseText, isNote);
|
||||
var m = KindOf(row.MineText, isNote);
|
||||
var r = KindOf(row.RemoteText, isNote);
|
||||
var g = KindOf(row.MergedText, isNote);
|
||||
|
||||
if (!Mixed(b, m, r, g))
|
||||
{
|
||||
output.Add(row);
|
||||
continue;
|
||||
}
|
||||
|
||||
output.Add(new FourWayRow(
|
||||
b == Kind.Note ? row.BaseText : null, b == Kind.Note ? row.BaseNumber : null,
|
||||
m == Kind.Note ? row.MineText : null, m == Kind.Note ? row.MineNumber : null,
|
||||
r == Kind.Note ? row.RemoteText : null, r == Kind.Note ? row.RemoteNumber : null,
|
||||
g == Kind.Note ? row.MergedText : null, g == Kind.Note ? row.MergedNumber : null));
|
||||
|
||||
var code = new FourWayRow(
|
||||
b == Kind.Code ? row.BaseText : null, b == Kind.Code ? row.BaseNumber : null,
|
||||
m == Kind.Code ? row.MineText : null, m == Kind.Code ? row.MineNumber : null,
|
||||
r == Kind.Code ? row.RemoteText : null, r == Kind.Code ? row.RemoteNumber : null,
|
||||
g == Kind.Code ? row.MergedText : null, g == Kind.Code ? row.MergedNumber : null);
|
||||
|
||||
if (i + 1 < rows.Count && CanJoin(code, rows[i + 1], isNote))
|
||||
{
|
||||
var next = rows[i + 1];
|
||||
output.Add(new FourWayRow(
|
||||
code.BaseText ?? next.BaseText, code.BaseText is not null ? code.BaseNumber : next.BaseNumber,
|
||||
code.MineText ?? next.MineText, code.MineText is not null ? code.MineNumber : next.MineNumber,
|
||||
code.RemoteText ?? next.RemoteText, code.RemoteText is not null ? code.RemoteNumber : next.RemoteNumber,
|
||||
code.MergedText ?? next.MergedText, code.MergedText is not null ? code.MergedNumber : next.MergedNumber));
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Add(code);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public static IReadOnlyList<AlignedRow> SplitTwo(IReadOnlyList<AlignedRow> rows, Func<string, bool> isNote)
|
||||
{
|
||||
var output = new List<AlignedRow>(rows.Count);
|
||||
|
||||
for (var i = 0; i < rows.Count; i++)
|
||||
{
|
||||
var row = rows[i];
|
||||
var b = KindOf(row.BaseText, isNote);
|
||||
var l = KindOf(row.LocalText, isNote);
|
||||
|
||||
if (!Mixed(b, l))
|
||||
{
|
||||
output.Add(row);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 두 칸이면 섞인 행은 한쪽이 주석 · 한쪽이 코드뿐이다.
|
||||
var note = b == Kind.Note
|
||||
? new AlignedRow(row.BaseText, row.BaseNumber, null, null)
|
||||
: new AlignedRow(null, null, row.LocalText, row.LocalNumber);
|
||||
var code = b == Kind.Code
|
||||
? new AlignedRow(row.BaseText, row.BaseNumber, null, null)
|
||||
: new AlignedRow(null, null, row.LocalText, row.LocalNumber);
|
||||
|
||||
output.Add(note);
|
||||
|
||||
var next = i + 1 < rows.Count ? rows[i + 1] : null;
|
||||
if (next is not null && KindOf(next.BaseText, isNote) != Kind.Note && KindOf(next.LocalText, isNote) != Kind.Note &&
|
||||
(code.BaseText is null || next.BaseText is null) && (code.LocalText is null || next.LocalText is null))
|
||||
{
|
||||
output.Add(new AlignedRow(
|
||||
code.BaseText ?? next.BaseText, code.BaseText is not null ? code.BaseNumber : next.BaseNumber,
|
||||
code.LocalText ?? next.LocalText, code.LocalText is not null ? code.LocalNumber : next.LocalNumber));
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Add(code);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private static Kind KindOf(string? text, Func<string, bool> isNote) =>
|
||||
text is null ? Kind.None : isNote(text) ? Kind.Note : Kind.Code;
|
||||
|
||||
private static bool Mixed(params Kind[] kinds)
|
||||
{
|
||||
bool note = false, code = false;
|
||||
foreach (var kind in kinds)
|
||||
{
|
||||
note |= kind == Kind.Note;
|
||||
code |= kind == Kind.Code;
|
||||
}
|
||||
|
||||
return note && code;
|
||||
}
|
||||
|
||||
/// <summary>떼어 낸 코드 조각과 다음 행이 칸을 나눠 가질 수 있는지. 다음 행에 주석 줄이 있으면 합치지 않는다.</summary>
|
||||
private static bool CanJoin(FourWayRow code, FourWayRow next, Func<string, bool> isNote)
|
||||
{
|
||||
if (Mixed(KindOf(next.BaseText, isNote), KindOf(next.MineText, isNote),
|
||||
KindOf(next.RemoteText, isNote), KindOf(next.MergedText, isNote))) return false;
|
||||
|
||||
foreach (var text in new[] { next.BaseText, next.MineText, next.RemoteText, next.MergedText })
|
||||
{
|
||||
if (text is not null && isNote(text)) return false;
|
||||
}
|
||||
|
||||
return (code.BaseText is null || next.BaseText is null) &&
|
||||
(code.MineText is null || next.MineText is null) &&
|
||||
(code.RemoteText is null || next.RemoteText is null) &&
|
||||
(code.MergedText is null || next.MergedText is null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -333,6 +333,7 @@ namespace MergeHub.ViewModels
|
||||
IncludeSubfolders = IncludeSubfolders,
|
||||
IncludePattern = IncludePattern,
|
||||
ExcludePattern = ExcludePattern,
|
||||
IgnoreMergeNotes = _workspace.Settings.IgnoreMergeNotes,
|
||||
};
|
||||
|
||||
var left = LeftFolder;
|
||||
|
||||
@ -230,14 +230,27 @@ namespace MergeHub.ViewModels
|
||||
var branch = SelectedBranch!.FriendlyName;
|
||||
var result = _workspace.Git.Merge(branch, commitOnSuccess: false);
|
||||
|
||||
// 머지 주석만 다른 충돌은 사람에게 묻지 않고 푼다. 양쪽 주석을 다 남긴다(6차 B-5).
|
||||
IReadOnlyList<string> noteOnly = result.HasConflicts && _workspace.Settings.IgnoreMergeNotes
|
||||
? _workspace.Git.ResolveNoteOnlyConflicts()
|
||||
: Array.Empty<string>();
|
||||
var remaining = noteOnly.Count > 0 ? _workspace.Git.GetConflicts().Count : result.Conflicts.Count;
|
||||
|
||||
_workspace.OnUi(() =>
|
||||
{
|
||||
_workspace.AppendLog(result.Message);
|
||||
if (noteOnly.Count > 0)
|
||||
_workspace.AppendLog($"머지 주석만 다른 충돌 {noteOnly.Count}개 파일은 자동으로 풀었습니다. 남은 충돌 {remaining}건.");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(CommitMessage))
|
||||
CommitMessage = $"Merge branch '{branch}' into {_workspace.CurrentBranch}";
|
||||
|
||||
if (result.HasConflicts)
|
||||
MessageUtil.ShowWarningToast($"충돌 {result.Conflicts.Count}건 - 충돌 탭에서 파일을 두 번 누르면 파일 비교 탭에서 세 칸으로 풉니다.");
|
||||
var autoNote = noteOnly.Count > 0 ? $" (머지 주석만 다른 {noteOnly.Count}건은 자동으로 풀림)" : string.Empty;
|
||||
|
||||
if (result.HasConflicts && remaining > 0)
|
||||
MessageUtil.ShowWarningToast($"충돌 {remaining}건{autoNote} - 충돌 탭에서 파일을 두 번 누르면 파일 비교 탭에서 세 칸으로 풉니다.");
|
||||
else if (result.HasConflicts)
|
||||
MessageUtil.ShowSuccessToast($"머지 주석만 다른 충돌 {noteOnly.Count}건을 자동으로 풀었습니다 - 병합 커밋을 할 수 있습니다.");
|
||||
else
|
||||
MessageUtil.ShowToast(result.Message);
|
||||
});
|
||||
|
||||
@ -1368,6 +1368,7 @@ namespace MergeHub.ViewModels
|
||||
string? mergedText = null)
|
||||
{
|
||||
Rules.LinePatterns = SplitLines(_workspace.Settings.LineFilters ?? string.Empty);
|
||||
Rules.NotePath = CurrentFilePath; // 머지 주석 기호는 확장자로 고른다(6차 B-5)
|
||||
|
||||
var baseLines = TrimTrailingBlank(SplitLines(baseText));
|
||||
var mineLines = TrimTrailingBlank(SplitLines(mineText));
|
||||
@ -1386,6 +1387,9 @@ namespace MergeHub.ViewModels
|
||||
var aligned = ThreeWayAligner.AlignFour(baseLines, mineLines, remoteLines, mergedLines,
|
||||
baseKeys, mineKeys, remoteKeys, mergedKeys);
|
||||
|
||||
// 머지 주석 줄과 코드 줄이 한 행에 짝지어졌으면 떼어 놓는다(6차 B-5, NoteRows 설명 참고).
|
||||
if (Rules.IgnoreMergeNotes) aligned = NoteRows.SplitFour(aligned, IsNoteLine);
|
||||
|
||||
var rows = new List<MergeLine>(aligned.Count);
|
||||
|
||||
foreach (var row in aligned)
|
||||
@ -1423,7 +1427,8 @@ namespace MergeHub.ViewModels
|
||||
/// <summary>네 쪽 모두(있는 쪽만) 줄 필터에 걸리면 차이로 세지 않는다.</summary>
|
||||
private bool IsFilteredFourWayRow(FourWayRow row)
|
||||
{
|
||||
if (!Rules.LineFilterEnabled) return false;
|
||||
// 머지 주석 무시는 줄 필터와 따로 켠다(6차 B-5). 둘 다 꺼져 있을 때만 바로 돌아간다.
|
||||
if (!Rules.LineFilterEnabled && !Rules.IgnoreMergeNotes) return false;
|
||||
|
||||
var any = false;
|
||||
|
||||
@ -1441,6 +1446,7 @@ namespace MergeHub.ViewModels
|
||||
{
|
||||
// 줄 필터는 설정 화면에서 언제든 바뀔 수 있다. 비교할 때마다 최신 값을 가져온다.
|
||||
Rules.LinePatterns = SplitLines(_workspace.Settings.LineFilters ?? string.Empty);
|
||||
Rules.NotePath = CurrentFilePath; // 머지 주석 기호는 확장자로 고른다(6차 B-5)
|
||||
|
||||
// 파일 끝의 빈 줄(마지막 엔터)은 내용 차이가 아니다 - 한쪽만 있어도 무시한다.
|
||||
var baseLines = TrimTrailingBlank(SplitLines(baseText));
|
||||
@ -1455,6 +1461,9 @@ namespace MergeHub.ViewModels
|
||||
|
||||
var aligned = LineAligner.Align(baseLines, localLines, baseKeys, localKeys);
|
||||
|
||||
// 머지 주석 줄과 코드 줄이 한 행에 짝지어졌으면 떼어 놓는다(6차 B-5).
|
||||
if (Rules.IgnoreMergeNotes) aligned = NoteRows.SplitTwo(aligned, IsNoteLine);
|
||||
|
||||
var rows = new List<MergeLine>(aligned.Count);
|
||||
foreach (var row in aligned)
|
||||
{
|
||||
@ -1517,6 +1526,9 @@ namespace MergeHub.ViewModels
|
||||
return Rules.IsFiltered(row.BaseText) && Rules.IsFiltered(row.LocalText);
|
||||
}
|
||||
|
||||
/// <summary>지금 파일의 주석 기호로 본 머지 주석 줄인지.</summary>
|
||||
private bool IsNoteLine(string line) => MergeNote.LooksLikeNote(line, Rules.NotePath);
|
||||
|
||||
private static string[] SplitLines(string text) =>
|
||||
text.Length == 0 ? Array.Empty<string>() : text.Replace("\r\n", "\n").Split('\n');
|
||||
|
||||
@ -2672,6 +2684,7 @@ namespace MergeHub.ViewModels
|
||||
|
||||
Rules.IgnoreCase = settings.IgnoreCase;
|
||||
Rules.LineFilterEnabled = settings.UseLineFilters;
|
||||
Rules.IgnoreMergeNotes = settings.IgnoreMergeNotes;
|
||||
Rules.LinePatterns = SplitLines(settings.LineFilters ?? string.Empty);
|
||||
|
||||
_appliedSettingsSignature = SettingsSignature();
|
||||
@ -2684,7 +2697,7 @@ namespace MergeHub.ViewModels
|
||||
private string SettingsSignature()
|
||||
{
|
||||
var s = _workspace.Settings;
|
||||
return $"{s.WhitespaceMode}|{s.IgnoreCase}|{s.UseLineFilters}|{s.LineFilters}";
|
||||
return $"{s.WhitespaceMode}|{s.IgnoreCase}|{s.UseLineFilters}|{s.LineFilters}|{s.IgnoreMergeNotes}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user