mirror of
https://github.com/git/git.git
synced 2024-11-30 21:44:02 +08:00
Merge branch 'en/merge-tree-sequence'
"git merge-tree --stdin" is a new way to request a series of merges and report the merge results. * en/merge-tree-sequence: merge-tree: support multiple batched merges with --stdin merge-tree: update documentation for differences in -z output
This commit is contained in:
commit
b1e3dd68ee
@ -81,6 +81,31 @@ Whereas for a conflicted merge, the output is by default of the form:
|
||||
|
||||
These are discussed individually below.
|
||||
|
||||
However, there is an exception. If `--stdin` is passed, then there is
|
||||
an extra section at the beginning, a NUL character at the end, and then
|
||||
all the sections repeat for each line of input. Thus, if the first merge
|
||||
is conflicted and the second is clean, the output would be of the form:
|
||||
|
||||
<Merge status>
|
||||
<OID of toplevel tree>
|
||||
<Conflicted file info>
|
||||
<Informational messages>
|
||||
NUL
|
||||
<Merge status>
|
||||
<OID of toplevel tree>
|
||||
NUL
|
||||
|
||||
[[MS]]
|
||||
Merge status
|
||||
~~~~~~~~~~~~
|
||||
|
||||
This is an integer status followed by a NUL character. The integer status is:
|
||||
|
||||
0: merge had conflicts
|
||||
1: merge was clean
|
||||
<0: something prevented the merge from running (e.g. access to repository
|
||||
objects denied by filesystem)
|
||||
|
||||
[[OIDTLT]]
|
||||
OID of toplevel tree
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
@ -108,18 +133,50 @@ character instead of a newline character.
|
||||
Informational messages
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This always starts with a blank line (or NUL if `-z` is passed) to
|
||||
separate it from the previous sections, and then has free-form
|
||||
messages about the merge, such as:
|
||||
This section provides informational messages, typically about
|
||||
conflicts. The format of the section varies significantly depending
|
||||
on whether `-z` is passed.
|
||||
|
||||
If `-z` is passed:
|
||||
|
||||
The output format is zero or more conflict informational records, each
|
||||
of the form:
|
||||
|
||||
<list-of-paths><conflict-type>NUL<conflict-message>NUL
|
||||
|
||||
where <list-of-paths> is of the form
|
||||
|
||||
<number-of-paths>NUL<path1>NUL<path2>NUL...<pathN>NUL
|
||||
|
||||
and includes paths (or branch names) affected by the conflict or
|
||||
informational message in <conflict-message>. Also, <conflict-type> is a
|
||||
stable string explaining the type of conflict, such as
|
||||
|
||||
* "Auto-merging"
|
||||
* "CONFLICT (rename/delete)"
|
||||
* "CONFLICT (submodule lacks merge base)"
|
||||
* "CONFLICT (binary)"
|
||||
|
||||
and <conflict-message> is a more detailed message about the conflict which often
|
||||
(but not always) embeds the <stable-short-type-description> within it. These
|
||||
strings may change in future Git versions. Some examples:
|
||||
|
||||
* "Auto-merging <file>"
|
||||
* "CONFLICT (rename/delete): <oldfile> renamed...but deleted in..."
|
||||
* "Failed to merge submodule <submodule> (<reason>)"
|
||||
* "Failed to merge submodule <submodule> (no merge base)"
|
||||
* "Warning: cannot merge binary files: <filename>"
|
||||
|
||||
Note that these free-form messages will never have a NUL character
|
||||
in or between them, even if -z is passed. It is simply a large block
|
||||
of text taking up the remainder of the output.
|
||||
If `-z` is NOT passed:
|
||||
|
||||
This section starts with a blank line to separate it from the previous
|
||||
sections, and then only contains the <conflict-message> information
|
||||
from the previous section (separated by newlines). These are
|
||||
non-stable strings that should not be parsed by scripts, and are just
|
||||
meant for human consumption. Also, note that while <conflict-message>
|
||||
strings usually do not contain embedded newlines, they sometimes do.
|
||||
(However, the free-form messages will never have an embedded NUL
|
||||
character). So, the entire block of information is meant for human
|
||||
readers as an agglomeration of all conflict messages.
|
||||
|
||||
EXIT STATUS
|
||||
-----------
|
||||
@ -127,7 +184,10 @@ EXIT STATUS
|
||||
For a successful, non-conflicted merge, the exit status is 0. When the
|
||||
merge has conflicts, the exit status is 1. If the merge is not able to
|
||||
complete (or start) due to some kind of error, the exit status is
|
||||
something other than 0 or 1 (and the output is unspecified).
|
||||
something other than 0 or 1 (and the output is unspecified). When
|
||||
--stdin is passed, the return status is 0 for both successful and
|
||||
conflicted merges, and something other than 0 or 1 if it cannot complete
|
||||
all the requested merges.
|
||||
|
||||
USAGE NOTES
|
||||
-----------
|
||||
|
@ -402,6 +402,7 @@ struct merge_tree_options {
|
||||
int allow_unrelated_histories;
|
||||
int show_messages;
|
||||
int name_only;
|
||||
int use_stdin;
|
||||
};
|
||||
|
||||
static int real_merge(struct merge_tree_options *o,
|
||||
@ -412,6 +413,7 @@ static int real_merge(struct merge_tree_options *o,
|
||||
struct commit_list *merge_bases = NULL;
|
||||
struct merge_options opt;
|
||||
struct merge_result result = { 0 };
|
||||
int show_messages = o->show_messages;
|
||||
|
||||
parent1 = get_merge_parent(branch1);
|
||||
if (!parent1)
|
||||
@ -443,9 +445,11 @@ static int real_merge(struct merge_tree_options *o,
|
||||
if (result.clean < 0)
|
||||
die(_("failure to merge"));
|
||||
|
||||
if (o->show_messages == -1)
|
||||
o->show_messages = !result.clean;
|
||||
if (show_messages == -1)
|
||||
show_messages = !result.clean;
|
||||
|
||||
if (o->use_stdin)
|
||||
printf("%d%c", result.clean, line_termination);
|
||||
printf("%s%c", oid_to_hex(&result.tree->object.oid), line_termination);
|
||||
if (!result.clean) {
|
||||
struct string_list conflicted_files = STRING_LIST_INIT_NODUP;
|
||||
@ -467,11 +471,13 @@ static int real_merge(struct merge_tree_options *o,
|
||||
}
|
||||
string_list_clear(&conflicted_files, 1);
|
||||
}
|
||||
if (o->show_messages) {
|
||||
if (show_messages) {
|
||||
putchar(line_termination);
|
||||
merge_display_update_messages(&opt, line_termination == '\0',
|
||||
&result);
|
||||
}
|
||||
if (o->use_stdin)
|
||||
putchar(line_termination);
|
||||
merge_finalize(&opt, &result);
|
||||
return !result.clean; /* result.clean < 0 handled above */
|
||||
}
|
||||
@ -505,6 +511,10 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
|
||||
&o.allow_unrelated_histories,
|
||||
N_("allow merging unrelated histories"),
|
||||
PARSE_OPT_NONEG),
|
||||
OPT_BOOL_F(0, "stdin",
|
||||
&o.use_stdin,
|
||||
N_("perform multiple merges, one per line of input"),
|
||||
PARSE_OPT_NONEG),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@ -512,6 +522,32 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
|
||||
original_argc = argc - 1; /* ignoring argv[0] */
|
||||
argc = parse_options(argc, argv, prefix, mt_options,
|
||||
merge_tree_usage, PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
/* Handle --stdin */
|
||||
if (o.use_stdin) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
if (o.mode == MODE_TRIVIAL)
|
||||
die(_("--trivial-merge is incompatible with all other options"));
|
||||
line_termination = '\0';
|
||||
while (strbuf_getline_lf(&buf, stdin) != EOF) {
|
||||
struct strbuf **split;
|
||||
int result;
|
||||
|
||||
split = strbuf_split(&buf, ' ');
|
||||
if (!split[0] || !split[1] || split[2])
|
||||
die(_("malformed input line: '%s'."), buf.buf);
|
||||
strbuf_rtrim(split[0]);
|
||||
result = real_merge(&o, split[0]->buf, split[1]->buf, prefix);
|
||||
if (result < 0)
|
||||
die(_("merging cannot continue; got unclean result of %d"), result);
|
||||
strbuf_list_free(split);
|
||||
}
|
||||
strbuf_release(&buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Figure out which mode to use */
|
||||
switch (o.mode) {
|
||||
default:
|
||||
BUG("unexpected command mode %d", o.mode);
|
||||
|
@ -819,4 +819,45 @@ test_expect_success SANITY 'merge-ort fails gracefully in a read-only repository
|
||||
test_must_fail git -C read-only merge-tree side1 side2
|
||||
'
|
||||
|
||||
test_expect_success '--stdin with both a successful and a conflicted merge' '
|
||||
printf "side1 side3\nside1 side2" | git merge-tree --stdin >actual &&
|
||||
|
||||
git checkout side1^0 &&
|
||||
git merge side3 &&
|
||||
|
||||
printf "1\0" >expect &&
|
||||
git rev-parse HEAD^{tree} | lf_to_nul >>expect &&
|
||||
printf "\0" >>expect &&
|
||||
|
||||
git checkout side1^0 &&
|
||||
test_must_fail git merge side2 &&
|
||||
sed s/HEAD/side1/ greeting >tmp &&
|
||||
mv tmp greeting &&
|
||||
git add -u &&
|
||||
git mv whatever~HEAD whatever~side1 &&
|
||||
|
||||
printf "0\0" >>expect &&
|
||||
git write-tree | lf_to_nul >>expect &&
|
||||
|
||||
cat <<-EOF | q_to_tab | lf_to_nul >>expect &&
|
||||
100644 $(git rev-parse side1~1:greeting) 1Qgreeting
|
||||
100644 $(git rev-parse side1:greeting) 2Qgreeting
|
||||
100644 $(git rev-parse side2:greeting) 3Qgreeting
|
||||
100644 $(git rev-parse side1~1:whatever) 1Qwhatever~side1
|
||||
100644 $(git rev-parse side1:whatever) 2Qwhatever~side1
|
||||
EOF
|
||||
|
||||
q_to_nul <<-EOF >>expect &&
|
||||
Q1QgreetingQAuto-mergingQAuto-merging greeting
|
||||
Q1QgreetingQCONFLICT (contents)QCONFLICT (content): Merge conflict in greeting
|
||||
Q1QnumbersQAuto-mergingQAuto-merging numbers
|
||||
Q2Qwhatever~side1QwhateverQCONFLICT (file/directory)QCONFLICT (file/directory): directory in the way of whatever from side1; moving it to whatever~side1 instead.
|
||||
Q1Qwhatever~side1QCONFLICT (modify/delete)QCONFLICT (modify/delete): whatever~side1 deleted in side2 and modified in side1. Version side1 of whatever~side1 left in tree.
|
||||
EOF
|
||||
|
||||
printf "\0\0" >>expect &&
|
||||
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
Loading…
Reference in New Issue
Block a user