ll-merge: pass the original path to external drivers

The interface to custom low-level merge driver was modeled to be capable of driving programs like "merge" (from the RCS suite) that can produce result solely by looking at three files that hold contents of common ancestor, ours and theirs. The information we feed to the external drivers via the command line placeholders %O, %A, and %B were designed to be purely about contents by giving names of the temporary files that hold these variants without exposing the original pathname. No matter where the result goes, merging the same three variants should produce the same result, contents is the king, that is the Git way. The external driver interface, however, is meant to help people to step outside the Git worldview, and sometimes people want to know the final path that the resulting merged contents would be stored in. Expose this to the external drivers via a new placeholder %P. Requested-by: Andreas Gondek Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jun 4, 2015 at 15:10 UTC ef45bb1f8156030446658d5bfb3983ce214a9e16
3 files changed +21 -8
Documentation/gitattributes.txt
+4 -1
@@ -774,7 +774,7 @@ To define a custom merge driver `filfre`, add a section to your
774 ----------------------------------------------------------------
775 [merge "filfre"]
776 name = feel-free merge driver
777 - driver = filfre %O %A %B
777 + driver = filfre %O %A %B %L %P
778 recursive = binary
779 ----------------------------------------------------------------
780
@@ -800,6 +800,9 @@ merge between common ancestors, when there are more than one.
800 When left unspecified, the driver itself is used for both
801 internal merge and the final merge.
802
803 +The merge driver can learn the pathname in which the merged result
804 +will be stored via placeholder `%P`.
805 +
806
807 `conflict-marker-size`
808 ^^^^^^^^^^^^^^^^^^^^^^
ll-merge.c
+8 -2
@@ -9,6 +9,7 @@
9 #include "xdiff-interface.h"
10 #include "run-command.h"
11 #include "ll-merge.h"
12 +#include "quote.h"
13
14 struct ll_merge_driver;
15
@@ -166,17 +167,20 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
167 {
168 char temp[4][50];
169 struct strbuf cmd = STRBUF_INIT;
169 - struct strbuf_expand_dict_entry dict[5];
170 + struct strbuf_expand_dict_entry dict[6];
171 + struct strbuf path_sq = STRBUF_INIT;
172 const char *args[] = { NULL, NULL };
173 int status, fd, i;
174 struct stat st;
175 assert(opts);
176
177 + sq_quote_buf(&path_sq, path);
178 dict[0].placeholder = "O"; dict[0].value = temp[0];
179 dict[1].placeholder = "A"; dict[1].value = temp[1];
180 dict[2].placeholder = "B"; dict[2].value = temp[2];
181 dict[3].placeholder = "L"; dict[3].value = temp[3];
179 - dict[4].placeholder = NULL; dict[4].value = NULL;
182 + dict[4].placeholder = "P"; dict[4].value = path_sq.buf;
183 + dict[5].placeholder = NULL; dict[5].value = NULL;
184
185 if (fn->cmdline == NULL)
186 die("custom merge driver %s lacks command line.", fn->name);
@@ -210,6 +214,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
214 for (i = 0; i < 3; i++)
215 unlink_or_warn(temp[i]);
216 strbuf_release(&cmd);
217 + strbuf_release(&path_sq);
218 return status;
219 }
220
@@ -269,6 +274,7 @@ static int read_merge_config(const char *var, const char *value, void *cb)
274 * %A - temporary file name for our version.
275 * %B - temporary file name for the other branches' version.
276 * %L - conflict marker length
277 + * %P - the original path (safely quoted for the shell)
278 *
279 * The external merge driver should write the results in the
280 * file named by %A, and signal that it has done with zero exit
t/t6026-merge-attr.sh
+9 -5
@@ -85,11 +85,12 @@ test_expect_success 'retry the merge with longer context' '
85 cat >./custom-merge <<\EOF
86 #!/bin/sh
87
88 -orig="$1" ours="$2" theirs="$3" exit="$4"
88 +orig="$1" ours="$2" theirs="$3" exit="$4" path=$5
89 (
90 echo "orig is $orig"
91 echo "ours is $ours"
92 echo "theirs is $theirs"
93 + echo "path is $path"
94 echo "=== orig ==="
95 cat "$orig"
96 echo "=== ours ==="
@@ -110,7 +111,7 @@ test_expect_success 'custom merge backend' '
111
112 git reset --hard anchor &&
113 git config --replace-all \
113 - merge.custom.driver "./custom-merge %O %A %B 0" &&
114 + merge.custom.driver "./custom-merge %O %A %B 0 %P" &&
115 git config --replace-all \
116 merge.custom.name "custom merge driver for testing" &&
117
@@ -121,7 +122,7 @@ test_expect_success 'custom merge backend' '
122 o=$(git unpack-file master^:text) &&
123 a=$(git unpack-file side^:text) &&
124 b=$(git unpack-file master:text) &&
124 - sh -c "./custom-merge $o $a $b 0" &&
125 + sh -c "./custom-merge $o $a $b 0 'text'" &&
126 sed -e 1,3d $a >check-2 &&
127 cmp check-1 check-2 &&
128 rm -f $o $a $b
@@ -131,7 +132,7 @@ test_expect_success 'custom merge backend' '
132
133 git reset --hard anchor &&
134 git config --replace-all \
134 - merge.custom.driver "./custom-merge %O %A %B 1" &&
135 + merge.custom.driver "./custom-merge %O %A %B 1 %P" &&
136 git config --replace-all \
137 merge.custom.name "custom merge driver for testing" &&
138
@@ -148,9 +149,12 @@ test_expect_success 'custom merge backend' '
149 o=$(git unpack-file master^:text) &&
150 a=$(git unpack-file anchor:text) &&
151 b=$(git unpack-file master:text) &&
151 - sh -c "./custom-merge $o $a $b 0" &&
152 + sh -c "./custom-merge $o $a $b 0 'text'" &&
153 sed -e 1,3d $a >check-2 &&
154 cmp check-1 check-2 &&
155 + sed -e 1,3d -e 4q $a >check-3 &&
156 + echo "path is text" >expect &&
157 + cmp expect check-3 &&
158 rm -f $o $a $b
159 '
160