зеркало из https://github.com/microsoft/git.git
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:
Коммит
1a5f1a3f25
62
builtin/am.c
62
builtin/am.c
|
@ -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
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)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче