Skip to content

Implement ObjectDatabase.ShortenObjectId() #677

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 2 commits into from
Jun 7, 2014
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
42 changes: 42 additions & 0 deletions LibGit2Sharp.Tests/ObjectDatabaseFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -556,5 +556,47 @@ public void CalculatingHistoryDivergenceWithBadParamsThrows()
() => repo.ObjectDatabase.CalculateHistoryDivergence(null, repo.Head.Tip));
}
}

[Fact]
public void CanShortenObjectIdentifier()
{
/*
* $ echo "aabqhq" | git hash-object -t blob --stdin
* dea509d0b3cb8ee0650f6ca210bc83f4678851ba
*
* $ echo "aaazvc" | git hash-object -t blob --stdin
* dea509d097ce692e167dfc6a48a7a280cc5e877e
*/

string path = CloneBareTestRepo();
using (var repo = new Repository(path))
{
repo.Config.Set("core.abbrev", 4);

Blob blob1 = CreateBlob(repo, "aabqhq\n");
Assert.Equal("dea509d0b3cb8ee0650f6ca210bc83f4678851ba", blob1.Sha);

Assert.Equal("dea5", repo.ObjectDatabase.ShortenObjectId(blob1));
Assert.Equal("dea509d0b3cb", repo.ObjectDatabase.ShortenObjectId(blob1, 12));
Assert.Equal("dea509d0b3cb8ee0650f6ca210bc83f4678851b", repo.ObjectDatabase.ShortenObjectId(blob1, 39));

Blob blob2 = CreateBlob(repo, "aaazvc\n");
Assert.Equal("dea509d09", repo.ObjectDatabase.ShortenObjectId(blob2));
Assert.Equal("dea509d09", repo.ObjectDatabase.ShortenObjectId(blob2, 4));
Assert.Equal("dea509d0b", repo.ObjectDatabase.ShortenObjectId(blob1));
Assert.Equal("dea509d0b", repo.ObjectDatabase.ShortenObjectId(blob1, 7));

Assert.Equal("dea509d0b3cb", repo.ObjectDatabase.ShortenObjectId(blob1, 12));
Assert.Equal("dea509d097ce", repo.ObjectDatabase.ShortenObjectId(blob2, 12));
}
}

private static Blob CreateBlob(Repository repo, string content)
{
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(content)))
{
return repo.ObjectDatabase.CreateBlob(stream);
}
}
}
}
76 changes: 76 additions & 0 deletions LibGit2Sharp.Tests/OdbBackendFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,50 @@ public void CanPushWithACustomBackend()
}
}

[Fact]
public void CanShortenObjectIdentifier()
{
/*
* $ echo "aabqhq" | git hash-object -t blob --stdin
* dea509d0b3cb8ee0650f6ca210bc83f4678851ba
*
* $ echo "aaazvc" | git hash-object -t blob --stdin
* dea509d097ce692e167dfc6a48a7a280cc5e877e
*/

string path = CloneBareTestRepo();
using (var repo = new Repository(path))
{
repo.ObjectDatabase.AddBackend(new MockOdbBackend(), 5);

repo.Config.Set("core.abbrev", 4);

Blob blob1 = CreateBlob(repo, "aabqhq\n");
Assert.Equal("dea509d0b3cb8ee0650f6ca210bc83f4678851ba", blob1.Sha);

Assert.Equal("dea5", repo.ObjectDatabase.ShortenObjectId(blob1));
Assert.Equal("dea509d0b3cb", repo.ObjectDatabase.ShortenObjectId(blob1, 12));
Assert.Equal("dea509d0b3cb8ee0650f6ca210bc83f4678851b", repo.ObjectDatabase.ShortenObjectId(blob1, 39));

Blob blob2 = CreateBlob(repo, "aaazvc\n");
Assert.Equal("dea509d09", repo.ObjectDatabase.ShortenObjectId(blob2));
Assert.Equal("dea509d09", repo.ObjectDatabase.ShortenObjectId(blob2, 4));
Assert.Equal("dea509d0b", repo.ObjectDatabase.ShortenObjectId(blob1));
Assert.Equal("dea509d0b", repo.ObjectDatabase.ShortenObjectId(blob1, 7));

Assert.Equal("dea509d0b3cb", repo.ObjectDatabase.ShortenObjectId(blob1, 12));
Assert.Equal("dea509d097ce", repo.ObjectDatabase.ShortenObjectId(blob2, 12));
}
}

private static Blob CreateBlob(Repository repo, string content)
{
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(content)))
{
return repo.ObjectDatabase.CreateBlob(stream);
}
}

#region MockOdbBackend

private class MockOdbBackend : OdbBackend
Expand All @@ -190,6 +234,7 @@ protected override OdbBackendOperations SupportedOperations
OdbBackendOperations.Write |
OdbBackendOperations.WriteStream |
OdbBackendOperations.Exists |
OdbBackendOperations.ExistsPrefix |
OdbBackendOperations.ForEach |
OdbBackendOperations.ReadHeader;
}
Expand Down Expand Up @@ -301,6 +346,37 @@ public override bool Exists(ObjectId oid)
return m_objectIdToContent.ContainsKey(oid);
}

public override int ExistsPrefix(string shortSha, out ObjectId found)
{
found = null;
int numFound = 0;

foreach (ObjectId id in m_objectIdToContent.Keys)
{
if (!id.Sha.StartsWith(shortSha))
{
continue;
}

found = id;
numFound++;

if (numFound > 1)
{
found = null;
return (int) ReturnCode.GIT_EAMBIGUOUS;
}
}

if (numFound == 0)
{
found = null;
return (int)ReturnCode.GIT_ENOTFOUND;
}

return (int)ReturnCode.GIT_OK;
}

public override int ReadHeader(ObjectId oid, out int length, out ObjectType objectType)
{
objectType = default(ObjectType);
Expand Down
19 changes: 18 additions & 1 deletion LibGit2Sharp/Core/GitOdbBackend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ static GitOdbBackend()
public writestream_callback WriteStream;
public readstream_callback ReadStream;
public exists_callback Exists;
public IntPtr ExistsPrefix;
public exists_prefix_callback ExistsPrefix;
public IntPtr Refresh;
public foreach_callback Foreach;
public IntPtr Writepack;
Expand Down Expand Up @@ -157,6 +157,23 @@ public delegate bool exists_callback(
IntPtr backend,
ref GitOid oid);

/// <summary>
/// The backend is passed a short OID and the number of characters in that short OID.
/// The backend is asked to return a value that indicates whether or not
/// the object exists in the backing store. The short OID might not be long enough to resolve
/// to just one object. In that case the backend should return GIT_EAMBIGUOUS.
/// </summary>
/// <param name="found_oid">[out] If the call is successful, the backend will write the full OID if the object here.</param>
/// <param name="backend">[in] A pointer to the backend which is being asked to perform the task.</param>
/// <param name="short_oid">[in] The short-form OID which the backend is being asked to look up.</param>
/// <param name="len">[in] The length of the short-form OID (short_oid).</param>
/// <returns>1 if the object exists, 0 if the object doesn't; an error code otherwise.</returns>
public delegate int exists_prefix_callback(
ref GitOid found_oid,
IntPtr backend,
ref GitOid short_oid,
UIntPtr len);

/// <summary>
/// The backend is passed a callback function and a void* to pass through to the callback. The backend is
/// asked to iterate through all objects in the backing store, invoking the callback for each item.
Expand Down
5 changes: 5 additions & 0 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -753,6 +753,11 @@ internal static extern int git_object_peel(
GitObjectSafeHandle obj,
GitObjectType type);

[DllImport(libgit2)]
internal static extern int git_object_short_id(
GitBuf buf,
GitObjectSafeHandle obj);

[DllImport(libgit2)]
internal static extern GitObjectType git_object_type(GitObjectSafeHandle obj);

Expand Down
13 changes: 13 additions & 0 deletions LibGit2Sharp/Core/Proxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1251,6 +1251,19 @@ public static GitObjectSafeHandle git_object_peel(RepositorySafeHandle repo, Obj
}
}

public static string git_object_short_id(RepositorySafeHandle repo, ObjectId id)
{
using (ThreadAffinity())
using (var obj = new ObjectSafeWrapper(id, repo))
using (var buf = new GitBuf())
{
int res = NativeMethods.git_object_short_id(buf, obj.ObjectPtr);
Ensure.Int32Result(res);

return LaxUtf8Marshaler.FromNative(buf.ptr);
}
}

public static GitObjectType git_object_type(GitObjectSafeHandle obj)
{
return NativeMethods.git_object_type(obj);
Expand Down
25 changes: 25 additions & 0 deletions LibGit2Sharp/ObjectDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -368,5 +368,30 @@ public virtual HistoryDivergence CalculateHistoryDivergence(Commit one, Commit a

return new HistoryDivergence(repo, one, another);
}

/// <summary>
/// Calculates the current shortest abbreviated <see cref="ObjectId"/>
/// string representation for a <see cref="GitObject"/>.
/// </summary>
/// <param name="gitObject">The <see cref="GitObject"/> which identifier should be shortened.</param>
/// <param name="minLength">Minimum length of the shortened representation.</param>
/// <returns>A short string representation of the <see cref="ObjectId"/>.</returns>
public virtual string ShortenObjectId(GitObject gitObject, int? minLength = null)
{
if (minLength.HasValue && (minLength <= 0 || minLength > ObjectId.HexSize))
{
throw new ArgumentOutOfRangeException("minLength", minLength,
string.Format("Expected value should be greater than zero and less than or equal to {0}.", ObjectId.HexSize));
}

string shortSha = Proxy.git_object_short_id(repo.Handle, gitObject.Id);

if (minLength == null || (minLength <= shortSha.Length))
{
return shortSha;
}

return gitObject.Sha.Substring(0, minLength.Value);
}
}
}
4 changes: 2 additions & 2 deletions LibGit2Sharp/ObjectId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ namespace LibGit2Sharp
public sealed class ObjectId : IEquatable<ObjectId>
{
private readonly GitOid oid;
private const int rawSize = 20;
private const int rawSize = GitOid.Size;
private readonly string sha;

/// <summary>
/// Size of the string-based representation of a SHA-1.
/// </summary>
private const int HexSize = rawSize * 2;
internal const int HexSize = rawSize * 2;

private const string hexDigits = "0123456789abcdef";
private static readonly byte[] reverseHexDigits = BuildReverseHexDigits();
Expand Down
52 changes: 51 additions & 1 deletion LibGit2Sharp/OdbBackend.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ public abstract int WriteStream(
/// </summary>
public abstract bool Exists(ObjectId id);

/// <summary>
/// Requests that this backend check if an object ID exists. The object ID may not be complete (may be a prefix).
/// </summary>
public abstract int ExistsPrefix(string shortSha, out ObjectId found);

/// <summary>
/// Requests that this backend enumerate all items in the backing store.
/// </summary>
Expand Down Expand Up @@ -169,6 +174,11 @@ internal IntPtr GitOdbBackendPointer
nativeBackend.Exists = BackendEntryPoints.ExistsCallback;
}

if ((supportedOperations & OdbBackendOperations.ExistsPrefix) != 0)
{
nativeBackend.ExistsPrefix = BackendEntryPoints.ExistsPrefixCallback;
}

if ((supportedOperations & OdbBackendOperations.ForEach) != 0)
{
nativeBackend.Foreach = BackendEntryPoints.ForEachCallback;
Expand Down Expand Up @@ -196,6 +206,7 @@ private static class BackendEntryPoints
public static readonly GitOdbBackend.write_callback WriteCallback = Write;
public static readonly GitOdbBackend.writestream_callback WriteStreamCallback = WriteStream;
public static readonly GitOdbBackend.exists_callback ExistsCallback = Exists;
public static readonly GitOdbBackend.exists_prefix_callback ExistsPrefixCallback = ExistsPrefix;
public static readonly GitOdbBackend.foreach_callback ForEachCallback = Foreach;
public static readonly GitOdbBackend.free_callback FreeCallback = Free;

Expand Down Expand Up @@ -498,6 +509,40 @@ private static bool Exists(
}
}

private static int ExistsPrefix(
ref GitOid found_oid,
IntPtr backend,
ref GitOid short_oid,
UIntPtr len)
{
OdbBackend odbBackend = MarshalOdbBackend(backend);
if (odbBackend == null)
{
return (int)GitErrorCode.Error;
}

try
{
ObjectId found;
var shortSha = ObjectId.ToString(short_oid.Id, (int)len);

found_oid.Id = ObjectId.Zero.RawId;
int result = odbBackend.ExistsPrefix(shortSha, out found);

if (result == (int) GitErrorCode.Ok)
{
found_oid.Id = found.RawId;
}

return result;
}
catch (Exception ex)
{
Proxy.giterr_set_str(GitErrorCategory.Odb, ex);
return (int)GitErrorCode.Error;
}
}

private static int Foreach(
IntPtr backend,
GitOdbBackend.foreach_callback_callback cb,
Expand Down Expand Up @@ -620,10 +665,15 @@ protected enum OdbBackendOperations
/// </summary>
Exists = 64,

/// <summary>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could add a blank line here to be consistent.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

/// This OdbBackend declares that it supports the ExistsPrefix method.
/// </summary>
ExistsPrefix = 128,

/// <summary>
/// This OdbBackend declares that it supports the Foreach method.
/// </summary>
ForEach = 128,
ForEach = 256,
}

/// <summary>
Expand Down