merge-recursive: fix assumption that head tree being merged is HEAD

`git merge-recursive` does a three-way merge between user-specified trees
base, head, and remote.  Since the user is allowed to specify head, we can
not necesarily assume that head == HEAD.

Modify index_has_changes() to take an extra argument specifying the tree
to compare against.  If NULL, it will compare to HEAD.  We then use this
from merge-recursive to make sure we compare to the user-specified head.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Elijah Newren
2018-06-30 18:25:00 -07:00
committed by Junio C Hamano
parent 92702392ce
commit e1f8694f33
5 changed files with 21 additions and 13 deletions

View File

@ -1763,7 +1763,7 @@ static void am_run(struct am_state *state, int resume)
refresh_and_write_cache(); refresh_and_write_cache();
if (index_has_changes(&the_index, &sb)) { if (index_has_changes(&the_index, NULL, &sb)) {
write_state_bool(state, "dirtyindex", 1); write_state_bool(state, "dirtyindex", 1);
die(_("Dirty index: cannot apply patches (dirty: %s)"), sb.buf); die(_("Dirty index: cannot apply patches (dirty: %s)"), sb.buf);
} }
@ -1820,7 +1820,8 @@ static void am_run(struct am_state *state, int resume)
* Applying the patch to an earlier tree and merging * Applying the patch to an earlier tree and merging
* the result may have produced the same tree as ours. * the result may have produced the same tree as ours.
*/ */
if (!apply_status && !index_has_changes(&the_index, NULL)) { if (!apply_status &&
!index_has_changes(&the_index, NULL, NULL)) {
say(state, stdout, _("No changes -- Patch already applied.")); say(state, stdout, _("No changes -- Patch already applied."));
goto next; goto next;
} }
@ -1878,7 +1879,7 @@ static void am_resolve(struct am_state *state)
say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg); say(state, stdout, _("Applying: %.*s"), linelen(state->msg), state->msg);
if (!index_has_changes(&the_index, NULL)) { if (!index_has_changes(&the_index, NULL, NULL)) {
printf_ln(_("No changes - did you forget to use 'git add'?\n" printf_ln(_("No changes - did you forget to use 'git add'?\n"
"If there is nothing left to stage, chances are that something else\n" "If there is nothing left to stage, chances are that something else\n"
"already introduced the same changes; you might want to skip this patch.")); "already introduced the same changes; you might want to skip this patch."));

11
cache.h
View File

@ -218,6 +218,7 @@ struct cache_entry {
/* Forward structure decls */ /* Forward structure decls */
struct pathspec; struct pathspec;
struct child_process; struct child_process;
struct tree;
/* /*
* Copy the sha1 and stat state of a cache entry from one to * Copy the sha1 and stat state of a cache entry from one to
@ -627,12 +628,14 @@ extern void move_index_extensions(struct index_state *dst, struct index_state *s
extern int unmerged_index(const struct index_state *); extern int unmerged_index(const struct index_state *);
/** /**
* Returns 1 if istate differs from HEAD, 0 otherwise. When on an unborn * Returns 1 if istate differs from tree, 0 otherwise. If tree is NULL,
* branch, returns 1 if there are entries in istate, 0 otherwise. If an * compares istate to HEAD. If tree is NULL and on an unborn branch,
* strbuf is provided, the space-separated list of files that differ will * returns 1 if there are entries in istate, 0 otherwise. If an strbuf is
* be appended to it. * provided, the space-separated list of files that differ will be appended
* to it.
*/ */
extern int index_has_changes(const struct index_state *istate, extern int index_has_changes(const struct index_state *istate,
struct tree *tree,
struct strbuf *sb); struct strbuf *sb);
extern int verify_path(const char *path, unsigned mode); extern int verify_path(const char *path, unsigned mode);

View File

@ -1983,7 +1983,7 @@ int merge_trees(struct merge_options *o,
if (oid_eq(&common->object.oid, &merge->object.oid)) { if (oid_eq(&common->object.oid, &merge->object.oid)) {
struct strbuf sb = STRBUF_INIT; struct strbuf sb = STRBUF_INIT;
if (!o->call_depth && index_has_changes(&the_index, &sb)) { if (!o->call_depth && index_has_changes(&the_index, head, &sb)) {
err(o, _("Your local changes to the following files would be overwritten by merge:\n %s"), err(o, _("Your local changes to the following files would be overwritten by merge:\n %s"),
sb.buf); sb.buf);
return -1; return -1;

View File

@ -1986,22 +1986,26 @@ int unmerged_index(const struct index_state *istate)
return 0; return 0;
} }
int index_has_changes(const struct index_state *istate, struct strbuf *sb) int index_has_changes(const struct index_state *istate,
struct tree *tree,
struct strbuf *sb)
{ {
struct object_id head; struct object_id cmp;
int i; int i;
if (istate != &the_index) { if (istate != &the_index) {
BUG("index_has_changes cannot yet accept istate != &the_index; do_diff_cache needs updating first."); BUG("index_has_changes cannot yet accept istate != &the_index; do_diff_cache needs updating first.");
} }
if (!get_oid_tree("HEAD", &head)) { if (tree)
cmp = tree->object.oid;
if (tree || !get_oid_tree("HEAD", &cmp)) {
struct diff_options opt; struct diff_options opt;
diff_setup(&opt); diff_setup(&opt);
opt.flags.exit_with_status = 1; opt.flags.exit_with_status = 1;
if (!sb) if (!sb)
opt.flags.quick = 1; opt.flags.quick = 1;
do_diff_cache(&head, &opt); do_diff_cache(&cmp, &opt);
diffcore_std(&opt); diffcore_std(&opt);
for (i = 0; sb && i < diff_queued_diff.nr; i++) { for (i = 0; sb && i < diff_queued_diff.nr; i++) {
if (i) if (i)

View File

@ -126,7 +126,7 @@ test_expect_success 'recursive, when merge branch matches merge base' '
test_path_is_missing .git/MERGE_HEAD test_path_is_missing .git/MERGE_HEAD
' '
test_expect_failure 'merge-recursive, when index==head but head!=HEAD' ' test_expect_success 'merge-recursive, when index==head but head!=HEAD' '
git reset --hard && git reset --hard &&
git checkout C^0 && git checkout C^0 &&