[PATCH 1/2] branch: do not track a start point with no ref

Volodymyr Vriukalo <[email protected]>
Newsgroups org.kernel.vger.git
Message-ID <20260822-vv-branch-recurse-no-start-ref-v1-1-46dc140acaa8@zitro.id>
Forcing a branch to a commit that no ref points at aborts when both
  `submodule.recurse` and `submodule.propagateBranches` are set and
  the repository has a remote configured:

    BUG: refspec.c:442: refspec_find_match: need either src or dst
    Aborted (core dumped)

`create_branches_recursively()` resolves the start point through
  `dwim_branch_start()`, which leaves `branch_point` NULL when the
  start point names no ref -- an object id, or a revision expression
  such as `HEAD~0`.  That NULL becomes `tracking_name`, and the
  `setup_tracking()` call below it is guarded on `track` alone.
  `setup_tracking()` assigns it to `tracking.spec.dst` without
  checking, then hands the spec to `for_each_remote()`, so
  `refspec_find_match()` receives a query with neither src nor dst
  and trips its assertion.
`for_each_remote()` never reaches that callback where no remote is
  configured, which is why the abort needs one.

961b130d20 (branch: add --recurse-submodules option for branch
  creation, 2022-01-28) added the call with no guard at all.
75388bf5b4 (branch: support more tracking modes when recursing,
  2022-03-29) added the guard on `track`.

Updating the branch happens before the abort, so the command does
  what was asked and then exits 134.
Callers that check the exit status therefore see a failure that did
  not happen, and one that rolls back on failure would undo a
  successful update.

`create_branch()` already declines this: it calls `setup_tracking()`
  under `if (real_ref && track)`, leaving tracking unset when the
  start point resolved to no ref.
Make the recursive path agree.
Checking for NULL inside `setup_tracking()` would also silence the
  abort, but it would put the decision in the callee for one caller
  that has the answer already, and leave the two creation paths
  disagreeing about when tracking is set up.

Reproducing it needs all four of:

  - `submodule.recurse=true`
  - `submodule.propagateBranches=true`
  - a configured remote
  - a start point that is not a ref name

Submodules take no part, so the new test builds a repository with
  neither a submodule nor a `.gitmodules`, where `propagateBranches`
  is set and has nothing to propagate to.

Assisted-by: An LLM.
Signed-off-by: Volodymyr Vriukalo <[email protected]>
---
 branch.c                    |  2 +-
 t/t3207-branch-submodule.sh | 17 +++++++++++++++++
 2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/branch.c b/branch.c
index 243db7d0fc..182fc4a3dd 100644
--- a/branch.c
+++ b/branch.c
@@ -806,7 +806,7 @@ void create_branches_recursively(struct repository *r, const char *name,
 	 * tedious to determine whether or not tracking was set up in the
 	 * superproject.
 	 */
-	if (track)
+	if (tracking_name && track)
 		setup_tracking(name, tracking_name, track, quiet);
 
 	for (i = 0; i < submodule_entry_list.entry_nr; i++) {
diff --git a/t/t3207-branch-submodule.sh b/t/t3207-branch-submodule.sh
index fe72b24716..54f7caeb2f 100755
--- a/t/t3207-branch-submodule.sh
+++ b/t/t3207-branch-submodule.sh
@@ -98,6 +98,23 @@ test_expect_success 'should respect submodule.recurse when creating branches' '
 	)
 '
 
+test_expect_success 'should move a branch to a start point that names no ref' '
+	test_when_finished "rm -rf no-submodules" &&
+	git init no-submodules &&
+	(
+		cd no-submodules &&
+		test_commit one &&
+		test_commit two &&
+		git remote add origin . &&
+		git config submodule.propagateBranches true &&
+		git config submodule.recurse true &&
+		git branch branch-a HEAD~1 &&
+		oid=$(git rev-parse HEAD) &&
+		git branch -f branch-a "$oid" &&
+		test_cmp_rev HEAD branch-a
+	)
+'
+
 test_expect_success 'should ignore submodule.recurse when not creating branches' '
 	test_when_finished "reset_test" &&
 	(

-- 
2.55.0.2.g927b4b9963
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.