merge-ll: expose revision names to custom drivers
Custom merge drivers need access to the names of the revisions they are working on, so that the merge conflict markers they introduce can refer to those revisions. The placeholders '%S', '%X' and '%Y' are introduced to this end. Signed-off-by: Antonin Delpeuch <antonin@delpeuch.eu> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Antonin Delpeuch committed
Jan 24, 2024 at 20:09 UTC
81effe94682dbfed55171468074db85fa661cc21
3 files changed
+30
-12
Documentation/gitattributes.txt
+5
-4
@@ -1137,11 +1137,11 @@ The `merge.*.name` variable gives the driver a human-readable
1137
name.
1138
1139
The `merge.*.driver` variable's value is used to construct a
1140
-command to run to merge ancestor's version (`%O`), current
1140
+command to run to common ancestor's version (`%O`), current
1141
version (`%A`) and the other branches' version (`%B`). These
1142
three tokens are replaced with the names of temporary files that
1143
hold the contents of these versions when the command line is
1144
-built. Additionally, %L will be replaced with the conflict marker
1144
+built. Additionally, `%L` will be replaced with the conflict marker
1145
size (see below).
1146
1147
The merge driver is expected to leave the result of the merge in
@@ -1159,8 +1159,9 @@ When left unspecified, the driver itself is used for both
1159
internal merge and the final merge.
1160
1161
The merge driver can learn the pathname in which the merged result
1162
-will be stored via placeholder `%P`.
1163
-
1162
+will be stored via placeholder `%P`. The conflict labels to be used
1163
+for the common ancestor, local head and other head can be passed by
1164
+using '%S', '%X' and '%Y` respectively.
1165
1166
`conflict-marker-size`
1167
^^^^^^^^^^^^^^^^^^^^^^
merge-ll.c
+14
-3
@@ -185,9 +185,9 @@ static void create_temp(mmfile_t *src, char *path, size_t len)
185
static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,
186
mmbuffer_t *result,
187
const char *path,
188
- mmfile_t *orig, const char *orig_name UNUSED,
189
- mmfile_t *src1, const char *name1 UNUSED,
190
- mmfile_t *src2, const char *name2 UNUSED,
188
+ mmfile_t *orig, const char *orig_name,
189
+ mmfile_t *src1, const char *name1,
190
+ mmfile_t *src2, const char *name2,
191
const struct ll_merge_options *opts,
192
int marker_size)
193
{
@@ -222,6 +222,12 @@ static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn,
222
strbuf_addf(&cmd, "%d", marker_size);
223
else if (skip_prefix(format, "P", &format))
224
sq_quote_buf(&cmd, path);
225
+ else if (skip_prefix(format, "S", &format))
226
+ sq_quote_buf(&cmd, orig_name ? orig_name : "");
227
+ else if (skip_prefix(format, "X", &format))
228
+ sq_quote_buf(&cmd, name1 ? name1 : "");
229
+ else if (skip_prefix(format, "Y", &format))
230
+ sq_quote_buf(&cmd, name2 ? name2 : "");
231
else
232
strbuf_addch(&cmd, '%');
233
}
@@ -315,7 +321,12 @@ static int read_merge_config(const char *var, const char *value,
321
* %B - temporary file name for the other branches' version.
322
* %L - conflict marker length
323
* %P - the original path (safely quoted for the shell)
324
+ * %S - the revision for the merge base
325
+ * %X - the revision for our version
326
+ * %Y - the revision for their version
327
*
328
+ * If the file is not named indentically in all versions, then each
329
+ * revision is joined with the corresponding path, separated by a colon.
330
* The external merge driver should write the results in the
331
* file named by %A, and signal that it has done with zero exit
332
* status.
t/t6406-merge-attr.sh
+11
-5
@@ -42,11 +42,15 @@ test_expect_success setup '
42
#!/bin/sh
43
44
orig="$1" ours="$2" theirs="$3" exit="$4" path=$5
45
+ orig_name="$6" our_name="$7" their_name="$8"
46
(
47
echo "orig is $orig"
48
echo "ours is $ours"
49
echo "theirs is $theirs"
50
echo "path is $path"
51
+ echo "orig_name is $orig_name"
52
+ echo "our_name is $our_name"
53
+ echo "their_name is $their_name"
54
echo "=== orig ==="
55
cat "$orig"
56
echo "=== ours ==="
@@ -121,7 +125,7 @@ test_expect_success 'custom merge backend' '
125
126
git reset --hard anchor &&
127
git config --replace-all \
124
- merge.custom.driver "./custom-merge %O %A %B 0 %P" &&
128
+ merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" &&
129
git config --replace-all \
130
merge.custom.name "custom merge driver for testing" &&
131
@@ -132,7 +136,8 @@ test_expect_success 'custom merge backend' '
136
o=$(git unpack-file main^:text) &&
137
a=$(git unpack-file side^:text) &&
138
b=$(git unpack-file main:text) &&
135
- sh -c "./custom-merge $o $a $b 0 text" &&
139
+ base_revid=$(git rev-parse --short main^) &&
140
+ sh -c "./custom-merge $o $a $b 0 text $base_revid HEAD main" &&
141
sed -e 1,3d $a >check-2 &&
142
cmp check-1 check-2 &&
143
rm -f $o $a $b
@@ -142,7 +147,7 @@ test_expect_success 'custom merge backend' '
147
148
git reset --hard anchor &&
149
git config --replace-all \
145
- merge.custom.driver "./custom-merge %O %A %B 1 %P" &&
150
+ merge.custom.driver "./custom-merge %O %A %B 1 %P %S %X %Y" &&
151
git config --replace-all \
152
merge.custom.name "custom merge driver for testing" &&
153
@@ -159,7 +164,8 @@ test_expect_success 'custom merge backend' '
164
o=$(git unpack-file main^:text) &&
165
a=$(git unpack-file anchor:text) &&
166
b=$(git unpack-file main:text) &&
162
- sh -c "./custom-merge $o $a $b 0 text" &&
167
+ base_revid=$(git rev-parse --short main^) &&
168
+ sh -c "./custom-merge $o $a $b 0 text $base_revid HEAD main" &&
169
sed -e 1,3d $a >check-2 &&
170
cmp check-1 check-2 &&
171
sed -e 1,3d -e 4q $a >check-3 &&
@@ -173,7 +179,7 @@ test_expect_success !WINDOWS 'custom merge driver that is killed with a signal'
179
180
git reset --hard anchor &&
181
git config --replace-all \
176
- merge.custom.driver "./custom-merge %O %A %B 0 %P" &&
182
+ merge.custom.driver "./custom-merge %O %A %B 0 %P %S %X %Y" &&
183
git config --replace-all \
184
merge.custom.name "custom merge driver for testing" &&
185