Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Introduce reflog #388

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 10, 2013
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
24 changes: 22 additions & 2 deletions LibGit2Sharp.Tests/CommitFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -614,14 +614,23 @@ public void CanCommitALittleBit()
Assert.Null(repo.Head[relativeFilepath]);

var author = DummySignature;
Commit commit = repo.Commit("Initial egotistic commit", author, author);
const string commitMessage = "Initial egotistic commit";
Commit commit = repo.Commit(commitMessage, author, author);

AssertBlobContent(repo.Head[relativeFilepath], "nulltoken\n");
AssertBlobContent(commit[relativeFilepath], "nulltoken\n");

Assert.Equal(0, commit.Parents.Count());
Assert.False(repo.Info.IsHeadOrphaned);

// Assert a reflog entry is created
Assert.Equal(1, repo.Refs.Log("HEAD").Count());
var reflogEntry = repo.Refs.Log("HEAD").First();
Assert.Equal(author, reflogEntry.Commiter);
Assert.Equal(commit.Id, reflogEntry.To);
Assert.Equal(ObjectId.Zero, reflogEntry.From);
Assert.Equal(string.Format("commit (initial): {0}", commitMessage), reflogEntry.Message);

File.WriteAllText(filePath, "nulltoken commits!\n");
repo.Index.Stage(relativeFilepath);

Expand All @@ -634,6 +643,10 @@ public void CanCommitALittleBit()
Assert.Equal(1, commit2.Parents.Count());
Assert.Equal(commit.Id, commit2.Parents.First().Id);

// Assert the reflog is shifted
Assert.Equal(2, repo.Refs.Log("HEAD").Count());
Assert.Equal(reflogEntry.To, repo.Refs.Log("HEAD").First().From);

Branch firstCommitBranch = repo.CreateBranch("davidfowl-rules", commit);
repo.Checkout(firstCommitBranch);

Expand Down Expand Up @@ -731,10 +744,17 @@ public void CanAmendACommitWithMoreThanOneParent()
repo.Reset(ResetOptions.Soft, mergedCommit.Sha);

CreateAndStageANewFile(repo);
const string commitMessage = "I'm rewriting the history!";

Commit amendedCommit = repo.Commit("I'm rewriting the history!", DummySignature, DummySignature, true);
Commit amendedCommit = repo.Commit(commitMessage, DummySignature, DummySignature, true);

AssertCommitHasBeenAmended(repo, amendedCommit, mergedCommit);

// Assert a reflog entry is created
var reflogEntry = repo.Refs.Log("HEAD").First();
Assert.Equal(amendedCommit.Committer, reflogEntry.Commiter);
Assert.Equal(amendedCommit.Id, reflogEntry.To);
Assert.Equal(string.Format("commit (amend): {0}", commitMessage), reflogEntry.Message);
}
}

Expand Down
1 change: 1 addition & 0 deletions LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
<Compile Include="CheckoutFixture.cs" />
<Compile Include="RemoteFixture.cs" />
<Compile Include="PushFixture.cs" />
<Compile Include="ReflogFixture.cs" />
<Compile Include="StageFixture.cs" />
<Compile Include="StashFixture.cs" />
<Compile Include="CloneFixture.cs" />
Expand Down
15 changes: 15 additions & 0 deletions LibGit2Sharp.Tests/ReferenceFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,21 @@ public void CanTellIfAReferenceIsValid(string refname, bool expectedResult)
}
}

[Fact]
public void CanUpdateTheTargetOfASymbolicReferenceWithAnotherSymbolicReference()
{
SelfCleaningDirectory scd = BuildSelfCleaningDirectory();

using (var repo = Repository.Init(scd.DirectoryPath))
{
Reference symbolicRef = repo.Refs.Add("refs/heads/unit_test", "refs/heads/master");

Reference newHead = repo.Refs.UpdateTarget(repo.Refs.Head, symbolicRef);
Assert.IsType<SymbolicReference>(newHead);
Assert.Equal(symbolicRef.CanonicalName, newHead.TargetIdentifier);
}
}

private static T[] SortedRefs<T>(IRepository repo, Func<Reference, T> selector)
{
return repo.Refs.OrderBy(r => r.CanonicalName, StringComparer.Ordinal).Select(selector).ToArray();
Expand Down
106 changes: 106 additions & 0 deletions LibGit2Sharp.Tests/ReflogFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System.IO;
using LibGit2Sharp.Tests.TestHelpers;
using System.Linq;
using Xunit;

namespace LibGit2Sharp.Tests
{
public class ReflogFixture : BaseFixture
{
[Fact]
public void CanReadReflog()
{
const int expectedReflogEntriesCount = 3;


using (var repo = new Repository(StandardTestRepoWorkingDirPath))
{
var reflog = repo.Refs.Log(repo.Refs.Head);

Assert.Equal(expectedReflogEntriesCount, reflog.Count());

// Initial commit assertions
Assert.Equal("[email protected]", reflog.Last().Commiter.Email);
Assert.True(reflog.Last().Message.StartsWith("clone: from"));
Assert.Equal(ObjectId.Zero, reflog.Last().From);

// second commit assertions
Assert.Equal("4c062a6361ae6959e06292c1fa5e2822d9c96345", reflog.ElementAt(expectedReflogEntriesCount - 2).From.Sha);
Assert.Equal("592d3c869dbc4127fc57c189cb94f2794fa84e7e", reflog.ElementAt(expectedReflogEntriesCount - 2).To.Sha);
}
}

[Fact]
public void CannotReadReflogOnUnknownReference()
{
using (var repo = new Repository(StandardTestRepoWorkingDirPath))
{
Assert.Throws<InvalidSpecificationException>(() => repo.Refs.Log("toto").Count());
}
}

[Fact]
public void CommitShouldCreateReflogEntryOnHeadandOnTargetedDirectReference()
{
SelfCleaningDirectory scd = BuildSelfCleaningDirectory();

using (var repo = Repository.Init(scd.DirectoryPath))
{
// setup refs as HEAD => unit_test => master
var newRef = repo.Refs.Add("refs/heads/unit_test", "refs/heads/master");
Assert.NotNull(newRef);
repo.Refs.UpdateTarget(repo.Refs.Head, newRef);

const string relativeFilepath = "new.txt";
string filePath = Path.Combine(repo.Info.WorkingDirectory, relativeFilepath);

File.WriteAllText(filePath, "content\n");
repo.Index.Stage(relativeFilepath);

var author = DummySignature;
const string commitMessage = "Hope reflog behaves as it should";
Commit commit = repo.Commit(commitMessage, author, author);

// Assert a reflog entry is created on HEAD
Assert.Equal(1, repo.Refs.Log("HEAD").Count());
var reflogEntry = repo.Refs.Log("HEAD").First();
Assert.Equal(author, reflogEntry.Commiter);
Assert.Equal(commit.Id, reflogEntry.To);
Assert.Equal(ObjectId.Zero, reflogEntry.From);

// Assert the same reflog entry is created on refs/heads/master
Assert.Equal(1, repo.Refs.Log("refs/heads/master").Count());
reflogEntry = repo.Refs.Log("HEAD").First();
Assert.Equal(author, reflogEntry.Commiter);
Assert.Equal(commit.Id, reflogEntry.To);
Assert.Equal(ObjectId.Zero, reflogEntry.From);

// Assert no reflog entry is created on refs/heads/unit_test
Assert.Equal(0, repo.Refs.Log("refs/heads/unit_test").Count());
}
}

[Fact]
public void CommitOnUnbornReferenceShouldCreateReflogEntryWithInitialTag()
{
SelfCleaningDirectory scd = BuildSelfCleaningDirectory();

using (var repo = Repository.Init(scd.DirectoryPath))
{
const string relativeFilepath = "new.txt";
string filePath = Path.Combine(repo.Info.WorkingDirectory, relativeFilepath);

File.WriteAllText(filePath, "content\n");
repo.Index.Stage(relativeFilepath);

var author = DummySignature;
const string commitMessage = "First commit should be logged as initial";
repo.Commit(commitMessage, author, author);

// Assert the reflog entry message is correct
Assert.Equal(1, repo.Refs.Log("HEAD").Count());
Assert.Equal(string.Format("commit (initial): {0}", commitMessage), repo.Refs.Log("HEAD").First().Message);
}
}
}
}
6 changes: 6 additions & 0 deletions LibGit2Sharp/Core/Handles/ReflogEntrySafeHandle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace LibGit2Sharp.Core.Handles
{
internal class ReflogEntrySafeHandle : NotOwnedSafeHandleBase
{
}
}
11 changes: 11 additions & 0 deletions LibGit2Sharp/Core/Handles/ReflogSafeHandle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace LibGit2Sharp.Core.Handles
{
internal class ReflogSafeHandle : SafeHandleBase
{
protected override bool ReleaseHandleImpl()
{
Proxy.git_reflog_free(handle);
return true;
}
}
}
44 changes: 44 additions & 0 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,50 @@ internal static extern int git_reference_symbolic_set_target(
[DllImport(libgit2)]
internal static extern GitReferenceType git_reference_type(ReferenceSafeHandle reference);

[DllImport(libgit2)]
internal static extern void git_reflog_free(
IntPtr reflog);

[DllImport(libgit2)]
internal static extern int git_reflog_read(
out ReflogSafeHandle ref_out,
ReferenceSafeHandle reference);

[DllImport(libgit2)]
internal static extern UIntPtr git_reflog_entrycount
(ReflogSafeHandle reflog);

[DllImport(libgit2)]
internal static extern ReflogEntrySafeHandle git_reflog_entry_byindex(
ReflogSafeHandle reflog,
UIntPtr idx);

[DllImport(libgit2)]
internal static extern OidSafeHandle git_reflog_entry_id_old(
SafeHandle entry);

[DllImport(libgit2)]
internal static extern OidSafeHandle git_reflog_entry_id_new(
SafeHandle entry);

[DllImport(libgit2)]
internal static extern IntPtr git_reflog_entry_committer(
SafeHandle entry);

[DllImport(libgit2)]
internal static extern int git_reflog_append(
ReflogSafeHandle reflog,
ref GitOid id,
SignatureSafeHandle committer,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string msg);

[DllImport(libgit2)]
internal static extern int git_reflog_write(ReflogSafeHandle reflog);

[DllImport(libgit2)]
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8NoCleanupMarshaler))]
internal static extern string git_reflog_entry_message(SafeHandle entry);

[DllImport(libgit2)]
internal static extern int git_refspec_rtransform(
byte[] target,
Expand Down
67 changes: 67 additions & 0 deletions LibGit2Sharp/Core/Proxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1267,6 +1267,73 @@ public static GitReferenceType git_reference_type(ReferenceSafeHandle reference)

#endregion

#region git_reflog_

public static void git_reflog_free(IntPtr reflog)
{
NativeMethods.git_reflog_free(reflog);
}

public static ReflogSafeHandle git_reflog_read(ReferenceSafeHandle reference)
{
using (ThreadAffinity())
{
ReflogSafeHandle reflog_out;

int res = NativeMethods.git_reflog_read(out reflog_out, reference);
Ensure.ZeroResult(res);

return reflog_out;
}
}

public static int git_reflog_entrycount(ReflogSafeHandle reflog)
{
return (int)NativeMethods.git_reflog_entrycount(reflog);
}

public static ReflogEntrySafeHandle git_reflog_entry_byindex(ReflogSafeHandle reflog, int idx)
{
return NativeMethods.git_reflog_entry_byindex(reflog, (UIntPtr)idx);
}

public static ObjectId git_reflog_entry_id_old(SafeHandle entry)
{
return NativeMethods.git_reflog_entry_id_old(entry).MarshalAsObjectId();
}

public static ObjectId git_reflog_entry_id_new(SafeHandle entry)
{
return NativeMethods.git_reflog_entry_id_new(entry).MarshalAsObjectId();
}

public static Signature git_reflog_entry_committer(SafeHandle entry)
{
return new Signature(NativeMethods.git_reflog_entry_committer(entry));
}

public static string git_reflog_entry_message(SafeHandle entry)
{
return NativeMethods.git_reflog_entry_message(entry);
}

public static void git_reflog_append(ReflogSafeHandle reflog, ObjectId commit_id, Signature committer, string message)
{
using (ThreadAffinity())
using (SignatureSafeHandle committerHandle = committer.BuildHandle())
{
var oid = commit_id.Oid;

int res = NativeMethods.git_reflog_append(reflog, ref oid, committerHandle, message);
Ensure.ZeroResult(res);

res = NativeMethods.git_reflog_write(reflog);
Ensure.ZeroResult(res);
}
}

#endregion

#region git_refspec

public static string git_refspec_rtransform(GitRefSpecHandle refSpecPtr, string name)
Expand Down
4 changes: 4 additions & 0 deletions LibGit2Sharp/LibGit2Sharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,14 @@
<Compile Include="ConflictCollection.cs" />
<Compile Include="Core\Handles\GitRefSpecHandle.cs" />
<Compile Include="UnmatchedPathException.cs" />
<Compile Include="Core\Handles\ReflogEntrySafeHandle.cs" />
<Compile Include="Core\Handles\ReflogSafeHandle.cs" />
<Compile Include="ReflogCollection.cs" />
<Compile Include="Core\PathCase.cs" />
<Compile Include="MatchedPathsAggregator.cs" />
<Compile Include="Network.cs" />
<Compile Include="Core\GitRemoteHead.cs" />
<Compile Include="ReflogEntry.cs" />
<Compile Include="Stash.cs" />
<Compile Include="StashOptions.cs" />
<Compile Include="OrphanedHeadException.cs" />
Expand Down
29 changes: 29 additions & 0 deletions LibGit2Sharp/ReferenceCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ private Reference UpdateTarget<T>(Reference reference, T target, Func<ReferenceS
return Add("HEAD", target as DirectReference, true);
}

if (target is SymbolicReference)
{
return Add("HEAD", target as SymbolicReference, true);
}

throw new ArgumentException(string.Format(CultureInfo.InvariantCulture,
"'{0}' is not a valid target type.", typeof(T)));
}
Expand Down Expand Up @@ -262,5 +267,29 @@ private string DebuggerDisplay
"Count = {0}", this.Count());
}
}

/// <summary>
/// Returns as a <see cref="ReflogCollection"/> the reflog of the <see cref="Reference"/> named <paramref name="canonicalName"/>
/// </summary>
/// <param name="canonicalName">The canonical name of the reference</param>
/// <returns>a <see cref="ReflogCollection"/>, enumerable of <see cref="ReflogEntry"/></returns>
public virtual ReflogCollection Log(string canonicalName)
{
Ensure.ArgumentNotNullOrEmptyString(canonicalName, "canonicalName");

return new ReflogCollection(repo, canonicalName);
}

/// <summary>
/// Returns as a <see cref="ReflogCollection"/> the reflog of the <see cref="Reference"/> <paramref name="reference"/>
/// </summary>
/// <param name="reference">The reference</param>
/// <returns>a <see cref="ReflogCollection"/>, enumerable of <see cref="ReflogEntry"/></returns>
public virtual ReflogCollection Log(Reference reference)
{
Ensure.ArgumentNotNull(reference, "reference");

return new ReflogCollection(repo, reference.CanonicalName);
}
}
}
Loading