From a0abfd7339536cfba02c112e7ac804bc6252ebdb Mon Sep 17 00:00:00 2001 From: Gavin Mak Date: Fri, 6 Feb 2026 14:19:00 -0800 Subject: [PATCH] project: resolve unborn HEAD robustly in reftable repos Use `git symbolic-ref` to resolve HEAD before trying to parse .git/HEAD directly which is unreliable for reftable repos. Bug: 476209856 Change-Id: I60185d945c5b43c871945c0126cfdf52194e745d Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/550762 Commit-Queue: Gavin Mak Reviewed-by: Mike Frysinger Tested-by: Gavin Mak --- project.py | 18 ++++++++++++++++++ tests/test_project.py | 27 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/project.py b/project.py index 3870ad379..caeaa5211 100644 --- a/project.py +++ b/project.py @@ -3945,6 +3945,24 @@ class Project: return self.rev_parse(HEAD) return symbolic_head except GitError as e: + # `git rev-parse --symbolic-full-name HEAD` will fail for unborn + # branches, so try symbolic-ref before falling back to raw file + # parsing. + try: + p = GitCommand( + self._project, + ["symbolic-ref", "-q", HEAD], + bare=True, + gitdir=self._gitdir, + capture_stdout=True, + capture_stderr=True, + log_as_error=False, + ) + if p.Wait() == 0: + return p.stdout.rstrip("\n") + except GitError: + pass + logger.warning( "project %s: unparseable HEAD; trying to recover.\n" "Check that HEAD ref in .git/HEAD is valid. The error " diff --git a/tests/test_project.py b/tests/test_project.py index 7c50ad52a..501707eaf 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -19,6 +19,7 @@ import os from pathlib import Path import subprocess import tempfile +from typing import Optional import unittest import utils_for_test @@ -45,6 +46,9 @@ class FakeProject: ) self.config = git_config.GitConfig.ForRepository(gitdir=self.gitdir) + def RelPath(self, local: Optional[bool] = None) -> str: + return self.name + class ReviewableBranchTests(unittest.TestCase): """Check ReviewableBranch behavior.""" @@ -98,6 +102,29 @@ class ProjectTests(unittest.TestCase): "abcd00%21%21_%2b", ) + @unittest.skipUnless( + utils_for_test.supports_reftable(), + "git reftable support is required for this test", + ) + def test_get_head_unborn_reftable(self): + with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir: + subprocess.check_call( + [ + "git", + "-c", + "init.defaultRefFormat=reftable", + "init", + "-q", + tempdir, + ] + ) + fakeproj = FakeProject(tempdir) + expected = subprocess.check_output( + ["git", "-C", tempdir, "symbolic-ref", "-q", "HEAD"], + encoding="utf-8", + ).strip() + self.assertEqual(expected, fakeproj.work_git.GetHead()) + class CopyLinkTestCase(unittest.TestCase): """TestCase for stub repo client checkouts.