Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions LibGit2Sharp.Tests/RevertFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -391,5 +391,72 @@ public void CanNotRevertAMergeCommitWithoutSpecifyingTheMainlineBranch()
Assert.Throws<LibGit2SharpException>(() => repo.Revert(commitToRevert, Constants.Signature));
}
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void RevertWithNothingToRevert(bool commitOnSuccess)
{
// The branch name to perform the revert on
const string revertBranchName = "refs/heads/revert";

string path = CloneRevertTestRepo();
using (var repo = new Repository(path))
{
// Checkout the revert branch.
Branch branch = repo.Checkout(revertBranchName);
Assert.NotNull(branch);

Commit commitToRevert = repo.Head.Tip;

// Revert tip commit.
RevertResult result = repo.Revert(commitToRevert, Constants.Signature);
Assert.NotNull(result);
Assert.Equal(RevertStatus.Reverted, result.Status);

// Revert the same commit a second time
result = repo.Revert(
commitToRevert,
Constants.Signature,
new RevertOptions() { CommitOnSuccess = commitOnSuccess });

Assert.NotNull(result);
Assert.Equal(null, result.Commit);
Assert.Equal(RevertStatus.NothingToRevert, result.Status);

if (commitOnSuccess)
{
Assert.Equal(CurrentOperation.None, repo.Info.CurrentOperation);
}
else
{
Assert.Equal(CurrentOperation.Revert, repo.Info.CurrentOperation);
}
}
}

[Fact]
public void RevertOrphanedBranchThrows()
{
// The branch name to perform the revert on
const string revertBranchName = "refs/heads/revert";

string path = CloneRevertTestRepo();
using (var repo = new Repository(path))
{
// Checkout the revert branch.
Branch branch = repo.Checkout(revertBranchName);
Assert.NotNull(branch);

Commit commitToRevert = repo.Head.Tip;

// Move the HEAD to an orphaned branch.
repo.Refs.UpdateTarget("HEAD", "refs/heads/orphan");
Assert.True(repo.Info.IsHeadUnborn);

// Revert the tip of the refs/heads/revert branch.
Assert.Throws<UnbornBranchException>(() => repo.Revert(commitToRevert, Constants.Signature));
}
}
}
}
43 changes: 41 additions & 2 deletions LibGit2Sharp/Repository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,14 @@ internal MergeResult MergeFetchHeads(Signature merger, MergeOptions options)

/// <summary>
/// Revert the specified commit.
/// <para>
/// If the revert is successful but there are no changes to commit,
/// then the <see cref="RevertStatus"/> will be <see cref="RevertStatus.NothingToRevert"/>.
/// If the revert is successful and there are changes to revert, then
/// the <see cref="RevertStatus"/> will be <see cref="RevertStatus.Reverted"/>.
/// If the revert resulted in conflicts, then the <see cref="RevertStatus"/>
/// will be <see cref="RevertStatus.Conflicts"/>.
/// </para>
/// </summary>
/// <param name="commit">The <see cref="Commit"/> to revert.</param>
/// <param name="reverter">The <see cref="Signature"/> of who is performing the revert.</param>
Expand All @@ -1094,6 +1102,11 @@ public RevertResult Revert(Commit commit, Signature reverter, RevertOptions opti
Ensure.ArgumentNotNull(commit, "commit");
Ensure.ArgumentNotNull(reverter, "reverter");

if (Info.IsHeadUnborn)
{
throw new UnbornBranchException("Can not revert the commit. The Head doesn't point at a commit.");
}

options = options ?? new RevertOptions();

RevertResult result = null;
Expand Down Expand Up @@ -1123,12 +1136,38 @@ public RevertResult Revert(Commit commit, Signature reverter, RevertOptions opti
if (Index.IsFullyMerged)
{
Commit revertCommit = null;

// Check if the revert generated any changes
// and set the revert status accordingly
bool anythingToRevert = Index.RetrieveStatus(
new StatusOptions()
{
DetectRenamesInIndex = false,
Show = StatusShowOption.IndexOnly
}).Any();

RevertStatus revertStatus = anythingToRevert ?
RevertStatus.Reverted : RevertStatus.NothingToRevert;

if (options.CommitOnSuccess)
{
revertCommit = this.Commit(Info.Message, author: reverter, committer: reverter);
if (!anythingToRevert)
{
// If there were no changes to revert, and we are
// asked to commit the changes, then cleanup
// the repository state (following command line behavior).
Proxy.git_repository_state_cleanup(handle);
}
else
{
revertCommit = this.Commit(
Info.Message,
author: reverter,
committer: reverter);
}
}

result = new RevertResult(RevertStatus.Reverted, revertCommit);
result = new RevertResult(revertStatus, revertCommit);
}
else
{
Expand Down
12 changes: 11 additions & 1 deletion LibGit2Sharp/RevertOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,17 @@ public RevertOptions()
public CheckoutNotifyHandler OnCheckoutNotify { get; set; }

/// <summary>
/// Commit the revert if the revert is successful.
/// Commit changes if there are no conflicts and the revert results
/// in changes.
/// <para>
/// Following command line behavior, if the revert results in no
/// changes, then Revert will cleanup the repository state if
/// <see cref="CommitOnSuccess"/> is true (i.e. the repository
/// will not be left in a "revert in progress" state).
/// If <see cref="CommitOnSuccess"/> is false and there are no
/// changes to revert, then the repository will be left in
/// the "revert in progress" state.
/// </para>
/// </summary>
public bool CommitOnSuccess { get; set; }

Expand Down
7 changes: 6 additions & 1 deletion LibGit2Sharp/RevertResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ public enum RevertStatus
/// <summary>
/// The revert resulted in conflicts.
/// </summary>
Conflicts
Conflicts,

/// <summary>
/// Revert was run, but there were no changes to commit.
/// </summary>
NothingToRevert,
}
}