are different between the current branch and the branch to
which you are switching, the command refuses to switch
branches in order to preserve your modifications in context.
- With this option, the conflicting local changes are
- automatically stashed before the switch and reapplied
- afterwards. If the local changes do not overlap with the
- differences between branches, the switch proceeds without
- stashing. If reapplying the stash results in conflicts, the
- entry is saved to the stash list. Resolve the conflicts
- and run `git stash drop` when done, or clear the working
- tree (e.g. with `git reset --hard`) before running `git stash
- pop` later to re-apply your changes.
+ However, with this option, a three-way merge between the current
+ branch, your working tree contents, and the new branch
+ is done, and you will be on the new branch.
++
+When a merge conflict happens, the index entries for conflicting
+paths are left unmerged, and you need to resolve the conflicts
+and mark the resolved paths with `git add` (or `git rm` if the merge
+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. This option cannot be
used when checking out paths from a tree-ish.
++
+When switching branches with `--merge`, staged changes may be lost.
`--conflict=<style>`::
The same as `--merge` option above, but changes the way the
error: You have local changes to 'frotz'; not switching branches.
------------
-You can give the `-m` flag to the command, which would carry your local
-changes to the new branch:
+You can give the `-m` flag to the command, which would try a
+three-way merge:
------------
$ git checkout -m mytopic
-Switched to branch 'mytopic'
+Auto-merging frotz
------------
-After the switch, the local modifications are reapplied and are _not_
+After this three-way merge, the local modifications are _not_
registered in your index file, so `git diff` would show you what
changes you made since the tip of the new branch.
=== 3. Merge conflict
-When the `--merge` (`-m`) option is in effect and the locally
-modified files overlap with files that need to be updated by the
-branch switch, the changes are stashed and reapplied after the
-switch. If this process results in conflicts, a stash entry is saved
-and made available in `git stash list`:
+When a merge conflict happens during switching branches with
+the `-m` option, you would see something like this:
------------
$ git checkout -m mytopic
-Your local changes are stashed, however, applying it to carry
-forward your local changes resulted in conflicts:
+Auto-merging frotz
+ERROR: Merge conflict in frotz
+fatal: merge program failed
+------------
- - You can try resolving them now. If you resolved them
- successfully, discard the stash entry with "git stash drop".
+At this point, `git diff` shows the changes cleanly merged as in
+the previous example, as well as the changes in the conflicted
+files. Edit and resolve the conflict and mark it resolved with
+`git add` as usual:
- - Alternatively you can "git reset --hard" if you do not want
- to deal with them right now, and later "git stash pop" to
- recover your local changes.
------------
-
-You can try resolving the conflicts now. Edit the conflicting files
-and mark them resolved with `git add` as usual, then run `git stash
-drop` to discard the stash entry. Alternatively, you can clear the
-working tree with `git reset --hard` and recover your local changes
-later with `git stash pop`.
+$ edit frotz
+$ git add frotz
+------------
CONFIGURATION
-------------
git stash show [-u | --include-untracked | --only-untracked] [<diff-options>] [<stash>]
git stash drop [-q | --quiet] [<stash>]
git stash pop [--index] [-q | --quiet] [<stash>]
-git stash apply [--index] [-q | --quiet] [--label-ours=<label>] [--label-theirs=<label>] [--label-base=<label>] [<stash>]
+git stash apply [--index] [-q | --quiet] [<stash>]
git stash branch <branchname> [<stash>]
git stash [push] [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
[-u | --include-untracked] [-a | --all] [(-m | --message) <message>]
(which are stored in the index, where you therefore can no longer
apply the changes as they were originally).
-`--label-ours=<label>`::
-`--label-theirs=<label>`::
-`--label-base=<label>`::
- These options are only valid for the `apply` command.
-+
-Use the given labels in conflict markers instead of the default
-"Updated upstream", "Stashed changes", and "Stash base".
-`--label-base` only has an effect with merge.conflictStyle=diff3.
-
`-k`::
`--keep-index`::
`--no-keep-index`::
`-m`::
`--merge`::
- If you have local modifications to one or more files that
- are different between the current branch and the branch to
- which you are switching, the command normally refuses to
- switch branches in order to preserve your modifications in
- context. However, with this option, the conflicting local
- changes are automatically stashed before the switch and
- reapplied afterwards. If the local changes do not overlap
- with the differences between branches, the switch proceeds
- without stashing. If reapplying the stash results in
- conflicts, the entry is saved to the stash list. Resolve
- the conflicts and run `git stash drop` when done, or clear
- the working tree (e.g. with `git reset --hard`) before
- running `git stash pop` later to re-apply your changes.
+ If you have local modifications to one or more files that are
+ different between the current branch and the branch to which
+ you are switching, the command refuses to switch branches in
+ order to preserve your modifications in context. However,
+ with this option, a three-way merge between the current
+ branch, your working tree contents, and the new branch is
+ done, and you will be on the new branch.
++
+When a merge conflict happens, the index entries for conflicting
+paths are left unmerged, and you need to resolve the conflicts
+and mark the resolved paths with `git add` (or `git rm` if the merge
+should result in deletion of the path).
`--conflict=<style>`::
The same as `--merge` option above, but changes the way the
error: You have local changes to 'frotz'; not switching branches.
------------
-You can give the `-m` flag to the command, which would carry your local
-changes to the new branch:
+You can give the `-m` flag to the command, which would try a three-way
+merge:
------------
$ git switch -m mytopic
-Switched to branch 'mytopic'
+Auto-merging frotz
------------
-After the switch, the local modifications are reapplied and are _not_
+After this three-way merge, the local modifications are _not_
registered in your index file, so `git diff` would show you what
changes you made since the tip of the new branch.
#include "merge-ll.h"
#include "lockfile.h"
#include "mem-pool.h"
+#include "merge-ort-wrappers.h"
#include "object-file.h"
#include "object-name.h"
#include "odb.h"
#include "repo-settings.h"
#include "resolve-undo.h"
#include "revision.h"
-#include "sequencer.h"
#include "setup.h"
#include "strvec.h"
#include "submodule.h"
struct tree *new_tree;
repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR);
- if (repo_read_index_preload(the_repository, NULL, 0) < 0) {
- rollback_lock_file(&lock_file);
+ if (repo_read_index_preload(the_repository, NULL, 0) < 0)
return error(_("index file corrupt"));
- }
resolve_undo_clear_index(the_repository->index);
if (opts->new_orphan_branch && opts->orphan_from_empty_tree) {
} else {
new_tree = repo_get_commit_tree(the_repository,
new_branch_info->commit);
- if (!new_tree) {
- rollback_lock_file(&lock_file);
+ if (!new_tree)
return error(_("unable to read tree (%s)"),
oid_to_hex(&new_branch_info->commit->object.oid));
- }
}
if (opts->discard_changes) {
ret = reset_tree(new_tree, opts, 1, writeout_error, new_branch_info);
- if (ret) {
- rollback_lock_file(&lock_file);
+ if (ret)
return ret;
- }
} else {
struct tree_desc trees[2];
struct tree *tree;
refresh_index(the_repository->index, REFRESH_QUIET, NULL, NULL, NULL);
if (unmerged_index(the_repository->index)) {
- rollback_lock_file(&lock_file);
error(_("you need to resolve your current index first"));
return 1;
}
ret = unpack_trees(2, trees, &topts);
clear_unpack_trees_porcelain(&topts);
if (ret == -1) {
- rollback_lock_file(&lock_file);
- return 1;
+ /*
+ * Unpack couldn't do a trivial merge; either
+ * give up or do a real merge, depending on
+ * whether the merge flag was used.
+ */
+ struct tree *work;
+ struct tree *old_tree;
+ struct merge_options o;
+ struct strbuf sb = STRBUF_INIT;
+ struct strbuf old_commit_shortname = STRBUF_INIT;
+
+ if (!opts->merge)
+ return 1;
+
+ /*
+ * Without old_branch_info->commit, the below is the same as
+ * the two-tree unpack we already tried and failed.
+ */
+ if (!old_branch_info->commit)
+ return 1;
+ old_tree = repo_get_commit_tree(the_repository,
+ old_branch_info->commit);
+
+ if (repo_index_has_changes(the_repository, old_tree, &sb))
+ die(_("cannot continue with staged changes in "
+ "the following files:\n%s"), sb.buf);
+ strbuf_release(&sb);
+
+ /* Do more real merge */
+
+ /*
+ * We update the index fully, then write the
+ * tree from the index, then merge the new
+ * branch with the current tree, with the old
+ * branch as the base. Then we reset the index
+ * (but not the working tree) to the new
+ * branch, leaving the working tree as the
+ * merged version, but skipping unmerged
+ * entries in the index.
+ */
+
+ add_files_to_cache(the_repository, NULL, NULL, NULL, 0,
+ 0, 0);
+ init_ui_merge_options(&o, the_repository);
+ o.verbosity = 0;
+ work = write_in_core_index_as_tree(the_repository,
+ the_repository->index);
+
+ ret = reset_tree(new_tree,
+ opts, 1,
+ writeout_error, new_branch_info);
+ if (ret)
+ return ret;
+ o.ancestor = old_branch_info->name;
+ if (!old_branch_info->name) {
+ strbuf_add_unique_abbrev(&old_commit_shortname,
+ &old_branch_info->commit->object.oid,
+ DEFAULT_ABBREV);
+ o.ancestor = old_commit_shortname.buf;
+ }
+ o.branch1 = new_branch_info->name;
+ o.branch2 = "local";
+ o.conflict_style = opts->conflict_style;
+ ret = merge_ort_nonrecursive(&o,
+ new_tree,
+ work,
+ old_tree);
+ if (ret < 0)
+ die(NULL);
+ ret = reset_tree(new_tree,
+ opts, 0,
+ writeout_error, new_branch_info);
+ strbuf_release(&o.obuf);
+ strbuf_release(&old_commit_shortname);
+ if (ret)
+ return ret;
}
}
struct object_id rev;
int flag, writeout_error = 0;
int do_merge = 1;
- int created_autostash = 0;
- struct strbuf old_commit_shortname = STRBUF_INIT;
- const char *stash_label_base = NULL;
trace2_cmd_mode("branch");
do_merge = 0;
}
- if (old_branch_info.name)
- stash_label_base = old_branch_info.name;
- else if (old_branch_info.commit) {
- strbuf_add_unique_abbrev(&old_commit_shortname,
- &old_branch_info.commit->object.oid,
- DEFAULT_ABBREV);
- stash_label_base = old_commit_shortname.buf;
- }
-
if (do_merge) {
ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
- if (ret && opts->merge) {
- create_autostash_ref_silent(the_repository,
- "CHECKOUT_AUTOSTASH_HEAD");
- created_autostash = 1;
- ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
- }
if (ret) {
- apply_autostash_ref_with_labels(the_repository,
- "CHECKOUT_AUTOSTASH_HEAD",
- new_branch_info->name,
- "local",
- stash_label_base);
branch_info_release(&old_branch_info);
- strbuf_release(&old_commit_shortname);
return ret;
}
}
update_refs_for_switch(opts, &old_branch_info, new_branch_info);
- if (opts->conflict_style >= 0) {
- struct strbuf cfg = STRBUF_INIT;
- strbuf_addf(&cfg, "merge.conflictStyle=%s",
- conflict_style_name(opts->conflict_style));
- git_config_push_parameter(cfg.buf);
- strbuf_release(&cfg);
- }
- apply_autostash_ref_with_labels(the_repository, "CHECKOUT_AUTOSTASH_HEAD",
- new_branch_info->name, "local",
- stash_label_base);
-
- discard_index(the_repository->index);
- if (repo_read_index(the_repository) < 0)
- die(_("index file corrupt"));
-
- if (created_autostash && !opts->discard_changes && !opts->quiet &&
- new_branch_info->commit)
- show_local_changes(&new_branch_info->commit->object,
- &opts->diff_options);
-
ret = post_checkout_hook(old_branch_info.commit, new_branch_info->commit, 1);
branch_info_release(&old_branch_info);
- strbuf_release(&old_commit_shortname);
return ret || writeout_error;
}
#define BUILTIN_STASH_POP_USAGE \
N_("git stash pop [--index] [-q | --quiet] [<stash>]")
#define BUILTIN_STASH_APPLY_USAGE \
- N_("git stash apply [--index] [-q | --quiet] [--label-ours=<label>] [--label-theirs=<label>] [--label-base=<label>] [<stash>]")
+ N_("git stash apply [--index] [-q | --quiet] [<stash>]")
#define BUILTIN_STASH_BRANCH_USAGE \
N_("git stash branch <branchname> [<stash>]")
#define BUILTIN_STASH_STORE_USAGE \
die(_("could not write index"));
}
-static int do_apply_stash_with_labels(const char *prefix,
- struct stash_info *info,
- int index, int quiet,
- const char *label_ours, const char *label_theirs,
- const char *label_base)
+static int do_apply_stash(const char *prefix, struct stash_info *info,
+ int index, int quiet)
{
int clean, ret;
int has_index = index;
init_ui_merge_options(&o, the_repository);
- o.branch1 = label_ours ? label_ours : "Updated upstream";
- o.branch2 = label_theirs ? label_theirs : "Stashed changes";
- o.ancestor = label_base ? label_base : "Stash base";
+ o.branch1 = "Updated upstream";
+ o.branch2 = "Stashed changes";
+ o.ancestor = "Stash base";
if (oideq(&info->b_tree, &c_tree))
o.branch1 = "Version stash was based on";
return ret;
}
-static int do_apply_stash(const char *prefix, struct stash_info *info,
- int index, int quiet)
-{
- return do_apply_stash_with_labels(prefix, info, index, quiet,
- NULL, NULL, NULL);
-}
-
static int apply_stash(int argc, const char **argv, const char *prefix,
struct repository *repo UNUSED)
{
int ret = -1;
int quiet = 0;
int index = use_index;
- const char *label_ours = NULL, *label_theirs = NULL, *label_base = NULL;
struct stash_info info = STASH_INFO_INIT;
struct option options[] = {
OPT__QUIET(&quiet, N_("be quiet, only report errors")),
OPT_BOOL(0, "index", &index,
N_("attempt to recreate the index")),
- OPT_STRING(0, "label-ours", &label_ours, N_("label"),
- N_("label for the upstream side in conflict markers")),
- OPT_STRING(0, "label-theirs", &label_theirs, N_("label"),
- N_("label for the stashed side in conflict markers")),
- OPT_STRING(0, "label-base", &label_base, N_("label"),
- N_("label for the base in diff3 conflict markers")),
OPT_END()
};
if (get_stash_info(&info, argc, argv))
goto cleanup;
- ret = do_apply_stash_with_labels(prefix, &info, index, quiet,
- label_ours, label_theirs, label_base);
+ ret = do_apply_stash(prefix, &info, index, quiet);
cleanup:
free_stash_info(&info);
return ret;
static void create_autostash_internal(struct repository *r,
const char *path,
- const char *refname,
- bool silent)
+ const char *refname)
{
struct strbuf buf = STRBUF_INIT;
struct lock_file lock_file = LOCK_INIT;
&oid, null_oid(the_hash_algo), 0, UPDATE_REFS_DIE_ON_ERR);
}
- if (!silent)
- printf(_("Created autostash: %s\n"), buf.buf);
+ printf(_("Created autostash: %s\n"), buf.buf);
if (reset_head(r, &ropts) < 0)
die(_("could not reset --hard"));
discard_index(r->index);
void create_autostash(struct repository *r, const char *path)
{
- create_autostash_internal(r, path, NULL, false);
+ create_autostash_internal(r, path, NULL);
}
void create_autostash_ref(struct repository *r, const char *refname)
{
- create_autostash_internal(r, NULL, refname, false);
+ create_autostash_internal(r, NULL, refname);
}
-void create_autostash_ref_silent(struct repository *r, const char *refname)
-{
- create_autostash_internal(r, NULL, refname, true);
-}
-
-static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply,
- const char *label_ours, const char *label_theirs,
- const char *label_base)
+static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply)
{
struct child_process child = CHILD_PROCESS_INIT;
int ret = 0;
child.no_stderr = 1;
strvec_push(&child.args, "stash");
strvec_push(&child.args, "apply");
- if (label_ours)
- strvec_pushf(&child.args, "--label-ours=%s", label_ours);
- if (label_theirs)
- strvec_pushf(&child.args, "--label-theirs=%s", label_theirs);
- if (label_base)
- strvec_pushf(&child.args, "--label-base=%s", label_base);
strvec_push(&child.args, stash_oid);
ret = run_command(&child);
}
strvec_push(&store.args, stash_oid);
if (run_command(&store))
ret = error(_("cannot store %s"), stash_oid);
- else if (attempt_apply)
- fprintf(stderr,
- _("Your local changes are stashed, however, applying it to carry\n"
- "forward your local changes resulted in conflicts:\n"
- "\n"
- " - You can try resolving them now. If you resolved them\n"
- " successfully, discard the stash entry with \"git stash drop\".\n"
- "\n"
- " - Alternatively you can \"git reset --hard\" if you do not want\n"
- " to deal with them right now, and later \"git stash pop\" to\n"
- " recover your local changes.\n"));
else
fprintf(stderr,
- _("Autostash exists; creating a new stash entry.\n"
+ _("%s\n"
"Your changes are safe in the stash.\n"
"You can run \"git stash pop\" or"
- " \"git stash drop\" at any time.\n"));
+ " \"git stash drop\" at any time.\n"),
+ attempt_apply ?
+ _("Applying autostash resulted in conflicts.") :
+ _("Autostash exists; creating a new stash entry."));
}
return ret;
}
strbuf_trim(&stash_oid);
- ret = apply_save_autostash_oid(stash_oid.buf, attempt_apply,
- NULL, NULL, NULL);
+ ret = apply_save_autostash_oid(stash_oid.buf, attempt_apply);
unlink(path);
strbuf_release(&stash_oid);
int apply_autostash_oid(const char *stash_oid)
{
- return apply_save_autostash_oid(stash_oid, 1, NULL, NULL, NULL);
+ return apply_save_autostash_oid(stash_oid, 1);
}
static int apply_save_autostash_ref(struct repository *r, const char *refname,
- int attempt_apply,
- const char *label_ours, const char *label_theirs,
- const char *label_base)
+ int attempt_apply)
{
struct object_id stash_oid;
char stash_oid_hex[GIT_MAX_HEXSZ + 1];
return error(_("autostash reference is a symref"));
oid_to_hex_r(stash_oid_hex, &stash_oid);
- ret = apply_save_autostash_oid(stash_oid_hex, attempt_apply,
- label_ours, label_theirs, label_base);
+ ret = apply_save_autostash_oid(stash_oid_hex, attempt_apply);
refs_delete_ref(get_main_ref_store(r), "", refname,
&stash_oid, REF_NO_DEREF);
int save_autostash_ref(struct repository *r, const char *refname)
{
- return apply_save_autostash_ref(r, refname, 0, NULL, NULL, NULL);
+ return apply_save_autostash_ref(r, refname, 0);
}
int apply_autostash_ref(struct repository *r, const char *refname)
{
- return apply_save_autostash_ref(r, refname, 1, NULL, NULL, NULL);
-}
-
-int apply_autostash_ref_with_labels(struct repository *r, const char *refname,
- const char *label_ours, const char *label_theirs,
- const char *label_base)
-{
- return apply_save_autostash_ref(r, refname, 1,
- label_ours, label_theirs, label_base);
+ return apply_save_autostash_ref(r, refname, 1);
}
static int checkout_onto(struct repository *r, struct replay_opts *opts,
void create_autostash(struct repository *r, const char *path);
void create_autostash_ref(struct repository *r, const char *refname);
-void create_autostash_ref_silent(struct repository *r, const char *refname);
int save_autostash(const char *path);
int save_autostash_ref(struct repository *r, const char *refname);
int apply_autostash(const char *path);
int apply_autostash_oid(const char *stash_oid);
int apply_autostash_ref(struct repository *r, const char *refname);
-int apply_autostash_ref_with_labels(struct repository *r, const char *refname,
- const char *label_ours, const char *label_theirs,
- const char *label_base);
#define SUMMARY_INITIAL_COMMIT (1 << 0)
#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
First, rewinding head to replay your work on top of it...
Applying: second commit
Applying: third commit
- Your local changes are stashed, however, applying it to carry
- forward your local changes resulted in conflicts:
-
- - You can try resolving them now. If you resolved them
- successfully, discard the stash entry with "git stash drop".
-
- - Alternatively you can "git reset --hard" if you do not want
- to deal with them right now, and later "git stash pop" to
- recover your local changes.
+ Applying autostash resulted in conflicts.
+ Your changes are safe in the stash.
+ You can run "git stash pop" or "git stash drop" at any time.
EOF
}
create_expected_failure_merge () {
cat >expected <<-EOF
$(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
- Your local changes are stashed, however, applying it to carry
- forward your local changes resulted in conflicts:
-
- - You can try resolving them now. If you resolved them
- successfully, discard the stash entry with "git stash drop".
-
- - Alternatively you can "git reset --hard" if you do not want
- to deal with them right now, and later "git stash pop" to
- recover your local changes.
+ Applying autostash resulted in conflicts.
+ Your changes are safe in the stash.
+ You can run "git stash pop" or "git stash drop" at any time.
Successfully rebased and updated refs/heads/rebased-feature-branch.
EOF
}
)
'
-test_expect_success 'apply with custom conflict labels' '
- git init conflict_labels &&
- (
- cd conflict_labels &&
- test_commit base file &&
- echo stashed >file &&
- git stash push -m "stashed" &&
- test_commit upstream file &&
- test_must_fail git -c merge.conflictStyle=diff3 stash apply --label-ours=UP --label-theirs=STASH &&
- test_grep "^<<<<<<< UP" file &&
- test_grep "^||||||| Stash base" file &&
- test_grep "^>>>>>>> STASH" file
- )
-'
-
-test_expect_success 'apply with empty conflict labels' '
- git init empty_labels &&
- (
- cd empty_labels &&
- test_commit base file &&
- echo stashed >file &&
- git stash push -m "stashed" &&
- test_commit upstream file &&
- test_must_fail git stash apply --label-ours= --label-theirs= &&
- test_grep "^<<<<<<<$" file &&
- test_grep "^>>>>>>>$" file
- )
-'
-
test_expect_success 'stash create reports a locked index' '
test_when_finished "rm -rf repo" &&
git init repo &&
test_cmp expect two
'
-test_expect_success 'checkout --merge --conflict=zdiff3 <branch>' '
- git checkout -f main &&
- git reset --hard &&
- git clean -f &&
-
- fill a b X d e >two &&
- git checkout --merge --conflict=zdiff3 simple &&
-
- cat <<-EOF >expect &&
- a
- <<<<<<< simple
- c
- ||||||| main
- b
- c
- d
- =======
- b
- X
- d
- >>>>>>> local
- e
- EOF
- test_cmp expect two
-'
-
-test_expect_success 'checkout -m respects merge.conflictStyle config' '
- git checkout -f main &&
- git reset --hard &&
- git clean -f &&
-
- test_config merge.conflictStyle diff3 &&
- fill b d >two &&
- git checkout -m simple &&
-
- cat <<-EOF >expect &&
- <<<<<<< simple
- a
- c
- e
- ||||||| main
- a
- b
- c
- d
- e
- =======
- b
- d
- >>>>>>> local
- EOF
- test_cmp expect two
-'
-
-test_expect_success 'checkout -m skips stash when no conflict' '
- git checkout -f main &&
- git clean -f &&
-
- fill 0 x y z >same &&
- git stash list >stash-before &&
- git checkout -m side >actual 2>&1 &&
- test_grep ! "Created autostash" actual &&
- git stash list >stash-after &&
- test_cmp stash-before stash-after &&
- fill 0 x y z >expect &&
- test_cmp expect same
-'
-
-test_expect_success 'checkout -m skips stash with non-conflicting dirty index' '
- git checkout -f main &&
- git clean -f &&
-
- fill 0 x y z >same &&
- git add same &&
- git checkout -m side >actual 2>&1 &&
- test_grep ! "Created autostash" actual &&
- fill 0 x y z >expect &&
- test_cmp expect same
-'
-
-test_expect_success 'checkout -m stashes and applies on conflicting changes' '
- git checkout -f main &&
- git clean -f &&
-
- fill 1 2 3 4 5 6 7 >one &&
- git checkout -m side >actual 2>&1 &&
- test_grep ! "Created autostash" actual &&
- test_grep "Applied autostash" actual &&
- fill 1 2 3 4 5 6 7 >expect &&
- test_cmp expect one
-'
-
-test_expect_success 'checkout -m with mixed staged and unstaged changes' '
- git checkout -f main &&
- git clean -f &&
-
- fill 0 x y z >same &&
- git add same &&
- fill 1 2 3 4 5 6 7 >one &&
- git checkout -m side >actual 2>&1 &&
- test_grep ! "Created autostash" actual &&
- test_grep "Applied autostash" actual &&
- fill 0 x y z >expect &&
- test_cmp expect same &&
- fill 1 2 3 4 5 6 7 >expect &&
- test_cmp expect one
-'
-
-test_expect_success 'checkout -m stashes on truly conflicting changes' '
- git checkout -f main &&
- git clean -f &&
-
- fill 1 2 3 4 5 >one &&
- test_must_fail git checkout side 2>stderr &&
- test_grep "Your local changes" stderr &&
- git checkout -m side >actual 2>&1 &&
- test_grep ! "Created autostash" actual &&
- test_grep "resulted in conflicts" actual &&
- test_grep "git stash drop" actual &&
- git stash drop &&
- git reset --hard
-'
-
-test_expect_success 'checkout -m produces usable stash on conflict' '
- git checkout -f main &&
- git clean -f &&
-
- fill 1 2 3 4 5 >one &&
- git checkout -m side >actual 2>&1 &&
- test_grep "recover your local changes" actual &&
- git checkout -f main &&
- git stash pop &&
- fill 1 2 3 4 5 >expect &&
- test_cmp expect one
-'
-
-test_expect_success 'checkout -m stashes on staged conflicting changes' '
- git checkout -f main &&
- git clean -f &&
-
- fill 1 2 3 4 5 >one &&
- git add one &&
- git checkout -m side >actual 2>&1 &&
- test_grep ! "Created autostash" actual &&
- test_grep "resulted in conflicts" actual &&
- test_grep "git stash drop" actual &&
- git stash drop &&
- git reset --hard
-'
-
-test_expect_success 'checkout -m applies stash cleanly with non-overlapping changes in same file' '
- git checkout -f main &&
- git reset --hard &&
- git clean -f &&
-
- git checkout -b nonoverlap_base &&
- fill a b c d >file &&
- git add file &&
- git commit -m "add file" &&
-
- git checkout -b nonoverlap_child &&
- fill a b c INSERTED d >file &&
- git commit -a -m "insert line near end of file" &&
-
- fill DIRTY a b c INSERTED d >file &&
-
- git stash list >stash-before &&
- git checkout -m nonoverlap_base 2>stderr &&
- test_grep "Applied autostash" stderr &&
- test_grep ! "resulted in conflicts" stderr &&
-
- git stash list >stash-after &&
- test_cmp stash-before stash-after &&
-
- fill DIRTY a b c d >expect &&
- test_cmp expect file &&
-
- git checkout -f main &&
- git branch -D nonoverlap_base &&
- git branch -D nonoverlap_child
-'
-
-test_expect_success 'checkout -m -b skips stash with dirty tree' '
- git checkout -f main &&
- git clean -f &&
-
- fill 0 x y z >same &&
- git checkout -m -b newbranch >actual 2>&1 &&
- test_grep ! "Created autostash" actual &&
- fill 0 x y z >expect &&
- test_cmp expect same &&
- git checkout main &&
- git branch -D newbranch
-'
-
test_expect_success 'switch to another branch while carrying a deletion' '
git checkout -f main &&
git reset --hard &&
git diff >expect &&
test_when_finished "test_might_fail git stash drop" &&
git merge --autostash c3 2>err &&
- test_grep "your local changes resulted in conflicts" err &&
+ test_grep "Applying autostash resulted in conflicts." err &&
git show HEAD:file >merge-result &&
test_cmp result.1-9 merge-result &&
git stash show -p >actual &&
return -1;
}
-const char *conflict_style_name(int style)
-{
- switch (style) {
- case XDL_MERGE_DIFF3:
- return "diff3";
- case XDL_MERGE_ZEALOUS_DIFF3:
- return "zdiff3";
- default:
- return "merge";
- }
-}
-
int git_xmerge_style = -1;
int git_xmerge_config(const char *var, const char *value,
void xdiff_clear_find_func(xdemitconf_t *xecfg);
struct config_context;
int parse_conflict_style_name(const char *value);
-const char *conflict_style_name(int style);
int git_xmerge_config(const char *var, const char *value,
const struct config_context *ctx, void *cb);
extern int git_xmerge_style;
int size, int i, int style,
xdmerge_t *m, char *dest, int marker_size)
{
- int marker1_size = (name1 && *name1 ? strlen(name1) + 1 : 0);
- int marker2_size = (name2 && *name2 ? strlen(name2) + 1 : 0);
- int marker3_size = (name3 && *name3 ? strlen(name3) + 1 : 0);
+ int marker1_size = (name1 ? strlen(name1) + 1 : 0);
+ int marker2_size = (name2 ? strlen(name2) + 1 : 0);
+ int marker3_size = (name3 ? strlen(name3) + 1 : 0);
int needs_cr = is_cr_needed(xe1, xe2, m);
if (marker_size <= 0)