&update_refs)) {
struct string_list_item *item;
for_each_string_list_item(item, &update_refs) {
+ char *resolved_ref;
+ int flags = 0;
+
old = strmap_put(¤t_checked_out_branches,
item->string,
xstrdup(wt->path));
free(old);
+
+ resolved_ref = refs_resolve_refdup(
+ get_main_ref_store(the_repository),
+ item->string, RESOLVE_REF_READING,
+ NULL, &flags);
+ if (resolved_ref && (flags & REF_ISSYMREF)) {
+ old = strmap_put(
+ ¤t_checked_out_branches,
+ resolved_ref, xstrdup(wt->path));
+ free(old);
+ }
+ free(resolved_ref);
}
string_list_clear(&update_refs, 1);
}
size_t items_alloc;
struct strbuf *buf;
struct string_list refs_to_oids;
+ struct string_list symref_update_targets;
};
static int add_decorations_to_list(const struct commit *commit,
while (decoration) {
struct todo_item *item;
const char *path;
+ const char *checked_ref;
char *resolved_ref;
int flags = 0;
size_t base_offset = ctx->buf->len;
}
path = branch_checked_out(decoration->name);
+ if (!path && resolved_ref && (flags & REF_ISSYMREF)) {
+ checked_ref = resolved_ref;
+ path = branch_checked_out(checked_ref);
+ }
+ if (!path && resolved_ref && (flags & REF_ISSYMREF) &&
+ string_list_has_string(&ctx->symref_update_targets,
+ resolved_ref)) {
+ free(resolved_ref);
+ decoration = decoration->next;
+ continue;
+ }
ALLOC_GROW(ctx->items,
ctx->items_nr + 1,
decoration->name, path);
} else {
struct string_list_item *sti;
+
+ if (resolved_ref && (flags & REF_ISSYMREF))
+ string_list_insert(&ctx->symref_update_targets,
+ resolved_ref);
item->command = TODO_UPDATE_REF;
strbuf_addf(ctx->buf, "%s\n", decoration->name);
struct todo_add_branch_context ctx = {
.buf = &todo_list->buf,
.refs_to_oids = STRING_LIST_INIT_DUP,
+ .symref_update_targets = STRING_LIST_INIT_DUP,
};
ctx.items_alloc = 2 * todo_list->nr + 1;
res = write_update_refs_state(&ctx.refs_to_oids);
string_list_clear(&ctx.refs_to_oids, 1);
+ string_list_clear(&ctx.symref_update_targets, 0);
if (res) {
/* we failed, so clean up the new list. */
test_cmp expect err.trimmed
'
+test_expect_success '--update-refs checks resolved non-branch symref target' '
+ test_when_finished "
+ git worktree remove --force checked-out-target-wt &&
+ git symbolic-ref -d refs/heads/non-branch-alias &&
+ git tag -d checked-out-target
+ " &&
+ git tag checked-out-target HEAD~1 &&
+ git symbolic-ref refs/heads/non-branch-alias refs/tags/checked-out-target &&
+ git worktree add --detach checked-out-target-wt checked-out-target &&
+ git -C checked-out-target-wt symbolic-ref HEAD refs/tags/checked-out-target &&
+
+ GIT_SEQUENCE_EDITOR="cat >todo" git rebase -i --update-refs HEAD~2 &&
+
+ test_grep "^# Ref refs/heads/non-branch-alias checked out at" todo &&
+ test_write_lines refs/tags/checked-out-target >expect &&
+ git symbolic-ref refs/heads/non-branch-alias >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--update-refs deduplicates non-branch symref targets' '
+ test_when_finished "
+ git symbolic-ref -d refs/heads/non-branch-alias-one &&
+ git symbolic-ref -d refs/heads/non-branch-alias-two &&
+ git tag -d shared-non-branch-target
+ " &&
+ git tag shared-non-branch-target HEAD~1 &&
+ git symbolic-ref refs/heads/non-branch-alias-one \
+ refs/tags/shared-non-branch-target &&
+ git symbolic-ref refs/heads/non-branch-alias-two \
+ refs/tags/shared-non-branch-target &&
+
+ GIT_SEQUENCE_EDITOR=: git rebase -i --force-rebase --update-refs HEAD~2 &&
+
+ test_cmp_rev HEAD~1 refs/heads/non-branch-alias-one &&
+ test_cmp_rev HEAD~1 refs/heads/non-branch-alias-two &&
+ test_write_lines refs/tags/shared-non-branch-target >expect &&
+ git symbolic-ref refs/heads/non-branch-alias-one >actual &&
+ test_cmp expect actual &&
+ git symbolic-ref refs/heads/non-branch-alias-two >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--update-refs honors non-branch symref reservations' '
+ test_when_finished "
+ test_might_fail git worktree remove --force reserved-target-wt &&
+ test_might_fail git symbolic-ref -d \
+ refs/heads/reserved-non-branch-alias-one &&
+ test_might_fail git symbolic-ref -d \
+ refs/heads/reserved-non-branch-alias-two &&
+ test_might_fail git tag -d reserved-non-branch-target
+ " &&
+ git tag reserved-non-branch-target HEAD~1 &&
+ git symbolic-ref refs/heads/reserved-non-branch-alias-one \
+ refs/tags/reserved-non-branch-target &&
+ git symbolic-ref refs/heads/reserved-non-branch-alias-two \
+ refs/tags/reserved-non-branch-target &&
+ git worktree add --detach reserved-target-wt HEAD &&
+ wt_gitdir=$(git -C reserved-target-wt rev-parse --absolute-git-dir) &&
+ mkdir -p "$wt_gitdir/rebase-merge" &&
+ old_oid=$(git rev-parse refs/heads/reserved-non-branch-alias-one) &&
+ test_write_lines refs/heads/reserved-non-branch-alias-one \
+ "$old_oid" "$old_oid" >"$wt_gitdir/rebase-merge/update-refs" &&
+
+ GIT_SEQUENCE_EDITOR="cat >todo" git rebase -i --update-refs HEAD~2 &&
+
+ test_grep "^# Ref refs/heads/reserved-non-branch-alias-one checked out at" \
+ todo &&
+ test_grep "^# Ref refs/heads/reserved-non-branch-alias-two checked out at" \
+ todo &&
+ test_grep ! "^update-ref refs/heads/reserved-non-branch-alias" todo
+'
+
test_expect_success 'respect user edits to update-ref steps' '
git checkout -B update-refs-break no-conflict-branch &&
git branch -f base HEAD~4 &&