Skip to content

Commit 409ca38

Browse files
committed
Merge commit 'a4188806' into fix/file-rename
2 parents 1bd2044 + a418880 commit 409ca38

File tree

6 files changed

+418
-82
lines changed

6 files changed

+418
-82
lines changed

src/Commands/CompareRevisions.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public partial class CompareRevisions : Command
88
{
99
[GeneratedRegex(@"^([MADC])\s+(.+)$")]
1010
private static partial Regex REG_FORMAT();
11-
[GeneratedRegex(@"^R[0-9]{0,4}\s+(.+)$")]
11+
[GeneratedRegex(@"^R[0-9]{0,4}\s+(.+)\s+(.+)$")]
1212
private static partial Regex REG_RENAME_FORMAT();
1313

1414
public CompareRevisions(string repo, string start, string end)
@@ -51,7 +51,11 @@ private void ParseLine(string line)
5151
match = REG_RENAME_FORMAT().Match(line);
5252
if (match.Success)
5353
{
54-
var renamed = new Models.Change() { Path = match.Groups[1].Value };
54+
var renamed = new Models.Change()
55+
{
56+
OriginalPath = match.Groups[1].Value,
57+
Path = match.Groups[2].Value
58+
};
5559
renamed.Set(Models.ChangeState.Renamed);
5660
_changes.Add(renamed);
5761
}

src/Commands/QueryCommits.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public QueryCommits(string repo, string filter, Models.CommitSearchMethod method
4343
}
4444
else if (method == Models.CommitSearchMethod.ByFile)
4545
{
46-
search += $"-- \"{filter}\"";
46+
search += $"--follow -- \"{filter}\"";
4747
}
4848
else
4949
{
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text.RegularExpressions;
4+
5+
namespace SourceGit.Commands
6+
{
7+
public partial class QueryFilePathInRevision : Command
8+
{
9+
[GeneratedRegex(@"^R[0-9]{0,4}\s+(.+)\s+(.+)$")]
10+
private static partial Regex REG_RENAME_FORMAT();
11+
12+
public QueryFilePathInRevision(string repo, string revision, string currentPath)
13+
{
14+
WorkingDirectory = repo;
15+
Context = repo;
16+
_revision = revision;
17+
_currentPath = currentPath;
18+
}
19+
20+
public string Result()
21+
{
22+
if (CheckPathExistsInRevision(_currentPath))
23+
return _currentPath;
24+
25+
string mappedPath = FindRenameHistory();
26+
return mappedPath ?? _currentPath;
27+
}
28+
29+
private bool CheckPathExistsInRevision(string path)
30+
{
31+
Args = $"ls-tree -r {_revision} -- \"{path}\"";
32+
var rs = ReadToEnd();
33+
return rs.IsSuccess && !string.IsNullOrEmpty(rs.StdOut);
34+
}
35+
36+
private string FindRenameHistory()
37+
{
38+
var fileHistory = BuildFileHistory();
39+
if (fileHistory == null || fileHistory.Count == 0)
40+
return null;
41+
42+
foreach (var entry in fileHistory)
43+
{
44+
if (!IsTargetRevisionBefore(entry.CommitSHA))
45+
continue;
46+
47+
if (CheckPathExistsInRevision(entry.OldPath))
48+
return entry.OldPath;
49+
}
50+
51+
if (fileHistory.Count > 0)
52+
{
53+
var oldestPath = fileHistory[^1].OldPath;
54+
if (CheckPathExistsInRevision(oldestPath))
55+
return oldestPath;
56+
}
57+
58+
return null;
59+
}
60+
61+
private bool IsTargetRevisionBefore(string commitSHA)
62+
{
63+
Args = $"merge-base --is-ancestor {_revision} {commitSHA}";
64+
var rs = ReadToEnd();
65+
return rs.IsSuccess;
66+
}
67+
68+
private List<RenameHistoryEntry> BuildFileHistory()
69+
{
70+
Args = $"log --follow --name-status --pretty=format:\"commit %H\" -M -- \"{_currentPath}\"";
71+
var rs = ReadToEnd();
72+
if (!rs.IsSuccess)
73+
return null;
74+
75+
var result = new List<RenameHistoryEntry>();
76+
var lines = rs.StdOut.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
77+
78+
string currentCommit = null;
79+
string currentPath = _currentPath;
80+
81+
foreach (var t in lines)
82+
{
83+
var line = t.Trim();
84+
85+
if (line.StartsWith("commit ", StringComparison.Ordinal))
86+
{
87+
currentCommit = line.Substring("commit ".Length);
88+
continue;
89+
}
90+
91+
var match = REG_RENAME_FORMAT().Match(line);
92+
if (match.Success && currentCommit != null)
93+
{
94+
var oldPath = match.Groups[1].Value;
95+
var newPath = match.Groups[2].Value;
96+
97+
if (newPath == currentPath)
98+
{
99+
result.Add(new RenameHistoryEntry
100+
{
101+
CommitSHA = currentCommit,
102+
OldPath = oldPath,
103+
NewPath = newPath
104+
});
105+
106+
currentPath = oldPath;
107+
}
108+
}
109+
}
110+
111+
return result;
112+
}
113+
114+
private class RenameHistoryEntry
115+
{
116+
public string CommitSHA { get; set; }
117+
public string OldPath { get; set; }
118+
public string NewPath { get; set; }
119+
}
120+
121+
private readonly string _revision;
122+
private readonly string _currentPath;
123+
}
124+
}

src/Models/Change.cs

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,18 @@ public enum ChangeViewMode
99
Tree,
1010
}
1111

12+
[Flags]
1213
public enum ChangeState
1314
{
14-
None,
15-
Modified,
16-
TypeChanged,
17-
Added,
18-
Deleted,
19-
Renamed,
20-
Copied,
21-
Untracked,
22-
Conflicted,
15+
None = 0,
16+
Modified = 1 << 0,
17+
TypeChanged = 1 << 1,
18+
Added = 1 << 2,
19+
Deleted = 1 << 3,
20+
Renamed = 1 << 4,
21+
Copied = 1 << 5,
22+
Untracked = 1 << 6,
23+
Conflicted = 1 << 7,
2324
}
2425

2526
public enum ConflictReason
@@ -85,6 +86,30 @@ public void Set(ChangeState index, ChangeState workTree = ChangeState.None)
8586
OriginalPath = OriginalPath.Substring(1, OriginalPath.Length - 2);
8687
}
8788

89+
public static ChangeState GetPrimaryState(ChangeState state)
90+
{
91+
if (state == ChangeState.None)
92+
return ChangeState.None;
93+
if ((state & ChangeState.Conflicted) != 0)
94+
return ChangeState.Conflicted;
95+
if ((state & ChangeState.Untracked) != 0)
96+
return ChangeState.Untracked;
97+
if ((state & ChangeState.Renamed) != 0)
98+
return ChangeState.Renamed;
99+
if ((state & ChangeState.Copied) != 0)
100+
return ChangeState.Copied;
101+
if ((state & ChangeState.Deleted) != 0)
102+
return ChangeState.Deleted;
103+
if ((state & ChangeState.Added) != 0)
104+
return ChangeState.Added;
105+
if ((state & ChangeState.TypeChanged) != 0)
106+
return ChangeState.TypeChanged;
107+
if ((state & ChangeState.Modified) != 0)
108+
return ChangeState.Modified;
109+
110+
return ChangeState.None;
111+
}
112+
88113
private static readonly string[] CONFLICT_MARKERS =
89114
[
90115
string.Empty,

0 commit comments

Comments
 (0)