8000 Lazily enumerate tree entry changes · libgit2/libgit2sharp@feb0d63 · GitHub
[go: up one dir, main page]

Skip to content

Commit feb0d63

Browse files
committed
Lazily enumerate tree entry changes
1 parent 1062426 commit feb0d63

File tree

2 files changed

+77
-49
lines changed

2 files changed

+77
-49
lines changed

LibGit2Sharp/TreeChanges.cs

Lines changed: 60 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,8 @@ namespace LibGit2Sharp
1717
[DebuggerDisplay("{DebuggerDisplay,nq}")]
1818
public class TreeChanges : IEnumerable<TreeEntryChanges>, IDiffResult
1919
{
20-
private readonly List<TreeEntryChanges> changes = new List<TreeEntryChanges>();
21-
private readonly List<TreeEntryChanges> added = new List<TreeEntryChanges>();
22-
private readonly List<TreeEntryChanges> deleted = new List<TreeEntryChanges>();
23-
private readonly List<TreeEntryChanges> modified = new List<TreeEntryChanges>();
24-
private readonly List<TreeEntryChanges> typeChanged = new List<TreeEntryChanges>();
25-
private readonly List<TreeEntryChanges> unmodified = new List<TreeEntryChanges>();
26-
private readonly List<TreeEntryChanges> renamed = new List<TreeEntryChanges>();
27-
private readonly List<TreeEntryChanges> copied = new List<TreeEntryChanges>();
28-
private readonly List<TreeEntryChanges> conflicted = new List<TreeEntryChanges>();
29-
30-
private readonly IDictionary<ChangeKind, Action<TreeChanges, TreeEntryChanges>> fileDispatcher = Build();
31-
32-
private static IDictionary<ChangeKind, Action<TreeChanges, TreeEntryChanges>> Build()
33-
{
34-
return new Dictionary<ChangeKind, Action<TreeChanges, TreeEntryChanges>>
35-
{
36-
{ ChangeKind.Modified, (de, d) => de.modified.Add(d) },
37-
{ ChangeKind.Deleted, (de, d) => de.deleted.Add(d) },
38-
{ ChangeKind.Added, (de, d) => de.added.Add(d) },
39-
{ ChangeKind.TypeChanged, (de, d) => de.typeChanged.Add(d) },
40-
{ ChangeKind.Unmodified, (de, d) => de.unmodified.Add(d) },
41-
{ ChangeKind.Renamed, (de, d) => de.renamed.Add(d) },
42-
{ ChangeKind.Copied, (de, d) => de.copied.Add(d) },
43-
{ ChangeKind.Conflicted, (de, d) => de.conflicted.Add(d) },
44-
};
45-
}
20+
private readonly DiffHandle diff;
21+
private readonly Lazy<int> count;
4622

4723
/// <summary>
4824
/// Needed for mocking purposes.
@@ -52,22 +28,46 @@ protected TreeChanges()
5228

5329
internal unsafe TreeChanges(DiffHandle diff)
5430
{
55-
using(diff)
56-
Proxy.git_diff_foreach(diff, FileCallback, null, null);
31+
this.diff = diff;
32+
this.count = new Lazy<int>(() => Proxy.git_diff_num_deltas(diff));
5733
}
5834

59-
private unsafe int FileCallback(git_diff_delta* delta, float progress, IntPtr payload)
35+
/// <summary>
36+
/// Enumerates the diff and yields deltas with the specified change kind.
37+
/// </summary>
38+
/// <param name="changeKind">Change type to filter on.</param>
39+
private IEnumerable<TreeEntryChanges> GetChangesOfKind(ChangeKind changeKind)
6040
{
61-
AddFileChange(delta);
62-
return 0;
41+
TreeEntryChanges entry;
42+
43+
for (int i = 0; i < count.Value; i++)
44+
{
45+
if (TryGetEntryWithChangeTypeAt(i, changeKind, out entry))
46+
{
47+
yield return entry;
48+
}
49+
}
6350
}
6451

65-
private unsafe void AddFileChange(git_diff_delta* delta)
52+
/// <summary>
53+
/// This is method exists to work around .net not allowing unsafe code
54+
/// in iterators.
55+
/// </summary>
56+
private unsafe bool TryGetEntryWithChangeTypeAt(int index, ChangeKind changeKind, out TreeEntryChanges entry)
6657
{
67-
var treeEntryChanges = new TreeEntryChanges(delta);
58+
if (index < 0 || index > count.Value)
59+
throw new ArgumentOutOfRangeException("index", "Index was out of range. Must be non-negative and less than the size of the collection.");
6860

69-
fileDispatcher[treeEntryChanges.Status](this, treeEntryChanges);
70-
changes.Add(treeEntryChanges);
61+
var delta = Proxy.git_diff_get_delta(diff, index);
62+
63+
if (TreeEntryChanges.GetStatusFromChangeKind(delta->status) == changeKind)
64+
{
65+
entry = new TreeEntryChanges(delta);
66+
return true;
67+
}
68+
69+
entry = null;
70+
return false;
7171
}
7272

7373
#region IEnumerable<TreeEntryChanges> Members
@@ -78,7 +78,22 @@ private unsafe void AddFileChange(git_diff_delta* delta)
7878
/// <returns>An <see cref="IEnumerator{T}"/> object that can be used to iterate through the collection.</returns>
7979
public virtual IEnumerator<TreeEntryChanges> GetEnumerator()
8080
{
81-
return changes.GetEnumerator();
81+
for (int i = 0; i < count.Value; i++)
82+
{
83+
yield return GetEntryAt(i);
84+
}
85+
}
86+
87+
/// <summary>
88+
/// This is method exists to work around .net not allowing unsafe code
89+
/// in iterators.
90+
/// </summary>
91+
private unsafe TreeEntryChanges GetEntryAt(int index)
92+
{
93+
if (index < 0 || index > count.Value)
94+
throw new ArgumentOutOfRangeException("index", "Index was out of range. Must be non-negative and less than the size of the collection.");
95+
96+
return new TreeEntryChanges(Proxy.git_diff_get_delta(diff, index));
8297
}
8398

8499
/// <summary>
@@ -97,63 +112,63 @@ IEnumerator IEnumerable.GetEnumerator()
97112
/// </summary>
98113
public virtual IEnumerable<TreeEntryChanges> Added
99114
{
100-
get { return added; }
115+
get { return GetChangesOfKind(ChangeKind.Added); }
101116
}
102117

103118
/// <summary>
104119
/// List of <see cref="TreeEntryChanges"/> that have been deleted.
105120
/// </summary>
106121
public virtual IEnumerable<TreeEntryChanges> Deleted
107122
{
108-
get { return deleted; }
123+
get { return GetChangesOfKind(ChangeKind.Deleted); }
109124
}
110125

111126
/// <summary>
112127
/// List of <see cref="TreeEntryChanges"/> that have been modified.
113128
/// </summary>
114129
public virtual IEnumerable<TreeEntryChanges> Modified
115130
{
116-
get { return modified; }
131+
get { return GetChangesOfKind(ChangeKind.Modified); }
117132
}
118133

119134
/// <summary>
120135
/// List of <see cref="TreeEntryChanges"/> which type have been changed.
121136
/// </summary>
122137
public virtual IEnumerable<TreeEntryChanges> TypeChanged
123138
{
124-
get { return typeChanged; }
139+
get { return GetChangesOfKind(ChangeKind.TypeChanged); }
125140
}
126141

127142
/// <summary>
128143
/// List of <see cref="TreeEntryChanges"/> which have been renamed
129144
/// </summary>
130145
public virtual IEnumerable<TreeEntryChanges> Renamed
131146
{
132-
get { return renamed; }
147+
get { return GetChangesOfKind(ChangeKind.Renamed); }
133148
}
134149

135150
/// <summary>
136151
/// List of <see cref="TreeEntryChanges"/> which have been copied
137152
/// </summary>
138153
public virtual IEnumerable<TreeEntryChanges> Copied
139154
{
140-
get { return copied; }
155+
get { return GetChangesOfKind(ChangeKind.Copied); }
141156
}
142157

143158
/// <summary>
144159
/// List of <see cref="TreeEntryChanges"/> which are unmodified
145160
/// </summary>
146161
public virtual IEnumerable<TreeEntryChanges> Unmodified
147162
{
148-
get { return unmodified; }
163+
get { return GetChangesOfKind(ChangeKind.Unmodified); }
149164
}
150165

151166
/// <summary>
152167
/// List of <see cref="TreeEntryChanges"/> which are conflicted
153168
/// </summary>
154169
public virtual IEnumerable<TreeEntryChanges> Conflicted
155170
{
156-
get { return conflicted; }
171+
get { return GetChangesOfKind(ChangeKind.Conflicted); }
157172
}
158173

159174
private string DebuggerDisplay
@@ -186,8 +201,7 @@ public void Dispose()
186201
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
187202
protected virtual void Dispose(bool disposing)
188203
{
189-
// This doesn't do anything yet because it loads everything
190-
// eagerly and disposes of the diff handle in the constructor.
204+
diff.SafeDispose();
191205
}
192206
}
193207
}

LibGit2Sharp/TreeEntryChanges.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,23 @@ internal unsafe TreeEntryChanges(git_diff_delta* delta)
2828
Exists = (delta->new_file.Flags & GitDiffFlags.GIT_DIFF_FLAG_EXISTS) != 0;
2929
OldExists = (delta->old_file.Flags & GitDiffFlags.GIT_DIFF_FLAG_EXISTS) != 0;
3030

31-
Status = (delta->status == ChangeKind.Untracked || delta->status == ChangeKind.Ignored)
32-
? ChangeKind.Added
33-
: delta->status;
31+
Status = GetStatusFromChangeKind(delta->status);
32+
}
33+
34+
// This treatment of change kind was apparently introduced in order to be able
35+
// to compare a tree against the index, see commit fdc972b. It's extracted
36+
// here so that TreeEntry can use the same rules without having to instantiate
37+
// a TreeEntryChanges object.
38+
internal static ChangeKind GetStatusFromChangeKind(ChangeKind changeKind)
39+
{
40+
switch (changeKind)
41+
{
42+
case ChangeKind.Untracked:
43+
case ChangeKind.Ignored:
44+
return ChangeKind.Added;
45+
default:
46+
return changeKind;
47+
}
3448
}
3549

3650
/// <summary>

0 commit comments

Comments
 (0)
0