mirror of
https://gerrit.googlesource.com/git-repo
synced 2026-10-15 18:07:18 +00:00
project: derive HEAD fallback from git's own default branch
When the default branch cannot be determined dynamically, repo fell back to a hardcoded "refs/heads/master". This can point at a branch that does not exist on the server, since projects increasingly default to "main". Rather than swap one hardcoded name for another, ask git itself what it would use. Bug: 483758905 Change-Id: Ic66712acea98f8e548a2d6d8211865ee5c416b4d Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/612845 Tested-by: Brian Gan <brgan@google.com> Reviewed-by: Gavin Mak <gavinmak@google.com> Commit-Queue: Brian Gan <brgan@google.com>
This commit is contained in:
1 parent
29b6630a65
commit
54fa31cd84
2 files changed
+81
-5
No files matched your search
+25
-4
@@ -15,6 +15,7 @@
|
||||
import datetime
|
||||
import errno
|
||||
import filecmp
|
||||
import functools
|
||||
import glob
|
||||
import os
|
||||
import platform
|
||||
@@ -4663,6 +4664,26 @@ class SyncBuffer:
|
||||
self._pending_failures = []
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def _DefaultBranchFallback() -> str:
|
||||
"""Return the ref to use when remote default branch can't be resolved."""
|
||||
|
||||
def _git(args: List[str]) -> str:
|
||||
p = GitCommand(
|
||||
None,
|
||||
args,
|
||||
capture_stdout=True,
|
||||
capture_stderr=True,
|
||||
log_as_error=False,
|
||||
)
|
||||
return p.stdout.strip() if p.Wait() == 0 else ""
|
||||
|
||||
branch = _git(["var", "GIT_DEFAULT_BRANCH"]) or _git(
|
||||
["config", "--get", "init.defaultBranch"]
|
||||
)
|
||||
return f"refs/heads/{branch or 'master'}"
|
||||
|
||||
|
||||
class MetaProject(Project):
|
||||
"""A special project housed under .repo."""
|
||||
|
||||
@@ -4676,7 +4697,7 @@ class MetaProject(Project):
|
||||
worktree=worktree,
|
||||
remote=RemoteSpec("origin"),
|
||||
relpath=".repo/%s" % name,
|
||||
revisionExpr="refs/heads/master",
|
||||
revisionExpr=_DefaultBranchFallback(),
|
||||
revisionId=None,
|
||||
groups=None,
|
||||
)
|
||||
@@ -5134,9 +5155,9 @@ class ManifestProject(MetaProject):
|
||||
if is_new:
|
||||
default_branch = self.ResolveRemoteHead()
|
||||
if default_branch is None:
|
||||
# If the remote doesn't have HEAD configured, default to
|
||||
# master.
|
||||
default_branch = "refs/heads/master"
|
||||
# If the remote doesn't have HEAD configured, fall back
|
||||
# to whatever git uses as its default branch.
|
||||
default_branch = _DefaultBranchFallback()
|
||||
self.revisionExpr = default_branch
|
||||
else:
|
||||
self.PreSync()
|
||||
|
||||
+56
-1
@@ -20,10 +20,11 @@ from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from typing import Optional
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
import utils_for_test
|
||||
|
||||
import error
|
||||
@@ -814,6 +815,60 @@ class ManifestPropertiesFetchedCorrectly(unittest.TestCase):
|
||||
self.assertFalse(os.path.exists(proj.gitdir))
|
||||
|
||||
|
||||
_VAR_CMD: List[str] = ["var", "GIT_DEFAULT_BRANCH"]
|
||||
_CONFIG_CMD: List[str] = ["config", "--get", "init.defaultBranch"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"responses, expected_ref, expected_calls",
|
||||
(
|
||||
# git >= 2.35 answers `git var GIT_DEFAULT_BRANCH`.
|
||||
({"var": (0, "jellybean\n")}, "refs/heads/jellybean", [_VAR_CMD]),
|
||||
# Older git: `git var` fails, so read init.defaultBranch instead.
|
||||
(
|
||||
{"var": (1, ""), "config": (0, "custom\n")},
|
||||
"refs/heads/custom",
|
||||
[_VAR_CMD, _CONFIG_CMD],
|
||||
),
|
||||
# Nothing configured anywhere: git's historical built-in default.
|
||||
(
|
||||
{"var": (1, ""), "config": (1, "")},
|
||||
"refs/heads/master",
|
||||
[_VAR_CMD, _CONFIG_CMD],
|
||||
),
|
||||
),
|
||||
ids=("git_var", "old_git_reads_config", "unconfigured_defaults_to_master"),
|
||||
)
|
||||
def test_default_branch_fallback(
|
||||
responses: Dict[str, Tuple[int, str]],
|
||||
expected_ref: str,
|
||||
expected_calls: List[List[str]],
|
||||
) -> None:
|
||||
"""_DefaultBranchFallback resolves the default branch via git."""
|
||||
seen: List[List[str]] = []
|
||||
|
||||
class FakeGitCommand:
|
||||
# Emulate git by returning the canned response for the subcommand.
|
||||
def __init__(
|
||||
self, project_: Optional[project.Project], cmdv: List[str], **kwargs
|
||||
) -> None:
|
||||
self.returncode, self.stdout = responses[cmdv[0]]
|
||||
seen.append(cmdv)
|
||||
|
||||
def Wait(self) -> int:
|
||||
return self.returncode
|
||||
|
||||
# The result is memoized, so clear it before (to bypass any cached real
|
||||
# value) and after (so the mocked value doesn't leak to other tests).
|
||||
project._DefaultBranchFallback.cache_clear()
|
||||
try:
|
||||
with mock.patch.object(project, "GitCommand", FakeGitCommand):
|
||||
assert project._DefaultBranchFallback() == expected_ref
|
||||
assert seen == expected_calls
|
||||
finally:
|
||||
project._DefaultBranchFallback.cache_clear()
|
||||
|
||||
|
||||
def _create_mock_project(
|
||||
tempdir,
|
||||
use_local_gitdirs=False,
|
||||
|
||||
Reference in new issue
Block a user