summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGavin Mak <gavinmak@google.com>2026-02-06 14:19:00 -0800
committerLUCI <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com>2026-03-17 14:30:20 -0700
commita0abfd7339536cfba02c112e7ac804bc6252ebdb (patch)
tree0107ca0ca0be1d68f008dce610b6b97a187155e2
parent403fedfeb555d4e6c3144f36777e88ec41535d9d (diff)
downloadgit-repo-a0abfd7339536cfba02c112e7ac804bc6252ebdb.tar.gz
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 <gavinmak@google.com> Reviewed-by: Mike Frysinger <vapier@google.com> Tested-by: Gavin Mak <gavinmak@google.com>
-rw-r--r--project.py18
-rw-r--r--tests/test_project.py27
2 files changed, 45 insertions, 0 deletions
diff --git a/project.py b/project.py
index 3870ad379..caeaa5211 100644
--- a/project.py
+++ b/project.py
@@ -3945,6 +3945,24 @@ class Project:
3945 return self.rev_parse(HEAD) 3945 return self.rev_parse(HEAD)
3946 return symbolic_head 3946 return symbolic_head
3947 except GitError as e: 3947 except GitError as e:
3948 # `git rev-parse --symbolic-full-name HEAD` will fail for unborn
3949 # branches, so try symbolic-ref before falling back to raw file
3950 # parsing.
3951 try:
3952 p = GitCommand(
3953 self._project,
3954 ["symbolic-ref", "-q", HEAD],
3955 bare=True,
3956 gitdir=self._gitdir,
3957 capture_stdout=True,
3958 capture_stderr=True,
3959 log_as_error=False,
3960 )
3961 if p.Wait() == 0:
3962 return p.stdout.rstrip("\n")
3963 except GitError:
3964 pass
3965
3948 logger.warning( 3966 logger.warning(
3949 "project %s: unparseable HEAD; trying to recover.\n" 3967 "project %s: unparseable HEAD; trying to recover.\n"
3950 "Check that HEAD ref in .git/HEAD is valid. The error " 3968 "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
19from pathlib import Path 19from pathlib import Path
20import subprocess 20import subprocess
21import tempfile 21import tempfile
22from typing import Optional
22import unittest 23import unittest
23 24
24import utils_for_test 25import utils_for_test
@@ -45,6 +46,9 @@ class FakeProject:
45 ) 46 )
46 self.config = git_config.GitConfig.ForRepository(gitdir=self.gitdir) 47 self.config = git_config.GitConfig.ForRepository(gitdir=self.gitdir)
47 48
49 def RelPath(self, local: Optional[bool] = None) -> str:
50 return self.name
51
48 52
49class ReviewableBranchTests(unittest.TestCase): 53class ReviewableBranchTests(unittest.TestCase):
50 """Check ReviewableBranch behavior.""" 54 """Check ReviewableBranch behavior."""
@@ -98,6 +102,29 @@ class ProjectTests(unittest.TestCase):
98 "abcd00%21%21_%2b", 102 "abcd00%21%21_%2b",
99 ) 103 )
100 104
105 @unittest.skipUnless(
106 utils_for_test.supports_reftable(),
107 "git reftable support is required for this test",
108 )
109 def test_get_head_unborn_reftable(self):
110 with tempfile.TemporaryDirectory(prefix="repo-tests") as tempdir:
111 subprocess.check_call(
112 [
113 "git",
114 "-c",
115 "init.defaultRefFormat=reftable",
116 "init",
117 "-q",
118 tempdir,
119 ]
120 )
121 fakeproj = FakeProject(tempdir)
122 expected = subprocess.check_output(
123 ["git", "-C", tempdir, "symbolic-ref", "-q", "HEAD"],
124 encoding="utf-8",
125 ).strip()
126 self.assertEqual(expected, fakeproj.work_git.GetHead())
127
101 128
102class CopyLinkTestCase(unittest.TestCase): 129class CopyLinkTestCase(unittest.TestCase):
103 """TestCase for stub repo client checkouts. 130 """TestCase for stub repo client checkouts.