mirror of
https://github.com/git/git.git
synced 2026-08-07 00:22:05 +00:00
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 -m: refine autostash fallback sequencer: teach autostash apply to report conflicts
This commit is contained in:
@@ -838,10 +838,11 @@ static void init_topts(struct unpack_trees_options *topts,
|
||||
static int merge_working_tree(const struct checkout_opts *opts,
|
||||
struct branch_info *old_branch_info,
|
||||
struct branch_info *new_branch_info,
|
||||
bool quiet,
|
||||
bool allow_autostash,
|
||||
int *writeout_error)
|
||||
{
|
||||
int ret;
|
||||
bool can_autostash = false;
|
||||
struct lock_file lock_file = LOCK_INIT;
|
||||
struct tree *new_tree;
|
||||
|
||||
@@ -888,9 +889,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (allow_autostash)
|
||||
can_autostash = has_unstaged_changes(the_repository, 1) ||
|
||||
has_uncommitted_changes(the_repository, 1);
|
||||
|
||||
/* 2-way merge to the new branch */
|
||||
init_topts(&topts, opts->show_progress,
|
||||
opts->overwrite_ignore, quiet);
|
||||
opts->overwrite_ignore, can_autostash);
|
||||
init_checkout_metadata(&topts.meta, new_branch_info->refname,
|
||||
new_branch_info->commit ?
|
||||
&new_branch_info->commit->object.oid :
|
||||
@@ -917,7 +922,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
|
||||
clear_unpack_trees_porcelain(&topts);
|
||||
if (ret == -1) {
|
||||
rollback_lock_file(&lock_file);
|
||||
return MERGE_WORKING_TREE_UNPACK_FAILED;
|
||||
return can_autostash ?
|
||||
MERGE_WORKING_TREE_UNPACK_FAILED : 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1166,6 +1172,7 @@ static int switch_branches(const struct checkout_opts *opts,
|
||||
int flag, writeout_error = 0;
|
||||
int do_merge = 1;
|
||||
int created_autostash = 0;
|
||||
bool autostash_conflicted = false;
|
||||
struct strbuf old_commit_shortname = STRBUF_INIT;
|
||||
struct strbuf autostash_msg = STRBUF_INIT;
|
||||
const char *stash_label_base = NULL;
|
||||
@@ -1242,7 +1249,8 @@ static int switch_branches(const struct checkout_opts *opts,
|
||||
new_branch_info->name,
|
||||
"local",
|
||||
stash_label_base,
|
||||
autostash_msg.buf);
|
||||
autostash_msg.buf,
|
||||
&autostash_conflicted);
|
||||
}
|
||||
if (ret) {
|
||||
branch_info_release(&old_branch_info);
|
||||
@@ -1255,6 +1263,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)
|
||||
orphaned_commit_warning(old_branch_info.commit, new_branch_info->commit);
|
||||
|
||||
if (autostash_conflicted && !opts->quiet)
|
||||
fputc('\n', stderr);
|
||||
update_refs_for_switch(opts, &old_branch_info, new_branch_info);
|
||||
|
||||
if (created_autostash) {
|
||||
|
||||
@@ -1980,7 +1980,7 @@ int cmd_commit(int argc,
|
||||
}
|
||||
|
||||
apply_autostash_ref(the_repository, "MERGE_AUTOSTASH",
|
||||
NULL, NULL, NULL, NULL);
|
||||
NULL, NULL, NULL, NULL, NULL);
|
||||
|
||||
cleanup:
|
||||
free_commit_extra_headers(extra);
|
||||
|
||||
@@ -538,7 +538,7 @@ static void finish(struct commit *head_commit,
|
||||
|
||||
if (new_head)
|
||||
apply_autostash_ref(the_repository, "MERGE_AUTOSTASH",
|
||||
NULL, NULL, NULL, NULL);
|
||||
NULL, NULL, NULL, NULL, NULL);
|
||||
strbuf_release(&reflog_message);
|
||||
}
|
||||
|
||||
@@ -1682,7 +1682,7 @@ int cmd_merge(int argc,
|
||||
&commit->object.oid,
|
||||
overwrite_ignore)) {
|
||||
apply_autostash_ref(the_repository, "MERGE_AUTOSTASH",
|
||||
NULL, NULL, NULL, NULL);
|
||||
NULL, NULL, NULL, NULL, NULL);
|
||||
ret = 1;
|
||||
goto done;
|
||||
}
|
||||
@@ -1846,7 +1846,7 @@ int cmd_merge(int argc,
|
||||
fprintf(stderr, _("Merge with strategy %s failed.\n"),
|
||||
use_strategies[0]->name);
|
||||
apply_autostash_ref(the_repository, "MERGE_AUTOSTASH",
|
||||
NULL, NULL, NULL, NULL);
|
||||
NULL, NULL, NULL, NULL, NULL);
|
||||
ret = 2;
|
||||
goto done;
|
||||
} else if (best_strategy == wt_strategy)
|
||||
|
||||
29
sequencer.c
29
sequencer.c
@@ -4796,7 +4796,8 @@ void create_autostash_ref(struct repository *r, const char *refname,
|
||||
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 *stash_msg)
|
||||
const char *stash_msg,
|
||||
bool *conflicted)
|
||||
{
|
||||
struct child_process child = CHILD_PROCESS_INIT;
|
||||
int ret = 0;
|
||||
@@ -4831,14 +4832,16 @@ static int apply_save_autostash_oid(const char *stash_oid, int attempt_apply,
|
||||
strvec_push(&store.args, stash_oid);
|
||||
if (run_command(&store))
|
||||
ret = error(_("cannot store %s"), stash_oid);
|
||||
else if (attempt_apply)
|
||||
else if (attempt_apply) {
|
||||
if (conflicted)
|
||||
*conflicted = true;
|
||||
fprintf(stderr,
|
||||
_("Your local changes are stashed, however applying them\n"
|
||||
"resulted in conflicts. You can either resolve the conflicts\n"
|
||||
"and then discard the stash with \"git stash drop\", or, if you\n"
|
||||
"do not want to resolve them now, run \"git reset --hard\" and\n"
|
||||
"apply the local changes later by running \"git stash pop\".\n"));
|
||||
else
|
||||
} else
|
||||
fprintf(stderr,
|
||||
_("Autostash exists; creating a new stash entry.\n"
|
||||
"Your changes are safe in the stash.\n"
|
||||
@@ -4862,7 +4865,7 @@ 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, NULL, NULL);
|
||||
|
||||
unlink(path);
|
||||
strbuf_release(&stash_oid);
|
||||
@@ -4881,19 +4884,24 @@ 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, NULL);
|
||||
return apply_save_autostash_oid(stash_oid, 1, NULL, 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 *stash_msg)
|
||||
const char *stash_msg,
|
||||
bool *conflicted)
|
||||
{
|
||||
struct object_id stash_oid;
|
||||
char stash_oid_hex[GIT_MAX_HEXSZ + 1];
|
||||
int flag, ret;
|
||||
|
||||
if (conflicted)
|
||||
*conflicted = false;
|
||||
|
||||
if (!refs_ref_exists(get_main_ref_store(r), refname))
|
||||
return 0;
|
||||
|
||||
@@ -4906,7 +4914,7 @@ 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,
|
||||
stash_msg);
|
||||
stash_msg, conflicted);
|
||||
|
||||
refs_delete_ref(get_main_ref_store(r), "", refname,
|
||||
&stash_oid, REF_NO_DEREF);
|
||||
@@ -4917,16 +4925,17 @@ 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, NULL);
|
||||
NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
int apply_autostash_ref(struct repository *r, const char *refname,
|
||||
const char *label_ours, const char *label_theirs,
|
||||
const char *label_base, const char *stash_msg)
|
||||
const char *label_base, const char *stash_msg,
|
||||
bool *conflicted)
|
||||
{
|
||||
return apply_save_autostash_ref(r, refname, 1,
|
||||
label_ours, label_theirs, label_base,
|
||||
stash_msg);
|
||||
stash_msg, conflicted);
|
||||
}
|
||||
|
||||
static int checkout_onto(struct repository *r, struct replay_opts *opts,
|
||||
|
||||
@@ -237,7 +237,8 @@ int apply_autostash(const char *path);
|
||||
int apply_autostash_oid(const char *stash_oid);
|
||||
int apply_autostash_ref(struct repository *r, const char *refname,
|
||||
const char *label_ours, const char *label_theirs,
|
||||
const char *label_base, const char *stash_msg);
|
||||
const char *label_base, const char *stash_msg,
|
||||
bool *conflicted);
|
||||
|
||||
#define SUMMARY_INITIAL_COMMIT (1 << 0)
|
||||
#define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
|
||||
|
||||
@@ -240,6 +240,14 @@ test_expect_success 'checkout -m creates a recoverable stash on conflict' '
|
||||
test_grep "git stash drop" actual &&
|
||||
test_grep "git stash pop" actual &&
|
||||
test_grep "The following paths have local changes" actual &&
|
||||
sed -n "/apply the local changes later/,/Switched to branch/p" \
|
||||
actual >separator.actual &&
|
||||
cat >separator.expect <<-EOF &&
|
||||
apply the local changes later by running "git stash pop".
|
||||
|
||||
Switched to branch ${SQ}side${SQ}
|
||||
EOF
|
||||
test_cmp separator.expect separator.actual &&
|
||||
git log -p -1 --format="%gs%n%B" -g --diff-merges=1 refs/stash >actual &&
|
||||
sed /^index/d actual >actual.trimmed &&
|
||||
cat >expect <<-EOF &&
|
||||
@@ -262,11 +270,18 @@ test_expect_success 'checkout -m creates a recoverable stash on conflict' '
|
||||
git reset --hard
|
||||
'
|
||||
|
||||
test_expect_success 'checkout -m which would overwrite untracked file' '
|
||||
test_expect_success 'checkout -m only retries untracked-file failure with local changes' '
|
||||
git checkout -f --detach main &&
|
||||
test_commit another-file &&
|
||||
git checkout HEAD^ &&
|
||||
>another-file.t &&
|
||||
test_must_fail env GIT_TRACE2_EVENT="$(pwd)/trace" \
|
||||
git checkout -m @{-1} 2>err &&
|
||||
test_grep "untracked working tree files" err &&
|
||||
grep "\"region_enter\".*\"category\":\"index\",\"label\":\"refresh\"" \
|
||||
trace >refresh.events &&
|
||||
test_line_count = 1 refresh.events &&
|
||||
|
||||
fill 1 2 3 4 5 >one &&
|
||||
test_must_fail git checkout -m @{-1} 2>err &&
|
||||
q_to_tab >expect <<-\EOF &&
|
||||
|
||||
Reference in New Issue
Block a user