@@ -627,6 +627,16 @@ struct GitInputScheme : InputScheme
627627 }
628628 }
629629
630+ /* *
631+ * Decide whether we can do a shallow clone, which is faster. This is possible if the user explicitly specified
632+ * `shallow = true`, or if we already have a `revCount`.
633+ */
634+ bool canDoShallow (const Input & input) const
635+ {
636+ bool shallow = getShallowAttr (input);
637+ return shallow || input.getRevCount ().has_value ();
638+ }
639+
630640 std::pair<ref<SourceAccessor>, Input>
631641 getAccessorFromCommit (const Settings & settings, ref<Store> store, RepoInfo & repoInfo, Input && input) const
632642 {
@@ -635,7 +645,7 @@ struct GitInputScheme : InputScheme
635645 auto origRev = input.getRev ();
636646
637647 auto originalRef = input.getRef ();
638- bool shallow = getShallowAttr (input);
648+ bool shallow = canDoShallow (input);
639649 auto ref = originalRef ? *originalRef : getDefaultRef (repoInfo, shallow);
640650 input.attrs .insert_or_assign (" ref" , ref);
641651
@@ -646,11 +656,27 @@ struct GitInputScheme : InputScheme
646656 if (!input.getRev ())
647657 input.attrs .insert_or_assign (" rev" , GitRepo::openRepo (repoDir)->resolveRef (ref).gitRev ());
648658 } else {
659+ auto rev = input.getRev ();
649660 auto repoUrl = std::get<ParsedURL>(repoInfo.location );
650661 std::filesystem::path cacheDir = getCachePath (repoUrl.to_string (), shallow);
651662 repoDir = cacheDir;
652663 repoInfo.gitDir = " ." ;
653664
665+ /* If shallow = false, but we have a non-shallow repo that already contains the desired rev, then use that
666+ * repo instead. */
667+ std::filesystem::path cacheDirNonShallow = getCachePath (repoUrl.to_string (), false );
668+ if (rev && shallow && pathExists (cacheDirNonShallow)) {
669+ auto nonShallowRepo = GitRepo::openRepo (cacheDirNonShallow, true , true );
670+ if (nonShallowRepo->hasObject (*rev)) {
671+ debug (
672+ " using non-shallow cached repo for '%s' since it contains rev '%s'" ,
673+ repoUrl.to_string (),
674+ rev->gitRev ());
675+ repoDir = cacheDirNonShallow;
676+ goto have_rev;
677+ }
678+ }
679+
654680 std::filesystem::create_directories (cacheDir.parent_path ());
655681 PathLocks cacheDirLock ({cacheDir.string ()});
656682
@@ -666,7 +692,7 @@ struct GitInputScheme : InputScheme
666692
667693 /* If a rev was specified, we need to fetch if it's not in the
668694 repo. */
669- if (auto rev = input. getRev () ) {
695+ if (rev) {
670696 doFetch = !repo->hasObject (*rev);
671697 } else {
672698 if (getAllRefsAttr (input)) {
@@ -680,7 +706,6 @@ struct GitInputScheme : InputScheme
680706 }
681707
682708 if (doFetch) {
683- bool shallow = getShallowAttr (input);
684709 try {
685710 auto fetchRef = getAllRefsAttr (input) ? " refs/*:refs/*"
686711 : input.getRev () ? input.getRev ()->gitRev ()
@@ -708,7 +733,7 @@ struct GitInputScheme : InputScheme
708733 warn (" could not update cached head '%s' for '%s'" , ref, repoInfo.locationToArg ());
709734 }
710735
711- if (auto rev = input. getRev () ) {
736+ if (rev) {
712737 if (!repo->hasObject (*rev))
713738 throw Error (
714739 " Cannot find Git revision '%s' in ref '%s' of repository '%s'! "
@@ -725,15 +750,9 @@ struct GitInputScheme : InputScheme
725750 // the remainder
726751 }
727752
753+ have_rev:
728754 auto repo = GitRepo::openRepo (repoDir);
729755
730- auto isShallow = repo->isShallow ();
731-
732- if (isShallow && !getShallowAttr (input))
733- throw Error (
734- " '%s' is a shallow Git repository, but shallow repositories are only allowed when `shallow = true;` is specified" ,
735- repoInfo.locationToArg ());
736-
737756 // FIXME: check whether rev is an ancestor of ref?
738757
739758 auto rev = *input.getRev ();
@@ -744,10 +763,16 @@ struct GitInputScheme : InputScheme
744763 if (!input.attrs .contains (" lastModified" ))
745764 input.attrs .insert_or_assign (" lastModified" , getLastModified (settings, repoInfo, repoDir, rev));
746765
747- if (!getShallowAttr (input)) {
748- /* Like lastModified, skip revCount if supplied by the caller. */
749- if (!input.attrs .contains (" revCount" ))
750- input.attrs .insert_or_assign (" revCount" , getRevCount (settings, repoInfo, repoDir, rev));
766+ /* Like lastModified, skip revCount if supplied by the caller. */
767+ if (!shallow && !input.attrs .contains (" revCount" )) {
768+ auto isShallow = repo->isShallow ();
769+
770+ if (isShallow && !shallow)
771+ throw Error (
772+ " '%s' is a shallow Git repository, but shallow repositories are only allowed when `shallow = true;` is specified" ,
773+ repoInfo.locationToArg ());
774+
775+ input.attrs .insert_or_assign (" revCount" , getRevCount (settings, repoInfo, repoDir, rev));
751776 }
752777
753778 printTalkative (" using revision %s of repo '%s'" , rev.gitRev (), repoInfo.locationToArg ());
0 commit comments