8000 Rebase: Handle case where patch has already been applied · libgit2/libgit2sharp@03a04d8 · GitHub
[go: up one dir, main page]

Skip to content

Commit 03a04d8

Browse files
committed
Rebase: Handle case where patch has already been applied
1 parent b05a2ea commit 03a04d8

File tree

6 files changed

+174
-18
lines changed

6 files changed

+174
-18
lines changed

LibGit2Sharp.Tests/RebaseFixture.cs

Lines changed: 100 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public class RebaseFixture : BaseFixture
1717
const string topicBranch1Name = "T1";
1818
const string topicBranch2Name = "T2";
1919
const string conflictBranch1Name = "C1";
20+
const string topicBranch1PrimeName = "T1Prime";
2021

2122
[Theory]
2223
[InlineData(topicBranch2Name, topicBranch2Name, topicBranch1Name, masterBranch1Name, 3)]
@@ -50,7 +51,7 @@ public void CanRebase(string initialBranchName,
5051
int afterStepCallCount = 0;
5152

5253
List<ObjectId> PreRebaseCommits = new List<ObjectId>();
53-
List<ObjectId> PostRebaseCommits = new List<ObjectId>();
54+
List<CompletedRebaseStepInfo> PostRebaseResults = new List<CompletedRebaseStepInfo>();
5455
ObjectId expectedParentId = upstream.Tip.Id;
5556

5657
RebaseOptions options = new RebaseOptions()
@@ -63,7 +64,7 @@ public void CanRebase(string initialBranchName,
6364
RebaseStepCompleted = x =>
6465
{
6566
afterStepCallCount++;
66-
PostRebaseCommits.Add(x.CommitId);
67+
PostRebaseResults.Add(new CompletedRebaseStepInfo(x.CommitId, x.WasPatchAlreadyApplied));
6768
},
6869
};
6970

@@ -91,19 +92,58 @@ public void CanRebase(string initialBranchName,
9192

9293
// Verify the chain of commits that resulted from the rebase.
9394
Commit expectedParent = expectedOntoCommit;
94-
foreach(Commit rebasedCommit in PostRebaseCommits.Select(id => repo.Lookup<Commit>(id)))
95+
foreach(CompletedRebaseStepInfo stepInfo in PostRebaseResults)
9596
{
97+
Commit rebasedCommit = repo.Lookup<Commit>(stepInfo.ObjectId);
9698
Assert.Equal(expectedParent.Id, rebasedCommit.Parents.First().Id);
99+
Assert.False(stepInfo.WasPatchAlreadyApplied);
97100
expectedParent = rebasedCommit;
98101
}
99102

100-
Assert.Equal(repo.Head.Tip.Id, PostRebaseCommits.Last());
103+
Assert.Equal(repo.Head.Tip.Id, PostRebaseResults.Last().ObjectId);
101104
}
102105
}
103106

104-
private class rebaseStepInfo
107+
private class CompletedRebaseStepInfo : IEqualityComparer<CompletedRebaseStepInfo>
105108
{
106-
public Commit Commit { get; set; }
109+
public CompletedRebaseStepInfo(ObjectId objectId, bool wasPatchAlreadyApplied)
110+
{
111+
ObjectId = objectId;
112+
WasPatchAlreadyApplied = wasPatchAlreadyApplied;
113+
}
114+
115+
public ObjectId ObjectId { get; set; }
116+
117+
public bool WasPatchAlreadyApplied { get; set; }
118+
119+
bool IEqualityComparer<CompletedRebaseStepInfo>.Equals(CompletedRebaseStepInfo x, CompletedRebaseStepInfo y)
120+
{
121+
if (x == null && y == null)
122+
{
123+
return true;
124+
}
125+
126+
if ((x == null && y != null ) ||
127+
(x != null && y == null ))
128+
{
129+
return false;
130+
}
131+
132+
return x.WasPatchAlreadyApplied == y.WasPatchAlreadyApplied &&
133+
ObjectId.Equals(x.ObjectId, y.ObjectId);
134+
}
135+
136+
int IEqualityComparer<CompletedRebaseStepInfo>.GetHashCode(CompletedRebaseStepInfo obj)
137+
{
138+
int hashCode = obj.WasPatchAlreadyApplied.GetHashCode();
139+
140+
if (obj.ObjectId != null)
141+
{
142+
hashCode += obj.ObjectId.GetHashCode();
143+
}
144+
145+
return hashCode;
146+
}
107147
}
108148

109149
/// <summary>
@@ -335,7 +375,7 @@ public void RebaseWhileAlreadyRebasingThrows()
335375

336376
Assert.Throws<LibGit2SharpException>(() =>
337377
repo.Rebase.Start(branch, upstream, onto, Constants.Signature, null));
338-
}
378+
}
339379
}
340380

341381
[Fact]
@@ -371,6 +411,57 @@ public void CurrentStepInfoIsNullWhenNotRebasing()
371411
}
372412
}
373413

414+
[Fact]
415+
public void CanHandlePatchAlreadyApplied()
416+
{
417+
SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
418+
var path = Repository.Init(scd.DirectoryPath);
419+
using (Repository repo = new Repository(path))
420+
{
421+
ConstructRebaseTestRepository(repo);
422+
423+
repo.Checkout(topicBranch1Name);
424+
425+
Branch topicBranch1Prime = repo.CreateBranch(topicBranch1PrimeName, masterBranch1Name);
426+
427+
string newFileRelativePath = "new_file.txt";
428+
Touch(repo.Info.WorkingDirectory, newFileRelativePath, "New Content");
429+
repo.Stage(newFileRelativePath);
430+
Commit commit = repo.Commit("new commit 1", Constants.Signature, Constants.Signature, new CommitOptions());
431+
432+
repo.Checkout(topicBranch1Prime);
433+
var cherryPickResult = repo.CherryPick(commit, Constants.Signature2);
434+
Assert.Equal(CherryPickStatus.CherryPicked, cherryPickResult.Status);
435+
436+
string newFileRelativePath2 = "new_file_2.txt";
437+
Touch(repo.Info.WorkingDirectory, newFileRelativePath2, "New Content for path 2");
438+
repo.Stage(newFileRelativePath2);
439+
repo.Commit("new commit 2", Constants.Signature, Constants.Signature, new CommitOptions());
440+
441+
Branch upstreamBranch = repo.Branches[topicBranch1Name];
442+
443+
List<CompletedRebaseStepInfo> rebaseResults = new List<CompletedRebaseStepInfo>();
444+
445+
RebaseOptions options = new RebaseOptions()
446+
{
447+
RebaseStepCompleted = x =>
448+
{
449+
rebaseResults.Add(new CompletedRebaseStepInfo(x.CommitId, x.WasPatchAlreadyApplied));
450+
}
451+
};
452+
453+
repo.Rebase.Start(null, upstreamBranch, null, Constants.Signature2, options);
454+
455+
List<CompletedRebaseStepInfo> expectedRebaseResults = new List<CompletedRebaseStepInfo>()
456+
{
457+
new CompletedRebaseStepInfo(null, true),
458+
new CompletedRebaseStepInfo(new ObjectId("ebdea37ecf583fb7fa5c806a1c00b82f3987fbaa"), false),
459+
};
460+
461+
Assert.Equal(expectedRebaseResults, rebaseResults);
462+
}
463+
}
464+
374465
private void ConstructRebaseTestRepository(Repository repo)
375466
{
376467
// Constructs a graph that looks like:
@@ -425,7 +516,7 @@ private void ConstructRebaseTestRepository(Repository repo)
425516
repo.Stage(filePathC);
426517
commit = repo.Commit("commit 3", Constants.Signature, Constants.Signature, new CommitOptions());
427518

428-
repo.CreateBranch(masterBranch1Name, commit, Constants.Signature);
519+
Branch masterBranch1 = repo.CreateBranch(masterBranch1Name, commit, Constants.Signature);
429520

430521
Touch(workdir, filePathB, string.Join(lineEnding, fileContentB1, fileContentB2));
431522
repo.Stage(filePathB);
@@ -455,7 +546,7 @@ private void ConstructRebaseTestRepository(Repository repo)
455546

456547
repo.CreateBranch(topicBranch2Name, commit, Constants.Signature);
457548

458-
repo.Checkout(masterBranch1Name);
549+
repo.Checkout(masterBranch1.Tip);
459550
Touch(workdir, filePathD, fileContentD1);
460551
repo.Stage(filePathD);
461552
commit = repo.Commit("commit 10", Constants.Signature, Constants.Signature, new CommitOptions());

LibGit2Sharp/AfterRebaseStepInfo.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ internal AfterRebaseStepInfo(RebaseStepInfo stepInfo, ObjectId commitId)
2020
{
2121
StepInfo = stepInfo;
2222
CommitId = commitId;
23+
WasPatchAlreadyApplied = false;
24+
}
25+
26+
/// <summary>
27+
/// Constructor to call when the patch has already been applied for this step.
28+
/// </summary>
29+
/// <param name="stepInfo"></param>
30+
internal AfterRebaseStepInfo(RebaseStepInfo stepInfo)
31+
: this (stepInfo, null)
32+
{
33+
WasPatchAlreadyApplied = true;
2334
}
2435

2536
/// <summary>
@@ -31,5 +42,11 @@ internal AfterRebaseStepInfo(RebaseStepInfo stepInfo, ObjectId commitId)
3142
/// The ID of the commit generated by the step, if any.
3243
/// </summary>
3344
public virtual ObjectId CommitId { get; private set; }
45+
46+
/// <summary>
47+
/// Was the changes for this step already applied. If so,
48+
/// <see cref="AfterRebaseStepInfo.CommitId"/> will be null.
49+
/// </summary>
50+
public virtual bool WasPatchAlreadyApplied { get; private set; }
3451
}
3552
}

LibGit2Sharp/Core/GitOid.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,13 @@ public static implicit operator ObjectId(GitOid? oid)
2727
{
2828
return oid == null ? null : new ObjectId(oid.Value);
2929
}
30+
31+
/// <summary>
32+
/// Static convenience property to return an id (all zeros).
33+
/// </summary>
34+
public static GitOid Empty
35+
{
36+
get { return new GitOid(); }
37+
}
3038
}
3139
}

LibGit2Sharp/Core/Proxy.cs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1661,15 +1661,15 @@ public static GitRebaseOperation git_rebase_next(RebaseSafeHandle rebase,
16611661
return operation;
16621662
}
16631663

1664-
public static GitOid git_rebase_commit(
1664+
public static GitRebaseCommitResult git_rebase_commit(
16651665
RebaseSafeHandle rebase,
16661666
Signature author,
16671667
Signature committer)
16681668
{
16691669
Ensure.ArgumentNotNull(rebase, "rebase");
16701670
Ensure.ArgumentNotNull(committer, "committer");
16711671

1672-
GitOid commitId = new GitOid();
1672+
GitRebaseCommitResult commitResult = new GitRebaseCommitResult();
16731673

16741674
using (ThreadAffinity())
16751675
{
@@ -1682,8 +1682,17 @@ public static GitOid git_rebase_commit(
16821682
authorHandle = author == null ?
16831683
new SignatureSafeHandle() : author.BuildHandle();
16841684

1685-
int result = NativeMethods.git_rebase_commit(ref commitId, rebase, authorHandle, committerHandle, IntPtr.Zero, IntPtr.Zero);
1686-
Ensure.ZeroResult(result);
1685+
int result = NativeMethods.git_rebase_commit(ref commitResult.CommitId, rebase, authorHandle, committerHandle, IntPtr.Zero, IntPtr.Zero);
1686+
1687+
if (result == (int)GitErrorCode.Applied)
1688+
{
1689+
commitResult.CommitId = GitOid.Empty;
1690+
commitResult.WasPatchAlreadyApplied = true;
1691+
}
1692+
else
1693+
{
1694+
Ensure.ZeroResult(result);
1695+
}
16871696
}
16881697
finally
16891698
{
@@ -1695,7 +1704,24 @@ public static GitOid git_rebase_commit(
16951704
}
16961705
}
16971706

1698-
return commitId;
1707+
return commitResult;
1708+
}
1709+
1710+
/// <summary>
1711+
/// Struct to report the result of calling git_rebase_commit.
1712+
/// </summary>
1713+
public struct GitRebaseCommitResult
1714+
{
1715+
/// <summary>
1716+
/// The ID of the commit that was generated, if any
1717+
/// </summary>
1718+
public GitOid CommitId;
1719+
1720+
/// <summary>
1721+
/// bool to indicate if the patch was already applied.
1722+
/// If Patch was already applied, then CommitId will be empty (all zeros).
1723+
/// </summary>
1724+
public bool WasPatchAlreadyApplied;
16991725
}
17001726

17011727
public static void git_rebase_abort(

LibGit2Sharp/Rebase.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ public virtual RebaseResult Continue(Signature committer, RebaseOptions options)
173173
try
174174
{
175175
rebase = Proxy.git_rebase_open(repository.Handle);
176+
var rebaseCommitResult = Proxy.git_rebase_commit(rebase, null, committer);
176177

177178
// Report that we just completed the step
178179
if (options.RebaseStepCompleted != null)
@@ -188,8 +189,14 @@ public virtual RebaseResult Continue(Signature committer, RebaseOptions options)
188189
currentStepIndex,
189190
totalStepCount);
190191

191-
GitOid id = Proxy.git_rebase_commit(rebase, null, committer);
192-
options.RebaseStepCompleted(new AfterRebaseStepInfo(stepInfo, new ObjectId(id)));
192+
if (rebaseCommitResult.WasPatchAlreadyApplied)
193+
{
194+
options.RebaseStepCompleted(new AfterRebaseStepInfo(stepInfo));
195+
}
196+
else
197+
{
198+
options.RebaseStepCompleted(new AfterRebaseStepInfo(stepInfo, new ObjectId(rebaseCommitResult.CommitId)));
199+
}
193200
}
194201

195202
RebaseResult rebaseResult = RebaseOperationImpl.Run(rebase, repository, committer, options);

LibGit2Sharp/RebaseOperationImpl.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,19 @@ public static RebaseResult Run(RebaseSafeHandle rebaseOperationHandle,
7878
// commit and continue.
7979
if (repository.Index.IsFullyMerged)
8080
{
81-
GitOid id = Proxy.git_rebase_commit(rebaseOperationHandle, null, committer);
81+
Proxy.GitRebaseCommitResult rebase_commit_result = Proxy.git_rebase_commit(rebaseOperationHandle, null, committer);
8282

8383
// Report that we just completed the step
8484
if (options.RebaseStepCompleted != null)
8585
{
86-
options.RebaseStepCompleted(new AfterRebaseStepInfo(stepInfo, new ObjectId(id)));
86+
if (rebase_commit_result.WasPatchAlreadyApplied)
87+
{
88+
options.RebaseStepCompleted(new AfterRebaseStepInfo(stepInfo));
89+
}
90+
else
91+
{
92+
options.RebaseStepCompleted(new AfterRebaseStepInfo(stepInfo, new ObjectId(rebase_commit_result.CommitId)));
93+
}
8794
}
8895
}
8996
else

0 commit comments

Comments
 (0)
0