Skip to content

Commit 5e4956c

Browse files
angusbjonesjamill
authored andcommitted
Introduced suggested GitHub changes. +Added more tests.
Put an implicit fast forward into the Merge method in Repository.cs, cleaned up usings/comments, removed GitMergeHead and made other suggested changes from github.
1 parent c2d0e1e commit 5e4956c

File tree

7 files changed

+190
-60
lines changed

7 files changed

+190
-60
lines changed

LibGit2Sharp.Tests/MergeFixture.cs

Lines changed: 160 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
using LibGit2Sharp.Tests.TestHelpers;
2-
using System.Linq;
1+
using System.Linq;
2+
using LibGit2Sharp.Tests.TestHelpers;
33
using Xunit;
44

55
namespace LibGit2Sharp.Tests
@@ -84,36 +84,184 @@ public void CanRetrieveTheBranchBeingMerged()
8484
[Fact]
8585
public void CanMergeRepos()
8686
{
87+
string firstBranchFileName = "first branch file.txt";
88+
string secondBranchFileName = "second branch file.txt";
89+
string sharedBranchFileName = "first+second branch file.txt";
90+
8791
string path = CloneStandardTestRepo();
8892
using (var repo = new Repository(path))
8993
{
9094
var firstBranch = repo.CreateBranch("FirstBranch");
9195
firstBranch.Checkout();
9296
var originalTreeCount = firstBranch.Tip.Tree.Count;
9397

94-
//Commit with ONE new file to both first & second branch (SecondBranch is created on this commit).
95-
AddFileCommitToRepo(repo, "first+second branch file");
96-
98+
// Commit with ONE new file to both first & second branch (SecondBranch is created on this commit).
99+
AddFileCommitToRepo(repo, sharedBranchFileName);
100+
97101
var secondBranch = repo.CreateBranch("SecondBranch");
98-
//Commit with ONE new file to first branch (FirstBranch moves forward as it is checked out, SecondBranch stays back one).
99-
var firstBranchCommit = AddFileCommitToRepo(repo, "first branch file");
102+
// Commit with ONE new file to first branch (FirstBranch moves forward as it is checked out, SecondBranch stays back one).
103+
var firstBranchCommit = AddFileCommitToRepo(repo, firstBranchFileName);
100104

101105
secondBranch.Checkout();
102-
//Commit with ONE new file to second branch (FirstBranch and SecondBranch now point to separate commits that both have the same parent commit).
103-
var secondBranchCommit = AddFileCommitToRepo(repo, "second branch file");
106+
// Commit with ONE new file to second branch (FirstBranch and SecondBranch now point to separate commits that both have the same parent commit).
107+
var secondBranchCommit = AddFileCommitToRepo(repo, secondBranchFileName);
104108

105109
MergeResult mergeResult = repo.Merge(repo.Branches["FirstBranch"].Tip);
106110

111+
Assert.False(mergeResult.IsUpToDate);
112+
Assert.False(mergeResult.IsFastForward);
113+
114+
var mergeCommit = repo.Commit("Merge First+Second", Constants.Signature, Constants.Signature);
115+
116+
Assert.Equal(mergeCommit.Tree.Count, originalTreeCount + 3); // Expecting original tree count plussed by the 3 added files.
117+
Assert.Equal(mergeCommit.Parents.Count(), 2); // Merge commit should have 2 parents
118+
}
119+
}
120+
121+
[Fact]
122+
public void IsUpToDateMerge()
123+
{
124+
string sharedBranchFileName = "first+second branch file.txt";
125+
126+
string path = CloneStandardTestRepo();
127+
using (var repo = new Repository(path))
128+
{
129+
var firstBranch = repo.CreateBranch("FirstBranch");
130+
firstBranch.Checkout();
131+
var originalTreeCount = firstBranch.Tip.Tree.Count;
132+
133+
// Commit with ONE new file to both first & second branch (SecondBranch is created on this commit).
134+
AddFileCommitToRepo(repo, sharedBranchFileName);
135+
136+
var secondBranch = repo.CreateBranch("SecondBranch");
137+
138+
secondBranch.Checkout();
139+
140+
MergeResult mergeResult = repo.Merge(repo.Branches["FirstBranch"].Tip);
141+
142+
Assert.True(mergeResult.IsUpToDate);
143+
Assert.False(mergeResult.IsFastForward);
144+
}
145+
}
146+
147+
[Fact]
148+
public void CanFastForwardRepos()
149+
{
150+
string firstBranchFileName = "first branch file.txt";
151+
string sharedBranchFileName = "first+second branch file.txt";
152+
153+
string path = CloneStandardTestRepo();
154+
using (var repo = new Repository(path))
155+
{
156+
var firstBranch = repo.CreateBranch("FirstBranch");
157+
firstBranch.Checkout();
158+
var originalTreeCount = firstBranch.Tip.Tree.Count;
159+
160+
// Commit with ONE new file to both first & second branch (SecondBranch is created on this commit).
161+
AddFileCommitToRepo(repo, sharedBranchFileName);
162+
163+
var secondBranch = repo.CreateBranch("SecondBranch");
164+
// Commit with ONE new file to first branch (FirstBranch moves forward as it is checked out, SecondBranch stays back one).
165+
var firstBranchCommit = AddFileCommitToRepo(repo, firstBranchFileName);
166+
167+
secondBranch.Checkout();
168+
169+
MergeResult mergeResult = repo.Merge(repo.Branches["FirstBranch"].Tip);
170+
171+
Assert.False(mergeResult.IsUpToDate);
172+
Assert.True(mergeResult.IsFastForward);
173+
107174
var mergeCommit = repo.Commit("Merge First+Second", Constants.Signature, Constants.Signature);
108175

109-
Assert.Equal(mergeCommit.Tree.Count, originalTreeCount + 3); //Expecting original tree count plussed by the 3 added files.
110-
Assert.Equal(mergeCommit.Parents.Count(), 2); //Merge commit should have 2 parents
176+
Assert.Equal(mergeCommit.Tree.Count, originalTreeCount + 2); // Expecting original tree count plussed by the 3 added files.
177+
}
178+
}
179+
180+
[Fact]
181+
public void ConflictingMergeRepos()
182+
{
183+
string firstBranchFileName = "first branch file.txt";
184+
string secondBranchFileName = "second branch file.txt";
185+
string sharedBranchFileName = "first+second branch file.txt";
186+
187+
string path = CloneStandardTestRepo();
188+
using (var repo = new Repository(path))
189+
{
190+
var firstBranch = repo.CreateBranch("FirstBranch");
191+
firstBranch.Checkout();
192+
var originalTreeCount = firstBranch.Tip.Tree.Count;
193+
194+
// Commit with ONE new file to both first & second branch (SecondBranch is created on this commit).
195+
AddFileCommitToRepo(repo, sharedBranchFileName);
196+
197+
var secondBranch = repo.CreateBranch("SecondBranch");
198+
// Commit with ONE new file to first branch (FirstBranch moves forward as it is checked out, SecondBranch stays back one).
199+
AddFileCommitToRepo(repo, firstBranchFileName);
200+
AddFileCommitToRepo(repo, sharedBranchFileName, "The first branches comment"); // Change file in first branch
201+
202+
secondBranch.Checkout();
203+
// Commit with ONE new file to second branch (FirstBranch and SecondBranch now point to separate commits that both have the same parent commit).
204+
AddFileCommitToRepo(repo, secondBranchFileName);
205+
AddFileCommitToRepo(repo, sharedBranchFileName, "The second branches comment"); // Change file in second branch
206+
207+
MergeResult mergeResult = repo.Merge(repo.Branches["FirstBranch"].Tip);
208+
209+
Assert.False(mergeResult.IsUpToDate);
210+
Assert.False(mergeResult.IsFastForward);
211+
212+
Assert.Equal(repo.Index.Conflicts.Count(), 1);
213+
214+
var conflict = repo.Index.Conflicts.First();
215+
var changes = repo.Diff.Compare(repo.Lookup<Blob>(conflict.Theirs.Id), repo.Lookup<Blob>(conflict.Ours.Id));
216+
217+
Assert.False(changes.IsBinaryComparison);
218+
}
219+
}
220+
221+
[Fact]
222+
public void ConflictingMergeReposBinary()
223+
{
224+
string firstBranchFileName = "first branch file.bin";
225+
string secondBranchFileName = "second branch file.bin";
226+
string sharedBranchFileName = "first+second branch file.bin";
227+
228+
string path = CloneStandardTestRepo();
229+
using (var repo = new Repository(path))
230+
{
231+
var firstBranch = repo.CreateBranch("FirstBranch");
232+
firstBranch.Checkout();
233+
var originalTreeCount = firstBranch.Tip.Tree.Count;
234+
235+
// Commit with ONE new file to both first & second branch (SecondBranch is created on this commit).
236+
AddFileCommitToRepo(repo, sharedBranchFileName);
237+
238+
var secondBranch = repo.CreateBranch("SecondBranch");
239+
// Commit with ONE new file to first branch (FirstBranch moves forward as it is checked out, SecondBranch stays back one).
240+
AddFileCommitToRepo(repo, firstBranchFileName);
241+
AddFileCommitToRepo(repo, sharedBranchFileName, "\0The first branches comment\0"); // Change file in first branch
242+
243+
secondBranch.Checkout();
244+
// Commit with ONE new file to second branch (FirstBranch and SecondBranch now point to separate commits that both have the same parent commit).
245+
AddFileCommitToRepo(repo, secondBranchFileName);
246+
AddFileCommitToRepo(repo, sharedBranchFileName, "\0The second branches comment\0"); // Change file in second branch
247+
248+
MergeResult mergeResult = repo.Merge(repo.Branches["FirstBranch"].Tip);
249+
250+
Assert.False(mergeResult.IsUpToDate);
251+
Assert.False(mergeResult.IsFastForward);
252+
253+
Assert.Equal(repo.Index.Conflicts.Count(), 1);
254+
255+
Conflict conflict = repo.Index.Conflicts.First();
256+
257+
var changes = repo.Diff.Compare(repo.Lookup<Blob>(conflict.Theirs.Id), repo.Lookup<Blob>(conflict.Ours.Id));
258+
259+
Assert.True(changes.IsBinaryComparison);
111260
}
112261
}
113262

114263
private Commit AddFileCommitToRepo(IRepository repository, string filename, string content = null)
115264
{
116-
filename = filename + ".txt";
117265
Touch(repository.Info.WorkingDirectory, filename, content);
118266

119267
repository.Index.Stage(filename);

LibGit2Sharp/Core/GitMergeHead.cs

Lines changed: 0 additions & 17 deletions
This file was deleted.

LibGit2Sharp/Core/GitMergeTreeOpts.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using LibGit2Sharp.Core.Handles;
2-
using System;
1+
using System;
32
using System.Runtime.InteropServices;
43

54
namespace LibGit2Sharp.Core
@@ -9,7 +8,7 @@ internal enum MergeTreeFlags
98
{
109
GIT_MERGE_TREE_FIND_RENAMES = (1 << 0),
1110
}
12-
[Flags]
11+
1312
internal enum MergeAutomergeFlags
1413
{
1514
GIT_MERGE_AUTOMERGE_NORMAL = 0,

LibGit2Sharp/Core/Proxy.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -955,8 +955,6 @@ public static GitMergeHeadHandle git_merge_head_from_oid(RepositorySafeHandle re
955955
GitMergeHeadHandle their_head;
956956

957957
int res = NativeMethods.git_merge_head_from_oid(out their_head, repo, ref oid);
958-
if (res == (int)GitErrorCode.NotFound)
959-
return null;
960958

961959
Ensure.ZeroResult(res);
962960

LibGit2Sharp/LibGit2Sharp.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@
7979
<Compile Include="RefSpec.cs" />
8080
<Compile Include="RefSpecCollection.cs" />
8181
<Compile Include="Core\EncodingMarshaler.cs" />
82-
<Compile Include="Core\GitMergeHead.cs" />
8382
<Compile Include="Core\GitMergeOpts.cs" />
8483
<Compile Include="Core\GitMergeTreeOpts.cs" />
8584
<Compile Include="Core\Handles\BranchIteratorSafeHandle.cs" />

LibGit2Sharp/MergeResult.cs

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,26 @@
11
using LibGit2Sharp.Core;
22
using LibGit2Sharp.Core.Handles;
3-
using System.Diagnostics;
43

54
namespace LibGit2Sharp
65
{
76
public class MergeResult
87
{
9-
public MergeResult(){}
8+
protected MergeResult(){}
109
internal MergeResult(GitMergeResultHandle handle)
1110
{
12-
_isUpToDate = Proxy.git_merge_result_is_uptodate(handle);
13-
_isFastForward = Proxy.git_merge_result_is_fastforward(handle);
11+
IsUpToDate = Proxy.git_merge_result_is_uptodate(handle);
12+
IsFastForward = Proxy.git_merge_result_is_fastforward(handle);
1413

15-
if (_isFastForward)
16-
_oid = Proxy.git_merge_result_fastforward_oid(handle);
14+
if (IsFastForward)
15+
{
16+
FastForwardOid = Proxy.git_merge_result_fastforward_oid(handle);
17+
}
1718
}
1819

19-
private bool _isUpToDate;
20-
public virtual bool IsUpToDate
21-
{
22-
get { return _isUpToDate; }
23-
}
20+
public virtual bool IsUpToDate { get; private set; }
2421

25-
private bool _isFastForward;
26-
public virtual bool IsFastForward
27-
{
28-
get { return _isFastForward; }
29-
}
22+
public virtual bool IsFastForward { get; private set; }
3023

31-
private readonly GitOid _oid;
32-
internal GitOid FastForwardOid
33-
{
34-
get { return _oid; }
35-
}
24+
internal GitOid FastForwardOid { get; private set; }
3625
}
3726
}

LibGit2Sharp/Repository.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,8 +1033,10 @@ public IEnumerable<MergeHead> MergeHeads
10331033
}
10341034

10351035
/// <summary>
1036-
/// Merges the given commit into HEAD.
1036+
/// Merges the given commit into HEAD as well as performing a Fast Forward if possible.
10371037
/// </summary>
1038+
/// <param name="commit">The commit to use as a reference for the changes that should be merged into HEAD.</param>
1039+
/// <returns>The result of the performed merge <see cref="MergeResult"/>.</returns>
10381040
public MergeResult Merge(Commit commit)
10391041
{
10401042
using (GitMergeHeadHandle mergeHeadHandle = Proxy.git_merge_head_from_oid(Handle, commit.Id.Oid))
@@ -1043,15 +1045,27 @@ public MergeResult Merge(Commit commit)
10431045
{
10441046
Version = 1,
10451047
MergeTreeOpts = { Version = 1 },
1046-
CheckoutOpts = { version = 1 }
1048+
CheckoutOpts = { version = 1 },
10471049
};
10481050
using (GitMergeResultHandle mergeResultHandle = Proxy.git_merge(Handle, new GitMergeHeadHandle[] { mergeHeadHandle }, opts))
1049-
{
1050-
return new MergeResult(mergeResultHandle);
1051+
{
1052+
var result = new MergeResult(mergeResultHandle);
1053+
if (result.IsFastForward)
1054+
FastForward(result);
1055+
return result;
10511056
}
10521057
}
10531058
}
10541059

1060+
internal Branch FastForward(MergeResult mergeResult)
1061+
{
1062+
if (mergeResult == null)
1063+
throw new ArgumentNullException("A merge result must be provided.");
1064+
if (!mergeResult.IsFastForward)
1065+
throw new ArgumentException("The given merge result does not contain a Fast Forward OID.");
1066+
return this.Checkout(new ObjectId(mergeResult.FastForwardOid).Sha);
1067+
}
1068+
10551069
internal StringComparer PathComparer
10561070
{
10571071
get { return pathCase.Value.Comparer; }

0 commit comments

Comments
 (0)