Skip to content

Commit

Permalink
Revparse support in Repository.Lookup.
Browse files Browse the repository at this point in the history
Now running lookups through git_revparse_single.
  • Loading branch information
Ben Straub authored and ben committed Aug 14, 2012
1 parent 4bda9ff commit 8c39bd3
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 71 deletions.
23 changes: 14 additions & 9 deletions LibGit2Sharp.Tests/BranchFixture.cs
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
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
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
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
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
Expand Up @@ -639,6 +639,10 @@ public static bool RepositoryStateChecker(RepositorySafeHandle repositoryPtr, Fu
[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
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
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

0 comments on commit 8c39bd3

Please sign in to comment.