1616using System . IO ;
1717using System . Text ;
1818using System . Threading ;
19+ using static GVFS . Common . Git . LibGit2Repo ;
1920
2021namespace GVFS . Mount
2122{
@@ -44,6 +45,8 @@ public class InProcessMount
4445 private HeartbeatThread heartbeat ;
4546 private ManualResetEvent unmountEvent ;
4647
48+ private readonly Dictionary < string , string > treesWithRecentlyDownloadedCommits = new Dictionary < string , string > ( ) ;
49+
4750 // True if InProcessMount is calling git reset as part of processing
4851 // a folder dehydrate request
4952 private volatile bool resetForDehydrateInProgress ;
@@ -504,7 +507,21 @@ private void HandleDownloadObjectRequest(NamedPipeMessages.Message message, Name
504507 else
505508 {
506509 Stopwatch downloadTime = Stopwatch . StartNew ( ) ;
507- if ( this . gitObjects . TryDownloadAndSaveObject ( objectSha , GVFSGitObjects . RequestSource . NamedPipeMessage ) == GitObjects . DownloadAndSaveObjectResult . Success )
510+
511+ /* If this is the root tree for a commit that was was just downloaded, assume that more
512+ * trees will be needed soon and download them as well by using the download commit API.
513+ *
514+ * Otherwise, or as a fallback if the commit download fails, download the object directly.
515+ */
516+ if ( this . treesWithRecentlyDownloadedCommits . TryGetValue ( objectSha , out string commitSha )
517+ && this . gitObjects . TryDownloadCommit ( commitSha ) )
518+ {
519+ this . treesWithRecentlyDownloadedCommits . Remove ( objectSha ) ;
520+ response = new NamedPipeMessages . DownloadObject . Response ( NamedPipeMessages . DownloadObject . SuccessResult ) ;
521+ // FUTURE: Should the stats be updated to reflect all the trees in the pack?
522+ // FUTURE: Should we try to clean up duplicate trees or increase depth of the commit download?
523+ }
524+ else if ( this . gitObjects . TryDownloadAndSaveObject ( objectSha , GVFSGitObjects . RequestSource . NamedPipeMessage ) == GitObjects . DownloadAndSaveObjectResult . Success )
508525 {
509526 response = new NamedPipeMessages . DownloadObject . Response ( NamedPipeMessages . DownloadObject . SuccessResult ) ;
510527 }
@@ -513,9 +530,20 @@ private void HandleDownloadObjectRequest(NamedPipeMessages.Message message, Name
513530 response = new NamedPipeMessages . DownloadObject . Response ( NamedPipeMessages . DownloadObject . DownloadFailed ) ;
514531 }
515532
516- bool isBlob ;
517- this . context . Repository . TryGetIsBlob ( objectSha , out isBlob ) ;
518- this . context . Repository . GVFSLock . Stats . RecordObjectDownload ( isBlob , downloadTime . ElapsedMilliseconds ) ;
533+ Native . ObjectTypes ? objectType ;
534+ this . context . Repository . TryGetObjectType ( objectSha , out objectType ) ;
535+ this . context . Repository . GVFSLock . Stats . RecordObjectDownload ( objectType == Native . ObjectTypes . Blob , downloadTime . ElapsedMilliseconds ) ;
536+
537+ if ( objectType == Native . ObjectTypes . Commit
538+ && ! this . context . Repository . CommitAndRootTreeExists ( objectSha , out var treeSha )
539+ && ! string . IsNullOrEmpty ( treeSha ) )
540+ {
541+ /* If a commit is downloaded but it's tree doesn't exist, it means it hadn't been prefetched and all its trees
542+ * may be needed soon depending on the context. e.g. git log (without a pathspec) doesn't need trees, but git checkout does.
543+ * save the tree/commit so
544+ */
545+ this . treesWithRecentlyDownloadedCommits [ treeSha ] = objectSha ;
546+ }
519547 }
520548 }
521549
0 commit comments