diff --git a/GVFS/GVFS.Common/Git/GitRepo.cs b/GVFS/GVFS.Common/Git/GitRepo.cs index e5aefa579..933a0fabd 100644 --- a/GVFS/GVFS.Common/Git/GitRepo.cs +++ b/GVFS/GVFS.Common/Git/GitRepo.cs @@ -101,6 +101,30 @@ public virtual bool CommitAndRootTreeExists(string commitSha, out string rootTre return output; } + /// + /// Check whether a given object SHA exists as a loose object file + /// in the shared cache or local object store. + /// + public virtual bool LooseObjectExists(string sha) + { + string looseObjectPath = Path.Combine( + this.enlistment.GitObjectsRoot, + sha.Substring(0, 2), + sha.Substring(2)); + + if (this.fileSystem.FileExists(looseObjectPath)) + { + return true; + } + + looseObjectPath = Path.Combine( + this.enlistment.LocalObjectsRoot, + sha.Substring(0, 2), + sha.Substring(2)); + + return this.fileSystem.FileExists(looseObjectPath); + } + public virtual bool ObjectExists(string blobSha) { bool output = false; diff --git a/GVFS/GVFS/CommandLine/GVFSVerb.cs b/GVFS/GVFS/CommandLine/GVFSVerb.cs index c44608daf..fe3a91e83 100644 --- a/GVFS/GVFS/CommandLine/GVFSVerb.cs +++ b/GVFS/GVFS/CommandLine/GVFSVerb.cs @@ -487,15 +487,33 @@ protected bool TryDownloadCommit( out string error, bool checkLocalObjectCache = true) { - if (!checkLocalObjectCache || !repo.CommitAndRootTreeExists(commitId, out _)) + if (checkLocalObjectCache && repo.CommitAndRootTreeExists(commitId, out _)) { - if (!gitObjects.TryDownloadCommit(commitId)) + if (repo.LooseObjectExists(commitId)) { - error = "Could not download commit " + commitId + " from: " + Uri.EscapeDataString(objectRequestor.CacheServer.ObjectsEndpointUrl); - return false; + // The commit exists as a loose object (e.g., from a prior 'git show' + // or 'git log' in a mounted enlistment). Loose commits do not include + // their reachable trees — those would need to be fetched individually. + // Download the commit pack which includes all reachable trees so that + // operations like 'git checkout -f' can succeed without the read-object + // hook. + } + else + { + // The commit exists in a pack file (prefetch pack or a previous commit + // pack download). Packs from the GVFS protocol include all reachable + // trees, so we can safely skip re-downloading. + error = null; + return true; } } + if (!gitObjects.TryDownloadCommit(commitId)) + { + error = "Could not download commit " + commitId + " from: " + Uri.EscapeDataString(objectRequestor.CacheServer.ObjectsEndpointUrl); + return false; + } + error = null; return true; }