rebase: ignore non-branch update-refs

The following Git configuration breaks git rebase --update-refs:

    [rebase]
        instructionFormat = %s%d

The '%d' format requests all available decorations for a commit,
filling the global decoration table with all of them,
which --update-refs then uses to populate 'update-ref' instructions
in the rebase todo list.

Specifically, this results in the following instruction:

    update-ref HEAD

The todo parser then rejects the instruction:

    error: update-ref requires a fully qualified refname e.g. refs/heads/HEAD
    error: invalid line 3: update-ref HEAD

To fix, ignore decorations that are not local branches
when scanning through the table.

This matches the documented contract:
it moves branch refs under refs/heads/
and leaves display-only decorations (HEAD, tags, etc.) alone.

Verification:
A regression test that fails without this fix is included.

Signed-off-by: Abhinav Gupta <mail@abhinavg.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
main
Abhinav Gupta 2026-05-10 15:41:11 -07:00 committed by Junio C Hamano
parent 67ad42147a
commit 106b6885c7
2 changed files with 25 additions and 1 deletions

View File

@ -6361,8 +6361,14 @@ static int add_decorations_to_list(const struct commit *commit,
/*
* If the branch is the current HEAD, then it will be
* updated by the default rebase behavior.
* Exclude it from the list of refs to update,
* as well as any non-branch decorations.
* Non-branch decorations may be present if the pretty format
* includes "%d", which would have loaded all refs
* into the global decoration table.
*/
if (head_ref && !strcmp(head_ref, decoration->name)) {
if ((head_ref && !strcmp(head_ref, decoration->name)) ||
(decoration->type != DECORATION_REF_LOCAL)) {
decoration = decoration->next;
continue;
}

View File

@ -1954,6 +1954,24 @@ test_expect_success '--update-refs adds commands with --rebase-merges' '
)
'

test_expect_success '--update-refs ignores non-branch decorations' '
test_when_finished "git branch -D update-refs" &&
test_when_finished "git checkout primary" &&
git checkout -B update-refs no-conflict-branch &&
(
set_cat_todo_editor &&

# rebase.instructionFormat=%d loads normal log decorations before
# --update-refs adds its branch placeholders so we must ignore
# all non-local decorations.
test_must_fail git -c rebase.instructionFormat="%s%d" \
rebase -i --update-refs HEAD^ >todo
) &&
grep ^update-ref todo >actual &&
test_write_lines "update-ref refs/heads/no-conflict-branch" >expect &&
test_cmp expect actual
'

test_expect_success '--update-refs updates refs correctly' '
git checkout -B update-refs no-conflict-branch &&
git branch -f base HEAD~4 &&