Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/scripts/fork_sync_digest.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,10 @@ def git(*args, **kw):
["git", *args], cwd=repo_dir, capture_output=True, **kw
)

fetched = git("fetch", "origin", branch)
# Explicit refspec: the clone is --single-branch, so a bare
# `fetch origin <branch>` only updates FETCH_HEAD, not
# refs/remotes/origin/<branch> — leaving the checkout below with no ref.
fetched = git("fetch", "origin", f"refs/heads/{branch}:refs/remotes/origin/{branch}")
if fetched.returncode != 0:
return row(500, "error", None, f"fetch failed: {fetched.stderr.decode()[:200]}")

Expand All @@ -336,7 +339,8 @@ def git(*args, **kw):
if behind == 0:
return row(200, "already-current", 0, "up to date with base")

git("checkout", "--detach", f"origin/{branch}", check=True)
# -f: discard the prior iteration's rebased worktree state cleanly.
git("checkout", "--detach", "-f", f"origin/{branch}", check=True)
git("clean", "-fdq")
mergebase = git("merge-base", base_ref, f"origin/{branch}").stdout.decode().strip()
# Content-preserving rebase: hooks off (nothing new to lint — the branch
Expand Down
32 changes: 32 additions & 0 deletions .github/scripts/tests/test_rebase_feature_branches.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,35 @@ def boom(*a, **k): # noqa: ANN002, ANN003

monkeypatch.setattr(fsd.subprocess, "run", boom)
assert fsd.rebase_feature_branches("ledoent/ddmrp", ["19.0-mig-*"], "19.0") == []


class _Fake:
def __init__(self, rc=0, out=b"", err=b""):
self.returncode, self.stdout, self.stderr = rc, out, err


def test_rebase_one_fetches_with_full_refspec(monkeypatch):
# Regression: a --single-branch clone + bare `fetch origin <branch>`
# populates FETCH_HEAD but NOT origin/<branch>, so the checkout below
# explodes. The fetch must use refs/heads/<b>:refs/remotes/origin/<b>.
from pathlib import Path

calls = []

def fake_run(cmd, cwd=None, capture_output=False, env=None, check=False, **k):
calls.append(cmd)
args = [c for c in cmd if c != "git"]
if "rev-list" in args:
return _Fake(0, out=b"2\n")
if "merge-base" in args:
return _Fake(0, out=b"abc123\n")
return _Fake(0)

monkeypatch.setattr(fsd.subprocess, "run", fake_run)
res = fsd._rebase_one(Path("/tmp/x"), "ledoent/ddmrp", "19.0-mig-foo", "19.0", "origin/19.0")
assert res["rebase_status"] == "rebased"
fetch_cmds = [c for c in calls if "fetch" in c]
assert fetch_cmds and any(
"refs/heads/19.0-mig-foo:refs/remotes/origin/19.0-mig-foo" in c
for c in fetch_cmds
), f"fetch must use a full refspec, got: {fetch_cmds}"
Loading