Merge branch 'en/merge-restore-to-pristine'

When "git merge" finds that it cannot perform a merge, it should
restore the working tree to the state before the command was
initiated, but in some corner cases it didn't.

* en/merge-restore-to-pristine:
  merge: do not exit restore_state() prematurely
  merge: ensure we can actually restore pre-merge state
  merge: make restore_state() restore staged state too
  merge: fix save_state() to work when there are stat-dirty files
  merge: do not abort early if one strategy fails to handle the merge
  merge: abort if index does not match HEAD for trivial merges
  merge-resolve: abort if index does not match HEAD
  merge-ort-wrappers: make printed message match the one from recursive
This commit is contained in:
Junio C Hamano
2022-08-03 13:36:09 -07:00
7 changed files with 154 additions and 17 deletions

View File

@ -313,8 +313,16 @@ static int save_state(struct object_id *stash)
int len;
struct child_process cp = CHILD_PROCESS_INIT;
struct strbuf buffer = STRBUF_INIT;
struct lock_file lock_file = LOCK_INIT;
int fd;
int rc = -1;
fd = repo_hold_locked_index(the_repository, &lock_file, 0);
refresh_cache(REFRESH_QUIET);
if (0 <= fd)
repo_update_index_if_able(the_repository, &lock_file);
rollback_lock_file(&lock_file);
strvec_pushl(&cp.args, "stash", "create", NULL);
cp.out = -1;
cp.git_cmd = 1;
@ -375,22 +383,26 @@ static void reset_hard(const struct object_id *oid, int verbose)
static void restore_state(const struct object_id *head,
const struct object_id *stash)
{
const char *args[] = { "stash", "apply", NULL, NULL };
if (is_null_oid(stash))
return;
struct strvec args = STRVEC_INIT;
reset_hard(head, 1);
args[2] = oid_to_hex(stash);
if (is_null_oid(stash))
goto refresh_cache;
strvec_pushl(&args, "stash", "apply", "--index", "--quiet", NULL);
strvec_push(&args, oid_to_hex(stash));
/*
* It is OK to ignore error here, for example when there was
* nothing to restore.
*/
run_command_v_opt(args, RUN_GIT_CMD);
run_command_v_opt(args.v, RUN_GIT_CMD);
strvec_clear(&args);
refresh_cache(REFRESH_QUIET);
refresh_cache:
if (discard_cache() < 0 || read_cache() < 0)
die(_("could not read index"));
}
/* This is called when no merge was necessary. */
@ -754,8 +766,10 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
else
clean = merge_recursive(&o, head, remoteheads->item,
reversed, &result);
if (clean < 0)
exit(128);
if (clean < 0) {
rollback_lock_file(&lock);
return 2;
}
if (write_locked_index(&the_index, &lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write %s"), get_index_file());
@ -1599,6 +1613,21 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
*/
refresh_cache(REFRESH_QUIET);
if (allow_trivial && fast_forward != FF_ONLY) {
/*
* Must first ensure that index matches HEAD before
* attempting a trivial merge.
*/
struct tree *head_tree = get_commit_tree(head_commit);
struct strbuf sb = STRBUF_INIT;
if (repo_index_has_changes(the_repository, head_tree,
&sb)) {
error(_("Your local changes to the following files would be overwritten by merge:\n %s"),
sb.buf);
strbuf_release(&sb);
return 2;
}
/* See if it is really trivial. */
git_committer_info(IDENT_STRICT);
printf(_("Trying really trivial in-index merge...\n"));
@ -1655,12 +1684,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* tree in the index -- this means that the index must be in
* sync with the head commit. The strategies are responsible
* to ensure this.
*
* Stash away the local changes so that we can try more than one
* and/or recover from merge strategies bailing while leaving the
* index and working tree polluted.
*/
if (use_strategies_nr == 1 ||
/*
* Stash away the local changes so that we can try more than one.
*/
save_state(&stash))
if (save_state(&stash))
oidclr(&stash);
for (i = 0; !merge_was_ok && i < use_strategies_nr; i++) {