Merge branch 'hn/checkout-m-autostash-refine' into jch

The autostash fallback in 'git checkout -m' has been refined to only
retry when there are local changes.  Additionally, a blank line now
visually separates autostash conflict advice from the subsequent
branch-switch message.

* hn/checkout-m-autostash-refine:
  checkout: print blank line after autostash conflict advice
jch
Junio C Hamano 2026-08-31 11:12:00 -07:00
commit a9463c05e2
3 changed files with 23 additions and 11 deletions

View File

@ -1166,6 +1166,7 @@ static int switch_branches(const struct checkout_opts *opts,
int flag, writeout_error = 0; int flag, writeout_error = 0;
int do_merge = 1; int do_merge = 1;
int created_autostash = 0; int created_autostash = 0;
int autostash_res = 0;
struct strbuf old_commit_shortname = STRBUF_INIT; struct strbuf old_commit_shortname = STRBUF_INIT;
struct strbuf autostash_msg = STRBUF_INIT; struct strbuf autostash_msg = STRBUF_INIT;
const char *stash_label_base = NULL; const char *stash_label_base = NULL;
@ -1237,12 +1238,12 @@ static int switch_branches(const struct checkout_opts *opts,
git_config_push_parameter(cfg.buf); git_config_push_parameter(cfg.buf);
strbuf_release(&cfg); strbuf_release(&cfg);
} }
apply_autostash_ref(the_repository, autostash_res = apply_autostash_ref(the_repository,
"CHECKOUT_AUTOSTASH_HEAD", "CHECKOUT_AUTOSTASH_HEAD",
new_branch_info->name, new_branch_info->name,
"local", "local",
stash_label_base, stash_label_base,
autostash_msg.buf); autostash_msg.buf);
} }
if (ret) { if (ret) {
branch_info_release(&old_branch_info); branch_info_release(&old_branch_info);
@ -1255,6 +1256,8 @@ static int switch_branches(const struct checkout_opts *opts,
if (!opts->quiet && !old_branch_info.path && old_branch_info.commit && new_branch_info->commit != old_branch_info.commit) if (!opts->quiet && !old_branch_info.path && old_branch_info.commit && new_branch_info->commit != old_branch_info.commit)
orphaned_commit_warning(old_branch_info.commit, new_branch_info->commit); orphaned_commit_warning(old_branch_info.commit, new_branch_info->commit);


if (autostash_res == 1 && !opts->quiet)
fputc('\n', stderr);
update_refs_for_switch(opts, &old_branch_info, new_branch_info); update_refs_for_switch(opts, &old_branch_info, new_branch_info);


if (created_autostash) { if (created_autostash) {

View File

@ -4815,7 +4815,8 @@ static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply,
if (label_base) if (label_base)
strvec_pushf(&child.args, "--label-base=%s", label_base); strvec_pushf(&child.args, "--label-base=%s", label_base);
strvec_push(&child.args, stash_oid); strvec_push(&child.args, stash_oid);
ret = run_command(&child); if (run_command(&child))
ret = 1;
} }


if (attempt_apply && !ret) if (attempt_apply && !ret)

View File

@ -236,10 +236,18 @@ test_expect_success 'checkout -m creates a recoverable stash on conflict' '
test_must_fail git checkout side 2>stderr && test_must_fail git checkout side 2>stderr &&
test_grep "Your local changes" stderr && test_grep "Your local changes" stderr &&
git checkout -m side >actual 2>&1 && git checkout -m side >actual 2>&1 &&
test_grep "resulted in conflicts" actual && cat >expect <<-EOF &&
test_grep "git stash drop" actual && Your local changes are stashed, however applying them
test_grep "git stash pop" actual && resulted in conflicts. You can either resolve the conflicts
test_grep "The following paths have local changes" actual && and then discard the stash with "git stash drop", or, if you
do not want to resolve them now, run "git reset --hard" and
apply the local changes later by running "git stash pop".

Switched to branch ${SQ}side${SQ}
The following paths have local changes:
M one
EOF
test_cmp expect actual &&
git log -p -1 --format="%gs%n%B" -g --diff-merges=1 refs/stash >actual && git log -p -1 --format="%gs%n%B" -g --diff-merges=1 refs/stash >actual &&
sed /^index/d actual >actual.trimmed && sed /^index/d actual >actual.trimmed &&
cat >expect <<-EOF && cat >expect <<-EOF &&