info: Report actual checked-out HEAD revision

When `repo info` runs, the reported "Current revision" is resolved using
the manifest's target branch tracking ref (e.g. refs/remotes/goog/main).

Introduce Project.GetHeadRevisionId(), which gets the checked-out HEAD
commit in the worktree, and use it in `repo info` with a fallback to the
old behavior if the project is not checked out.

Bug: 526685287
Change-Id: I72280ce27daa210cada27d722a94e365644f06e0
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/599481
Reviewed-by: Brian Gan <brgan@google.com>
Commit-Queue: Gavin Mak <gavinmak@google.com>
Tested-by: Gavin Mak <gavinmak@google.com>
This commit is contained in:
Gavin Mak
2026-06-22 22:36:20 +00:00
committed by gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com
parent c21a41c7cc
commit 88a7e88e54
4 changed files with 61 additions and 2 deletions
+12
View File
@@ -1647,6 +1647,18 @@ class Project:
f"revision {self.revisionExpr} in {self.name} not found"
)
def GetHeadRevisionId(self) -> Optional[str]:
"""Get the commit revision of the checked out HEAD.
Returns None if worktree is not checked out or HEAD cannot be resolved.
"""
if self.work_git:
try:
return self.work_git.rev_parse("HEAD")
except GitError:
pass
return None
def GetRevisionId(self, all_refs=None):
if self.revisionId:
return self.revisionId
+3 -2
View File
@@ -196,7 +196,8 @@ class Info(PagedCommand):
data = {
"name": project.name,
"mount_path": project.worktree,
"current_revision": project.GetRevisionId(),
"current_revision": project.GetHeadRevisionId()
or project.GetRevisionId(),
"manifest_revision": project.revisionExpr,
"local_branches": list(project.GetBranches()),
}
@@ -273,7 +274,7 @@ class Info(PagedCommand):
out.nl()
heading("Current revision: ")
headtext(project.GetRevisionId())
headtext(project.GetHeadRevisionId() or project.GetRevisionId())
out.nl()
currentBranch = project.CurrentBranch
+22
View File
@@ -104,6 +104,28 @@ class ProjectTests(unittest.TestCase):
"abcd00%21%21_%2b",
)
def test_get_head_revision_id(self):
"""Check GetHeadRevisionId behavior."""
with utils_for_test.TempGitTree() as tempdir:
proj = _create_mock_project(tempdir)
# Initially unborn HEAD should return None.
self.assertIsNone(proj.GetHeadRevisionId())
# Create a commit.
with open(os.path.join(tempdir, "readme"), "w") as fp:
fp.write("hello")
proj.work_git.add("readme")
proj.work_git.commit("-m", "initial commit")
# HEAD should resolve to the commit SHA.
commit_sha = proj.work_git.rev_parse("HEAD")
self.assertEqual(commit_sha, proj.GetHeadRevisionId())
# Even if worktree is detached.
proj.work_git.checkout("HEAD~0")
self.assertEqual(commit_sha, proj.GetHeadRevisionId())
@unittest.skipUnless(
utils_for_test.supports_reftable(),
"git reftable support is required for this test",
+24
View File
@@ -199,3 +199,27 @@ def test_text_enables_pager() -> None:
cmd = _get_cmd()
opts, _ = cmd.OptionParser.parse_args([])
assert cmd.WantPager(opts)
def test_get_project_data_uses_head_revision() -> None:
"""_getProjectData should use GetHeadRevisionId if available."""
cmd = _get_cmd()
project = mock.MagicMock()
project.name = "foo"
project.worktree = "/path/to/foo"
project.revisionExpr = "refs/heads/main"
project.GetBranches.return_value = []
# GetHeadRevisionId() returns a SHA, it should be used.
project.GetHeadRevisionId.return_value = "head_sha_12345"
project.GetRevisionId.return_value = "manifest_sha_54321"
data = cmd._getProjectData(project)
assert data["current_revision"] == "head_sha_12345"
project.GetHeadRevisionId.assert_called_once()
# GetHeadRevisionId() is None, fall back to GetRevisionId().
project.GetHeadRevisionId.reset_mock()
project.GetHeadRevisionId.return_value = None
data = cmd._getProjectData(project)
assert data["current_revision"] == "manifest_sha_54321"