diff --git a/GVFS/GVFS.Common/Git/GitRepo.cs b/GVFS/GVFS.Common/Git/GitRepo.cs
index d88ebbd89..7b6dfddb8 100644
--- a/GVFS/GVFS.Common/Git/GitRepo.cs
+++ b/GVFS/GVFS.Common/Git/GitRepo.cs
@@ -114,6 +114,18 @@ public virtual bool ObjectExists(string blobSha)
return output;
}
+ ///
+ /// 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 which is faster.
+ ///
+ public virtual bool ObjectCanBeParsed(string sha)
+ {
+ bool output = false;
+ this.libgit2RepoInvoker.TryInvoke(repo => repo.ObjectCanBeParsed(sha), out output);
+ return output;
+ }
+
///
/// Try to find the size of a given blob by SHA1 hash.
///
diff --git a/GVFS/GVFS.Common/Git/LibGit2Repo.cs b/GVFS/GVFS.Common/Git/LibGit2Repo.cs
index f0e7bc464..fb9a00cba 100644
--- a/GVFS/GVFS.Common/Git/LibGit2Repo.cs
+++ b/GVFS/GVFS.Common/Git/LibGit2Repo.cs
@@ -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);
@@ -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;
+ }
+
+ ///
+ /// 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 which is faster.
+ ///
+ public virtual bool ObjectCanBeParsed(string sha)
{
IntPtr objHandle;
if (Native.RevParseSingle(out objHandle, this.RepoHandle, sha) != Native.ResultCode.Success)
@@ -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;
@@ -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);
+
+ /// 1 if the object exists, 0 otherwise
+ [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")]
diff --git a/GVFS/GVFS.Common/Maintenance/LooseObjectsStep.cs b/GVFS/GVFS.Common/Maintenance/LooseObjectsStep.cs
index f71f0d6cc..f45049ec6 100644
--- a/GVFS/GVFS.Common/Maintenance/LooseObjectsStep.cs
+++ b/GVFS/GVFS.Common/Maintenance/LooseObjectsStep.cs
@@ -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);