]> git.ipfire.org Git - thirdparty/git.git/commitdiff
rebase: do not remove untracked files on checkout
authorPhillip Wood <phillip.wood@dunelm.org.uk>
Wed, 26 Jan 2022 13:05:39 +0000 (13:05 +0000)
committerJunio C Hamano <gitster@pobox.com>
Wed, 26 Jan 2022 20:08:52 +0000 (12:08 -0800)
If "git rebase [--apply|--merge] <upstream> <branch>" detects that
<upstream> is an ancestor of <branch> then it will fast-forward and
checkout <branch>. Normally a checkout or picking a commit during a
rebase will refuse to overwrite untracked files, however rebase does
overwrite untracked files when checking out <branch>.

The fix is to only set reset in `unpack_tree_opts` if flags contains
`RESET_HEAD_HARD`. t5403 may seem like an odd home for the new test
but it will be extended in the next commit to check that the
post-checkout hook is not run when the checkout fails.

The test for `!detach_head` dates back to the
original implementation of reset_head() in
ac7f467fef ("builtin/rebase: support running "git rebase <upstream>"",
2018-08-07) and was correct until e65123a71d
("builtin rebase: support `git rebase <upstream> <switch-to>`",
2018-09-04) started using reset_head() to checkout <switch-to> when
fast-forwarding.

Note that 480d3d6bf9 ("Change unpack_trees' 'reset' flag into an
enum", 2021-09-27) also fixes this bug as it changes reset_head() to
never remove untracked files. I think this fix is still worthwhile as
it makes it clear that the same settings are used for detached and
non-detached checkouts.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
reset.c
t/t5403-post-checkout-hook.sh

diff --git a/reset.c b/reset.c
index 315fef91d33aaa4b74c3b90ab026ff39bc81d9b5..3e7b9e2e1313947e627c6da6ae70151d83425645 100644 (file)
--- a/reset.c
+++ b/reset.c
@@ -59,7 +59,7 @@ int reset_head(struct repository *r, struct object_id *oid, const char *action,
        unpack_tree_opts.merge = 1;
        unpack_tree_opts.preserve_ignored = 0; /* FIXME: !overwrite_ignore */
        init_checkout_metadata(&unpack_tree_opts.meta, switch_to_branch, oid, NULL);
-       if (!detach_head)
+       if (reset_hard)
                unpack_tree_opts.reset = UNPACK_RESET_PROTECT_UNTRACKED;
 
        if (repo_read_index_unmerged(r) < 0) {
index 17ab518f26894d9367a32560662f54eb21c503d1..fd2817b4068c52908cc91b1e9fe4d3a336fe3a5e 100755 (executable)
@@ -85,6 +85,16 @@ test_rebase () {
                test_cmp_rev three $new &&
                test $flag = 1
        '
+
+       test_expect_success "rebase $args checkout does not remove untracked files" '
+               test_when_finished "test_might_fail git rebase --abort" &&
+               git update-ref refs/heads/rebase-fast-forward three &&
+               git checkout two &&
+               echo untracked >three.t &&
+               test_when_finished "rm three.t" &&
+               test_must_fail git rebase $args HEAD rebase-fast-forward 2>err &&
+               grep "untracked working tree files would be overwritten by checkout" err
+'
 }
 
 test_rebase --apply &&