Skip to content

Commit 9015578

Browse files
committed
Update Checkout and Merge options
Checkout methods now use CheckoutOptions Merge now takes several options: - Option to specify what is checked out for file conflicts. - Report CheckoutProgress and CheckoutNotify - Option to specify MergeFileFavor Updates for code review feedback
1 parent 81cb760 commit 9015578

27 files changed

+805
-140
lines changed

LibGit2Sharp.Tests/CheckoutFixture.cs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ public void CanForcefullyCheckoutWithConflictingStagedChanges()
266266
Assert.Throws<MergeConflictException>(() => repo.Checkout(master.CanonicalName));
267267

268268
// Checkout with force option should succeed.
269-
repo.Checkout(master.CanonicalName, CheckoutModifiers.Force, null, null);
269+
repo.Checkout(master.CanonicalName, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force});
270270

271271
// Assert that master branch is checked out.
272272
Assert.True(repo.Branches["master"].IsCurrentRepositoryHead);
@@ -338,8 +338,14 @@ public void CanCancelCheckoutThroughNotifyCallback()
338338

339339
// Verify that we get called for the notify conflict cb
340340
string conflictPath = string.Empty;
341-
CheckoutNotificationOptions checkoutNotifications = new CheckoutNotificationOptions((path, flags) => { conflictPath = path; return false; }, CheckoutNotifyFlags.Conflict);
342-
Assert.Throws<UserCancelledException>(() => repo.Checkout("master", CheckoutModifiers.None, null, checkoutNotifications));
341+
342+
CheckoutOptions options = new CheckoutOptions()
343+
{
344+
OnCheckoutNotify = (path, flags) => { conflictPath = path; return false; },
345+
CheckoutNotifyFlags = CheckoutNotifyFlags.Conflict,
346+
};
347+
348+
Assert.Throws<UserCancelledException>(() => repo.Checkout("master", options));
343349
Assert.Equal(relativePath, conflictPath);
344350
}
345351
}
@@ -398,7 +404,7 @@ public void CheckingOutThroughBranchCallsCheckoutProgress()
398404
bool wasCalled = false;
399405

400406
Branch branch = repo.Branches[otherBranchName];
401-
branch.Checkout(CheckoutModifiers.None, (path, completed, total) => wasCalled = true, null);
407+
branch.Checkout(new CheckoutOptions() { OnCheckoutProgress = (path, completed, total) => wasCalled = true});
402408

403409
Assert.True(wasCalled);
404410
}
@@ -414,7 +420,7 @@ public void CheckingOutThroughRepositoryCallsCheckoutProgress()
414420
PopulateBasicRepository(repo);
415421
bool wasCalled = false;
416422

417-
repo.Checkout(otherBranchName, CheckoutModifiers.None, (path, completed, total) => wasCalled = true, null);
423+
repo.Checkout(otherBranchName, new CheckoutOptions() { OnCheckoutProgress = (path, completed, total) => wasCalled = true});
418424

419425
Assert.True(wasCalled);
420426
}
@@ -486,11 +492,13 @@ public void CheckingOutCallsCheckoutNotify(CheckoutNotifyFlags notifyFlags, stri
486492
string actualNotificationPath = string.Empty;
487493
CheckoutNotifyFlags actualNotifyFlags = CheckoutNotifyFlags.None;
488494

489-
CheckoutNotificationOptions checkoutNotifications = new CheckoutNotificationOptions(
490-
(path, notificationType) => { wasCalled = true; actualNotificationPath = path; actualNotifyFlags = notificationType; return true; },
491-
notifyFlags);
495+
CheckoutOptions options = new CheckoutOptions()
496+
{
497+
OnCheckoutNotify = (path, notificationType) => { wasCalled = true; actualNotificationPath = path; actualNotifyFlags = notificationType; return true; },
498+
CheckoutNotifyFlags = notifyFlags,
499+
};
492500

493-
Assert.Throws<MergeConflictException>(() => repo.Checkout("master", CheckoutModifiers.None, null, checkoutNotifications));
501+
Assert.Throws<MergeConflictException>(() => repo.Checkout("master", options));
494502

495503
Assert.True(wasCalled);
496504
Assert.Equal(expectedNotificationPath, actualNotificationPath);
@@ -538,7 +546,7 @@ public void ForceCheckoutRetainsUntrackedChanges()
538546
Assert.Equal(1, repo.Index.RetrieveStatus().Untracked.Count());
539547
Assert.Equal(FileStatus.Untracked, repo.Index.RetrieveStatus(fullPathFileB));
540548

541-
repo.Checkout(otherBranchName, CheckoutModifiers.Force, null, null);
549+
repo.Checkout(otherBranchName, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force });
542550

543551
// Verify untracked entry still exists.
544552
Assert.Equal(1, repo.Index.RetrieveStatus().Untracked.Count());
@@ -641,7 +649,7 @@ public void ForceCheckoutRetainsIgnoredChanges()
641649

642650
Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(ignoredFilePath));
643651

644-
repo.Checkout(otherBranchName, CheckoutModifiers.Force, null, null);
652+
repo.Checkout(otherBranchName, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force });
645653

646654
// Verify that the ignored file still exists.
647655
Assert.Equal(FileStatus.Ignored, repo.Index.RetrieveStatus(ignoredFilePath));
@@ -718,7 +726,7 @@ public void CheckingOutABranchDoesNotAlterBinaryFiles()
718726
// The blob actually exists in the object database with the correct Sha
719727
Assert.Equal(expectedSha, repo.Lookup<Blob>(expectedSha).Sha);
720728

721-
repo.Checkout("refs/heads/logo", CheckoutModifiers.Force, null, null);
729+
repo.Checkout("refs/heads/logo", new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force });
722730

723731
// The Index has been updated as well with the blob
724732
Assert.Equal(expectedSha, repo.Index["square-logo.png"].Id.Sha);

LibGit2Sharp.Tests/MergeFixture.cs

Lines changed: 226 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
using System.Linq;
1+
using System;
2+
using System.Diagnostics;
3+
using System.IO;
4+
using System.Linq;
25
using LibGit2Sharp.Tests.TestHelpers;
36
using Xunit;
47
using Xunit.Extensions;
@@ -341,6 +344,122 @@ public void CanNonFastForwardMergeCommit(bool fromDetachedHead, FastForwardStrat
341344
}
342345
}
343346

347+
[Fact]
348+
public void MergeReportsCheckoutProgress()
349+
{
350+
string repoPath = CloneMergeTestRepo();
351+
using (var repo = new Repository(repoPath))
352+
{
353+
Commit commitToMerge = repo.Branches["normal_merge"].Tip;
354+
355+
bool wasCalled = false;
356+
357+
MergeOptions options = new MergeOptions()
358+
{
359+
OnCheckoutProgress = (path, completed, total) => wasCalled = true,
360+
};
361+
362+
MergeResult result = repo.Merge(commitToMerge, Constants.Signature, options);
363+
364+
Assert.True(wasCalled);
365+
}
366+
}
367+
368+
[Fact]
369+
public void MergeReportsCheckoutNotifications()
370+
{
371+
string repoPath = CloneMergeTestRepo();
372+
using (var repo = new Repository(repoPath))
373+
{
374+
Commit commitToMerge = repo.Branches["normal_merge"].Tip;
375+
376+
bool wasCalled = false;
377+
CheckoutNotifyFlags actualNotifyFlags = CheckoutNotifyFlags.None;
378+
379+
MergeOptions options = new MergeOptions()
380+
{
381+
OnCheckoutNotify = (path, notificationType) => { wasCalled = true; actualNotifyFlags = notificationType; return true; },
382+
CheckoutNotifyFlags = CheckoutNotifyFlags.Updated,
383+
};
384+
385+
MergeResult result = repo.Merge(commitToMerge, Constants.Signature, options);
386+
387+
Assert.True(wasCalled);
388+
Assert.Equal(CheckoutNotifyFlags.Updated, actualNotifyFlags);
389+
}
390+
}
391+
392+
[Fact]
393+
public void FastForwardMergeReportsCheckoutProgress()
394+
{
395+
string repoPath = CloneMergeTestRepo();
396+
using (var repo = new Repository(repoPath))
397+
{
398+
Commit commitToMerge = repo.Branches["fast_forward"].Tip;
399+
400+
bool wasCalled = false;
401+
402+
MergeOptions options = new MergeOptions()
403+
{
404+
OnCheckoutProgress = (path, completed, total) => wasCalled = true,
405+
};
406+
407+
MergeResult result = repo.Merge(commitToMerge, Constants.Signature, options);
408+
409+
Assert.True(wasCalled);
410+
}
411+
}
412+
413+
[Fact]
414+
public void FastForwardMergeReportsCheckoutNotifications()
415+
{
416+
string repoPath = CloneMergeTestRepo();
417+
using (var repo = new Repository(repoPath))
418+
{
419+
Commit commitToMerge = repo.Branches["fast_forward"].Tip;
420+
421+
bool wasCalled = false;
422+
CheckoutNotifyFlags actualNotifyFlags = CheckoutNotifyFlags.None;
423+
424+
MergeOptions options = new MergeOptions()
425+
{
426+
OnCheckoutNotify = (path, notificationType) => { wasCalled = true; actualNotifyFlags = notificationType; return true; },
427+
CheckoutNotifyFlags = CheckoutNotifyFlags.Updated,
428+
};
429+
430+
MergeResult result = repo.Merge(commitToMerge, Constants.Signature, options);
431+
432+
Assert.True(wasCalled);
433+
Assert.Equal(CheckoutNotifyFlags.Updated, actualNotifyFlags);
434+
}
435+
}
436+
437+
[Fact]
438+
public void MergeCanDetectRenames()
439+
{
440+
// The environment is set up such that:
441+
// file b.txt is edited in the "rename" branch and
442+
// edited and renamed in the "rename_source" branch.
443+
// The edits are automergable.
444+
// We can rename "rename_source" into "rename"
445+
// if rename detection is enabled,
446+
// but the merge will fail with conflicts if this
447+
// change is not detected as a rename.
448+
449+
string repoPath = CloneMergeTestRepo();
450+
using (var repo = new Repository(repoPath))
451+
{
452+
Branch currentBranch = repo.Checkout("rename_source");
453+
Assert.NotNull(currentBranch);
454+
455+
Branch branchToMerge = repo.Branches["rename"];
456+
457+
MergeResult result = repo.Merge(branchToMerge, Constants.Signature);
458+
459+
Assert.Equal(MergeStatus.NonFastForward, result.Status);
460+
}
461+
}
462+
344463
[Fact]
345464
public void FastForwardNonFastForwardableMergeThrows()
346465
{
@@ -422,6 +541,112 @@ public void CanMergeCommittish(string committish, FastForwardStrategy strategy,
422541
}
423542
}
424543

544+
[Theory]
545+
[InlineData(CheckoutFileConflictStrategy.Ours)]
546+
[InlineData(CheckoutFileConflictStrategy.Theirs)]
547+
public void CanSpecifyConflictFileStrategy(CheckoutFileConflictStrategy conflictStrategy)
548+
{
549+
const string conflictFile = "a.txt";
550+
const string conflictBranchName = "conflicts";
551+
552+
string path = CloneMergeTestRepo();
553+
using (var repo = new Repository(path))
554+
{
555+
Branch branch = repo.Branches[conflictBranchName];
556+
Assert.NotNull(branch);
557+
558+
MergeOptions mergeOptions = new MergeOptions()
559+
{
560+
FileConflictStrategy = conflictStrategy,
561+
};
562+
563+
MergeResult result = repo.Merge(branch, Constants.Signature, mergeOptions);
564+
Assert.Equal(MergeStatus.Conflicts, result.Status);
565+
566+
// Get the information on the conflict.
567+
Conflict conflict = repo.Index.Conflicts[conflictFile];
568+
569+
Assert.NotNull(conflict);
570+
Assert.NotNull(conflict.Theirs);
571+
Assert.NotNull(conflict.Ours);
572+
573+
// Get the blob containing the expected content.
574+
Blob expectedBlob = null;
575+
switch(conflictStrategy)
576+
{
577+
case CheckoutFileConflictStrategy.Theirs:
578+
expectedBlob = repo.Lookup<Blob>(conflict.Theirs.Id);
579+
break;
580+
case CheckoutFileConflictStrategy.Ours:
581+
expectedBlob = repo.Lookup<Blob>(conflict.Ours.Id);
582+
break;
583+
default:
584+
throw new Exception("Unexpected FileConflictStrategy");
585+
}
586+
587+
Assert.NotNull(expectedBlob);
588+
589+
// Check the content of the file on disk matches what is expected.
590+
string expectedContent = expectedBlob.GetContentText(new FilteringOptions(conflictFile));
591+
Assert.Equal(expectedContent, File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, conflictFile)));
592+
}
593+
}
594+
595+
[Theory]
596+
[InlineData(MergeFileFavor.Ours)]
597+
[InlineData(MergeFileFavor.Theirs)]
598+
public void MergeCanSpecifyMergeFileFavorOption(MergeFileFavor fileFavorFlag)
599+
{
600+
const string conflictFile = "a.txt";
601+
const string conflictBranchName = "conflicts";
602+
603+
string path = CloneMergeTestRepo();
604+
using (var repo = InitIsolatedRepository(path))
605+
{
606+
Branch branch = repo.Branches[conflictBranchName];
607+
Assert.NotNull(branch);
608+
609+
var status = repo.Index.RetrieveStatus();
610+
MergeOptions mergeOptions = new MergeOptions()
611+
{
612+
MergeFileFavor = fileFavorFlag,
613+
};
614+
615+
MergeResult result = repo.Merge(branch, Constants.Signature, mergeOptions);
616+
617+
Assert.Equal(MergeStatus.NonFastForward, result.Status);
618+
619+
// Verify that the index and working directory are clean
620+
Assert.True(repo.Index.IsFullyMerged);
621+
Assert.False(repo.Index.RetrieveStatus().IsDirty);
622+
623+
// Get the blob containing the expected content.
624+
Blob expectedBlob = null;
625+
switch (fileFavorFlag)
626+
{
627+
case MergeFileFavor.Theirs:
628+
expectedBlob = repo.Lookup<Blob>("3dd9738af654bbf1c363f6c3bbc323bacdefa179");
629+
break;
630+
case MergeFileFavor.Ours:
631+
expectedBlob = repo.Lookup<Blob>("610b16886ca829cebd2767d9196f3c4378fe60b5");
632+
break;
633+
default:
634+
throw new Exception("Unexpected MergeFileFavor");
635+
}
636+
637+
Assert.NotNull(expectedBlob);
638+
639+
// Verify the index has the expected contents
640+
IndexEntry entry = repo.Index[conflictFile];
641+
Assert.NotNull(entry);
642+
Assert.Equal(expectedBlob.Id, entry.Id);
643+
644+
// Verify the content of the file on disk matches what is expected.
645+
string expectedContent = expectedBlob.GetContentText(new FilteringOptions(conflictFile));
646+
Assert.Equal(expectedContent, File.ReadAllText(Path.Combine(repo.Info.WorkingDirectory, conflictFile)));
647+
}
648+
}
649+
425650
[Theory]
426651
[InlineData("refs/heads/normal_merge", FastForwardStrategy.Default, MergeStatus.NonFastForward)]
427652
[InlineData("fast_forward", FastForwardStrategy.Default, MergeStatus.FastForward)]

LibGit2Sharp.Tests/RepositoryFixture.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ public void QueryingTheRemoteForADetachedHeadBranchReturnsNull()
592592
string path = CloneStandardTestRepo();
593593
using (var repo = new Repository(path))
594594
{
595-
repo.Checkout(repo.Head.Tip.Sha, CheckoutModifiers.Force, null, null);
595+
repo.Checkout(repo.Head.Tip.Sha, new CheckoutOptions() { CheckoutModifiers = CheckoutModifiers.Force });
596596
Branch trackLocal = repo.Head;
597597
Assert.Null(trackLocal.Remote);
598598
}
Binary file not shown.

LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/info/refs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@
22
4dfaa1500526214ae7b33f9b2c1144ca8b6b1f53 refs/heads/fast_forward
33
83cebf5389a4adbcb80bda6b68513caee4559802 refs/heads/master
44
625186280ed2a6ec9b65d250ed90cf2e4acef957 refs/heads/normal_merge
5+
24434077dec097c1203ef9e1345c0545c190936a refs/heads/rename
6+
2bc71d0e8acfbb9fd1cc2d9d48c23dbf8aea52c9 refs/heads/rename_source
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
0000000000000000000000000000000000000000 83cebf5389a4adbcb80bda6b68513caee4559802 Jameson Miller <[email protected]> 1398366152 -0400 branch: Created from HEAD
2+
83cebf5389a4adbcb80bda6b68513caee4559802 9075c06ff9cd736610dea688bca7e912903ff2d1 Jameson Miller <[email protected]> 1398366254 -0400 commit: Add content to b.txt
3+
9075c06ff9cd736610dea688bca7e912903ff2d1 24434077dec097c1203ef9e1345c0545c190936a Jameson Miller <[email protected]> 1398366641 -0400 commit: edit to b.txt
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
0000000000000000000000000000000000000000 9075c06ff9cd736610dea688bca7e912903ff2d1 Jameson Miller <[email protected]> 1398366267 -0400 branch: Created from rename
2+
9075c06ff9cd736610dea688bca7e912903ff2d1 2bc71d0e8acfbb9fd1cc2d9d48c23dbf8aea52c9 Jameson Miller <[email protected]> 1398366593 -0400 commit: rename and edit b.txt
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
P pack-52e214e240728e3b0ce2fc2d5e6513772fed0523.pack
1+
P pack-f9b2f231d5e59d4a265578d02283f848a5dc4694.pack
22

LibGit2Sharp.Tests/Resources/merge_testrepo_wd/dot_git/packed-refs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@
33
4dfaa1500526214ae7b33f9b2c1144ca8b6b1f53 refs/heads/fast_forward
44
83cebf5389a4adbcb80bda6b68513caee4559802 refs/heads/master
55
625186280ed2a6ec9b65d250ed90cf2e4acef957 refs/heads/normal_merge
6+
24434077dec097c1203ef9e1345c0545c190936a refs/heads/rename
7+
2bc71d0e8acfbb9fd1cc2d9d48c23dbf8aea52c9 refs/heads/rename_source

0 commit comments

Comments
 (0)