project: centralize safe branch and commit resolution

Resolve commit-ish values through one typed helper using rev-parse
--verify --quiet and, on Git 2.30+, --end-of-options. Reject option-like
revisions on older clients, reuse the helper for project and manifest
resolution, and validate user branch names with check-ref-format
--branch.

Bug: 553599402
Change-Id: I2dda49ff3e781cec14d3b84263a8dace06b4073f
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/623182
Tested-by: Gavin Mak <gavinmak@google.com>
Commit-Queue: Gavin Mak <gavinmak@google.com>
Reviewed-by: Brian Gan <brgan@google.com>
This commit is contained in:
Gavin Mak authored and gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com committed 2026-08-31 12:34:53 -07:00
1 parent 6541729a18
commit d27034bf62
7 files changed
+95 -10

No files matched your search

+18
View File
@@ -231,6 +231,24 @@ class GitCommandStreamLogsTest(unittest.TestCase):
class GitCallUnitTest(unittest.TestCase):
"""Tests the _GitCall class (via git_command.git)."""
def test_valid_branch_name_uses_branch_mode(self) -> None:
"""Branch validation applies Git's branch-specific restrictions."""
command = mock.MagicMock()
command.Wait.return_value = 1
with mock.patch.object(
git_command, "GitCommand", return_value=command
) as check:
self.assertFalse(git_command.IsValidBranchName("-topic"))
check.assert_called_once_with(
None,
["check-ref-format", "--branch", "-topic"],
capture_stdout=True,
capture_stderr=True,
add_event_log=False,
log_as_error=False,
)
def test_version_tuple(self):
"""Check git.version_tuple() handling."""
ver = git_command.git.version_tuple()
+43
View File
@@ -386,6 +386,49 @@ class ProjectTests(unittest.TestCase):
proj.work_git.checkout("HEAD~0")
self.assertEqual(commit_sha, proj.GetHeadRevisionId())
def test_resolve_commit_uses_end_of_options_when_supported(self) -> None:
"""Commit resolution separates user revisions from options."""
proj = mock.MagicMock(name="project")
proj.name = "project"
git = project.Project._GitGetByExec(proj, bare=True, gitdir="gitdir")
command = mock.MagicMock()
command.stdout = "1" * 40 + "\n"
command.Wait.return_value = 0
with mock.patch.object(
project, "git_require", return_value=True
), mock.patch.object(
project, "GitCommand", return_value=command
) as cmd:
self.assertEqual("1" * 40, git.ResolveCommit("topic"))
cmd.assert_called_once_with(
proj,
[
"rev-parse",
"--verify",
"--quiet",
"--end-of-options",
"topic^{commit}",
],
bare=True,
gitdir="gitdir",
capture_stdout=True,
capture_stderr=True,
verify_command=True,
log_as_error=False,
)
def test_resolve_commit_rejects_option_on_old_git(self) -> None:
"""Old Git never receives a revision that looks like an option."""
proj = mock.MagicMock(name="project")
proj.name = "project"
git = project.Project._GitGetByExec(proj, bare=True, gitdir="gitdir")
with mock.patch.object(project, "git_require", return_value=False):
with self.assertRaises(error.GitError):
git.ResolveCommit("--not-a-revision")
def test_get_branches_reuses_ref_snapshot_for_current_branch(self) -> None:
"""GetBranches derives the current branch from the loaded refs."""
with utils_for_test.TempGitTree() as tempdir: