Skip to content

Revparse #200

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

Closed
wants to merge 1 commit into from
Closed
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
23 changes: 14 additions & 9 deletions LibGit2Sharp.Tests/BranchFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,20 @@ public void CanCreateBranchFromCommit()
}
}

[Fact]
public void CanCreateBranchFromRevparseSpec()
{
TemporaryCloneOfTestRepo path = BuildTemporaryCloneOfTestRepo();
using (var repo = new Repository(path.RepositoryPath))
{
const string name = "revparse_branch";
var target = repo.Lookup<Commit>("master~2");
Branch newBranch = repo.CreateBranch(name, target);
Assert.NotNull(newBranch);
Assert.Equal("9fd738e8f7967c078dceed8190330fc8648ee56a", newBranch.Tip.Sha);
}
}

[Fact]
public void CreatingABranchFromATagPeelsToTheCommit()
{
Expand Down Expand Up @@ -150,15 +164,6 @@ public void CreatingBranchWithUnknownShaTargetThrows()
}
}

[Fact]
public void CreatingABranchPointingAtANonCanonicalReferenceThrows()
{
using (var repo = new Repository(BareTestRepoPath))
{
Assert.Throws<LibGit2SharpException>(() => repo.Branches.Add("nocanonicaltarget", "br2"));
}
}

[Fact]
public void CreatingBranchWithBadParamsThrows()
{
Expand Down
31 changes: 30 additions & 1 deletion LibGit2Sharp.Tests/RepositoryFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,35 @@ public void CanLookupWhithShortIdentifers()
}
}

[Fact]
public void CanLookupUsingRevparseSyntax()
{
using (var repo = new Repository(BareTestRepoPath))
{
Assert.Null(repo.Lookup<Tree>("master^"));

Assert.NotNull(repo.Lookup("master:new.txt"));
Assert.NotNull(repo.Lookup<Blob>("master:new.txt"));
Assert.NotNull(repo.Lookup("master^"));
Assert.NotNull(repo.Lookup<Commit>("master^"));
Assert.NotNull(repo.Lookup("master~3"));
Assert.NotNull(repo.Lookup("HEAD"));
Assert.NotNull(repo.Lookup("refs/heads/br2"));
}
}

[Fact]
public void CanResolveAmbiguousRevparseSpecs()
{
using (var repo = new Repository(BareTestRepoPath))
{
var o1 = repo.Lookup("e90810b"); // This resolves to a tag
Assert.Equal("7b4384978d2493e851f9cca7858815fac9b10980", o1.Sha);
var o2 = repo.Lookup("e90810b8"); // This resolves to a commit
Assert.Equal("e90810b8df3e80c413d903f631643c716887138d", o2.Sha);
}
}

[Fact]
public void LookingUpWithBadParamsThrows()
{
Expand All @@ -310,7 +339,7 @@ public void LookingUpWithBadParamsThrows()
}
}

[Fact]
[Fact, SkippableFact(Skip = "libgit2 not ready")]
public void LookingUpWithATooShortShaThrows()
{
using (var repo = new Repository(BareTestRepoPath))
Expand Down
35 changes: 26 additions & 9 deletions LibGit2Sharp.Tests/TagFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ public void CanAddALightweightTagFromABranchName()
}
}

[Fact]
public void CanAddALightweightTagFromARevparseSpec()
{
TemporaryCloneOfTestRepo path = BuildTemporaryCloneOfTestRepo();
using (var repo = new Repository(path.RepositoryPath))
{
Tag newTag = repo.Tags.Add("i_am_lightweight", "master^1^2");
Assert.False(newTag.IsAnnotated);
Assert.NotNull(newTag);
Assert.Equal("c47800c7266a2be04c571c04d5a6614691ea99bd", newTag.Target.Sha);
}
}

[Fact]
public void CanAddAndOverwriteALightweightTag()
{
Expand Down Expand Up @@ -124,6 +137,19 @@ public void CanAddAnAnnotatedTagFromSha()
}
}

[Fact]
public void CanAddAnAnnotatedTagFromARevparseSpec()
{
TemporaryCloneOfTestRepo path = BuildTemporaryCloneOfTestRepo();
using (var repo = new Repository(path.RepositoryPath))
{
Tag newTag = repo.Tags.Add("unit_test", "master^1^2", signatureTim, "a new tag");
Assert.NotNull(newTag);
Assert.True(newTag.IsAnnotated);
Assert.Equal("c47800c7266a2be04c571c04d5a6614691ea99bd", newTag.Target.Sha);
}
}

[Fact]
// Ported from cgit (https://github.com/git/git/blob/1c08bf50cfcf924094eca56c2486a90e2bf1e6e2/t/t7004-tag.sh#L359)
public void CanAddAnAnnotatedTagWithAnEmptyMessage()
Expand Down Expand Up @@ -201,15 +227,6 @@ public void CreatingATagForAnUnknowReferenceThrows()
}
}

[Fact]
public void CreatingATagForANonCanonicalReferenceThrows()
{
using (var repo = new Repository(BareTestRepoPath))
{
Assert.Throws<LibGit2SharpException>(() => repo.ApplyTag("noncanonicaltarget", "br2"));
}
}

[Fact]
// Ported from cgit (https://github.com/git/git/blob/1c08bf50cfcf924094eca56c2486a90e2bf1e6e2/t/t7004-tag.sh#L42)
public void CreatingATagForAnUnknowObjectIdThrows()
Expand Down
7 changes: 7 additions & 0 deletions LibGit2Sharp.Tests/TreeFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,15 @@ public void CanRetrieveTreeEntryPath()
TreeEntry anotherInstance = tree["branch_file.txt"];
Assert.Equal("branch_file.txt", anotherInstance.Path);

// From a rev-parse statement
var revparseTree = repo.Lookup<Tree>("master:1");
TreeEntry yetAnotherInstance = revparseTree["branch_file.txt"];
Assert.Equal(completePath, yetAnotherInstance.Path);

Assert.Equal(tree, subTree);
Assert.Equal(revparseTree, tree);
Assert.Equal(anotherInstance, anInstance);
Assert.Equal(yetAnotherInstance, anotherInstance);
Assert.NotEqual(anotherInstance.Path, anInstance.Path);
Assert.NotSame(anotherInstance, anInstance);
}
Expand Down
12 changes: 6 additions & 6 deletions LibGit2Sharp/BranchCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,14 @@ IEnumerator IEnumerable.GetEnumerator()
/// Create a new local branch with the specified name
/// </summary>
/// <param name = "name">The name of the branch.</param>
/// <param name = "shaOrReferenceName">The target which can be sha or a canonical reference name.</param>
/// <param name = "commitish">Revparse spec for the target commit.</param>
/// <param name = "allowOverwrite">True to allow silent overwriting a potentially existing branch, false otherwise.</param>
/// <returns></returns>
public virtual Branch Add(string name, string shaOrReferenceName, bool allowOverwrite = false)
public virtual Branch Add(string name, string commitish, bool allowOverwrite = false)
{
Ensure.ArgumentNotNullOrEmptyString(name, "name");

ObjectId commitId = repo.LookupCommit(shaOrReferenceName).Id;
ObjectId commitId = repo.LookupCommit(commitish).Id;

using (var osw = new ObjectSafeWrapper(commitId, repo))
{
Expand All @@ -159,13 +159,13 @@ public virtual Branch Add(string name, string shaOrReferenceName, bool allowOver
/// Create a new local branch with the specified name
/// </summary>
/// <param name = "name">The name of the branch.</param>
/// <param name = "shaOrReferenceName">The target which can be sha or a canonical reference name.</param>
/// <param name = "commitish">Revparse spec for the target commit.</param>
/// <param name = "allowOverwrite">True to allow silent overwriting a potentially existing branch, false otherwise.</param>
/// <returns></returns>
[Obsolete("This method will be removed in the next release. Please use Add() instead.")]
public virtual Branch Create(string name, string shaOrReferenceName, bool allowOverwrite = false)
public virtual Branch Create(string name, string commitish, bool allowOverwrite = false)
{
return Add(name, shaOrReferenceName, allowOverwrite);
return Add(name, commitish, allowOverwrite);
}

/// <summary>
Expand Down
4 changes: 4 additions & 0 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,10 @@ public static extern int git_repository_set_workdir(
[return : MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(FilePathMarshaler))]
public static extern FilePath git_repository_workdir(RepositorySafeHandle repository);

[DllImport(libgit2)]
public static extern int git_revparse_single(out GitObjectSafeHandle obj, RepositorySafeHandle repo,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))] string spec);

[DllImport(libgit2)]
public static extern void git_revwalk_free(IntPtr walker);

Expand Down
8 changes: 4 additions & 4 deletions LibGit2Sharp/IRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ public interface IRepository : IDisposable
/// <summary>
/// Checkout the specified branch, reference or SHA.
/// </summary>
/// <param name = "shaOrReferenceName">The sha of the commit, a canonical reference name or the name of the branch to checkout.</param>
/// <param name = "commitOrBranchSpec">A revparse spec for the commit or branch to checkout.</param>
/// <returns>The new HEAD.</returns>
Branch Checkout(string shaOrReferenceName);
Branch Checkout(string commitOrBranchSpec);

/// <summary>
/// Try to lookup an object by its <see cref = "ObjectId" /> and <see cref = "GitObjectType" />. If no matching object is found, null will be returned.
Expand All @@ -84,10 +84,10 @@ public interface IRepository : IDisposable
/// <summary>
/// Try to lookup an object by its sha or a reference canonical name and <see cref = "GitObjectType" />. If no matching object is found, null will be returned.
/// </summary>
/// <param name = "shaOrReferenceName">The sha or reference canonical name to lookup.</param>
/// <param name = "objectish">A revparse spec for the object to lookup.</param>
/// <param name = "type">The kind of <see cref = "GitObject" /> being looked up</param>
/// <returns>The <see cref = "GitObject" /> or null if it was not found.</returns>
GitObject Lookup(string shaOrReferenceName, GitObjectType type = GitObjectType.Any);
GitObject Lookup(string objectish, GitObjectType type = GitObjectType.Any);

/// <summary>
/// Stores the content of the <see cref = "Repository.Index" /> as a new <see cref = "Commit" /> into the repository.
Expand Down
89 changes: 50 additions & 39 deletions LibGit2Sharp/Repository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using LibGit2Sharp.Core;
using LibGit2Sharp.Core.Compat;
using LibGit2Sharp.Core.Handles;
Expand Down Expand Up @@ -346,61 +347,71 @@ internal GitObject LookupInternal(ObjectId id, GitObjectType type, FilePath know
/// <summary>
/// Try to lookup an object by its sha or a reference canonical name and <see cref = "GitObjectType" />. If no matching object is found, null will be returned.
/// </summary>
/// <param name = "shaOrReferenceName">The sha or reference canonical name to lookup.</param>
/// <param name = "objectish">A revparse spec for the object to lookup.</param>
/// <param name = "type">The kind of <see cref = "GitObject" /> being looked up</param>
/// <returns>The <see cref = "GitObject" /> or null if it was not found.</returns>
public GitObject Lookup(string shaOrReferenceName, GitObjectType type = GitObjectType.Any)
public GitObject Lookup(string objectish, GitObjectType type = GitObjectType.Any)
{
return Lookup(shaOrReferenceName, type, LookUpOptions.None);
return Lookup(objectish, type, LookUpOptions.None);
}

internal GitObject Lookup(string shaOrReferenceName, GitObjectType type, LookUpOptions lookUpOptions)
private string PathFromRevparseSpec(string spec)
{
ObjectId id;
if (spec.StartsWith(":/")) return null;
if (Regex.IsMatch(spec, @"^:.*:")) return null;

Reference reference = Refs[shaOrReferenceName];
if (reference != null)
{
id = reference.PeelToTargetObjectId();
}
else
{
ObjectId.TryParseInternal(shaOrReferenceName, out id, IdentifierSize.Shortest);
}
var m = Regex.Match(spec, @"[^@^ ]*:(.*)");
return (m.Groups.Count > 1) ? m.Groups[1].Value : null;
}

internal GitObject Lookup(string objectish, GitObjectType type, LookUpOptions lookUpOptions)
{
Ensure.ArgumentNotNullOrEmptyString(objectish, "commitOrBranchSpec");

GitObjectSafeHandle sh;
int result = NativeMethods.git_revparse_single(out sh, Handle, objectish);

if (id == null)
if ((GitErrorCode)result != GitErrorCode.Ok || sh.IsInvalid)
{
if (lookUpOptions.Has(LookUpOptions.ThrowWhenNoGitObjectHasBeenFound))
if (lookUpOptions.Has(LookUpOptions.ThrowWhenNoGitObjectHasBeenFound) &&
result == (int)GitErrorCode.NotFound)
{
Ensure.GitObjectIsNotNull(null, shaOrReferenceName);
Ensure.GitObjectIsNotNull(null, objectish);
}

if (result == (int)GitErrorCode.Ambiguous)
{
throw new AmbiguousException(string.Format(CultureInfo.InvariantCulture, "Provided abbreviated ObjectId '{0}' is too short.", objectish));
}

return null;
}

GitObject gitObj = Lookup(id, type);

if (lookUpOptions.Has(LookUpOptions.ThrowWhenNoGitObjectHasBeenFound))
if (type != GitObjectType.Any && NativeMethods.git_object_type(sh) != type)
{
Ensure.GitObjectIsNotNull(gitObj, shaOrReferenceName);
sh.SafeDispose();
return null;
}

if (!lookUpOptions.Has(LookUpOptions.DereferenceResultToCommit))
var obj = GitObject.CreateFromPtr(sh, GitObject.ObjectIdOf(sh), this, PathFromRevparseSpec(objectish));
sh.SafeDispose();

if (lookUpOptions.Has(LookUpOptions.DereferenceResultToCommit))
{
return gitObj;
return obj.DereferenceToCommit(objectish,
lookUpOptions.Has(LookUpOptions.ThrowWhenCanNotBeDereferencedToACommit));
}

return gitObj.DereferenceToCommit(shaOrReferenceName, lookUpOptions.Has(LookUpOptions.ThrowWhenCanNotBeDereferencedToACommit));
return obj;
}

/// <summary>
/// Lookup a commit by its SHA or name, or throw if a commit is not found.
/// </summary>
/// <param name="shaOrReferenceName">The SHA or name of the commit.</param>
/// <param name="commitish">A revparse spec for the commit.</param>
/// <returns>The commit.</returns>
internal Commit LookupCommit(string shaOrReferenceName)
internal Commit LookupCommit(string commitish)
{
return (Commit)Lookup(shaOrReferenceName, GitObjectType.Any, LookUpOptions.ThrowWhenNoGitObjectHasBeenFound | LookUpOptions.DereferenceResultToCommit | LookUpOptions.ThrowWhenCanNotBeDereferencedToACommit);
return (Commit)Lookup(commitish, GitObjectType.Any, LookUpOptions.ThrowWhenNoGitObjectHasBeenFound | LookUpOptions.DereferenceResultToCommit | LookUpOptions.ThrowWhenCanNotBeDereferencedToACommit);
}

/// <summary>
Expand Down Expand Up @@ -430,20 +441,20 @@ public static string Discover(string startingPath)
/// <summary>
/// Checkout the specified branch, reference or SHA.
/// </summary>
/// <param name = "shaOrReferenceName">The sha of the commit, a canonical reference name or the name of the branch to checkout.</param>
/// <param name = "commitOrBranchSpec">A revparse spec for the commit or branch to checkout.</param>
/// <returns>The new HEAD.</returns>
public Branch Checkout(string shaOrReferenceName)
public Branch Checkout(string commitOrBranchSpec)
{
// TODO: This does not yet checkout (write) the working directory

var branch = Branches[shaOrReferenceName];
var branch = Branches[commitOrBranchSpec];

if (branch != null)
{
return Checkout(branch);
}

var commitId = LookupCommit(shaOrReferenceName).Id;
var commitId = LookupCommit(commitOrBranchSpec).Id;
Refs.UpdateTarget("HEAD", commitId.Sha);
return Head;
}
Expand All @@ -466,17 +477,17 @@ public Branch Checkout(Branch branch)
/// the content of the working tree to match.
/// </summary>
/// <param name = "resetOptions">Flavor of reset operation to perform.</param>
/// <param name = "shaOrReferenceName">The sha or reference canonical name of the target commit object.</param>
public void Reset(ResetOptions resetOptions, string shaOrReferenceName = "HEAD")
/// <param name = "commitish">A revparse spec for the target commit object.</param>
public void Reset(ResetOptions resetOptions, string commitish = "HEAD")
{
Ensure.ArgumentNotNullOrEmptyString(shaOrReferenceName, "shaOrReferenceName");
Ensure.ArgumentNotNullOrEmptyString(commitish, "commitOrBranchSpec");

if (resetOptions.Has(ResetOptions.Mixed) && Info.IsBare)
{
throw new LibGit2SharpException("Mixed reset is not allowed in a bare repository");
}

Commit commit = LookupCommit(shaOrReferenceName);
Commit commit = LookupCommit(commitish);

//TODO: Check for unmerged entries

Expand All @@ -501,16 +512,16 @@ public void Reset(ResetOptions resetOptions, string shaOrReferenceName = "HEAD")
/// <summary>
/// Replaces entries in the <see cref="Index"/> with entries from the specified commit.
/// </summary>
/// <param name = "shaOrReferenceName">The sha or reference canonical name of the target commit object.</param>
/// <param name = "commitish">A revparse spec for the target commit object.</param>
/// <param name = "paths">The list of paths (either files or directories) that should be considered.</param>
public void Reset(string shaOrReferenceName = "HEAD", IEnumerable<string> paths = null)
public void Reset(string commitish = "HEAD", IEnumerable<string> paths = null)
{
if (Info.IsBare)
{
throw new LibGit2SharpException("Reset is not allowed in a bare repository");
}

Commit commit = LookupCommit(shaOrReferenceName);
Commit commit = LookupCommit(commitish);
TreeChanges changes = Diff.Compare(commit.Tree, DiffTarget.Index, paths);

Index.Reset(changes);
Expand Down
Loading