Skip to content
Open
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
12 changes: 12 additions & 0 deletions GVFS/GVFS.Common/Git/GitRepo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,18 @@ public virtual bool ObjectExists(string blobSha)
return output;
}

/// <summary>
/// Checks whether the object can be fully parsed by libgit2 (not just that it exists).
/// Use this to detect corrupt objects. For simple existence checks,
/// prefer <see cref="ObjectExists"/> which is faster.
/// </summary>
public virtual bool ObjectCanBeParsed(string sha)
{
bool output = false;
this.libgit2RepoInvoker.TryInvoke(repo => repo.ObjectCanBeParsed(sha), out output);
return output;
}

/// <summary>
/// Try to find the size of a given blob by SHA1 hash.
///
Expand Down
59 changes: 59 additions & 0 deletions GVFS/GVFS.Common/Git/LibGit2Repo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace GVFS.Common.Git
public class LibGit2Repo : IDisposable
{
private bool disposedValue = false;
private IntPtr odbHandle = IntPtr.Zero;

public delegate void MultiVarConfigCallback(string value);

Expand Down Expand Up @@ -104,6 +105,42 @@ public virtual bool CommitAndRootTreeExists(string commitish, out string treeSha
}

public virtual bool ObjectExists(string sha)
{
if (this.odbHandle == IntPtr.Zero)
{
if (Native.Odb.GetOdb(out this.odbHandle, this.RepoHandle) != Native.ResultCode.Success)
{
return this.ObjectExistsFallback(sha);
}
}

GitOid oid;
if (Native.Odb.OidFromStr(out oid, sha) != Native.ResultCode.Success)
{
return false;
}

return Native.Odb.Exists(this.odbHandle, ref oid) == 1;
}

private bool ObjectExistsFallback(string sha)
{
IntPtr objHandle;
if (Native.RevParseSingle(out objHandle, this.RepoHandle, sha) != Native.ResultCode.Success)
{
return false;
}

Native.Object.Free(objHandle);
return true;
}

/// <summary>
/// Checks whether the object can be fully parsed by libgit2 (not just that it exists).
/// Use this when you need to detect corrupt objects. For simple existence checks,
/// prefer <see cref="ObjectExists"/> which is faster.
/// </summary>
public virtual bool ObjectCanBeParsed(string sha)
{
IntPtr objHandle;
if (Native.RevParseSingle(out objHandle, this.RepoHandle, sha) != Native.ResultCode.Success)
Expand Down Expand Up @@ -360,6 +397,12 @@ protected virtual void Dispose(bool disposing)
{
if (!this.disposedValue)
{
if (this.odbHandle != IntPtr.Zero)
{
Native.Odb.Free(this.odbHandle);
this.odbHandle = IntPtr.Zero;
}

Native.Repo.Free(this.RepoHandle);
Native.Shutdown();
this.disposedValue = true;
Expand Down Expand Up @@ -504,6 +547,22 @@ public static class Repo
public static extern void Free(IntPtr repoHandle);
}

public static class Odb
{
[DllImport(Git2NativeLibName, EntryPoint = "git_repository_odb")]
public static extern ResultCode GetOdb(out IntPtr odbHandle, IntPtr repoHandle);

/// <returns>1 if the object exists, 0 otherwise</returns>
[DllImport(Git2NativeLibName, EntryPoint = "git_odb_exists")]
public static extern int Exists(IntPtr odbHandle, ref GitOid id);

[DllImport(Git2NativeLibName, EntryPoint = "git_odb_free")]
public static extern void Free(IntPtr odbHandle);

[DllImport(Git2NativeLibName, EntryPoint = "git_oid_fromstr")]
public static extern ResultCode OidFromStr(out GitOid oid, string str);
}

public static class Config
{
[DllImport(Git2NativeLibName, EntryPoint = "git_repository_config")]
Expand Down
2 changes: 1 addition & 1 deletion GVFS/GVFS.Common/Maintenance/LooseObjectsStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ public void ClearCorruptLooseObjects(EventMetadata metadata)
// may be more bad objects in the next batch after deleting the corrupt objects.
foreach (string objectId in this.GetBatchOfLooseObjects(2 * this.MaxLooseObjectsInPack))
{
if (!this.Context.Repository.ObjectExists(objectId))
if (!this.Context.Repository.ObjectCanBeParsed(objectId))
{
string objectFile = this.GetLooseObjectFileName(objectId);

Expand Down
Loading