rebase --abort/--quit: cleanup refs/rewritten

When `rebase -r` finishes it removes any refs under refs/rewritten that
it has created. However if the user aborts or quits the rebase refs are
not removed. This can cause problems for future rebases. For example I
recently wanted to merge a updated version of a topic branch into an
integration branch so ran `rebase -ir` and removed the picks and label
for the topic branch from the todo list so that

    merge -C <old-merge> topic

would pick up the new version of topic. Unfortunately
refs/rewritten/topic already existed from a previous rebase that had
been aborted so the rebase just used the old topic, not the new one.

The logic for the non-interactive quit case is changed to ensure
`buf` is always freed.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Phillip Wood 2019-05-14 19:03:49 +01:00 committed by Junio C Hamano
parent 37e9ee5cb9
commit d559f502c5
2 changed files with 42 additions and 10 deletions

View file

@ -770,10 +770,18 @@ static int finish_rebase(struct rebase_options *opts)
* user should see them.
*/
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
strbuf_addstr(&dir, opts->state_dir);
if (remove_dir_recursively(&dir, 0))
ret = error(_("could not remove '%s'"), opts->state_dir);
strbuf_release(&dir);
if (opts->type == REBASE_INTERACTIVE) {
struct replay_opts replay = REPLAY_OPTS_INIT;
replay.action = REPLAY_INTERACTIVE_REBASE;
ret = sequencer_remove_state(&replay);
} else {
strbuf_addstr(&dir, opts->state_dir);
if (remove_dir_recursively(&dir, 0))
ret = error(_("could not remove '%s'"),
opts->state_dir);
strbuf_release(&dir);
}
return ret;
}
@ -1651,11 +1659,19 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
goto cleanup;
}
case ACTION_QUIT: {
strbuf_reset(&buf);
strbuf_addstr(&buf, options.state_dir);
ret = !!remove_dir_recursively(&buf, 0);
if (ret)
die(_("could not remove '%s'"), options.state_dir);
if (options.type == REBASE_INTERACTIVE) {
struct replay_opts replay = REPLAY_OPTS_INIT;
replay.action = REPLAY_INTERACTIVE_REBASE;
ret = !!sequencer_remove_state(&replay);
} else {
strbuf_reset(&buf);
strbuf_addstr(&buf, options.state_dir);
ret = !!remove_dir_recursively(&buf, 0);
if (ret)
error(_("could not remove '%s'"),
options.state_dir);
}
goto cleanup;
}
case ACTION_EDIT_TODO:

View file

@ -224,8 +224,24 @@ test_expect_success 'refs/rewritten/* is worktree-local' '
test_cmp_rev HEAD "$(cat wt/b)"
'
test_expect_success '--abort cleans up refs/rewritten' '
git checkout -b abort-cleans-refs-rewritten H &&
GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
git rev-parse --verify refs/rewritten/onto &&
git rebase --abort &&
test_must_fail git rev-parse --verify refs/rewritten/onto
'
test_expect_success '--quit cleans up refs/rewritten' '
git checkout -b quit-cleans-refs-rewritten H &&
GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
git rev-parse --verify refs/rewritten/onto &&
git rebase --quit &&
test_must_fail git rev-parse --verify refs/rewritten/onto
'
test_expect_success 'post-rewrite hook and fixups work for merges' '
git checkout -b post-rewrite &&
git checkout -b post-rewrite H &&
test_commit same1 &&
git reset --hard HEAD^ &&
test_commit same2 &&