Skip to content

Commit b43a20b

Browse files
committed
project: Avoid skipping fetches for shallow clones without .git/shallow
When optimizing fetches for projects with immutable revisions, the fetch should not be skipped if the project is configured for a shallow clone (depth > 0) but the .git/shallow file is missing. The absence of the .git/shallow file means the repository is not a shallow clone, or the shallow clone is incomplete, so a fetch is necessary to ensure the revision is present. Bug: 503081454 Change-Id: Ic3549612bcd69050a926652ee4e522c79ad8124c Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/573821 Tested-by: Becky Siegel <beckysiegel@google.com> Reviewed-by: Gavin Mak <gavinmak@google.com> Commit-Queue: Becky Siegel <beckysiegel@google.com>
1 parent 8869a30 commit b43a20b

2 files changed

Lines changed: 118 additions & 0 deletions

File tree

project.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,10 @@ def Sync_NetworkHalf(
14951495
and self._CheckForImmutableRevision(
14961496
use_superproject=use_superproject
14971497
)
1498+
and (
1499+
not depth
1500+
or os.path.exists(os.path.join(self.gitdir, "shallow"))
1501+
)
14981502
):
14991503
remote_fetched = True
15001504
try:
@@ -2616,6 +2620,9 @@ def _RemoteFetch(
26162620
if is_sha1 or tag_name is not None:
26172621
if self._CheckForImmutableRevision(
26182622
use_superproject=use_superproject
2623+
) and (
2624+
not depth
2625+
or os.path.exists(os.path.join(self.gitdir, "shallow"))
26192626
):
26202627
if verbose:
26212628
print(

tests/test_project.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import contextlib
1818
import os
1919
from pathlib import Path
20+
import shutil
2021
import subprocess
2122
import tempfile
2223
from typing import Optional
@@ -683,3 +684,113 @@ def test_sync_network_half_stateless_skips_if_local_commits(self):
683684

684685
self.assertTrue(res.success)
685686
self.assertFalse(getattr(proj, "stateless_prune_needed", False))
687+
688+
689+
class SyncOptimizationTests(unittest.TestCase):
690+
"""Tests for sync optimization logic involving shallow clones."""
691+
692+
def _get_project(self, tempdir, depth=None):
693+
manifest = mock.MagicMock()
694+
manifest.manifestProject.depth = depth
695+
manifest.manifestProject.dissociate = False
696+
manifest.manifestProject.clone_filter = None
697+
manifest.is_multimanifest = False
698+
manifest.manifestProject.config.GetBoolean.return_value = False
699+
manifest.IsMirror = False
700+
701+
remote = mock.MagicMock()
702+
remote.name = "origin"
703+
remote.url = "http://"
704+
705+
proj = project.Project(
706+
manifest=manifest,
707+
name="test-project",
708+
remote=remote,
709+
gitdir=os.path.join(tempdir, "gitdir"),
710+
objdir=os.path.join(tempdir, "objdir"),
711+
worktree=tempdir,
712+
relpath="test-project",
713+
revisionExpr="0123456789abcdef0123456789abcdef01234567",
714+
revisionId=None,
715+
)
716+
proj._CheckForImmutableRevision = mock.MagicMock(return_value=True)
717+
proj.DeleteWorktree = mock.MagicMock()
718+
proj._InitGitDir = mock.MagicMock()
719+
proj._InitRemote = mock.MagicMock()
720+
proj._InitMRef = mock.MagicMock()
721+
return proj
722+
723+
def test_sync_network_half_shallow_missing_fetches(self):
724+
"""Test Sync_NetworkHalf fetches if shallow file is missing."""
725+
with utils_for_test.TempGitTree() as tempdir:
726+
proj = self._get_project(tempdir, depth=1)
727+
# Ensure gitdir does not exist to simulate new project
728+
if os.path.exists(proj.gitdir):
729+
shutil.rmtree(proj.gitdir)
730+
shallow_path = os.path.join(proj.gitdir, "shallow")
731+
if os.path.exists(shallow_path):
732+
os.unlink(shallow_path)
733+
734+
proj._RemoteFetch = mock.MagicMock(return_value=True)
735+
736+
res = proj.Sync_NetworkHalf(optimized_fetch=True)
737+
738+
self.assertTrue(res.success)
739+
proj._RemoteFetch.assert_called_once()
740+
741+
def test_sync_network_half_shallow_exists_skips(self):
742+
"""Test Sync_NetworkHalf skips fetch if shallow file exists."""
743+
with utils_for_test.TempGitTree() as tempdir:
744+
proj = self._get_project(tempdir, depth=1)
745+
os.makedirs(proj.gitdir, exist_ok=True)
746+
os.makedirs(proj.objdir, exist_ok=True)
747+
with open(os.path.join(proj.gitdir, "shallow"), "w") as f:
748+
f.write("")
749+
750+
proj._RemoteFetch = mock.MagicMock()
751+
752+
res = proj.Sync_NetworkHalf(optimized_fetch=True)
753+
754+
self.assertTrue(res.success)
755+
proj._RemoteFetch.assert_not_called()
756+
757+
def test_remote_fetch_shallow_missing_fetches(self):
758+
"""Test _RemoteFetch fetches if shallow file is missing."""
759+
with utils_for_test.TempGitTree() as tempdir:
760+
proj = self._get_project(tempdir, depth=1)
761+
shallow_path = os.path.join(proj.gitdir, "shallow")
762+
if os.path.exists(shallow_path):
763+
os.unlink(shallow_path)
764+
765+
with mock.patch("project.GitCommand") as mock_git_cmd:
766+
mock_cmd_instance = mock.MagicMock()
767+
mock_cmd_instance.Wait.return_value = 0
768+
mock_git_cmd.return_value = mock_cmd_instance
769+
770+
res = proj._RemoteFetch(
771+
current_branch_only=True,
772+
depth=1,
773+
use_superproject=False,
774+
)
775+
776+
self.assertTrue(res)
777+
mock_git_cmd.assert_called()
778+
779+
def test_remote_fetch_shallow_exists_skips(self):
780+
"""Test _RemoteFetch skips fetch if shallow file exists."""
781+
with utils_for_test.TempGitTree() as tempdir:
782+
proj = self._get_project(tempdir, depth=1)
783+
os.makedirs(proj.gitdir, exist_ok=True)
784+
os.makedirs(proj.objdir, exist_ok=True)
785+
with open(os.path.join(proj.gitdir, "shallow"), "w") as f:
786+
f.write("")
787+
788+
with mock.patch("project.GitCommand") as mock_git_cmd:
789+
res = proj._RemoteFetch(
790+
current_branch_only=True,
791+
depth=1,
792+
use_superproject=False,
793+
)
794+
795+
self.assertTrue(res)
796+
mock_git_cmd.assert_not_called()

0 commit comments

Comments
 (0)