mirror of
https://gerrit.googlesource.com/git-repo
synced 2026-10-20 20:28:25 +00:00
project: allow re-reading submodule gitlinks
A discovered submodule becomes a project whose revision is the gitlink read from its parent at manifest load time, which is before the parent has been fetched. Remember which gitlink a submodule was derived from, and add a way to read those gitlinks again, so that callers can resolve a submodule revision once its parent is up-to-date. A revision that is set does not have to be fetched, so verify its objects are really there. Without them a submodule that was removed cannot be told apart from one that was never fetched, which the caller has to know about. Bug: 550074864 Change-Id: I4f35d53607126508eec6f02158a3fb604917a199 Signed-off-by: kimhappy <hwanhee.kim@laplacian.cc> Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/621661 Reviewed-by: Brian Gan <brgan@google.com> Reviewed-by: Gavin Mak <gavinmak@google.com>
This commit is contained in:
1 parent
b85886fa9f
commit
3f1775607f
2 files changed
+75
No files matched your search
+36
@@ -568,6 +568,7 @@ class Project:
|
||||
parent=None,
|
||||
use_git_worktrees=False,
|
||||
is_derived=False,
|
||||
gitlink_path: Optional[str] = None,
|
||||
dest_branch=None,
|
||||
optimized_fetch=False,
|
||||
retry_fetches=0,
|
||||
@@ -597,6 +598,8 @@ class Project:
|
||||
use_git_worktrees: Whether to use `git worktree` for this project.
|
||||
is_derived: False if the project was explicitly defined in the
|
||||
manifest; True if the project is a discovered submodule.
|
||||
gitlink_path: For a discovered submodule, its path inside the
|
||||
parent project.
|
||||
dest_branch: The branch to which to push changes for review by
|
||||
default.
|
||||
optimized_fetch: If True, when a project is set to a sha1 revision,
|
||||
@@ -624,6 +627,7 @@ class Project:
|
||||
# See the XmlManifest init code for more info.
|
||||
self.use_git_worktrees = use_git_worktrees
|
||||
self.is_derived = is_derived
|
||||
self.gitlink_path = gitlink_path
|
||||
self.optimized_fetch = optimized_fetch
|
||||
self.retry_fetches = max(0, retry_fetches)
|
||||
self.subprojects = []
|
||||
@@ -2573,6 +2577,37 @@ class Project:
|
||||
return []
|
||||
return get_submodules(self.gitdir, rev)
|
||||
|
||||
def GetSubmoduleRevisions(self) -> Optional[Dict[str, str]]:
|
||||
"""Read the gitlinks of our submodules at our current revision.
|
||||
|
||||
Discovered submodules are derived from the revision their parent was
|
||||
at when the manifest was loaded, which is before it gets fetched.
|
||||
Once it is up-to-date, its gitlinks have to be read again, otherwise
|
||||
the submodules would be synced to the revisions of the previous sync.
|
||||
|
||||
Returns:
|
||||
The revision of every submodule, keyed by its path inside this
|
||||
project, or None if our revision is not available locally. A
|
||||
revision that is merely set does not have to be fetched yet, and
|
||||
without its objects a removed submodule cannot be told apart from
|
||||
one that was never fetched.
|
||||
"""
|
||||
try:
|
||||
rev = self.GetRevisionId()
|
||||
self.bare_git.rev_list(
|
||||
"-1",
|
||||
"--missing=allow-any",
|
||||
f"{rev}^0",
|
||||
"--",
|
||||
log_as_error=False,
|
||||
)
|
||||
except (GitError, ManifestInvalidRevisionError):
|
||||
return None
|
||||
|
||||
return {
|
||||
path: sha for sha, path, _url, _shallow in self._GetSubmodules()
|
||||
}
|
||||
|
||||
def GetDerivedSubprojects(self):
|
||||
result = []
|
||||
if not self.Exists:
|
||||
@@ -2620,6 +2655,7 @@ class Project:
|
||||
parent=self,
|
||||
clone_depth=clone_depth,
|
||||
is_derived=True,
|
||||
gitlink_path=path,
|
||||
)
|
||||
result.append(subproject)
|
||||
result.extend(subproject.GetDerivedSubprojects())
|
||||
|
||||
@@ -948,6 +948,45 @@ def _create_mock_project(
|
||||
return proj
|
||||
|
||||
|
||||
class GetSubmoduleRevisions(unittest.TestCase):
|
||||
"""Tests for Project.GetSubmoduleRevisions."""
|
||||
|
||||
def test_gitlinks_are_keyed_by_their_path(self) -> None:
|
||||
with utils_for_test.TempGitTree() as tempdir:
|
||||
parent = _create_mock_project(tempdir)
|
||||
parent.GetRevisionId = mock.MagicMock(return_value="cafe1234")
|
||||
parent._GetSubmodules = mock.MagicMock(
|
||||
return_value=[("1234abcd", "sub", "http://example.com/sub", "")]
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
parent.GetSubmoduleRevisions(), {"sub": "1234abcd"}
|
||||
)
|
||||
|
||||
def test_none_when_the_revision_is_not_fetched(self) -> None:
|
||||
with utils_for_test.TempGitTree() as tempdir:
|
||||
parent = _create_mock_project(tempdir)
|
||||
parent.GetRevisionId = mock.MagicMock(return_value="cafe1234")
|
||||
parent.bare_git.rev_list = mock.MagicMock(
|
||||
side_effect=error.GitError("missing object")
|
||||
)
|
||||
parent._GetSubmodules = mock.MagicMock()
|
||||
|
||||
self.assertIsNone(parent.GetSubmoduleRevisions())
|
||||
parent._GetSubmodules.assert_not_called()
|
||||
|
||||
def test_none_without_a_revision(self) -> None:
|
||||
with utils_for_test.TempGitTree() as tempdir:
|
||||
parent = _create_mock_project(tempdir)
|
||||
parent.GetRevisionId = mock.MagicMock(
|
||||
side_effect=error.ManifestInvalidRevisionError("no revision")
|
||||
)
|
||||
parent._GetSubmodules = mock.MagicMock()
|
||||
|
||||
self.assertIsNone(parent.GetSubmoduleRevisions())
|
||||
parent._GetSubmodules.assert_not_called()
|
||||
|
||||
|
||||
class StatelessSyncTests(unittest.TestCase):
|
||||
"""Tests for stateless sync strategy."""
|
||||
|
||||
|
||||
Reference in new issue
Block a user