Skip to content

Commit 0821b73

Browse files
committed
Merge branch 'sb/submodule-merge-in-merge-recursive'
By code restructuring of submodule merge in merge-recursive, informational messages from the codepath are now given using the same mechanism as other output, and honor the merge.verbosity configuration. The code also learned to give a few new messages when a submodule three-way merge resolves cleanly when one side records a descendant of the commit chosen by the other side. * sb/submodule-merge-in-merge-recursive: merge-recursive: give notice when submodule commit gets fast-forwarded merge-recursive: i18n submodule merge output and respect verbosity submodule.c: move submodule merging to merge-recursive.c
2 parents 2305770 + 76f4212 commit 0821b73

File tree

3 files changed

+186
-173
lines changed

3 files changed

+186
-173
lines changed

merge-recursive.c

Lines changed: 182 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "merge-recursive.h"
2424
#include "dir.h"
2525
#include "submodule.h"
26+
#include "revision.h"
2627

2728
struct path_hashmap_entry {
2829
struct hashmap_entry e;
@@ -1082,6 +1083,185 @@ static int merge_3way(struct merge_options *o,
10821083
return merge_status;
10831084
}
10841085

1086+
static int find_first_merges(struct object_array *result, const char *path,
1087+
struct commit *a, struct commit *b)
1088+
{
1089+
int i, j;
1090+
struct object_array merges = OBJECT_ARRAY_INIT;
1091+
struct commit *commit;
1092+
int contains_another;
1093+
1094+
char merged_revision[42];
1095+
const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path",
1096+
"--all", merged_revision, NULL };
1097+
struct rev_info revs;
1098+
struct setup_revision_opt rev_opts;
1099+
1100+
memset(result, 0, sizeof(struct object_array));
1101+
memset(&rev_opts, 0, sizeof(rev_opts));
1102+
1103+
/* get all revisions that merge commit a */
1104+
xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
1105+
oid_to_hex(&a->object.oid));
1106+
init_revisions(&revs, NULL);
1107+
rev_opts.submodule = path;
1108+
/* FIXME: can't handle linked worktrees in submodules yet */
1109+
revs.single_worktree = path != NULL;
1110+
setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
1111+
1112+
/* save all revisions from the above list that contain b */
1113+
if (prepare_revision_walk(&revs))
1114+
die("revision walk setup failed");
1115+
while ((commit = get_revision(&revs)) != NULL) {
1116+
struct object *o = &(commit->object);
1117+
if (in_merge_bases(b, commit))
1118+
add_object_array(o, NULL, &merges);
1119+
}
1120+
reset_revision_walk();
1121+
1122+
/* Now we've got all merges that contain a and b. Prune all
1123+
* merges that contain another found merge and save them in
1124+
* result.
1125+
*/
1126+
for (i = 0; i < merges.nr; i++) {
1127+
struct commit *m1 = (struct commit *) merges.objects[i].item;
1128+
1129+
contains_another = 0;
1130+
for (j = 0; j < merges.nr; j++) {
1131+
struct commit *m2 = (struct commit *) merges.objects[j].item;
1132+
if (i != j && in_merge_bases(m2, m1)) {
1133+
contains_another = 1;
1134+
break;
1135+
}
1136+
}
1137+
1138+
if (!contains_another)
1139+
add_object_array(merges.objects[i].item, NULL, result);
1140+
}
1141+
1142+
object_array_clear(&merges);
1143+
return result->nr;
1144+
}
1145+
1146+
static void print_commit(struct commit *commit)
1147+
{
1148+
struct strbuf sb = STRBUF_INIT;
1149+
struct pretty_print_context ctx = {0};
1150+
ctx.date_mode.type = DATE_NORMAL;
1151+
format_commit_message(commit, " %h: %m %s", &sb, &ctx);
1152+
fprintf(stderr, "%s\n", sb.buf);
1153+
strbuf_release(&sb);
1154+
}
1155+
1156+
static int merge_submodule(struct merge_options *o,
1157+
struct object_id *result, const char *path,
1158+
const struct object_id *base, const struct object_id *a,
1159+
const struct object_id *b)
1160+
{
1161+
struct commit *commit_base, *commit_a, *commit_b;
1162+
int parent_count;
1163+
struct object_array merges;
1164+
1165+
int i;
1166+
int search = !o->call_depth;
1167+
1168+
/* store a in result in case we fail */
1169+
oidcpy(result, a);
1170+
1171+
/* we can not handle deletion conflicts */
1172+
if (is_null_oid(base))
1173+
return 0;
1174+
if (is_null_oid(a))
1175+
return 0;
1176+
if (is_null_oid(b))
1177+
return 0;
1178+
1179+
if (add_submodule_odb(path)) {
1180+
output(o, 1, _("Failed to merge submodule %s (not checked out)"), path);
1181+
return 0;
1182+
}
1183+
1184+
if (!(commit_base = lookup_commit_reference(base)) ||
1185+
!(commit_a = lookup_commit_reference(a)) ||
1186+
!(commit_b = lookup_commit_reference(b))) {
1187+
output(o, 1, _("Failed to merge submodule %s (commits not present)"), path);
1188+
return 0;
1189+
}
1190+
1191+
/* check whether both changes are forward */
1192+
if (!in_merge_bases(commit_base, commit_a) ||
1193+
!in_merge_bases(commit_base, commit_b)) {
1194+
output(o, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path);
1195+
return 0;
1196+
}
1197+
1198+
/* Case #1: a is contained in b or vice versa */
1199+
if (in_merge_bases(commit_a, commit_b)) {
1200+
oidcpy(result, b);
1201+
if (show(o, 3)) {
1202+
output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
1203+
output_commit_title(o, commit_b);
1204+
} else if (show(o, 2))
1205+
output(o, 2, _("Fast-forwarding submodule %s to %s"), path, oid_to_hex(b));
1206+
else
1207+
; /* no output */
1208+
1209+
return 1;
1210+
}
1211+
if (in_merge_bases(commit_b, commit_a)) {
1212+
oidcpy(result, a);
1213+
if (show(o, 3)) {
1214+
output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path);
1215+
output_commit_title(o, commit_a);
1216+
} else if (show(o, 2))
1217+
output(o, 2, _("Fast-forwarding submodule %s to %s"), path, oid_to_hex(a));
1218+
else
1219+
; /* no output */
1220+
1221+
return 1;
1222+
}
1223+
1224+
/*
1225+
* Case #2: There are one or more merges that contain a and b in
1226+
* the submodule. If there is only one, then present it as a
1227+
* suggestion to the user, but leave it marked unmerged so the
1228+
* user needs to confirm the resolution.
1229+
*/
1230+
1231+
/* Skip the search if makes no sense to the calling context. */
1232+
if (!search)
1233+
return 0;
1234+
1235+
/* find commit which merges them */
1236+
parent_count = find_first_merges(&merges, path, commit_a, commit_b);
1237+
switch (parent_count) {
1238+
case 0:
1239+
output(o, 1, _("Failed to merge submodule %s (merge following commits not found)"), path);
1240+
break;
1241+
1242+
case 1:
1243+
output(o, 1, _("Failed to merge submodule %s (not fast-forward)"), path);
1244+
output(o, 2, _("Found a possible merge resolution for the submodule:\n"));
1245+
print_commit((struct commit *) merges.objects[0].item);
1246+
output(o, 2, _(
1247+
"If this is correct simply add it to the index "
1248+
"for example\n"
1249+
"by using:\n\n"
1250+
" git update-index --cacheinfo 160000 %s \"%s\"\n\n"
1251+
"which will accept this suggestion.\n"),
1252+
oid_to_hex(&merges.objects[0].item->oid), path);
1253+
break;
1254+
1255+
default:
1256+
output(o, 1, _("Failed to merge submodule %s (multiple merges found)"), path);
1257+
for (i = 0; i < merges.nr; i++)
1258+
print_commit((struct commit *) merges.objects[i].item);
1259+
}
1260+
1261+
object_array_clear(&merges);
1262+
return 0;
1263+
}
1264+
10851265
static int merge_file_1(struct merge_options *o,
10861266
const struct diff_filespec *one,
10871267
const struct diff_filespec *a,
@@ -1145,12 +1325,11 @@ static int merge_file_1(struct merge_options *o,
11451325
return ret;
11461326
result->clean = (merge_status == 0);
11471327
} else if (S_ISGITLINK(a->mode)) {
1148-
result->clean = merge_submodule(&result->oid,
1328+
result->clean = merge_submodule(o, &result->oid,
11491329
one->path,
11501330
&one->oid,
11511331
&a->oid,
1152-
&b->oid,
1153-
!o->call_depth);
1332+
&b->oid);
11541333
} else if (S_ISLNK(a->mode)) {
11551334
switch (o->recursive_variant) {
11561335
case MERGE_RECURSIVE_NORMAL:

submodule.c

Lines changed: 2 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ void stage_updated_gitmodules(struct index_state *istate)
153153
die(_("staging updated .gitmodules failed"));
154154
}
155155

156-
static int add_submodule_odb(const char *path)
156+
/* TODO: remove this function, use repo_submodule_init instead. */
157+
int add_submodule_odb(const char *path)
157158
{
158159
struct strbuf objects_directory = STRBUF_INIT;
159160
int ret = 0;
@@ -1700,171 +1701,6 @@ int submodule_move_head(const char *path,
17001701
return ret;
17011702
}
17021703

1703-
static int find_first_merges(struct object_array *result, const char *path,
1704-
struct commit *a, struct commit *b)
1705-
{
1706-
int i, j;
1707-
struct object_array merges = OBJECT_ARRAY_INIT;
1708-
struct commit *commit;
1709-
int contains_another;
1710-
1711-
char merged_revision[42];
1712-
const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path",
1713-
"--all", merged_revision, NULL };
1714-
struct rev_info revs;
1715-
struct setup_revision_opt rev_opts;
1716-
1717-
memset(result, 0, sizeof(struct object_array));
1718-
memset(&rev_opts, 0, sizeof(rev_opts));
1719-
1720-
/* get all revisions that merge commit a */
1721-
xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
1722-
oid_to_hex(&a->object.oid));
1723-
init_revisions(&revs, NULL);
1724-
rev_opts.submodule = path;
1725-
/* FIXME: can't handle linked worktrees in submodules yet */
1726-
revs.single_worktree = path != NULL;
1727-
setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
1728-
1729-
/* save all revisions from the above list that contain b */
1730-
if (prepare_revision_walk(&revs))
1731-
die("revision walk setup failed");
1732-
while ((commit = get_revision(&revs)) != NULL) {
1733-
struct object *o = &(commit->object);
1734-
if (in_merge_bases(b, commit))
1735-
add_object_array(o, NULL, &merges);
1736-
}
1737-
reset_revision_walk();
1738-
1739-
/* Now we've got all merges that contain a and b. Prune all
1740-
* merges that contain another found merge and save them in
1741-
* result.
1742-
*/
1743-
for (i = 0; i < merges.nr; i++) {
1744-
struct commit *m1 = (struct commit *) merges.objects[i].item;
1745-
1746-
contains_another = 0;
1747-
for (j = 0; j < merges.nr; j++) {
1748-
struct commit *m2 = (struct commit *) merges.objects[j].item;
1749-
if (i != j && in_merge_bases(m2, m1)) {
1750-
contains_another = 1;
1751-
break;
1752-
}
1753-
}
1754-
1755-
if (!contains_another)
1756-
add_object_array(merges.objects[i].item, NULL, result);
1757-
}
1758-
1759-
object_array_clear(&merges);
1760-
return result->nr;
1761-
}
1762-
1763-
static void print_commit(struct commit *commit)
1764-
{
1765-
struct strbuf sb = STRBUF_INIT;
1766-
struct pretty_print_context ctx = {0};
1767-
ctx.date_mode.type = DATE_NORMAL;
1768-
format_commit_message(commit, " %h: %m %s", &sb, &ctx);
1769-
fprintf(stderr, "%s\n", sb.buf);
1770-
strbuf_release(&sb);
1771-
}
1772-
1773-
#define MERGE_WARNING(path, msg) \
1774-
warning("Failed to merge submodule %s (%s)", path, msg);
1775-
1776-
int merge_submodule(struct object_id *result, const char *path,
1777-
const struct object_id *base, const struct object_id *a,
1778-
const struct object_id *b, int search)
1779-
{
1780-
struct commit *commit_base, *commit_a, *commit_b;
1781-
int parent_count;
1782-
struct object_array merges;
1783-
1784-
int i;
1785-
1786-
/* store a in result in case we fail */
1787-
oidcpy(result, a);
1788-
1789-
/* we can not handle deletion conflicts */
1790-
if (is_null_oid(base))
1791-
return 0;
1792-
if (is_null_oid(a))
1793-
return 0;
1794-
if (is_null_oid(b))
1795-
return 0;
1796-
1797-
if (add_submodule_odb(path)) {
1798-
MERGE_WARNING(path, "not checked out");
1799-
return 0;
1800-
}
1801-
1802-
if (!(commit_base = lookup_commit_reference(base)) ||
1803-
!(commit_a = lookup_commit_reference(a)) ||
1804-
!(commit_b = lookup_commit_reference(b))) {
1805-
MERGE_WARNING(path, "commits not present");
1806-
return 0;
1807-
}
1808-
1809-
/* check whether both changes are forward */
1810-
if (!in_merge_bases(commit_base, commit_a) ||
1811-
!in_merge_bases(commit_base, commit_b)) {
1812-
MERGE_WARNING(path, "commits don't follow merge-base");
1813-
return 0;
1814-
}
1815-
1816-
/* Case #1: a is contained in b or vice versa */
1817-
if (in_merge_bases(commit_a, commit_b)) {
1818-
oidcpy(result, b);
1819-
return 1;
1820-
}
1821-
if (in_merge_bases(commit_b, commit_a)) {
1822-
oidcpy(result, a);
1823-
return 1;
1824-
}
1825-
1826-
/*
1827-
* Case #2: There are one or more merges that contain a and b in
1828-
* the submodule. If there is only one, then present it as a
1829-
* suggestion to the user, but leave it marked unmerged so the
1830-
* user needs to confirm the resolution.
1831-
*/
1832-
1833-
/* Skip the search if makes no sense to the calling context. */
1834-
if (!search)
1835-
return 0;
1836-
1837-
/* find commit which merges them */
1838-
parent_count = find_first_merges(&merges, path, commit_a, commit_b);
1839-
switch (parent_count) {
1840-
case 0:
1841-
MERGE_WARNING(path, "merge following commits not found");
1842-
break;
1843-
1844-
case 1:
1845-
MERGE_WARNING(path, "not fast-forward");
1846-
fprintf(stderr, "Found a possible merge resolution "
1847-
"for the submodule:\n");
1848-
print_commit((struct commit *) merges.objects[0].item);
1849-
fprintf(stderr,
1850-
"If this is correct simply add it to the index "
1851-
"for example\n"
1852-
"by using:\n\n"
1853-
" git update-index --cacheinfo 160000 %s \"%s\"\n\n"
1854-
"which will accept this suggestion.\n",
1855-
oid_to_hex(&merges.objects[0].item->oid), path);
1856-
break;
1857-
1858-
default:
1859-
MERGE_WARNING(path, "multiple merges found");
1860-
for (i = 0; i < merges.nr; i++)
1861-
print_commit((struct commit *) merges.objects[i].item);
1862-
}
1863-
1864-
object_array_clear(&merges);
1865-
return 0;
1866-
}
1867-
18681704
/*
18691705
* Embeds a single submodules git directory into the superprojects git dir,
18701706
* non recursively.

0 commit comments

Comments
 (0)