Skip to content

Commit ffc8f11

Browse files
committed
Merge branch 'en/ort-inner-merge-error-fix'
The "ort" merge backend saw one bugfix for a crash that happens when inner merge gets killed, and assorted code clean-ups. * en/ort-inner-merge-error-fix: merge-ort: fix missing early return merge-ort: convert more error() cases to path_msg() merge-ort: upon merge abort, only show messages causing the abort merge-ort: loosen commented requirements merge-ort: clearer propagation of failure-to-function from merge_submodule merge-ort: fix type of local 'clean' var in handle_content_merge () merge-ort: maintain expected invariant for priv member merge-ort: extract handling of priv member into reusable function
2 parents ad850ef + fcf59ac commit ffc8f11

File tree

2 files changed

+165
-46
lines changed

2 files changed

+165
-46
lines changed

merge-ort.c

Lines changed: 124 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -545,17 +545,35 @@ enum conflict_and_info_types {
545545
CONFLICT_SUBMODULE_HISTORY_NOT_AVAILABLE,
546546
CONFLICT_SUBMODULE_MAY_HAVE_REWINDS,
547547
CONFLICT_SUBMODULE_NULL_MERGE_BASE,
548-
CONFLICT_SUBMODULE_CORRUPT,
548+
549+
/* INSERT NEW ENTRIES HERE */
550+
551+
/*
552+
* Keep this entry after all regular conflict and info types; only
553+
* errors (failures causing immediate abort of the merge) should
554+
* come after this.
555+
*/
556+
NB_REGULAR_CONFLICT_TYPES,
557+
558+
/*
559+
* Something is seriously wrong; cannot even perform merge;
560+
* Keep this group _last_ other than NB_TOTAL_TYPES
561+
*/
562+
ERROR_SUBMODULE_CORRUPT,
563+
ERROR_THREEWAY_CONTENT_MERGE_FAILED,
564+
ERROR_OBJECT_WRITE_FAILED,
565+
ERROR_OBJECT_READ_FAILED,
566+
ERROR_OBJECT_NOT_A_BLOB,
549567

550568
/* Keep this entry _last_ in the list */
551-
NB_CONFLICT_TYPES,
569+
NB_TOTAL_TYPES,
552570
};
553571

554572
/*
555573
* Short description of conflict type, relied upon by external tools.
556574
*
557575
* We can add more entries, but DO NOT change any of these strings. Also,
558-
* Order MUST match conflict_info_and_types.
576+
* please ensure the order matches what is used in conflict_info_and_types.
559577
*/
560578
static const char *type_short_descriptions[] = {
561579
/*** "Simple" conflicts and informational messages ***/
@@ -599,8 +617,18 @@ static const char *type_short_descriptions[] = {
599617
"CONFLICT (submodule may have rewinds)",
600618
[CONFLICT_SUBMODULE_NULL_MERGE_BASE] =
601619
"CONFLICT (submodule lacks merge base)",
602-
[CONFLICT_SUBMODULE_CORRUPT] =
603-
"CONFLICT (submodule corrupt)"
620+
621+
/* Something is seriously wrong; cannot even perform merge */
622+
[ERROR_SUBMODULE_CORRUPT] =
623+
"ERROR (submodule corrupt)",
624+
[ERROR_THREEWAY_CONTENT_MERGE_FAILED] =
625+
"ERROR (three-way content merge failed)",
626+
[ERROR_OBJECT_WRITE_FAILED] =
627+
"ERROR (object write failed)",
628+
[ERROR_OBJECT_READ_FAILED] =
629+
"ERROR (object read failed)",
630+
[ERROR_OBJECT_NOT_A_BLOB] =
631+
"ERROR (object is not a blob)",
604632
};
605633

606634
struct logical_conflict_info {
@@ -764,7 +792,8 @@ static void path_msg(struct merge_options *opt,
764792

765793
/* Sanity checks */
766794
assert(omittable_hint ==
767-
!starts_with(type_short_descriptions[type], "CONFLICT") ||
795+
(!starts_with(type_short_descriptions[type], "CONFLICT") &&
796+
!starts_with(type_short_descriptions[type], "ERROR")) ||
768797
type == CONFLICT_DIR_RENAME_SUGGESTED);
769798
if (opt->record_conflict_msgs_as_headers && omittable_hint)
770799
return; /* Do not record mere hints in headers */
@@ -1819,9 +1848,9 @@ static int merge_submodule(struct merge_options *opt,
18191848
/* check whether both changes are forward */
18201849
ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_a);
18211850
if (ret2 < 0) {
1822-
path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
1851+
path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
18231852
path, NULL, NULL, NULL,
1824-
_("Failed to merge submodule %s "
1853+
_("error: failed to merge submodule %s "
18251854
"(repository corrupt)"),
18261855
path);
18271856
ret = -1;
@@ -1830,9 +1859,9 @@ static int merge_submodule(struct merge_options *opt,
18301859
if (ret2 > 0)
18311860
ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_b);
18321861
if (ret2 < 0) {
1833-
path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
1862+
path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
18341863
path, NULL, NULL, NULL,
1835-
_("Failed to merge submodule %s "
1864+
_("error: failed to merge submodule %s "
18361865
"(repository corrupt)"),
18371866
path);
18381867
ret = -1;
@@ -1850,9 +1879,9 @@ static int merge_submodule(struct merge_options *opt,
18501879
/* Case #1: a is contained in b or vice versa */
18511880
ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b);
18521881
if (ret2 < 0) {
1853-
path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
1882+
path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
18541883
path, NULL, NULL, NULL,
1855-
_("Failed to merge submodule %s "
1884+
_("error: failed to merge submodule %s "
18561885
"(repository corrupt)"),
18571886
path);
18581887
ret = -1;
@@ -1869,9 +1898,9 @@ static int merge_submodule(struct merge_options *opt,
18691898
}
18701899
ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a);
18711900
if (ret2 < 0) {
1872-
path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
1901+
path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
18731902
path, NULL, NULL, NULL,
1874-
_("Failed to merge submodule %s "
1903+
_("error: failed to merge submodule %s "
18751904
"(repository corrupt)"),
18761905
path);
18771906
ret = -1;
@@ -1903,9 +1932,9 @@ static int merge_submodule(struct merge_options *opt,
19031932
&merges);
19041933
switch (parent_count) {
19051934
case -1:
1906-
path_msg(opt, CONFLICT_SUBMODULE_CORRUPT, 0,
1935+
path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
19071936
path, NULL, NULL, NULL,
1908-
_("Failed to merge submodule %s "
1937+
_("error: failed to merge submodule %s "
19091938
"(repository corrupt)"),
19101939
path);
19111940
ret = -1;
@@ -2111,7 +2140,7 @@ static int handle_content_merge(struct merge_options *opt,
21112140
* merges, which happens for example with rename/rename(2to1) and
21122141
* rename/add conflicts.
21132142
*/
2114-
unsigned clean = 1;
2143+
int clean = 1;
21152144

21162145
/*
21172146
* handle_content_merge() needs both files to be of the same type, i.e.
@@ -2175,25 +2204,37 @@ static int handle_content_merge(struct merge_options *opt,
21752204
pathnames, extra_marker_size,
21762205
&result_buf);
21772206

2178-
if ((merge_status < 0) || !result_buf.ptr)
2179-
ret = error(_("failed to execute internal merge"));
2207+
if ((merge_status < 0) || !result_buf.ptr) {
2208+
path_msg(opt, ERROR_THREEWAY_CONTENT_MERGE_FAILED, 0,
2209+
pathnames[0], pathnames[1], pathnames[2], NULL,
2210+
_("error: failed to execute internal merge for %s"),
2211+
path);
2212+
ret = -1;
2213+
}
21802214

21812215
if (!ret &&
21822216
write_object_file(result_buf.ptr, result_buf.size,
2183-
OBJ_BLOB, &result->oid))
2184-
ret = error(_("unable to add %s to database"), path);
2185-
2217+
OBJ_BLOB, &result->oid)) {
2218+
path_msg(opt, ERROR_OBJECT_WRITE_FAILED, 0,
2219+
pathnames[0], pathnames[1], pathnames[2], NULL,
2220+
_("error: unable to add %s to database"), path);
2221+
ret = -1;
2222+
}
21862223
free(result_buf.ptr);
2224+
21872225
if (ret)
21882226
return -1;
2189-
clean &= (merge_status == 0);
2227+
if (merge_status > 0)
2228+
clean = 0;
21902229
path_msg(opt, INFO_AUTO_MERGING, 1, path, NULL, NULL, NULL,
21912230
_("Auto-merging %s"), path);
21922231
} else if (S_ISGITLINK(a->mode)) {
21932232
int two_way = ((S_IFMT & o->mode) != (S_IFMT & a->mode));
21942233
clean = merge_submodule(opt, pathnames[0],
21952234
two_way ? null_oid() : &o->oid,
21962235
&a->oid, &b->oid, &result->oid);
2236+
if (clean < 0)
2237+
return -1;
21972238
if (opt->priv->call_depth && two_way && !clean) {
21982239
result->mode = o->mode;
21992240
oidcpy(&result->oid, &o->oid);
@@ -3559,18 +3600,27 @@ static int sort_dirs_next_to_their_children(const char *one, const char *two)
35593600
return c1 - c2;
35603601
}
35613602

3562-
static int read_oid_strbuf(const struct object_id *oid,
3563-
struct strbuf *dst)
3603+
static int read_oid_strbuf(struct merge_options *opt,
3604+
const struct object_id *oid,
3605+
struct strbuf *dst,
3606+
const char *path)
35643607
{
35653608
void *buf;
35663609
enum object_type type;
35673610
unsigned long size;
35683611
buf = repo_read_object_file(the_repository, oid, &type, &size);
3569-
if (!buf)
3570-
return error(_("cannot read object %s"), oid_to_hex(oid));
3612+
if (!buf) {
3613+
path_msg(opt, ERROR_OBJECT_READ_FAILED, 0,
3614+
path, NULL, NULL, NULL,
3615+
_("error: cannot read object %s"), oid_to_hex(oid));
3616+
return -1;
3617+
}
35713618
if (type != OBJ_BLOB) {
35723619
free(buf);
3573-
return error(_("object %s is not a blob"), oid_to_hex(oid));
3620+
path_msg(opt, ERROR_OBJECT_NOT_A_BLOB, 0,
3621+
path, NULL, NULL, NULL,
3622+
_("error: object %s is not a blob"), oid_to_hex(oid));
3623+
return -1;
35743624
}
35753625
strbuf_attach(dst, buf, size, size + 1);
35763626
return 0;
@@ -3594,8 +3644,8 @@ static int blob_unchanged(struct merge_options *opt,
35943644
if (oideq(&base->oid, &side->oid))
35953645
return 1;
35963646

3597-
if (read_oid_strbuf(&base->oid, &basebuf) ||
3598-
read_oid_strbuf(&side->oid, &sidebuf))
3647+
if (read_oid_strbuf(opt, &base->oid, &basebuf, path) ||
3648+
read_oid_strbuf(opt, &side->oid, &sidebuf, path))
35993649
goto error_return;
36003650
/*
36013651
* Note: binary | is used so that both renormalizations are
@@ -4645,6 +4695,7 @@ void merge_display_update_messages(struct merge_options *opt,
46454695
struct hashmap_iter iter;
46464696
struct strmap_entry *e;
46474697
struct string_list olist = STRING_LIST_INIT_NODUP;
4698+
FILE *o = stdout;
46484699

46494700
if (opt->record_conflict_msgs_as_headers)
46504701
BUG("Either display conflict messages or record them as headers, not both");
@@ -4661,32 +4712,42 @@ void merge_display_update_messages(struct merge_options *opt,
46614712
}
46624713
string_list_sort(&olist);
46634714

4715+
/* Print to stderr if we hit errors rather than just conflicts */
4716+
if (result->clean < 0)
4717+
o = stderr;
4718+
46644719
/* Iterate over the items, printing them */
46654720
for (int path_nr = 0; path_nr < olist.nr; ++path_nr) {
46664721
struct string_list *conflicts = olist.items[path_nr].util;
46674722
for (int i = 0; i < conflicts->nr; i++) {
46684723
struct logical_conflict_info *info =
46694724
conflicts->items[i].util;
46704725

4726+
/* On failure, ignore regular conflict types */
4727+
if (result->clean < 0 &&
4728+
info->type < NB_REGULAR_CONFLICT_TYPES)
4729+
continue;
4730+
46714731
if (detailed) {
4672-
printf("%lu", (unsigned long)info->paths.nr);
4673-
putchar('\0');
4732+
fprintf(o, "%lu", (unsigned long)info->paths.nr);
4733+
fputc('\0', o);
46744734
for (int n = 0; n < info->paths.nr; n++) {
4675-
fputs(info->paths.v[n], stdout);
4676-
putchar('\0');
4735+
fputs(info->paths.v[n], o);
4736+
fputc('\0', o);
46774737
}
4678-
fputs(type_short_descriptions[info->type],
4679-
stdout);
4680-
putchar('\0');
4738+
fputs(type_short_descriptions[info->type], o);
4739+
fputc('\0', o);
46814740
}
4682-
puts(conflicts->items[i].string);
4741+
fputs(conflicts->items[i].string, o);
4742+
fputc('\n', o);
46834743
if (detailed)
4684-
putchar('\0');
4744+
fputc('\0', o);
46854745
}
46864746
}
46874747
string_list_clear(&olist, 0);
46884748

4689-
print_submodule_conflict_suggestion(&opti->conflicted_submodules);
4749+
if (result->clean >= 0)
4750+
print_submodule_conflict_suggestion(&opti->conflicted_submodules);
46904751

46914752
/* Also include needed rename limit adjustment now */
46924753
diff_warn_rename_limit("merge.renamelimit",
@@ -5002,6 +5063,26 @@ static void merge_check_renames_reusable(struct merge_result *result,
50025063

50035064
/*** Function Grouping: merge_incore_*() and their internal variants ***/
50045065

5066+
static void move_opt_priv_to_result_priv(struct merge_options *opt,
5067+
struct merge_result *result)
5068+
{
5069+
/*
5070+
* opt->priv and result->priv are a bit weird. opt->priv contains
5071+
* information that we can re-use in subsequent merge operations to
5072+
* enable our cached renames optimization. The best way to provide
5073+
* that to subsequent merges is putting it in result->priv.
5074+
* However, putting it directly there would mean retrofitting lots
5075+
* of functions in this file to also take a merge_result pointer,
5076+
* which is ugly and annoying. So, we just make sure at the end of
5077+
* the merge (the outer merge if there are internal recursive ones)
5078+
* to move it.
5079+
*/
5080+
assert(opt->priv && !result->priv);
5081+
result->priv = opt->priv;
5082+
result->_properly_initialized = RESULT_INITIALIZED;
5083+
opt->priv = NULL;
5084+
}
5085+
50055086
/*
50065087
* Originally from merge_trees_internal(); heavily adapted, though.
50075088
*/
@@ -5032,6 +5113,7 @@ static void merge_ort_nonrecursive_internal(struct merge_options *opt,
50325113
oid_to_hex(&side1->object.oid),
50335114
oid_to_hex(&side2->object.oid));
50345115
result->clean = -1;
5116+
move_opt_priv_to_result_priv(opt, result);
50355117
return;
50365118
}
50375119
trace2_region_leave("merge", "collect_merge_info", opt->repo);
@@ -5062,11 +5144,8 @@ static void merge_ort_nonrecursive_internal(struct merge_options *opt,
50625144
/* existence of conflicted entries implies unclean */
50635145
result->clean &= strmap_empty(&opt->priv->conflicted);
50645146
}
5065-
if (!opt->priv->call_depth) {
5066-
result->priv = opt->priv;
5067-
result->_properly_initialized = RESULT_INITIALIZED;
5068-
opt->priv = NULL;
5069-
}
5147+
if (!opt->priv->call_depth || result->clean < 0)
5148+
move_opt_priv_to_result_priv(opt, result);
50705149
}
50715150

50725151
/*

t/t6406-merge-attr.sh

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ test_expect_success !WINDOWS 'custom merge driver that is killed with a signal'
185185
186186
>./please-abort &&
187187
echo "* merge=custom" >.gitattributes &&
188-
test_must_fail git merge main 2>err &&
188+
test_expect_code 2 git merge main 2>err &&
189189
grep "^error: failed to execute internal merge" err &&
190190
git ls-files -u >output &&
191191
git diff --name-only HEAD >>output &&
@@ -261,4 +261,44 @@ test_expect_success 'binary files with union attribute' '
261261
grep -i "warning.*cannot merge.*HEAD vs. bin-main" output
262262
'
263263

264+
test_expect_success !WINDOWS 'custom merge driver that is killed with a signal on recursive merge' '
265+
test_when_finished "rm -f output please-abort" &&
266+
test_when_finished "git checkout side" &&
267+
268+
git reset --hard anchor &&
269+
270+
git checkout -b base-a main^ &&
271+
echo base-a >text &&
272+
git commit -m base-a text &&
273+
274+
git checkout -b base-b main^ &&
275+
echo base-b >text &&
276+
git commit -m base-b text &&
277+
278+
git checkout -b recursive-a base-a &&
279+
test_must_fail git merge base-b &&
280+
echo recursive-a >text &&
281+
git add text &&
282+
git commit -m recursive-a &&
283+
284+
git checkout -b recursive-b base-b &&
285+
test_must_fail git merge base-a &&
286+
echo recursive-b >text &&
287+
git add text &&
288+
git commit -m recursive-b &&
289+
290+
git config --replace-all \
291+
merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" &&
292+
git config --replace-all \
293+
merge.custom.name "custom merge driver for testing" &&
294+
295+
>./please-abort &&
296+
echo "* merge=custom" >.gitattributes &&
297+
test_expect_code 2 git merge recursive-a 2>err &&
298+
grep "error: failed to execute internal merge" err &&
299+
git ls-files -u >output &&
300+
git diff --name-only HEAD >>output &&
301+
test_must_be_empty output
302+
'
303+
264304
test_done

0 commit comments

Comments
 (0)