Permalink
Browse files

Make Repository.Commit() able to amend the current tip of the Head

  • Loading branch information...
1 parent fb60830 commit 2ba6961327473478ea4862f6bb373f2c0f751bee @nulltoken nulltoken committed Feb 7, 2012
View
97 LibGit2Sharp.Tests/CommitFixture.cs
@@ -487,7 +487,7 @@ public void CanCommitALittleBit()
repo.Head[relativeFilepath].ShouldBeNull();
- var author = new Signature("Author N. Ame", "him@there.com", DateTimeOffset.Now.AddSeconds(-10));
+ var author = DummySignature;
Commit commit = repo.Commit("Initial egotistic commit", author, author);
AssertBlobContent(repo.Head[relativeFilepath], "nulltoken\n");
@@ -534,12 +534,9 @@ private static void AssertBlobContent(TreeEntry entry, string expectedContent)
((Blob)(entry.Target)).ContentAsUtf8().ShouldEqual(expectedContent);
}
- [Test]
- public void CanGeneratePredictableObjectShas()
+ private static void CommitToANewRepository(string path)
{
- SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
-
- using (var repo = Repository.Init(scd.DirectoryPath))
+ using (Repository repo = Repository.Init(path))
{
const string relativeFilepath = "test.txt";
string filePath = Path.Combine(repo.Info.WorkingDirectory, relativeFilepath);
@@ -548,16 +545,100 @@ public void CanGeneratePredictableObjectShas()
repo.Index.Stage(relativeFilepath);
var author = new Signature("nulltoken", "emeric.fermas@gmail.com", DateTimeOffset.Parse("Wed, Dec 14 2011 08:29:03 +0100"));
- Commit commit = repo.Commit("Initial commit\n", author, author);
+ repo.Commit("Initial commit\n", author, author);
+ }
+ }
- commit.Sha.ShouldEqual("1fe3126578fc4eca68c193e4a3a0a14a0704624d");
+ [Test]
+ public void CanGeneratePredictableObjectShas()
+ {
+ SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
+
+ CommitToANewRepository(scd.DirectoryPath);
+ using (var repo = new Repository(scd.DirectoryPath))
+ {
+ Commit commit = repo.Commits.Single();
+ commit.Sha.ShouldEqual("1fe3126578fc4eca68c193e4a3a0a14a0704624d");
Tree tree = commit.Tree;
tree.Sha.ShouldEqual("2b297e643c551e76cfa1f93810c50811382f9117");
Blob blob = tree.Files.Single();
blob.Sha.ShouldEqual("9daeafb9864cf43055ae93beb0afd6c7d144bfa4");
}
}
+
+ [Test]
+ public void CanAmendARootCommit()
+ {
+ SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
+
+ CommitToANewRepository(scd.DirectoryPath);
+
+ using (var repo = new Repository(scd.DirectoryPath))
+ {
+ repo.Head.Commits.Count().ShouldEqual(1);
+
+ Commit originalCommit = repo.Head.Tip;
+ originalCommit.Parents.Count().ShouldEqual(0);
+
+ CreateAndStageANewFile(repo);
+
+ Commit amendedCommit = repo.Commit("I'm rewriting the history!", DummySignature, DummySignature, true);
+
+ repo.Head.Commits.Count().ShouldEqual(1);
+
+ AssertCommitHasBeenAmended(repo, amendedCommit, originalCommit);
+ }
+ }
+
+ [Test]
+ public void CanAmendACommitWithMoreThanOneParent()
+ {
+ TemporaryCloneOfTestRepo path = BuildTemporaryCloneOfTestRepo(StandardTestRepoPath);
+ using (var repo = new Repository(path.RepositoryPath))
+ {
+ var mergedCommit = repo.Lookup<Commit>("be3563a");
+ mergedCommit.ShouldNotBeNull();
+ mergedCommit.Parents.Count().ShouldEqual(2);
+
+ repo.Reset(ResetOptions.Soft, mergedCommit.Sha);
+
+ CreateAndStageANewFile(repo);
+
+ Commit amendedCommit = repo.Commit("I'm rewriting the history!", DummySignature, DummySignature, true);
+
+ AssertCommitHasBeenAmended(repo, amendedCommit, mergedCommit);
+ }
+ }
+
+ private static void CreateAndStageANewFile(Repository repo)
+ {
+ string relativeFilepath = string.Format("new-file-{0}.txt", Guid.NewGuid());
+ string filePath = Path.Combine(repo.Info.WorkingDirectory, relativeFilepath);
+
+ File.WriteAllText(filePath, "brand new content\n");
+ repo.Index.Stage(relativeFilepath);
+ }
+
+ private void AssertCommitHasBeenAmended(Repository repo, Commit amendedCommit, Commit originalCommit)
+ {
+ Commit headCommit = repo.Head.Tip;
+ headCommit.ShouldEqual(amendedCommit);
+
+ amendedCommit.Sha.ShouldNotEqual(originalCommit.Sha);
+ CollectionAssert.AreEqual(originalCommit.Parents, amendedCommit.Parents);
+ }
+
+ [Test]
+ public void CanNotAmendAnEmptyRepository()
+ {
+ SelfCleaningDirectory scd = BuildSelfCleaningDirectory();
+
+ using (Repository repo = Repository.Init(scd.DirectoryPath))
+ {
+ Assert.Throws<LibGit2Exception>(() => repo.Commit("I can not amend anything !:(", DummySignature, DummySignature, true));
+ }
+ }
}
}
View
2 LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs
@@ -20,6 +20,8 @@ static BaseFixture()
public static string StandardTestRepoPath { get; private set; }
public static DirectoryInfo ResourcesDirectory { get; private set; }
+ public static readonly Signature DummySignature = new Signature("Author N. Ame", "him@there.com", DateTimeOffset.Now);
+
private static void SetUpTestEnvironment()
{
var source = new DirectoryInfo(@"../../Resources");
View
42 LibGit2Sharp/CommitCollection.cs
@@ -129,55 +129,55 @@ private static bool PointsAtTheHead(string shaOrRefName)
/// <param name = "message">The description of why a change was made to the repository.</param>
/// <param name = "author">The <see cref = "Signature" /> of who made the change.</param>
/// <param name = "committer">The <see cref = "Signature" /> of who added the change to the repository.</param>
+ /// <param name="amendPreviousCommit">True to amend the current <see cref="Commit"/> pointed at by <see cref="Repository.Head"/>, false otherwise.</param>
/// <returns>The generated <see cref = "Commit" />.</returns>
- public Commit Create(string message, Signature author, Signature committer)
+ public Commit Create(string message, Signature author, Signature committer, bool amendPreviousCommit)
{
Ensure.ArgumentNotNull(message, "message");
Ensure.ArgumentNotNull(author, "author");
Ensure.ArgumentNotNull(committer, "committer");
+ if (amendPreviousCommit && repo.Info.IsEmpty)
+ {
+ throw new LibGit2Exception("Can not amend anything. The Head doesn't point at any commit.");
+ }
+
GitOid treeOid;
int res = NativeMethods.git_tree_create_fromindex(out treeOid, repo.Index.Handle);
- string encoding = null;
-
Ensure.Success(res);
- Reference head = repo.Refs["HEAD"];
+ var parentIds = RetrieveParentIdsOfTheCommitBeingCreated(repo, amendPreviousCommit);
GitOid commitOid;
using (var treePtr = new ObjectSafeWrapper(new ObjectId(treeOid), repo))
- using (ObjectSafeWrapper headPtr = RetrieveHeadCommitPtr(head))
+ using (var parentObjectPtrs = new DisposableEnumerable<ObjectSafeWrapper>(parentIds.Select(id => new ObjectSafeWrapper(id, repo))))
using (SignatureSafeHandle authorHandle = author.BuildHandle())
using (SignatureSafeHandle committerHandle = committer.BuildHandle())
{
- IntPtr[] parentPtrs = BuildArrayFrom(headPtr);
- res = NativeMethods.git_commit_create(out commitOid, repo.Handle, head.CanonicalName, authorHandle,
- committerHandle, encoding, message, treePtr.ObjectPtr, parentPtrs.Count(), parentPtrs);
+ string encoding = null; //TODO: Handle the encoding of the commit to be created
+
+ IntPtr[] parentsPtrs = parentObjectPtrs.Select(o => o.ObjectPtr ).ToArray();
+ res = NativeMethods.git_commit_create(out commitOid, repo.Handle, repo.Refs["HEAD"].CanonicalName, authorHandle,
+ committerHandle, encoding, message, treePtr.ObjectPtr, parentObjectPtrs.Count(), parentsPtrs);
+ Ensure.Success(res);
}
- Ensure.Success(res);
return repo.Lookup<Commit>(new ObjectId(commitOid));
}
- private static IntPtr[] BuildArrayFrom(ObjectSafeWrapper headPtr)
+ private static IEnumerable<ObjectId> RetrieveParentIdsOfTheCommitBeingCreated(Repository repo, bool amendPreviousCommit)
{
- if (headPtr.ObjectPtr == IntPtr.Zero)
+ if (amendPreviousCommit)
{
- return new IntPtr[] { };
+ return repo.Head.Tip.Parents.Select(c => c.Id);
}
- return new[] { headPtr.ObjectPtr };
- }
-
- private ObjectSafeWrapper RetrieveHeadCommitPtr(Reference head)
- {
- DirectReference oidRef = head.ResolveToDirectReference();
- if (oidRef == null)
+ if (repo.Info.IsEmpty)
{
- return new ObjectSafeWrapper(null, repo);
+ return Enumerable.Empty<ObjectId>();
}
- return new ObjectSafeWrapper(new ObjectId(oidRef.TargetIdentifier), repo);
+ return new[] { repo.Head.Tip.Id };
}
private class CommitEnumerator : IEnumerator<Commit>
View
34 LibGit2Sharp/Core/DisposableEnumerable.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace LibGit2Sharp.Core
+{
+ internal class DisposableEnumerable<T> : IEnumerable<T>, IDisposable where T : IDisposable
+ {
+ private readonly IEnumerable<T> enumerable;
+
+ public DisposableEnumerable(IEnumerable<T> enumerable)
+ {
+ this.enumerable = enumerable;
+ }
+
+ public IEnumerator<T> GetEnumerator()
+ {
+ return enumerable.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public void Dispose()
+ {
+ foreach (var entry in enumerable)
+ {
+ entry.Dispose();
+ }
+ }
+ }
+}
View
3 LibGit2Sharp/IQueryableCommitCollection.cs
@@ -15,7 +15,8 @@ public interface IQueryableCommitCollection : ICommitCollection //TODO: Find a n
/// <param name = "message">The description of why a change was made to the repository.</param>
/// <param name = "author">The <see cref = "Signature" /> of who made the change.</param>
/// <param name = "committer">The <see cref = "Signature" /> of who added the change to the repository.</param>
+ /// <param name="amendPreviousCommit">True to amend the current <see cref="Commit"/> pointed at by <see cref="Repository.Head"/>, false otherwise.</param>
/// <returns>The generated <see cref = "Commit" />.</returns>
- Commit Create(string message, Signature author, Signature committer);
+ Commit Create(string message, Signature author, Signature committer, bool amendPreviousCommit);
}
}
View
1 LibGit2Sharp/LibGit2Sharp.csproj
@@ -53,6 +53,7 @@
<Compile Include="Configuration.cs" />
<Compile Include="ConfigurationLevel.cs" />
<Compile Include="Core\Compat\Tuple.cs" />
+ <Compile Include="Core\DisposableEnumerable.cs" />
<Compile Include="Core\EnumExtensions.cs" />
<Compile Include="Core\GitObjectExtensions.cs" />
<Compile Include="Core\ReferenceExtensions.cs" />
View
15 LibGit2Sharp/RepositoryExtensions.cs
@@ -104,12 +104,13 @@ public static Branch CreateBranch(this Repository repository, string branchName,
/// </summary>
/// <param name = "repository">The <see cref = "Repository" /> being worked with.</param>
/// <param name = "message">The description of why a change was made to the repository.</param>
+ /// <param name="amendPreviousCommit">True to amend the current <see cref="Commit"/> pointed at by <see cref="Repository.Head"/>, false otherwise.</param>
/// <returns>The generated <see cref = "LibGit2Sharp.Commit" />.</returns>
- public static Commit Commit(this Repository repository, string message)
+ public static Commit Commit(this Repository repository, string message, bool amendPreviousCommit = false)
{
Signature author = BuildSignatureFromGlobalConfiguration(repository, DateTimeOffset.Now);
- return repository.Commit(message, author);
+ return repository.Commit(message, author, amendPreviousCommit);
}
/// <summary>
@@ -119,12 +120,13 @@ public static Commit Commit(this Repository repository, string message)
/// <param name = "repository">The <see cref = "Repository" /> being worked with.</param>
/// <param name = "author">The <see cref = "Signature" /> of who made the change.</param>
/// <param name = "message">The description of why a change was made to the repository.</param>
+ /// <param name="amendPreviousCommit">True to amend the current <see cref="Commit"/> pointed at by <see cref="Repository.Head"/>, false otherwise.</param>
/// <returns>The generated <see cref = "LibGit2Sharp.Commit" />.</returns>
- public static Commit Commit(this Repository repository, string message, Signature author)
+ public static Commit Commit(this Repository repository, string message, Signature author, bool amendPreviousCommit = false)
{
Signature committer = BuildSignatureFromGlobalConfiguration(repository, DateTimeOffset.Now);
- return repository.Commit(message, author, committer);
+ return repository.Commit(message, author, committer, amendPreviousCommit);
}
/// <summary>
@@ -134,10 +136,11 @@ public static Commit Commit(this Repository repository, string message, Signatur
/// <param name = "author">The <see cref = "Signature" /> of who made the change.</param>
/// <param name = "committer">The <see cref = "Signature" /> of who added the change to the repository.</param>
/// <param name = "message">The description of why a change was made to the repository.</param>
+ /// <param name="amendPreviousCommit">True to amend the current <see cref="Commit"/> pointed at by <see cref="Repository.Head"/>, false otherwise.</param>
/// <returns>The generated <see cref = "LibGit2Sharp.Commit" />.</returns>
- public static Commit Commit(this Repository repository, string message, Signature author, Signature committer)
+ public static Commit Commit(this Repository repository, string message, Signature author, Signature committer, bool amendPreviousCommit = false)
{
- return repository.Commits.Create(message, author, committer);
+ return repository.Commits.Create(message, author, committer, amendPreviousCommit);
}
private static Signature BuildSignatureFromGlobalConfiguration(Repository repository, DateTimeOffset now)

0 comments on commit 2ba6961

Please sign in to comment.