[PATCH 0/2] branch: fix --recurse-submodules with a nameless start point
Volodymyr Vriukalo <[email protected]>
| Newsgroups | org.kernel.vger.git |
|---|---|
| Message-ID | <20260822-vv-branch-recurse-no-start-ref-v1-0-46dc140acaa8@zitro.id> |
vv/branch-recurse-no-start-ref
"git branch" with submodule.propagateBranches enabled mishandled a
start point that names no ref, such as a raw object id. In a
repository with no submodules it aborted with a BUG after having
already moved the branch; with submodules it failed earlier, when the
helper it invokes rejected a truncated argument list. Both are the
same missing tracking name, which has been corrected.
I hit the first of these while scripting a branch rewrite -- the sort
that moves a branch to a commit no ref points at yet, which for such a
script is the ordinary case rather than the exception:
BUG: refspec.c:442: refspec_find_match: need either src or dst
Aborted (core dumped)
The ref had already been updated. So the abort is loud and the damage
is quiet, which is the wrong way round: an exit status of 134 on an
operation that in fact completed will fool any caller that checks it,
and one whose error path rolls back will helpfully undo a successful
update.
The exposure is bounded. submodule.propagateBranches is documented as
experimental, and reproducing it needs all four of that setting,
submodule.recurse, a configured remote, and a start point that is not a
ref name; drop any one and the command succeeds. Within those bounds
it is not exotic -- a script that moves a branch by object id hits it
on the first attempt, the commit it wants having no ref on it yet,
which is rather the point of moving a branch. It reproduces
identically on 2.54.0, 2.55.0 and master, which is why this is based on
maint.
Both patches guard at the call site rather than inside the callee.
That follows create_branch(), which already declines the same value
with "if (real_ref && track)", and it leaves setup_tracking()'s own
convention intact: it opens by BUG()ing on a caller that should not
have called it, so absorbing a NULL quietly would contradict that four
lines later. Neither patch changes anything for a start point that
does name a ref.
This series was written with LLM assistance, recorded as an
"Assisted-by: An LLM." trailer on both patches. I used it for the
whole change rather than as step-by-step guidance, because at this
size the distinction is irrelevant: two guards and two tests, short
enough to read in full. I have read the surrounding code and checked
every claim in the commit messages against the source myself.
Two things I would welcome direction on.
dwim_and_setup_tracking() carries the same unguarded call. It survives
only because its single caller passes BRANCH_TRACK_OVERRIDE, under
which dwim_branch_start() dies rather than returning NULL -- safe by an
argument its caller happens to pass, not by anything in the function
itself. Nothing enforces that, so a future caller could reintroduce
the same abort. I left it alone to keep the series to the bug I
actually hit, but I am happy to add a third patch.
The first test uses no submodule, because the bug does not need one. I
put it in t3207 since create_branches_recursively() is only reached
under submodule.propagateBranches, but t3200 is a perfectly defensible
home and I will move it on request.
The series merges cleanly into next and seen; both merge results build
and pass t3200 and t3207, and the full suite passes on the topic
itself.
---
Volodymyr Vriukalo (2):
branch: do not track a start point with no ref
branch: allow recursion with no tracking name
branch.c | 12 ++++++++++--
builtin/submodule--helper.c | 7 ++++---
t/t3207-branch-submodule.sh | 30 ++++++++++++++++++++++++++++++
3 files changed, 44 insertions(+), 5 deletions(-)
---
base-commit: e9019fcafe0040228b8631c30f97ae1adb61bcdc
change-id: 20260822-vv-branch-recurse-no-start-ref-31fac1e34eab
Best regards,
--
Volodymyr Vriukalo <[email protected]>