Skip to content

Commit af818d6

Browse files
committed
Revert "Merge branch 'hn/git-checkout-m-with-stash' into next"
This reverts commit b4e5a96, reversing changes made to 0e6c98f, as the topic is still getting rerolled.
1 parent 1d1c64f commit af818d6

File tree

14 files changed

+157
-455
lines changed

14 files changed

+157
-455
lines changed

Documentation/git-checkout.adoc

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -251,19 +251,20 @@ working tree, by copying them from elsewhere, extracting a tarball, etc.
251251
are different between the current branch and the branch to
252252
which you are switching, the command refuses to switch
253253
branches in order to preserve your modifications in context.
254-
With this option, the conflicting local changes are
255-
automatically stashed before the switch and reapplied
256-
afterwards. If the local changes do not overlap with the
257-
differences between branches, the switch proceeds without
258-
stashing. If reapplying the stash results in conflicts, the
259-
entry is saved to the stash list. Resolve the conflicts
260-
and run `git stash drop` when done, or clear the working
261-
tree (e.g. with `git reset --hard`) before running `git stash
262-
pop` later to re-apply your changes.
254+
However, with this option, a three-way merge between the current
255+
branch, your working tree contents, and the new branch
256+
is done, and you will be on the new branch.
257+
+
258+
When a merge conflict happens, the index entries for conflicting
259+
paths are left unmerged, and you need to resolve the conflicts
260+
and mark the resolved paths with `git add` (or `git rm` if the merge
261+
should result in deletion of the path).
263262
+
264263
When checking out paths from the index, this option lets you recreate
265264
the conflicted merge in the specified paths. This option cannot be
266265
used when checking out paths from a tree-ish.
266+
+
267+
When switching branches with `--merge`, staged changes may be lost.
267268
268269
`--conflict=<style>`::
269270
The same as `--merge` option above, but changes the way the
@@ -577,44 +578,39 @@ $ git checkout mytopic
577578
error: You have local changes to 'frotz'; not switching branches.
578579
------------
579580
580-
You can give the `-m` flag to the command, which would carry your local
581-
changes to the new branch:
581+
You can give the `-m` flag to the command, which would try a
582+
three-way merge:
582583
583584
------------
584585
$ git checkout -m mytopic
585-
Switched to branch 'mytopic'
586+
Auto-merging frotz
586587
------------
587588
588-
After the switch, the local modifications are reapplied and are _not_
589+
After this three-way merge, the local modifications are _not_
589590
registered in your index file, so `git diff` would show you what
590591
changes you made since the tip of the new branch.
591592
592593
=== 3. Merge conflict
593594
594-
When the `--merge` (`-m`) option is in effect and the locally
595-
modified files overlap with files that need to be updated by the
596-
branch switch, the changes are stashed and reapplied after the
597-
switch. If this process results in conflicts, a stash entry is saved
598-
and made available in `git stash list`:
595+
When a merge conflict happens during switching branches with
596+
the `-m` option, you would see something like this:
599597
600598
------------
601599
$ git checkout -m mytopic
602-
Your local changes are stashed, however, applying it to carry
603-
forward your local changes resulted in conflicts:
600+
Auto-merging frotz
601+
ERROR: Merge conflict in frotz
602+
fatal: merge program failed
603+
------------
604604
605-
- You can try resolving them now. If you resolved them
606-
successfully, discard the stash entry with "git stash drop".
605+
At this point, `git diff` shows the changes cleanly merged as in
606+
the previous example, as well as the changes in the conflicted
607+
files. Edit and resolve the conflict and mark it resolved with
608+
`git add` as usual:
607609
608-
- Alternatively you can "git reset --hard" if you do not want
609-
to deal with them right now, and later "git stash pop" to
610-
recover your local changes.
611610
------------
612-
613-
You can try resolving the conflicts now. Edit the conflicting files
614-
and mark them resolved with `git add` as usual, then run `git stash
615-
drop` to discard the stash entry. Alternatively, you can clear the
616-
working tree with `git reset --hard` and recover your local changes
617-
later with `git stash pop`.
611+
$ edit frotz
612+
$ git add frotz
613+
------------
618614
619615
CONFIGURATION
620616
-------------

Documentation/git-stash.adoc

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ git stash list [<log-options>]
1212
git stash show [-u | --include-untracked | --only-untracked] [<diff-options>] [<stash>]
1313
git stash drop [-q | --quiet] [<stash>]
1414
git stash pop [--index] [-q | --quiet] [<stash>]
15-
git stash apply [--index] [-q | --quiet] [--label-ours=<label>] [--label-theirs=<label>] [--label-base=<label>] [<stash>]
15+
git stash apply [--index] [-q | --quiet] [<stash>]
1616
git stash branch <branchname> [<stash>]
1717
git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
1818
[-u | --include-untracked] [-a | --all] [(-m | --message) <message>]
@@ -195,15 +195,6 @@ the index's ones. However, this can fail, when you have conflicts
195195
(which are stored in the index, where you therefore can no longer
196196
apply the changes as they were originally).
197197
198-
`--label-ours=<label>`::
199-
`--label-theirs=<label>`::
200-
`--label-base=<label>`::
201-
These options are only valid for the `apply` command.
202-
+
203-
Use the given labels in conflict markers instead of the default
204-
"Updated upstream", "Stashed changes", and "Stash base".
205-
`--label-base` only has an effect with merge.conflictStyle=diff3.
206-
207198
`-k`::
208199
`--keep-index`::
209200
`--no-keep-index`::

Documentation/git-switch.adoc

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -123,19 +123,18 @@ variable.
123123

124124
`-m`::
125125
`--merge`::
126-
If you have local modifications to one or more files that
127-
are different between the current branch and the branch to
128-
which you are switching, the command normally refuses to
129-
switch branches in order to preserve your modifications in
130-
context. However, with this option, the conflicting local
131-
changes are automatically stashed before the switch and
132-
reapplied afterwards. If the local changes do not overlap
133-
with the differences between branches, the switch proceeds
134-
without stashing. If reapplying the stash results in
135-
conflicts, the entry is saved to the stash list. Resolve
136-
the conflicts and run `git stash drop` when done, or clear
137-
the working tree (e.g. with `git reset --hard`) before
138-
running `git stash pop` later to re-apply your changes.
126+
If you have local modifications to one or more files that are
127+
different between the current branch and the branch to which
128+
you are switching, the command refuses to switch branches in
129+
order to preserve your modifications in context. However,
130+
with this option, a three-way merge between the current
131+
branch, your working tree contents, and the new branch is
132+
done, and you will be on the new branch.
133+
+
134+
When a merge conflict happens, the index entries for conflicting
135+
paths are left unmerged, and you need to resolve the conflicts
136+
and mark the resolved paths with `git add` (or `git rm` if the merge
137+
should result in deletion of the path).
139138

140139
`--conflict=<style>`::
141140
The same as `--merge` option above, but changes the way the
@@ -218,15 +217,15 @@ $ git switch mytopic
218217
error: You have local changes to 'frotz'; not switching branches.
219218
------------
220219
221-
You can give the `-m` flag to the command, which would carry your local
222-
changes to the new branch:
220+
You can give the `-m` flag to the command, which would try a three-way
221+
merge:
223222
224223
------------
225224
$ git switch -m mytopic
226-
Switched to branch 'mytopic'
225+
Auto-merging frotz
227226
------------
228227
229-
After the switch, the local modifications are reapplied and are _not_
228+
After this three-way merge, the local modifications are _not_
230229
registered in your index file, so `git diff` would show you what
231230
changes you made since the tip of the new branch.
232231

builtin/checkout.c

Lines changed: 80 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "merge-ll.h"
1818
#include "lockfile.h"
1919
#include "mem-pool.h"
20+
#include "merge-ort-wrappers.h"
2021
#include "object-file.h"
2122
#include "object-name.h"
2223
#include "odb.h"
@@ -29,7 +30,6 @@
2930
#include "repo-settings.h"
3031
#include "resolve-undo.h"
3132
#include "revision.h"
32-
#include "sequencer.h"
3333
#include "setup.h"
3434
#include "strvec.h"
3535
#include "submodule.h"
@@ -790,10 +790,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
790790
struct tree *new_tree;
791791

792792
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
793-
if (repo_read_index_preload(the_repository, NULL, 0) < 0) {
794-
rollback_lock_file(&lock_file);
793+
if (repo_read_index_preload(the_repository, NULL, 0) < 0)
795794
return error(_("index file corrupt"));
796-
}
797795

798796
resolve_undo_clear_index(the_repository->index);
799797
if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
@@ -806,18 +804,14 @@ static int merge_working_tree(const struct checkout_opts *opts,
806804
} else {
807805
new_tree = repo_get_commit_tree(the_repository,
808806
new_branch_info->commit);
809-
if (!new_tree) {
810-
rollback_lock_file(&lock_file);
807+
if (!new_tree)
811808
return error(_("unable to read tree (%s)"),
812809
oid_to_hex(&new_branch_info->commit->object.oid));
813-
}
814810
}
815811
if (opts->discard_changes) {
816812
ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info);
817-
if (ret) {
818-
rollback_lock_file(&lock_file);
813+
if (ret)
819814
return ret;
820-
}
821815
} else {
822816
struct tree_desc trees[2];
823817
struct tree *tree;
@@ -827,7 +821,6 @@ static int merge_working_tree(const struct checkout_opts *opts,
827821
refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
828822

829823
if (unmerged_index(the_repository->index)) {
830-
rollback_lock_file(&lock_file);
831824
error(_("you need to resolve your current index first"));
832825
return 1;
833826
}
@@ -860,8 +853,82 @@ static int merge_working_tree(const struct checkout_opts *opts,
860853
ret = unpack_trees(2, trees, &topts);
861854
clear_unpack_trees_porcelain(&topts);
862855
if (ret == -1) {
863-
rollback_lock_file(&lock_file);
864-
return 1;
856+
/*
857+
* Unpack couldn't do a trivial merge; either
858+
* give up or do a real merge, depending on
859+
* whether the merge flag was used.
860+
*/
861+
struct tree *work;
862+
struct tree *old_tree;
863+
struct merge_options o;
864+
struct strbuf sb = STRBUF_INIT;
865+
struct strbuf old_commit_shortname = STRBUF_INIT;
866+
867+
if (!opts->merge)
868+
return 1;
869+
870+
/*
871+
* Without old_branch_info->commit, the below is the same as
872+
* the two-tree unpack we already tried and failed.
873+
*/
874+
if (!old_branch_info->commit)
875+
return 1;
876+
old_tree = repo_get_commit_tree(the_repository,
877+
old_branch_info->commit);
878+
879+
if (repo_index_has_changes(the_repository, old_tree, &sb))
880+
die(_("cannot continue with staged changes in "
881+
"the following files:\n%s"), sb.buf);
882+
strbuf_release(&sb);
883+
884+
/* Do more real merge */
885+
886+
/*
887+
* We update the index fully, then write the
888+
* tree from the index, then merge the new
889+
* branch with the current tree, with the old
890+
* branch as the base. Then we reset the index
891+
* (but not the working tree) to the new
892+
* branch, leaving the working tree as the
893+
* merged version, but skipping unmerged
894+
* entries in the index.
895+
*/
896+
897+
add_files_to_cache(the_repository, NULL, NULL, NULL, 0,
898+
0, 0);
899+
init_ui_merge_options(&o, the_repository);
900+
o.verbosity = 0;
901+
work = write_in_core_index_as_tree(the_repository,
902+
the_repository->index);
903+
904+
ret = reset_tree(new_tree,
905+
opts, 1,
906+
writeout_error, new_branch_info);
907+
if (ret)
908+
return ret;
909+
o.ancestor = old_branch_info->name;
910+
if (!old_branch_info->name) {
911+
strbuf_add_unique_abbrev(&old_commit_shortname,
912+
&old_branch_info->commit->object.oid,
913+
DEFAULT_ABBREV);
914+
o.ancestor = old_commit_shortname.buf;
915+
}
916+
o.branch1 = new_branch_info->name;
917+
o.branch2 = "local";
918+
o.conflict_style = opts->conflict_style;
919+
ret = merge_ort_nonrecursive(&o,
920+
new_tree,
921+
work,
922+
old_tree);
923+
if (ret < 0)
924+
die(NULL);
925+
ret = reset_tree(new_tree,
926+
opts, 0,
927+
writeout_error, new_branch_info);
928+
strbuf_release(&o.obuf);
929+
strbuf_release(&old_commit_shortname);
930+
if (ret)
931+
return ret;
865932
}
866933
}
867934

@@ -1106,9 +1173,6 @@ static int switch_branches(const struct checkout_opts *opts,
11061173
struct object_id rev;
11071174
int flag, writeout_error = 0;
11081175
int do_merge = 1;
1109-
int created_autostash = 0;
1110-
struct strbuf old_commit_shortname = STRBUF_INIT;
1111-
const char *stash_label_base = NULL;
11121176

11131177
trace2_cmd_mode("branch");
11141178

@@ -1146,31 +1210,10 @@ static int switch_branches(const struct checkout_opts *opts,
11461210
do_merge = 0;
11471211
}
11481212

1149-
if (old_branch_info.name)
1150-
stash_label_base = old_branch_info.name;
1151-
else if (old_branch_info.commit) {
1152-
strbuf_add_unique_abbrev(&old_commit_shortname,
1153-
&old_branch_info.commit->object.oid,
1154-
DEFAULT_ABBREV);
1155-
stash_label_base = old_commit_shortname.buf;
1156-
}
1157-
11581213
if (do_merge) {
11591214
ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
1160-
if (ret && opts->merge) {
1161-
create_autostash_ref_silent(the_repository,
1162-
"CHECKOUT_AUTOSTASH_HEAD");
1163-
created_autostash = 1;
1164-
ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
1165-
}
11661215
if (ret) {
1167-
apply_autostash_ref_with_labels(the_repository,
1168-
"CHECKOUT_AUTOSTASH_HEAD",
1169-
new_branch_info->name,
1170-
"local",
1171-
stash_label_base);
11721216
branch_info_release(&old_branch_info);
1173-
strbuf_release(&old_commit_shortname);
11741217
return ret;
11751218
}
11761219
}
@@ -1180,29 +1223,8 @@ static int switch_branches(const struct checkout_opts *opts,
11801223

11811224
update_refs_for_switch(opts, &old_branch_info, new_branch_info);
11821225

1183-
if (opts->conflict_style >= 0) {
1184-
struct strbuf cfg = STRBUF_INIT;
1185-
strbuf_addf(&cfg, "merge.conflictStyle=%s",
1186-
conflict_style_name(opts->conflict_style));
1187-
git_config_push_parameter(cfg.buf);
1188-
strbuf_release(&cfg);
1189-
}
1190-
apply_autostash_ref_with_labels(the_repository, "CHECKOUT_AUTOSTASH_HEAD",
1191-
new_branch_info->name, "local",
1192-
stash_label_base);
1193-
1194-
discard_index(the_repository->index);
1195-
if (repo_read_index(the_repository) < 0)
1196-
die(_("index file corrupt"));
1197-
1198-
if (created_autostash && !opts->discard_changes && !opts->quiet &&
1199-
new_branch_info->commit)
1200-
show_local_changes(&new_branch_info->commit->object,
1201-
&opts->diff_options);
1202-
12031226
ret = post_checkout_hook(old_branch_info.commit, new_branch_info->commit, 1);
12041227
branch_info_release(&old_branch_info);
1205-
strbuf_release(&old_commit_shortname);
12061228

12071229
return ret || writeout_error;
12081230
}

0 commit comments

Comments
 (0)