[PATCH] checkout.txt: note about losing staged changes with --merge
- From
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
- Date
- Mar 19, 2019, 09:39 UTC
- Message-ID
- <20190319093910.20229-1-pclouds@gmail.com>
- In-Reply-To
- <78628256-79dc-3036-c57b-a96797ceb120@gmail.com>
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.wood123@gmail.com> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> --- This is my "fix" for Phillip's second problem. I chose to reply here because this is where an actual fix was discussed. The test script to demonstate it is here
https://public-inbox.org/git/7d3742d6-73e4-2750-6ecb-9edf761d96dd@gmail.com/
Documentation/git-checkout.txt | 2 ++ builtin/checkout.c | 9 +++++++++ 2 files changed, 11 insertions(+)
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index f179b43732..877e5f503a 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -242,6 +242,8 @@ should result in deletion of the path). + When checking out paths from the index, this option lets you recreate the conflicted merge in the specified paths. ++ +When switching branches with `--merge`, staged changes may be lost. --conflict=<style>:: The same as --merge option above, but changes the way the diff --git a/builtin/checkout.c b/builtin/checkout.c index 0e6037b296..f95e7975f7 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -726,6 +726,8 @@ static int merge_working_tree(const struct checkout_opts *opts, struct tree *result; struct tree *work; struct merge_options o; + struct strbuf sb = STRBUF_INIT; + if (!opts->merge) return 1; @@ -736,6 +738,13 @@ static int merge_working_tree(const struct checkout_opts *opts, if (!old_branch_info->commit) 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 */ /*
-- 2.21.0.548.gd3c7d92dc2