Skip to content

Commit 90c9b20

Browse files
committed
Insert new reflog entry on commit
Fix #371 Insert entry on HEAD and target direct reference on commit Tag as initial the first commit
1 parent 030089b commit 90c9b20

6 files changed

Lines changed: 161 additions & 5 deletions

File tree

LibGit2Sharp.Tests/CommitFixture.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -614,14 +614,23 @@ public void CanCommitALittleBit()
614614
Assert.Null(repo.Head[relativeFilepath]);
615615

616616
var author = DummySignature;
617-
Commit commit = repo.Commit("Initial egotistic commit", author, author);
617+
const string commitMessage = "Initial egotistic commit";
618+
Commit commit = repo.Commit(commitMessage, author, author);
618619

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

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

626+
// Assert a reflog entry is created
627+
Assert.Equal(1, repo.Refs.Log("HEAD").Count());
628+
var reflogEntry = repo.Refs.Log("HEAD").First();
629+
Assert.Equal(author, reflogEntry.Commiter);
630+
Assert.Equal(commit.Id, reflogEntry.To);
631+
Assert.Equal(ObjectId.Zero, reflogEntry.From);
632+
Assert.Equal(string.Format("commit (initial): {0}", commitMessage), reflogEntry.Message);
633+
625634
File.WriteAllText(filePath, "nulltoken commits!\n");
626635
repo.Index.Stage(relativeFilepath);
627636

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

646+
// Assert the reflog is shifted
647+
Assert.Equal(2, repo.Refs.Log("HEAD").Count());
648+
Assert.Equal(reflogEntry.To, repo.Refs.Log("HEAD").First().From);
649+
637650
Branch firstCommitBranch = repo.CreateBranch("davidfowl-rules", commit);
638651
repo.Checkout(firstCommitBranch);
639652

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

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

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

737751
AssertCommitHasBeenAmended(repo, amendedCommit, mergedCommit);
752+
753+
// Assert a reflog entry is created
754+
var reflogEntry = repo.Refs.Log("HEAD").First();
755+
Assert.Equal(amendedCommit.Committer, reflogEntry.Commiter);
756+
Assert.Equal(amendedCommit.Id, reflogEntry.To);
757+
Assert.Equal(string.Format("commit (amend): {0}", commitMessage), reflogEntry.Message);
738758
}
739759
}
740760

LibGit2Sharp.Tests/ReflogFixture.cs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using LibGit2Sharp.Tests.TestHelpers;
1+
using System.IO;
2+
using LibGit2Sharp.Tests.TestHelpers;
23
using System.Linq;
34
using Xunit;
45

@@ -37,5 +38,69 @@ public void CannotReadReflogOnUnknownReference()
3738
Assert.Throws<InvalidSpecificationException>(() => repo.Refs.Log("toto").Count());
3839
}
3940
}
41+
42+
[Fact]
43+
public void CommitShouldCreateReflogEntryOnHeadandOnTargetedDirectReference()
44+
{
45+
SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
46+
47+
using (var repo = Repository.Init(scd.DirectoryPath))
48+
{
49+
// setup refs as HEAD => unit_test => master
50+
var newRef = repo.Refs.Add("refs/heads/unit_test", "refs/heads/master");
51+
Assert.NotNull(newRef);
52+
repo.Refs.UpdateTarget(repo.Refs.Head, newRef);
53+
54+
const string relativeFilepath = "new.txt";
55+
string filePath = Path.Combine(repo.Info.WorkingDirectory, relativeFilepath);
56+
57+
File.WriteAllText(filePath, "content\n");
58+
repo.Index.Stage(relativeFilepath);
59+
60+
var author = DummySignature;
61+
const string commitMessage = "Hope reflog behaves as it should";
62+
Commit commit = repo.Commit(commitMessage, author, author);
63+
64+
// Assert a reflog entry is created on HEAD
65+
Assert.Equal(1, repo.Refs.Log("HEAD").Count());
66+
var reflogEntry = repo.Refs.Log("HEAD").First();
67+
Assert.Equal(author, reflogEntry.Commiter);
68+
Assert.Equal(commit.Id, reflogEntry.To);
69+
Assert.Equal(ObjectId.Zero, reflogEntry.From);
70+
71+
// Assert the same reflog entry is created on refs/heads/master
72+
Assert.Equal(1, repo.Refs.Log("refs/heads/master").Count());
73+
reflogEntry = repo.Refs.Log("HEAD").First();
74+
Assert.Equal(author, reflogEntry.Commiter);
75+
Assert.Equal(commit.Id, reflogEntry.To);
76+
Assert.Equal(ObjectId.Zero, reflogEntry.From);
77+
78+
// Assert no reflog entry is created on refs/heads/unit_test
79+
Assert.Equal(0, repo.Refs.Log("refs/heads/unit_test").Count());
80+
}
81+
}
82+
83+
[Fact]
84+
public void CommitOnUnbornReferenceShouldCreateReflogEntryWithInitialTag()
85+
{
86+
SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
87+
88+
using (var repo = Repository.Init(scd.DirectoryPath))
89+
{
90+
const string relativeFilepath = "new.txt";
91+
string filePath = Path.Combine(repo.Info.WorkingDirectory, relativeFilepath);
92+
93+
File.WriteAllText(filePath, "content\n");
94+
repo.Index.Stage(relativeFilepath);
95+
96+
var author = DummySignature;
97+
const string commitMessage = "First commit should be logged as initial";
98+
repo.Commit(commitMessage, author, author);
99+
100+
// Assert the reflog entry message is correct
101+
Assert.Equal(1, repo.Refs.Log("HEAD").Count());
102+
Assert.Equal(string.Format("commit (initial): {0}", commitMessage), repo.Refs.Log("HEAD").First().Message);
103+
}
104+
}
40105
}
41106
}

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,16 @@ internal static extern OidSafeHandle git_reflog_entry_id_new(
729729
internal static extern IntPtr git_reflog_entry_committer(
730730
SafeHandle entry);
731731

732+
[DllImport(libgit2)]
733+
internal static extern int git_reflog_append(
734+
ReflogSafeHandle reflog,
735+
ref GitOid id,
736+
SignatureSafeHandle committer,
737+
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string msg);
738+
739+
[DllImport(libgit2)]
740+
internal static extern int git_reflog_write(ReflogSafeHandle reflog);
741+
732742
[DllImport(libgit2)]
733743
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8NoCleanupMarshaler))]
734744
internal static extern string git_reflog_entry_message(SafeHandle entry);

LibGit2Sharp/Core/Proxy.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,6 +1317,21 @@ public static string git_reflog_entry_message(SafeHandle entry)
13171317
return NativeMethods.git_reflog_entry_message(entry);
13181318
}
13191319

1320+
public static void git_reflog_append(ReflogSafeHandle reflog, ObjectId commit_id, Signature committer, string message)
1321+
{
1322+
using (ThreadAffinity())
1323+
using (SignatureSafeHandle committerHandle = committer.BuildHandle())
1324+
{
1325+
var oid = commit_id.Oid;
1326+
1327+
int res = NativeMethods.git_reflog_append(reflog, ref oid, committerHandle, message);
1328+
Ensure.ZeroResult(res);
1329+
1330+
res = NativeMethods.git_reflog_write(reflog);
1331+
Ensure.ZeroResult(res);
1332+
}
1333+
}
1334+
13201335
#endregion
13211336

13221337
#region git_refspec

LibGit2Sharp/ReflogCollection.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System;
2-
using System.Collections;
1+
using System.Collections;
32
using System.Collections.Generic;
43
using System.Diagnostics;
54
using System.Globalization;
@@ -87,5 +86,22 @@ private string DebuggerDisplay
8786
"Count = {0}", this.Count());
8887
}
8988
}
89+
90+
/// <summary>
91+
/// Add a new <see cref="ReflogEntry"/> to the current <see cref="ReflogCollection"/>. It will be created as first item of the collection
92+
/// The native reflog object will be saved right after inserting the entry.
93+
/// </summary>
94+
/// <param name="objectId">the <see cref="ObjectId"/> of the new commit the <see cref="Reference"/> will point out.</param>
95+
/// <param name="committer"><see cref="Signature"/> of the author of the new commit.</param>
96+
/// <param name="message">the message associated with the new <see cref="ReflogEntry"/>.</param>
97+
internal virtual void Append(ObjectId objectId, Signature committer, string message)
98+
{
99+
using (ReferenceSafeHandle reference = Proxy.git_reference_lookup(repo.Handle, canonicalName, true))
100+
using (ReflogSafeHandle reflog = Proxy.git_reflog_read(reference))
101+
{
102+
string prettifiedMessage = Proxy.git_message_prettify(message);
103+
Proxy.git_reflog_append(reflog, objectId, committer, prettifiedMessage);
104+
}
105+
}
90106
}
91107
}

LibGit2Sharp/Repository.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,9 +684,39 @@ public Commit Commit(string message, Signature author, Signature committer, bool
684684

685685
Proxy.git_repository_merge_cleanup(handle);
686686

687+
// Insert reflog entry
688+
LogCommit(result, amendPreviousCommit, parents.Count() == 0);
689+
687690
return result;
688691
}
689692

693+
private void LogCommit(Commit commit, bool amendPreviousCommit, bool isInitialCommit)
694+
{
695+
// Compute reflog message
696+
string reflogMessage = "commit";
697+
if (isInitialCommit)
698+
{
699+
reflogMessage += " (initial)";
700+
}
701+
else if(amendPreviousCommit)
702+
{
703+
reflogMessage += " (amend)";
704+
}
705+
706+
reflogMessage = string.Format("{0}: {1}", reflogMessage, commit.Message);
707+
708+
var headRef = Refs.Head;
709+
710+
// in case HEAD targets a symbolic reference, log commit on the targeted direct reference
711+
if(headRef is SymbolicReference)
712+
{
713+
Refs.Log(headRef.ResolveToDirectReference()).Append(commit.Id, commit.Committer, reflogMessage);
714+
}
715+
716+
// Log commit on HEAD
717+
Refs.Log(headRef).Append(commit.Id, commit.Committer, reflogMessage);
718+
}
719+
690720
private IEnumerable<Commit> RetrieveParentsOfTheCommitBeingCreated(bool amendPreviousCommit)
691721
{
692722
if (amendPreviousCommit)

0 commit comments

Comments
 (0)