checkout.txt: note about losing staged changes with --merge

If you have staged changes in path A and perform 'checkout
--merge' (which could result in conflicts in a totally unrelated path
B), changes in A will be gone. Which is unexpected. We are supposed
to keep all changes, or kick and scream otherwise.

This is the result of how --merge is implemented, from the very first
day in 1be0659efc (checkout: merge local modifications while switching
branches., 2006-01-12):

1. a merge is done, unmerged entries are collected
2. a hard switch to a new branch is done, then unmerged entries added
   back

There is no trivial fix for this. Going with 3-way merge one file at a
time loses rename detection. Going with 3-way merge by trees requires
teaching the algorithm to pick up staged changes. And even if we detect
staged changes with --merge and abort for safety, an option to continue
--merge is very weird. Such an option would keep worktree changes, but
drop staged changes.

Because the problem has been with us since the introduction of --merge
and everybody has been pretty happy (except Phillip, who found this
problem), I'll just take a note here to acknowledge it and wait for
merge wizards to come in and work their magic. There may be a way
forward [1].

[1] CABPp-BFoL_U=bzON4SEMaQSKU2TKwnOgNqjt5MUaOejTKGUJxw@mail.gmail.com

Reported-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Nguyễn Thái Ngọc Duy 2019-03-19 16:39:10 +07:00 committed by Junio C Hamano
parent aeb582a983
commit a7256debd4
2 changed files with 11 additions and 0 deletions

View File

@ -242,6 +242,8 @@ should result in deletion of the path).
+ +
When checking out paths from the index, this option lets you recreate When checking out paths from the index, this option lets you recreate
the conflicted merge in the specified paths. the conflicted merge in the specified paths.
+
When switching branches with `--merge`, staged changes may be lost.
--conflict=<style>:: --conflict=<style>::
The same as --merge option above, but changes the way the The same as --merge option above, but changes the way the

View File

@ -676,6 +676,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
struct tree *result; struct tree *result;
struct tree *work; struct tree *work;
struct merge_options o; struct merge_options o;
struct strbuf sb = STRBUF_INIT;
if (!opts->merge) if (!opts->merge)
return 1; return 1;
@ -686,6 +688,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
if (!old_branch_info->commit) if (!old_branch_info->commit)
return 1; return 1;
if (repo_index_has_changes(the_repository,
get_commit_tree(old_branch_info->commit),
&sb))
warning(_("staged changes in the following files may be lost: %s"),
sb.buf);
strbuf_release(&sb);
/* Do more real merge */ /* Do more real merge */
/* /*