mirror of
https://gerrit.googlesource.com/git-repo
synced 2026-07-16 05:36:59 +00:00
project: fix sync of shallow projects sharing objdir
Repo sync fails when the following conditions are met: * There are several checkouts of the same project in different paths. * The checkouts are using git hashes as revisions (not branches). * There is a clone-depth set on these projects. * sync-c="true" is set in the manifest. * The revision specified in the manifest has moved forward since the first repo init. The sync fails because only the first gitdir gets the "shallow" file, and subsequent dirs can't be synced. Do not optimize away the fetch when the conditions above happen. Simplified the boolean check in Sync_NetworkHalf and _RemoteFetch using has_shallow, renamed loop variable to avoid shadowing. Test: create a manifest matching conditions above, repo init, forward the hash, and repo sync. Test: added tests in test_project.py Bug: 505072873 Originally-by: Elvira Khabirova <elvira.khabirova@volvocars.com> Change-Id: I37c533c382e34fc5ddab489c5593b9e5d3875be2 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/601441 Tested-by: Sainath Varanasi <varanasisai@google.com> Reviewed-by: Gavin Mak <gavinmak@google.com> Commit-Queue: Sainath Varanasi <varanasisai@google.com>
This commit is contained in:
committed by
gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
d32b70275c
commit
3af9e2f146
+25
-4
@@ -1538,6 +1538,7 @@ class Project:
|
||||
|
||||
# See if we can skip the network fetch entirely.
|
||||
remote_fetched = False
|
||||
has_shallow = os.path.exists(os.path.join(self.gitdir, "shallow"))
|
||||
if not (
|
||||
optimized_fetch
|
||||
and IsId(self.revisionExpr)
|
||||
@@ -1545,8 +1546,8 @@ class Project:
|
||||
use_superproject=use_superproject
|
||||
)
|
||||
and (
|
||||
not depth
|
||||
or os.path.exists(os.path.join(self.gitdir, "shallow"))
|
||||
has_shallow
|
||||
or (not depth and not self._SharingProjectHasShallow())
|
||||
)
|
||||
):
|
||||
remote_fetched = True
|
||||
@@ -2643,6 +2644,23 @@ class Project:
|
||||
# There is no such persistent revision. We have to fetch it.
|
||||
return False
|
||||
|
||||
def _SharingProjectHasShallow(self) -> bool:
|
||||
"""Check if another project sharing this objdir has a "shallow" file.
|
||||
|
||||
If any project sharing this objdir has a shallow file in its gitdir,
|
||||
then the shared objdir may be depth-limited, and every other project
|
||||
sharing this objdir needs its own shallow file so that git knows
|
||||
where history is truncated.
|
||||
"""
|
||||
other_projects = self.manifest.GetProjectsWithName(
|
||||
self.name, all_manifests=True
|
||||
)
|
||||
for proj in other_projects:
|
||||
if proj.objdir == self.objdir and proj.gitdir != self.gitdir:
|
||||
if os.path.exists(os.path.join(proj.gitdir, "shallow")):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _FetchArchive(self, tarpath, cwd=None):
|
||||
cmd = ["archive", "-v", "-o", tarpath]
|
||||
cmd.append("--remote=%s" % self.remote.url)
|
||||
@@ -2702,11 +2720,14 @@ class Project:
|
||||
tag_name = self.upstream[len(R_TAGS) :]
|
||||
|
||||
if is_sha1 or tag_name is not None:
|
||||
has_shallow = os.path.exists(
|
||||
os.path.join(self.gitdir, "shallow")
|
||||
)
|
||||
if self._CheckForImmutableRevision(
|
||||
use_superproject=use_superproject
|
||||
) and (
|
||||
not depth
|
||||
or os.path.exists(os.path.join(self.gitdir, "shallow"))
|
||||
has_shallow
|
||||
or (not depth and not self._SharingProjectHasShallow())
|
||||
):
|
||||
if verbose:
|
||||
print(
|
||||
|
||||
@@ -812,6 +812,7 @@ def _create_mock_project(
|
||||
|
||||
proj.bare_git = mock.MagicMock()
|
||||
proj._LsRemote = mock.MagicMock(return_value="1234abcd\trefs/heads/main\n")
|
||||
manifest.GetProjectsWithName.return_value = [proj]
|
||||
|
||||
return proj
|
||||
|
||||
@@ -957,6 +958,31 @@ class SyncOptimizationTests(unittest.TestCase):
|
||||
proj._InitMRef = mock.MagicMock()
|
||||
return proj
|
||||
|
||||
def _create_sharing_project(self, tempdir, proj, share_objdir=True):
|
||||
"""Create another project with the same name but a different gitdir.
|
||||
|
||||
Args:
|
||||
share_objdir: a boolean, if True - the new project shares the same
|
||||
objdir, if False - the new project has a different objdir.
|
||||
"""
|
||||
if share_objdir:
|
||||
other_objdir = proj.objdir
|
||||
else:
|
||||
other_objdir = os.path.join(tempdir, "other_objdir")
|
||||
other = project.Project(
|
||||
manifest=proj.manifest,
|
||||
name=proj.name,
|
||||
remote=proj.remote,
|
||||
gitdir=os.path.join(tempdir, "other_gitdir"),
|
||||
objdir=other_objdir,
|
||||
worktree=os.path.join(tempdir, "other_worktree"),
|
||||
relpath="other-test-project",
|
||||
revisionExpr=proj.revisionExpr,
|
||||
revisionId=None,
|
||||
)
|
||||
proj.manifest.GetProjectsWithName.return_value.append(other)
|
||||
return other
|
||||
|
||||
def test_sync_network_half_shallow_missing_fetches(self):
|
||||
"""Test Sync_NetworkHalf fetches if shallow file is missing."""
|
||||
with utils_for_test.TempGitTree() as tempdir:
|
||||
@@ -991,6 +1017,72 @@ class SyncOptimizationTests(unittest.TestCase):
|
||||
self.assertTrue(res.success)
|
||||
proj._RemoteFetch.assert_not_called()
|
||||
|
||||
def test_sync_network_half_sharing_project_shallow_missing_fetches(
|
||||
self,
|
||||
):
|
||||
"""Test Sync_NetworkHalf fetches when sharing project has shallow
|
||||
file but this project does not."""
|
||||
with utils_for_test.TempGitTree() as tempdir:
|
||||
proj = self._get_project(tempdir, depth=1)
|
||||
os.makedirs(proj.gitdir, exist_ok=True)
|
||||
os.makedirs(proj.objdir, exist_ok=True)
|
||||
|
||||
other = self._create_sharing_project(tempdir, proj)
|
||||
os.makedirs(other.gitdir, exist_ok=True)
|
||||
with open(os.path.join(other.gitdir, "shallow"), "w") as f:
|
||||
f.write("")
|
||||
|
||||
proj._RemoteFetch = mock.MagicMock(return_value=True)
|
||||
|
||||
res = proj.Sync_NetworkHalf(optimized_fetch=True)
|
||||
|
||||
self.assertTrue(res.success)
|
||||
proj._RemoteFetch.assert_called_once()
|
||||
|
||||
def test_sync_network_half_different_objdir_shallow_exists_skips(self):
|
||||
"""Test Sync_NetworkHalf skips when same-name project has shallow file
|
||||
but different objdir (like in a multi-manifest setup)."""
|
||||
with utils_for_test.TempGitTree() as tempdir:
|
||||
proj = self._get_project(tempdir, depth=1)
|
||||
os.makedirs(proj.gitdir, exist_ok=True)
|
||||
os.makedirs(proj.objdir, exist_ok=True)
|
||||
|
||||
other = self._create_sharing_project(
|
||||
tempdir, proj, share_objdir=False
|
||||
)
|
||||
os.makedirs(other.gitdir, exist_ok=True)
|
||||
with open(os.path.join(other.gitdir, "shallow"), "w") as f:
|
||||
f.write("")
|
||||
|
||||
proj._RemoteFetch = mock.MagicMock()
|
||||
|
||||
res = proj.Sync_NetworkHalf(optimized_fetch=True)
|
||||
|
||||
self.assertTrue(res.success)
|
||||
proj._RemoteFetch.assert_not_called()
|
||||
|
||||
def test_sync_network_half_sharing_project_both_shallow_skips(self):
|
||||
"""Test Sync_NetworkHalf skips when both this project and the sharing
|
||||
project have shallow files."""
|
||||
with utils_for_test.TempGitTree() as tempdir:
|
||||
proj = self._get_project(tempdir, depth=1)
|
||||
os.makedirs(proj.gitdir, exist_ok=True)
|
||||
os.makedirs(proj.objdir, exist_ok=True)
|
||||
with open(os.path.join(proj.gitdir, "shallow"), "w") as f:
|
||||
f.write("")
|
||||
|
||||
other = self._create_sharing_project(tempdir, proj)
|
||||
os.makedirs(other.gitdir, exist_ok=True)
|
||||
with open(os.path.join(other.gitdir, "shallow"), "w") as f:
|
||||
f.write("")
|
||||
|
||||
proj._RemoteFetch = mock.MagicMock()
|
||||
|
||||
res = proj.Sync_NetworkHalf(optimized_fetch=True)
|
||||
|
||||
self.assertTrue(res.success)
|
||||
proj._RemoteFetch.assert_not_called()
|
||||
|
||||
def test_remote_fetch_shallow_missing_fetches(self):
|
||||
"""Test _RemoteFetch fetches if shallow file is missing."""
|
||||
with utils_for_test.TempGitTree() as tempdir:
|
||||
@@ -1032,6 +1124,58 @@ class SyncOptimizationTests(unittest.TestCase):
|
||||
self.assertTrue(res)
|
||||
mock_git_cmd.assert_not_called()
|
||||
|
||||
def test_remote_fetch_sharing_project_shallow_missing_fetches(self):
|
||||
"""Test _RemoteFetch fetches when sharing project has shallow file
|
||||
but this project does not."""
|
||||
with utils_for_test.TempGitTree() as tempdir:
|
||||
proj = self._get_project(tempdir, depth=1)
|
||||
os.makedirs(proj.gitdir, exist_ok=True)
|
||||
os.makedirs(proj.objdir, exist_ok=True)
|
||||
|
||||
other = self._create_sharing_project(tempdir, proj)
|
||||
os.makedirs(other.gitdir, exist_ok=True)
|
||||
with open(os.path.join(other.gitdir, "shallow"), "w") as f:
|
||||
f.write("")
|
||||
|
||||
with mock.patch("project.GitCommand") as mock_git_cmd:
|
||||
mock_cmd_instance = mock.MagicMock()
|
||||
mock_cmd_instance.Wait.return_value = 0
|
||||
mock_git_cmd.return_value = mock_cmd_instance
|
||||
|
||||
res = proj._RemoteFetch(
|
||||
current_branch_only=True,
|
||||
depth=1,
|
||||
use_superproject=False,
|
||||
)
|
||||
|
||||
self.assertTrue(res)
|
||||
mock_git_cmd.assert_called()
|
||||
|
||||
def test_remote_fetch_sharing_project_both_shallow_skips(self):
|
||||
"""Test _RemoteFetch skips when both this project and the sharing
|
||||
project have shallow files."""
|
||||
with utils_for_test.TempGitTree() as tempdir:
|
||||
proj = self._get_project(tempdir, depth=1)
|
||||
os.makedirs(proj.gitdir, exist_ok=True)
|
||||
os.makedirs(proj.objdir, exist_ok=True)
|
||||
with open(os.path.join(proj.gitdir, "shallow"), "w") as f:
|
||||
f.write("")
|
||||
|
||||
other = self._create_sharing_project(tempdir, proj)
|
||||
os.makedirs(other.gitdir, exist_ok=True)
|
||||
with open(os.path.join(other.gitdir, "shallow"), "w") as f:
|
||||
f.write("")
|
||||
|
||||
with mock.patch("project.GitCommand") as mock_git_cmd:
|
||||
res = proj._RemoteFetch(
|
||||
current_branch_only=True,
|
||||
depth=1,
|
||||
use_superproject=False,
|
||||
)
|
||||
|
||||
self.assertTrue(res)
|
||||
mock_git_cmd.assert_not_called()
|
||||
|
||||
|
||||
class GetEnvVarsTests(unittest.TestCase):
|
||||
"""Tests for GetEnvVars project environment variable generation."""
|
||||
|
||||
Reference in New Issue
Block a user