Harald Nordgren (4):
stash: add --label-ours, --label-theirs, --label-base for apply
sequencer: allow create_autostash to run silently
sequencer: teach autostash apply to take optional conflict marker
labels
checkout: -m (--merge) uses autostash when switching branches Documentation/git-checkout.adoc | 58 ++++-----
Documentation/git-stash.adoc | 11 +-
Documentation/git-switch.adoc | 33 ++---
builtin/checkout.c | 147 ++++++++++------------
builtin/stash.c | 32 +++--
sequencer.c | 87 ++++++++++---
sequencer.h | 8 ++
t/t3420-rebase-autostash.sh | 24 +++-
t/t3903-stash.sh | 29 +++++
t/t7201-co.sh | 208 ++++++++++++++++++++++++++++++++
t/t7600-merge.sh | 2 +-
xdiff-interface.c | 12 ++
xdiff-interface.h | 1 +
xdiff/xmerge.c | 6 +-
14 files changed, 499 insertions(+), 159 deletions(-)
base-commit: 9e8f4e9c04e3efa494e78b710e0c5f6cc77a0a5e
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2234%2FHaraldNordgren%2Fcheckout_autostash-v11
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2234/HaraldNordgren/checkout_autostash-v11
Pull-Request: https://github.com/git/git/pull/2234
Range-diff vs v10:
1: aa519c50be = 1: 9ab5431b47 stash: add --label-ours, --label-theirs, --label-base for apply
2: 8173a4eb72 = 2: e7f8328e3c sequencer: allow create_autostash to run silently
3: f36fedd8c2 = 3: 3242fd3261 sequencer: teach autostash apply to take optional conflict marker labels
4: b6e5546d74 ! 4: 97a5d87c81 checkout: -m (--merge) uses autostash when switching branches
@@ builtin/checkout.c: static int switch_branches(const struct checkout_opts *opts,
int do_merge = 1;
+ int created_autostash = 0;
+ struct strbuf old_commit_shortname = STRBUF_INIT;
++ struct strbuf autostash_msg = STRBUF_INIT;
+ const char *stash_label_base = NULL;
trace2_cmd_mode("branch");
@@ builtin/checkout.c: static int switch_branches(const struct checkout_opts *opts,
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");
++ strbuf_addf(&autostash_msg,
++ "autostash while switching to '%s'",
++ new_branch_info->name);
++ create_autostash_ref_silent_with_msg(the_repository,
++ "CHECKOUT_AUTOSTASH_HEAD",
++ autostash_msg.buf);
+ created_autostash = 1;
+ ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
+ }
@@ builtin/checkout.c: static int switch_branches(const struct checkout_opts *opts,
+ "CHECKOUT_AUTOSTASH_HEAD",
+ new_branch_info->name,
+ "local",
-+ stash_label_base);
++ stash_label_base,
++ autostash_msg.len ? autostash_msg.buf : NULL);
branch_info_release(&old_branch_info);
+ strbuf_release(&old_commit_shortname);
++ strbuf_release(&autostash_msg);
return ret;
}
}
@@ builtin/checkout.c: static int switch_branches(const struct checkout_opts *opts,
+ }
+ apply_autostash_ref_with_labels(the_repository, "CHECKOUT_AUTOSTASH_HEAD",
+ new_branch_info->name, "local",
-+ stash_label_base);
++ stash_label_base,
++ autostash_msg.len ? autostash_msg.buf : NULL);
+
+ discard_index(the_repository->index);
+ if (repo_read_index(the_repository) < 0)
@@ builtin/checkout.c: static int switch_branches(const struct checkout_opts *opts,
ret = post_checkout_hook(old_branch_info.commit, new_branch_info->commit, 1);
branch_info_release(&old_branch_info);
+ strbuf_release(&old_commit_shortname);
++ strbuf_release(&autostash_msg);
return ret || writeout_error;
}
## sequencer.c ##
+@@ sequencer.c: static enum todo_command peek_command(struct todo_list *todo_list, int offset)
+ static void create_autostash_internal(struct repository *r,
+ const char *path,
+ const char *refname,
++ const char *message,
+ bool silent)
+ {
+ struct strbuf buf = STRBUF_INIT;
+@@ sequencer.c: static void create_autostash_internal(struct repository *r,
+ struct object_id oid;
+
+ strvec_pushl(&stash.args,
+- "stash", "create", "autostash", NULL);
++ "stash", "create",
++ message ? message : "autostash", NULL);
+ stash.git_cmd = 1;
+ stash.no_stdin = 1;
+ strbuf_reset(&buf);
+@@ sequencer.c: static void create_autostash_internal(struct repository *r,
+
+ void create_autostash(struct repository *r, const char *path)
+ {
+- create_autostash_internal(r, path, NULL, false);
++ create_autostash_internal(r, path, NULL, NULL, false);
+ }
+
+ void create_autostash_ref(struct repository *r, const char *refname)
+ {
+- create_autostash_internal(r, NULL, refname, false);
++ create_autostash_internal(r, NULL, refname, NULL, false);
+ }
+
+ void create_autostash_ref_silent(struct repository *r, const char *refname)
+ {
+- create_autostash_internal(r, NULL, refname, true);
++ create_autostash_internal(r, NULL, refname, NULL, true);
++}
++
++void create_autostash_ref_silent_with_msg(struct repository *r,
++ const char *refname,
++ const char *message)
++{
++ create_autostash_internal(r, NULL, refname, message, 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)
++ const char *label_base,
++ const char *stash_msg)
+ {
+ struct child_process child = CHILD_PROCESS_INIT;
+ int ret = 0;
@@ sequencer.c: static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply,
+ strvec_push(&store.args, "stash");
+ strvec_push(&store.args, "store");
+ strvec_push(&store.args, "-m");
+- strvec_push(&store.args, "autostash");
++ strvec_push(&store.args, stash_msg ? stash_msg : "autostash");
+ strvec_push(&store.args, "-q");
strvec_push(&store.args, stash_oid);
if (run_command(&store))
ret = error(_("cannot store %s"), stash_oid);
@@ sequencer.c: static int apply_save_autostash_oid(const char *stash_oid, int atte
}
return ret;
+@@ sequencer.c: static int apply_save_autostash(const char *path, int attempt_apply)
+ strbuf_trim(&stash_oid);
+
+ ret = apply_save_autostash_oid(stash_oid.buf, attempt_apply,
+- NULL, NULL, NULL);
++ NULL, NULL, NULL, NULL);
+
+ unlink(path);
+ strbuf_release(&stash_oid);
+@@ sequencer.c: int apply_autostash(const char *path)
+
+ 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, NULL, NULL, NULL, NULL);
+ }
+
+ 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)
++ const char *label_base,
++ const char *stash_msg)
+ {
+ struct object_id stash_oid;
+ char stash_oid_hex[GIT_MAX_HEXSZ + 1];
+@@ sequencer.c: static int apply_save_autostash_ref(struct repository *r, const char *refname,
+
+ 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);
++ label_ours, label_theirs, label_base,
++ stash_msg);
+
+ refs_delete_ref(get_main_ref_store(r), "", refname,
+ &stash_oid, REF_NO_DEREF);
+@@ sequencer.c: static int apply_save_autostash_ref(struct repository *r, const char *refname,
+
+ 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,
++ NULL, NULL, NULL, NULL);
+ }
+
+ int apply_autostash_ref(struct repository *r, const char *refname)
+ {
+- return apply_save_autostash_ref(r, refname, 1, NULL, NULL, NULL);
++ return apply_save_autostash_ref(r, refname, 1,
++ NULL, 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)
++ const char *label_base,
++ const char *stash_msg)
+ {
+ return apply_save_autostash_ref(r, refname, 1,
+- label_ours, label_theirs, label_base);
++ label_ours, label_theirs, label_base,
++ stash_msg);
+ }
+
+ static int checkout_onto(struct repository *r, struct replay_opts *opts,
+
+ ## sequencer.h ##
+@@ sequencer.h: void commit_post_rewrite(struct repository *r,
+ 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);
++void create_autostash_ref_silent_with_msg(struct repository *r,
++ const char *refname,
++ const char *message);
+ int save_autostash(const char *path);
+ int save_autostash_ref(struct repository *r, const char *refname);
+ int apply_autostash(const char *path);
+@@ sequencer.h: 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);
++ const char *label_base,
++ const char *stash_msg);
+
+ #define SUMMARY_INITIAL_COMMIT (1 << 0)
+ #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
## t/t3420-rebase-autostash.sh ##
@@ t/t3420-rebase-autostash.sh: create_expected_failure_apply () {
@@ t/t7201-co.sh: test_expect_success 'checkout --merge --conflict=diff3 <branch>'
+ test_cmp expect one
+'
+
++test_expect_success 'checkout -m autostash message includes target branch' '
++ git checkout -f main &&
++ git clean -f &&
++
++ fill 1 2 3 4 5 >one &&
++ git checkout -m side >actual 2>&1 &&
++ git stash list >stash-list &&
++ test_grep "autostash while switching to .side." stash-list &&
++ git stash drop &&
++ git checkout -f main &&
++ git reset --hard
++'
++
+test_expect_success 'checkout -m stashes on staged conflicting changes' '
+ git checkout -f main &&
+ git clean -f &&