Merge branch 'js/am-3-merge-recursive-direct'

"git am -3" calls "git merge-recursive" when it needs to fall back
to a three-way merge; this call has been turned into an internal
subroutine call instead of spawning a separate subprocess.

* js/am-3-merge-recursive-direct:
  merge-recursive: flush output buffer even when erroring out
  merge_trees(): ensure that the callers release output buffer
  merge-recursive: offer an option to retain the output in 'obuf'
  merge-recursive: write the commit title in one go
  merge-recursive: flush output buffer before printing error messages
  am -3: use merge_recursive() directly again
  merge-recursive: switch to returning errors instead of dying
  merge-recursive: handle return values indicating errors
  merge-recursive: allow write_tree_from_memory() to error out
  merge-recursive: avoid returning a wholesale struct
  merge_recursive: abort properly upon errors
  prepare the builtins for a libified merge_recursive()
  merge-recursive: clarify code in was_tracked()
  die(_("BUG")): avoid translating bug messages
  die("bug"): report bugs consistently
  t5520: verify that `pull --rebase` shows the helpful advice when failing
This commit is contained in:
Junio C Hamano 2016-08-10 12:33:20 -07:00
Родитель 7a3ea66633 6999bc7074
Коммит 1a5f1a3f25
15 изменённых файлов: 419 добавлений и 294 удалений

Просмотреть файл

@ -1578,48 +1578,19 @@ static int build_fake_ancestor(const struct am_state *state, const char *index_f
return 0; return 0;
} }
/**
* Do the three-way merge using fake ancestor, their tree constructed
* from the fake ancestor and the postimage of the patch, and our
* state.
*/
static int run_fallback_merge_recursive(const struct am_state *state,
unsigned char *orig_tree,
unsigned char *our_tree,
unsigned char *their_tree)
{
struct child_process cp = CHILD_PROCESS_INIT;
int status;
cp.git_cmd = 1;
argv_array_pushf(&cp.env_array, "GITHEAD_%s=%.*s",
sha1_to_hex(their_tree), linelen(state->msg), state->msg);
if (state->quiet)
argv_array_push(&cp.env_array, "GIT_MERGE_VERBOSITY=0");
argv_array_push(&cp.args, "merge-recursive");
argv_array_push(&cp.args, sha1_to_hex(orig_tree));
argv_array_push(&cp.args, "--");
argv_array_push(&cp.args, sha1_to_hex(our_tree));
argv_array_push(&cp.args, sha1_to_hex(their_tree));
status = run_command(&cp) ? (-1) : 0;
discard_cache();
read_cache();
return status;
}
/** /**
* Attempt a threeway merge, using index_path as the temporary index. * Attempt a threeway merge, using index_path as the temporary index.
*/ */
static int fall_back_threeway(const struct am_state *state, const char *index_path) static int fall_back_threeway(const struct am_state *state, const char *index_path)
{ {
unsigned char orig_tree[GIT_SHA1_RAWSZ], their_tree[GIT_SHA1_RAWSZ], struct object_id orig_tree, their_tree, our_tree;
our_tree[GIT_SHA1_RAWSZ]; const struct object_id *bases[1] = { &orig_tree };
struct merge_options o;
struct commit *result;
char *their_tree_name;
if (get_sha1("HEAD", our_tree) < 0) if (get_oid("HEAD", &our_tree) < 0)
hashcpy(our_tree, EMPTY_TREE_SHA1_BIN); hashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN);
if (build_fake_ancestor(state, index_path)) if (build_fake_ancestor(state, index_path))
return error("could not build fake ancestor"); return error("could not build fake ancestor");
@ -1627,7 +1598,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
discard_cache(); discard_cache();
read_cache_from(index_path); read_cache_from(index_path);
if (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL)) if (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL))
return error(_("Repository lacks necessary blobs to fall back on 3-way merge.")); return error(_("Repository lacks necessary blobs to fall back on 3-way merge."));
say(state, stdout, _("Using index info to reconstruct a base tree...")); say(state, stdout, _("Using index info to reconstruct a base tree..."));
@ -1643,7 +1614,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
init_revisions(&rev_info, NULL); init_revisions(&rev_info, NULL);
rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS; rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix); diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);
add_pending_sha1(&rev_info, "HEAD", our_tree, 0); add_pending_sha1(&rev_info, "HEAD", our_tree.hash, 0);
diff_setup_done(&rev_info.diffopt); diff_setup_done(&rev_info.diffopt);
run_diff_index(&rev_info, 1); run_diff_index(&rev_info, 1);
} }
@ -1652,7 +1623,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
return error(_("Did you hand edit your patch?\n" return error(_("Did you hand edit your patch?\n"
"It does not apply to blobs recorded in its index.")); "It does not apply to blobs recorded in its index."));
if (write_index_as_tree(their_tree, &the_index, index_path, 0, NULL)) if (write_index_as_tree(their_tree.hash, &the_index, index_path, 0, NULL))
return error("could not write tree"); return error("could not write tree");
say(state, stdout, _("Falling back to patching base and 3-way merge...")); say(state, stdout, _("Falling back to patching base and 3-way merge..."));
@ -1668,11 +1639,22 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
* changes. * changes.
*/ */
if (run_fallback_merge_recursive(state, orig_tree, our_tree, their_tree)) { init_merge_options(&o);
o.branch1 = "HEAD";
their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
o.branch2 = their_tree_name;
if (state->quiet)
o.verbosity = 0;
if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
rerere(state->allow_rerere_autoupdate); rerere(state->allow_rerere_autoupdate);
free(their_tree_name);
return error(_("Failed to merge in the changes.")); return error(_("Failed to merge in the changes."));
} }
free(their_tree_name);
return 0; return 0;
} }

Просмотреть файл

@ -567,10 +567,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
o.ancestor = old->name; o.ancestor = old->name;
o.branch1 = new->name; o.branch1 = new->name;
o.branch2 = "local"; o.branch2 = "local";
merge_trees(&o, new->commit->tree, work, ret = merge_trees(&o, new->commit->tree, work,
old->commit->tree, &result); old->commit->tree, &result);
if (ret < 0)
exit(128);
ret = reset_tree(new->commit->tree, opts, 0, ret = reset_tree(new->commit->tree, opts, 0,
writeout_error); writeout_error);
strbuf_release(&o.obuf);
if (ret) if (ret)
return ret; return ret;
} }

Просмотреть файл

@ -118,7 +118,8 @@ static void show_killed_files(struct dir_struct *dir)
*/ */
pos = cache_name_pos(ent->name, ent->len); pos = cache_name_pos(ent->name, ent->len);
if (0 <= pos) if (0 <= pos)
die("bug in show-killed-files"); die("BUG: killed-file %.*s not found",
ent->len, ent->name);
pos = -pos - 1; pos = -pos - 1;
while (pos < active_nr && while (pos < active_nr &&
ce_stage(active_cache[pos])) ce_stage(active_cache[pos]))

Просмотреть файл

@ -673,6 +673,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
hold_locked_index(&lock, 1); hold_locked_index(&lock, 1);
clean = merge_recursive(&o, head, clean = merge_recursive(&o, head,
remoteheads->item, reversed, &result); remoteheads->item, reversed, &result);
if (clean < 0)
exit(128);
if (active_cache_changed && if (active_cache_changed &&
write_locked_index(&the_index, &lock, COMMIT_LOCK)) write_locked_index(&the_index, &lock, COMMIT_LOCK))
die (_("unable to write %s"), get_index_file()); die (_("unable to write %s"), get_index_file());

Просмотреть файл

@ -1146,7 +1146,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
report(_("Untracked cache enabled for '%s'"), get_git_work_tree()); report(_("Untracked cache enabled for '%s'"), get_git_work_tree());
break; break;
default: default:
die("Bug: bad untracked_cache value: %d", untracked_cache); die("BUG: bad untracked_cache value: %d", untracked_cache);
} }
if (active_cache_changed) { if (active_cache_changed) {

8
grep.c
Просмотреть файл

@ -693,10 +693,10 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
for (p = opt->header_list; p; p = p->next) { for (p = opt->header_list; p; p = p->next) {
if (p->token != GREP_PATTERN_HEAD) if (p->token != GREP_PATTERN_HEAD)
die("bug: a non-header pattern in grep header list."); die("BUG: a non-header pattern in grep header list.");
if (p->field < GREP_HEADER_FIELD_MIN || if (p->field < GREP_HEADER_FIELD_MIN ||
GREP_HEADER_FIELD_MAX <= p->field) GREP_HEADER_FIELD_MAX <= p->field)
die("bug: unknown header field %d", p->field); die("BUG: unknown header field %d", p->field);
compile_regexp(p, opt); compile_regexp(p, opt);
} }
@ -709,7 +709,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
h = compile_pattern_atom(&pp); h = compile_pattern_atom(&pp);
if (!h || pp != p->next) if (!h || pp != p->next)
die("bug: malformed header expr"); die("BUG: malformed header expr");
if (!header_group[p->field]) { if (!header_group[p->field]) {
header_group[p->field] = h; header_group[p->field] = h;
continue; continue;
@ -1514,7 +1514,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
case GREP_BINARY_TEXT: case GREP_BINARY_TEXT:
break; break;
default: default:
die("bug: unknown binary handling mode"); die("BUG: unknown binary handling mode");
} }
} }

Просмотреть файл

@ -511,7 +511,7 @@ static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
va_start(va, fmt); va_start(va, fmt);
if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen) if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)
die("Fatal: buffer too small. Please report a bug."); die("BUG: buffer too small. Please report a bug.");
va_end(va); va_end(va);
return ret; return ret;
} }

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -13,7 +13,7 @@ struct merge_options {
MERGE_RECURSIVE_THEIRS MERGE_RECURSIVE_THEIRS
} recursive_variant; } recursive_variant;
const char *subtree_shift; const char *subtree_shift;
unsigned buffer_output : 1; unsigned buffer_output; /* 1: output at end, 2: keep buffered */
unsigned renormalize : 1; unsigned renormalize : 1;
long xdl_opts; long xdl_opts;
int verbosity; int verbosity;

Просмотреть файл

@ -293,6 +293,9 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
clean = merge_trees(&o, clean = merge_trees(&o,
head_tree, head_tree,
next_tree, base_tree, &result); next_tree, base_tree, &result);
strbuf_release(&o.obuf);
if (clean < 0)
return clean;
if (active_cache_changed && if (active_cache_changed &&
write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
@ -559,6 +562,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) { if (!opts->strategy || !strcmp(opts->strategy, "recursive") || opts->action == REPLAY_REVERT) {
res = do_recursive_merge(base, next, base_label, next_label, res = do_recursive_merge(base, next, base_label, next_label,
head, &msgbuf, opts); head, &msgbuf, opts);
if (res < 0)
return res;
write_message(&msgbuf, git_path_merge_msg()); write_message(&msgbuf, git_path_merge_msg());
} else { } else {
struct commit_list *common = NULL; struct commit_list *common = NULL;

Просмотреть файл

@ -791,7 +791,7 @@ void close_all_packs(void)
for (p = packed_git; p; p = p->next) for (p = packed_git; p; p = p->next)
if (p->do_not_close) if (p->do_not_close)
die("BUG! Want to close pack marked 'do-not-close'"); die("BUG: want to close pack marked 'do-not-close'");
else else
close_pack(p); close_pack(p);
} }
@ -2306,7 +2306,7 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
case OBJ_OFS_DELTA: case OBJ_OFS_DELTA:
case OBJ_REF_DELTA: case OBJ_REF_DELTA:
if (data) if (data)
die("BUG in unpack_entry: left loop at a valid delta"); die("BUG: unpack_entry: left loop at a valid delta");
break; break;
case OBJ_COMMIT: case OBJ_COMMIT:
case OBJ_TREE: case OBJ_TREE:

Просмотреть файл

@ -255,6 +255,38 @@ test_expect_success '--rebase' '
test new = "$(git show HEAD:file2)" test new = "$(git show HEAD:file2)"
' '
test_expect_success '--rebase with conflicts shows advice' '
test_when_finished "git rebase --abort; git checkout -f to-rebase" &&
git checkout -b seq &&
test_seq 5 >seq.txt &&
git add seq.txt &&
test_tick &&
git commit -m "Add seq.txt" &&
echo 6 >>seq.txt &&
test_tick &&
git commit -m "Append to seq.txt" seq.txt &&
git checkout -b with-conflicts HEAD^ &&
echo conflicting >>seq.txt &&
test_tick &&
git commit -m "Create conflict" seq.txt &&
test_must_fail git pull --rebase . seq 2>err >out &&
grep "When you have resolved this problem" out
'
test_expect_success 'failed --rebase shows advice' '
test_when_finished "git rebase --abort; git checkout -f to-rebase" &&
git checkout -b diverging &&
test_commit attributes .gitattributes "* text=auto" attrs &&
sha1="$(printf "1\\r\\n" | git hash-object -w --stdin)" &&
git update-index --cacheinfo 0644 $sha1 file &&
git commit -m v1-with-cr &&
# force checkout because `git reset --hard` will not leave clean `file`
git checkout -f -b fails-to-rebase HEAD^ &&
test_commit v2-without-cr file "2" file2-lf &&
test_must_fail git pull --rebase . diverging 2>err >out &&
grep "When you have resolved this problem" out
'
test_expect_success '--rebase fails with multiple branches' ' test_expect_success '--rebase fails with multiple branches' '
git reset --hard before-rebase && git reset --hard before-rebase &&
test_must_fail git pull --rebase . copy master 2>err && test_must_fail git pull --rebase . copy master 2>err &&

Просмотреть файл

@ -562,7 +562,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
warning(_("unknown value '%s' for key '%s'"), value, conf_key); warning(_("unknown value '%s' for key '%s'"), value, conf_key);
break; break;
default: default:
die("internal bug in trailer.c"); die("BUG: trailer.c: unhandled type %d", type);
} }
return 0; return 0;
} }

Просмотреть файл

@ -567,7 +567,7 @@ void transport_take_over(struct transport *transport,
struct git_transport_data *data; struct git_transport_data *data;
if (!transport->smart_options) if (!transport->smart_options)
die("Bug detected: Taking over transport requires non-NULL " die("BUG: taking over transport requires non-NULL "
"smart_options field."); "smart_options field.");
data = xcalloc(1, sizeof(*data)); data = xcalloc(1, sizeof(*data));

Просмотреть файл

@ -263,7 +263,7 @@ static const char *wt_status_unmerged_status_string(int stagemask)
case 7: case 7:
return _("both modified:"); return _("both modified:");
default: default:
die("bug: unhandled unmerged status %x", stagemask); die("BUG: unhandled unmerged status %x", stagemask);
} }
} }
@ -388,7 +388,7 @@ static void wt_status_print_change_data(struct wt_status *s,
status_printf(s, color(WT_STATUS_HEADER, s), "\t"); status_printf(s, color(WT_STATUS_HEADER, s), "\t");
what = wt_status_diff_status_string(status); what = wt_status_diff_status_string(status);
if (!what) if (!what)
die("bug: unhandled diff status %c", status); die("BUG: unhandled diff status %c", status);
len = label_width - utf8_strwidth(what); len = label_width - utf8_strwidth(what);
assert(len >= 0); assert(len >= 0);
if (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED) if (status == DIFF_STATUS_COPIED || status == DIFF_STATUS_RENAMED)