git_superproject: resolve one negotiation ref directly

The superproject only needs one branch tip or HEAD OID. Resolve that ref
with one quiet rev-parse instead of constructing GitRefs, which loads
the entire namespace and resolves HEAD in two or three processes on
older clients.

Bug: 553599402
Change-Id: I162d1133ebd1941e9de5aca43929fd5af17386ef
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/623906
Tested-by: Gavin Mak <gavinmak@google.com>
Reviewed-by: Brian Gan <brgan@google.com>
Commit-Queue: Gavin Mak <gavinmak@google.com>
This commit is contained in:
Gavin Mak
2026-09-02 17:46:01 -07:00
committed by gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com
parent d88ce8d952
commit ba8ddf396c
2 changed files with 44 additions and 7 deletions
+20 -3
View File
@@ -36,7 +36,6 @@ from git_command import git_require
from git_command import GitCommand
from git_config import IsId
from git_config import RepoConfig
from git_refs import GitRefs
import platform_utils
@@ -189,7 +188,7 @@ class Superproject:
if netloc:
parts = netloc.split("-review", 1)
host = parts[0]
rev = GitRefs(self._work_git).get("HEAD")
rev = self._GetRef("HEAD")
return f"{host}/{self.name}@{rev}"
return None
@@ -314,7 +313,10 @@ class Superproject:
# We use --negotiation-tip to speed up the fetch. Superproject branches
# do not share commits. So this lets git know it only needs to send
# commits reachable from the specified local refs.
rev_commit = GitRefs(self._work_git).get(f"refs/heads/{self.revision}")
negotiation_ref = self.revision
if negotiation_ref and not negotiation_ref.startswith("refs/"):
negotiation_ref = f"refs/heads/{negotiation_ref}"
rev_commit = self._GetRef(negotiation_ref) if negotiation_ref else ""
if rev_commit:
cmd.extend(["--negotiation-tip", rev_commit])
@@ -347,6 +349,21 @@ class Superproject:
return False
return True
def _GetRef(self, ref: str) -> str:
"""Resolve one local ref without loading the entire ref namespace."""
p = GitCommand(
None,
["rev-parse", "--verify", "--quiet", ref],
gitdir=self._work_git,
bare=True,
capture_stdout=True,
capture_stderr=True,
log_as_error=False,
)
if p.Wait() == 0:
return p.stdout.strip()
return ""
def _LsTree(self):
"""Gets the commit ids for all projects.
+24 -4
View File
@@ -542,14 +542,15 @@ class SuperprojectTestCase(unittest.TestCase):
with mock.patch(
"git_superproject.GitCommand", autospec=True
) as mock_git_command:
with mock.patch(
"git_superproject.GitRefs.get", autospec=True
) as mock_git_refs:
with mock.patch.object(
self._superproject, "_GetRef"
) as get_ref:
instance = mock_git_command.return_value
instance.Wait.return_value = 0
mock_git_refs.side_effect = ["", "1234"]
get_ref.side_effect = ["", "1234"]
self.assertTrue(self._superproject._Fetch())
get_ref.assert_called_with("refs/heads/main")
self.assertEqual(
# TODO: Once we require Python 3.8+,
# use 'mock_git_command.call_args.args'.
@@ -572,6 +573,7 @@ class SuperprojectTestCase(unittest.TestCase):
# If branch for revision exists, set as --negotiation-tip.
self.assertTrue(self._superproject._Fetch())
get_ref.assert_called_with("refs/heads/main")
self.assertEqual(
# TODO: Once we require Python 3.8+,
# use 'mock_git_command.call_args.args'.
@@ -593,3 +595,21 @@ class SuperprojectTestCase(unittest.TestCase):
],
),
)
def test_GetRef_resolves_only_the_requested_ref(self) -> None:
command = mock.MagicMock(stdout="1234\n")
command.Wait.return_value = 0
with mock.patch(
"git_superproject.GitCommand", return_value=command
) as git_command:
self.assertEqual("1234", self._superproject._GetRef("HEAD"))
git_command.assert_called_once_with(
None,
["rev-parse", "--verify", "--quiet", "HEAD"],
gitdir=self._superproject._work_git,
bare=True,
capture_stdout=True,
capture_stderr=True,
log_as_error=False,
)