refactor(update): build the tracking refspec in one place

The forced `+refs/heads/<b>:refs/remotes/<r>/<b>` refspec was spelled out at
all three fetch sites (update --check, the apply fetch, the fork upstream
sync). Its leading `+` is load-bearing on depth-1 clones, so one helper,
`update_cmd_check.tracking_refspec`, now owns the string and the reason.
This commit is contained in:
kshitijk4poor
2026-09-28 00:51:20 +05:30
committed by kshitij
parent abd72768fa
commit 0ee82ff4cc
3 changed files with 16 additions and 12 deletions

View File

@@ -1384,11 +1384,8 @@ def _cmd_update_impl(args, gateway_mode: bool):
if release_sha:
fetch_result = _git_run(git_cmd, ["fetch", "--no-tags", "origin", target_ref], network=True)
else:
# Explicit refspec so the tracking ref lands even on narrow clones whose only
# configured refspec is a tag (#125112): a branch-name fetch would leave the
# target unresolvable and the checkout/compare below would fail.
fetch_result = _git_run(
git_cmd, ["fetch", "origin", f"+refs/heads/{branch}:refs/remotes/origin/{branch}"], network=True)
git_cmd, ["fetch", "origin", _check.tracking_refspec("origin", branch)], network=True)
if fetch_result.returncode != 0:
_print_fetch_failure(fetch_result.stderr)
_m()._resume_windows_gateways_after_update(_windows_gateway_resume)

View File

@@ -71,13 +71,21 @@ def is_shallow_repository(git_cmd: list[str], root: Path) -> bool:
return _git(git_cmd, root, ["rev-parse", "--is-shallow-repository"]).stdout.strip() == "true"
def tracking_refspec(remote: str, branch: str) -> str:
"""Refspec that always writes ``<remote>/<branch>``, whatever ``remote.<remote>.fetch`` says.
Narrow clones (tag-pinned ``--single-branch``, #125112) map only the tag, so a by-name fetch
writes FETCH_HEAD and never the tracking ref the updater resolves. The ``+`` is load-bearing:
on a depth-1 clone the new tip is not a descendant of the old one.
"""
return f"+refs/heads/{branch}:refs/remotes/{remote}/{branch}"
def _fetch(git_cmd: list[str], root: Path, depth_args: list[str], remote: str, branch: str):
print(f"→ Fetching from {remote}...")
# Fetch by explicit refspec: on narrow clones (tag-pinned --single-branch, #125112) the
# configured remote.<remote>.fetch maps only the tag, so a branch-name fetch writes
# FETCH_HEAD and never creates the tracking ref the compare step resolves.
refspec = f"+refs/heads/{branch}:refs/remotes/{remote}/{branch}"
return _git(git_cmd, root, ["fetch", *depth_args, remote, refspec], **_uc()._no_prompt_git_kwargs())
return _git(
git_cmd, root, ["fetch", *depth_args, remote, tracking_refspec(remote, branch)],
**_uc()._no_prompt_git_kwargs())
def fetch_compare_branch(git_cmd: list[str], root: Path, branch: str, depth_args: list[str]):

View File

@@ -335,15 +335,14 @@ def _sync_with_upstream_if_needed(git_cmd: list[str], cwd: Path, *, assume_yes:
See #97052.
"""
from hermes_cli.update_cmd import _count_commits_between, _has_upstream_remote, _no_prompt_git_kwargs, _should_skip_upstream_prompt
from hermes_cli.update_cmd_check import tracking_refspec
if not _has_upstream_remote(git_cmd, cwd) and (
_should_skip_upstream_prompt() or not _offer_upstream_remote(git_cmd, cwd, assume_yes=assume_yes, input_fn=input_fn)
):
return False
print("\n→ Fetching upstream...")
try:
# Explicit refspec (#125112): tag-pinned narrow clones never build upstream/main
# from a branch-name fetch, and the comparison below resolves it.
subprocess.run(git_cmd + ["fetch", "upstream", "+refs/heads/main:refs/remotes/upstream/main", "--quiet"], cwd=cwd, capture_output=True, check=True, **_no_prompt_git_kwargs())
subprocess.run(git_cmd + ["fetch", "upstream", tracking_refspec("upstream", "main"), "--quiet"], cwd=cwd, capture_output=True, check=True, **_no_prompt_git_kwargs())
except subprocess.CalledProcessError:
print(" ✗ Failed to fetch upstream. Skipping upstream sync.")
return False