Skip to content

Commit e2523bb

Browse files
committed
Merge thoughts
1 parent 5e4956c commit e2523bb

File tree

8 files changed

+216
-50
lines changed

8 files changed

+216
-50
lines changed

LibGit2Sharp.Tests/MergeFixture.cs

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,11 @@ public void CanMergeRepos()
108108

109109
MergeResult mergeResult = repo.Merge(repo.Branches["FirstBranch"].Tip);
110110

111-
Assert.False(mergeResult.IsUpToDate);
112-
Assert.False(mergeResult.IsFastForward);
111+
Assert.Equal(MergeStatus.Normal, mergeResult.Status);
113112

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
113+
Assert.Equal(repo.Head.Tip, mergeResult.Commit);
114+
Assert.Equal(mergeResult.Commit.Tree.Count, originalTreeCount + 3); // Expecting original tree count plussed by the 3 added files.
115+
Assert.Equal(mergeResult.Commit.Parents.Count(), 2); // Merge commit should have 2 parents
118116
}
119117
}
120118

@@ -139,8 +137,7 @@ public void IsUpToDateMerge()
139137

140138
MergeResult mergeResult = repo.Merge(repo.Branches["FirstBranch"].Tip);
141139

142-
Assert.True(mergeResult.IsUpToDate);
143-
Assert.False(mergeResult.IsFastForward);
140+
Assert.Equal(MergeStatus.UpToDate, mergeResult.Status);
144141
}
145142
}
146143

@@ -153,6 +150,12 @@ public void CanFastForwardRepos()
153150
string path = CloneStandardTestRepo();
154151
using (var repo = new Repository(path))
155152
{
153+
// Reset the index and the working tree.
154+
repo.Reset(ResetMode.Hard);
155+
156+
// Clean the working directory.
157+
repo.RemoveUntrackedFiles();
158+
156159
var firstBranch = repo.CreateBranch("FirstBranch");
157160
firstBranch.Checkout();
158161
var originalTreeCount = firstBranch.Tip.Tree.Count;
@@ -168,12 +171,10 @@ public void CanFastForwardRepos()
168171

169172
MergeResult mergeResult = repo.Merge(repo.Branches["FirstBranch"].Tip);
170173

171-
Assert.False(mergeResult.IsUpToDate);
172-
Assert.True(mergeResult.IsFastForward);
173-
174-
var mergeCommit = repo.Commit("Merge First+Second", Constants.Signature, Constants.Signature);
175-
176-
Assert.Equal(mergeCommit.Tree.Count, originalTreeCount + 2); // Expecting original tree count plussed by the 3 added files.
174+
Assert.Equal(MergeStatus.FastForward, mergeResult.Status);
175+
Assert.Equal(repo.Branches["FirstBranch"].Tip, mergeResult.Commit);
176+
Assert.Equal(repo.Branches["FirstBranch"].Tip, repo.Head.Tip);
177+
Assert.Equal(0, repo.Index.RetrieveStatus().Count());
177178
}
178179
}
179180

@@ -206,8 +207,7 @@ public void ConflictingMergeRepos()
206207

207208
MergeResult mergeResult = repo.Merge(repo.Branches["FirstBranch"].Tip);
208209

209-
Assert.False(mergeResult.IsUpToDate);
210-
Assert.False(mergeResult.IsFastForward);
210+
Assert.Equal(MergeStatus.Conflicts, mergeResult.Status);
211211

212212
Assert.Equal(repo.Index.Conflicts.Count(), 1);
213213

@@ -247,8 +247,7 @@ public void ConflictingMergeReposBinary()
247247

248248
MergeResult mergeResult = repo.Merge(repo.Branches["FirstBranch"].Tip);
249249

250-
Assert.False(mergeResult.IsUpToDate);
251-
Assert.False(mergeResult.IsFastForward);
250+
Assert.Equal(MergeStatus.Conflicts, mergeResult.Status);
252251

253252
Assert.Equal(repo.Index.Conflicts.Count(), 1);
254253

LibGit2Sharp/Core/GitMergeOpts.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,21 @@
33

44
namespace LibGit2Sharp.Core
55
{
6-
[Flags]
7-
internal enum MergeFlags
6+
internal enum GitMergeFlags
87
{
8+
/// <summary>
9+
/// Default
10+
/// </summary>
11+
GIT_MERGE_DEFAULT = 0,
12+
13+
/// <summary>
14+
/// Do not fast-forward.
15+
/// </summary>
916
GIT_MERGE_NO_FASTFORWARD = 1,
17+
18+
/// <summary>
19+
/// Only perform fast-forward.
20+
/// </summary>
1021
GIT_MERGE_FASTFORWARD_ONLY = 2,
1122
}
1223

@@ -15,7 +26,7 @@ internal struct GitMergeOpts
1526
{
1627
public uint Version;
1728

18-
public MergeFlags MergeFlags;
29+
public GitMergeFlags MergeFlags;
1930
public GitMergeTreeOpts MergeTreeOpts;
2031
public GitCheckoutOpts CheckoutOpts;
2132
}

LibGit2Sharp/Core/GitMergeResult.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using LibGit2Sharp.Core;
2+
using LibGit2Sharp.Core.Handles;
3+
4+
namespace LibGit2Sharp
5+
{
6+
internal class GitMergeResult
7+
{
8+
internal GitMergeResult(GitMergeResultHandle handle)
9+
{
10+
IsUpToDate = Proxy.git_merge_result_is_uptodate(handle);
11+
IsFastForward = Proxy.git_merge_result_is_fastforward(handle);
12+
13+
if (IsFastForward)
14+
{
15+
FastForwardId = Proxy.git_merge_result_fastforward_oid(handle);
16+
}
17+
}
18+
19+
public virtual bool IsUpToDate { get; private set; }
20+
21+
public virtual bool IsFastForward { get; private set; }
22+
23+
/// <summary>
24+
/// The ID that a fast-forward merge should advance to.
25+
/// </summary>
26+
public virtual ObjectId FastForwardId { get; private set; }
27+
28+
public virtual MergeStatus Status
29+
{
30+
get
31+
{
32+
if (IsUpToDate)
33+
{
34+
return MergeStatus.UpToDate;
35+
}
36+
else if (IsFastForward)
37+
{
38+
return MergeStatus.FastForward;
39+
}
40+
else
41+
{
42+
return MergeStatus.Normal;
43+
}
44+
}
45+
}
46+
}
47+
}

LibGit2Sharp/Core/GitMergeTreeOpts.cs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,20 @@
44
namespace LibGit2Sharp.Core
55
{
66
[Flags]
7-
internal enum MergeTreeFlags
7+
internal enum GitMergeTreeFlags
88
{
9+
/// <summary>
10+
/// No options.
11+
/// </summary>
12+
GIT_MERGE_TREE_NORMAL = 0,
13+
14+
/// <summary>
15+
/// GIT_MERGE_TREE_FIND_RENAMES in libgit2
16+
/// </summary>
917
GIT_MERGE_TREE_FIND_RENAMES = (1 << 0),
1018
}
1119

12-
internal enum MergeAutomergeFlags
20+
internal enum GitMergeAutomergeFlags
1321
{
1422
GIT_MERGE_AUTOMERGE_NORMAL = 0,
1523
GIT_MERGE_AUTOMERGE_NONE = 1,
@@ -22,12 +30,28 @@ internal struct GitMergeTreeOpts
2230
{
2331
public uint Version;
2432

25-
public MergeTreeFlags MergeTreeFlags;
33+
public GitMergeTreeFlags MergeTreeFlags;
34+
35+
/// <summary>
36+
/// Similarity to consider a file renamed.
37+
/// </summary>
2638
public uint RenameThreshold;
39+
40+
/// <summary>
41+
/// Maximum similarity sources to examine (overrides
42+
/// 'merge.renameLimit' config (default 200)
43+
/// </summary>
2744
public uint TargetLimit;
2845

29-
public UIntPtr Metric;
46+
/// <summary>
47+
/// Pluggable similarityMetric; pass IntPtr.Zero
48+
/// to use internal metric.
49+
/// </summary>
50+
public IntPtr SimilarityMetric;
3051

31-
public MergeAutomergeFlags MergeAutomergeFlags;
52+
/// <summary>
53+
/// Flags for automerging content.
54+
/// </summary>
55+
public GitMergeAutomergeFlags MergeAutomergeFlags;
3256
}
3357
}

LibGit2Sharp/IRepository.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ public interface IRepository : IDisposable
196196
/// <summary>
197197
/// Merges the given commit into HEAD.
198198
/// </summary>
199+
/// <param name="commit">The commit to use as a reference for the changes that should be merged into HEAD.</param>
199200
MergeResult Merge(Commit commit);
200201

201202
/// <summary>

LibGit2Sharp/LibGit2Sharp.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
<Compile Include="CommitSortStrategies.cs" />
7777
<Compile Include="CompareOptions.cs" />
7878
<Compile Include="FetchOptions.cs" />
79+
<Compile Include="MergeResult.cs" />
7980
<Compile Include="RefSpec.cs" />
8081
<Compile Include="RefSpecCollection.cs" />
8182
<Compile Include="Core\EncodingMarshaler.cs" />
@@ -93,7 +94,7 @@
9394
<Compile Include="PushOptions.cs" />
9495
<Compile Include="Core\GitBuf.cs" />
9596
<Compile Include="FilteringOptions.cs" />
96-
<Compile Include="MergeResult.cs" />
97+
<Compile Include="Core\GitMergeResult.cs" />
9798
<Compile Include="ResetMode.cs" />
9899
<Compile Include="NoteCollectionExtensions.cs" />
99100
<Compile Include="RefSpecDirection.cs" />

LibGit2Sharp/MergeResult.cs

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,70 @@
1-
using LibGit2Sharp.Core;
2-
using LibGit2Sharp.Core.Handles;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
35

46
namespace LibGit2Sharp
57
{
8+
/// <summary>
9+
/// Class to report the result of a merge.
10+
/// </summary>
611
public class MergeResult
712
{
8-
protected MergeResult(){}
9-
internal MergeResult(GitMergeResultHandle handle)
13+
/// <summary>
14+
/// Needed for mocking purposes.
15+
/// </summary>
16+
protected MergeResult()
17+
{ }
18+
19+
internal MergeResult(MergeStatus status, Commit commit = null)
1020
{
11-
IsUpToDate = Proxy.git_merge_result_is_uptodate(handle);
12-
IsFastForward = Proxy.git_merge_result_is_fastforward(handle);
21+
this.Status = status;
22+
this.Commit = commit;
23+
}
1324

14-
if (IsFastForward)
15-
{
16-
FastForwardOid = Proxy.git_merge_result_fastforward_oid(handle);
17-
}
25+
/// <summary>
26+
/// The status of the merge.
27+
/// </summary>
28+
public virtual MergeStatus Status
29+
{
30+
get;
31+
private set;
1832
}
1933

20-
public virtual bool IsUpToDate { get; private set; }
34+
/// <summary>
35+
/// The resulting commit of the merge. For fast-forward merges, this is the
36+
/// commit that merge was fast forwared to.
37+
/// </summary>
38+
public virtual Commit Commit
39+
{
40+
get;
41+
private set;
42+
}
43+
}
44+
45+
/// <summary>
46+
/// The status of what happened as a result of a merge.
47+
/// </summary>
48+
public enum MergeStatus
49+
{
50+
/// <summary>
51+
/// Merge was up-to-date.
52+
/// </summary>
53+
UpToDate,
54+
55+
/// <summary>
56+
/// Fast-forward merge.
57+
/// </summary>
58+
FastForward,
2159

22-
public virtual bool IsFastForward { get; private set; }
60+
/// <summary>
61+
/// Normal merge.
62+
/// </summary>
63+
Normal,
2364

24-
internal GitOid FastForwardOid { get; private set; }
65+
/// <summary>
66+
/// Merge resulted in conflicts.
67+
/// </summary>
68+
Conflicts,
2569
}
2670
}

LibGit2Sharp/Repository.cs

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,23 +1047,62 @@ public MergeResult Merge(Commit commit)
10471047
MergeTreeOpts = { Version = 1 },
10481048
CheckoutOpts = { version = 1 },
10491049
};
1050+
1051+
1052+
// Perform the merge in libgit2 and get the result back.
1053+
GitMergeResult gitMergeResult;
10501054
using (GitMergeResultHandle mergeResultHandle = Proxy.git_merge(Handle, new GitMergeHeadHandle[] { mergeHeadHandle }, opts))
10511055
{
1052-
var result = new MergeResult(mergeResultHandle);
1053-
if (result.IsFastForward)
1054-
FastForward(result);
1055-
return result;
1056+
gitMergeResult = new GitMergeResult(mergeResultHandle);
1057+
}
1058+
1059+
// Handle the result of the merge performed in libgit2
1060+
// and commit the result / update the working directory as necessary.
1061+
MergeResult mergeResult;
1062+
switch(gitMergeResult.Status)
1063+
{
1064+
case MergeStatus.UpToDate:
1065+
mergeResult = new MergeResult(MergeStatus.UpToDate);
1066+
break;
1067+
case MergeStatus.FastForward:
1068+
Commit fastForwardCommit = this.Lookup<Commit>(gitMergeResult.FastForwardId);
1069+
FastForward(fastForwardCommit);
1070+
mergeResult = new MergeResult(MergeStatus.FastForward, fastForwardCommit);
1071+
break;
1072+
case MergeStatus.Normal:
1073+
{
1074+
if (Index.IsFullyMerged)
1075+
{
1076+
// Commit the merge
1077+
Commit mergeCommit = this.Commit(Info.Message);
1078+
mergeResult = new MergeResult(MergeStatus.Normal, mergeCommit);
1079+
}
1080+
else
1081+
{
1082+
mergeResult = new MergeResult(MergeStatus.Conflicts);
1083+
}
1084+
}
1085+
break;
1086+
default:
1087+
throw new NotImplementedException(string.Format("Unknown MergeStatus: {0}", gitMergeResult.Status));
10561088
}
1089+
1090+
return mergeResult;
10571091
}
10581092
}
10591093

1060-
internal Branch FastForward(MergeResult mergeResult)
1094+
private void FastForward(Commit fastForwardCommit)
10611095
{
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);
1096+
var checkoutOpts = new CheckoutOptions
1097+
{
1098+
CheckoutModifiers = CheckoutModifiers.None,
1099+
};
1100+
1101+
CheckoutTree(fastForwardCommit.Tree, null, checkoutOpts);
1102+
1103+
Refs.UpdateTarget("HEAD", fastForwardCommit.Id.Sha);
1104+
1105+
// TODO: Update Reflog...
10671106
}
10681107

10691108
internal StringComparer PathComparer

0 commit comments

Comments
 (0)