| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 1 | /* |
| 2 | * Low level 3-way in-core file merge. |
| 3 | * |
| 4 | * Copyright (c) 2007 Junio C Hamano |
| 5 | */ |
| 6 | |
| 7 | #include "cache.h" |
| 8 | #include "attr.h" |
| 9 | #include "xdiff-interface.h" |
| 10 | #include "run-command.h" |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 11 | #include "ll-merge.h" |
| 12 | |
| 13 | struct ll_merge_driver; |
| 14 | |
| 15 | typedef int (*ll_merge_fn)(const struct ll_merge_driver *, |
| 16 | mmbuffer_t *result, |
| 17 | const char *path, |
| 18 | mmfile_t *orig, |
| 19 | mmfile_t *src1, const char *name1, |
| 20 | mmfile_t *src2, const char *name2, |
| 21 | int virtual_ancestor); |
| 22 | |
| 23 | struct ll_merge_driver { |
| 24 | const char *name; |
| 25 | const char *description; |
| 26 | ll_merge_fn fn; |
| 27 | const char *recursive; |
| 28 | struct ll_merge_driver *next; |
| 29 | char *cmdline; |
| 30 | }; |
| 31 | |
| 32 | /* |
| 33 | * Built-in low-levels |
| 34 | */ |
| 35 | static int ll_binary_merge(const struct ll_merge_driver *drv_unused, |
| 36 | mmbuffer_t *result, |
| 37 | const char *path_unused, |
| 38 | mmfile_t *orig, |
| 39 | mmfile_t *src1, const char *name1, |
| 40 | mmfile_t *src2, const char *name2, |
| 41 | int virtual_ancestor) |
| 42 | { |
| 43 | /* |
| 44 | * The tentative merge result is "ours" for the final round, |
| 45 | * or common ancestor for an internal merge. Still return |
| 46 | * "conflicted merge" status. |
| 47 | */ |
| 48 | mmfile_t *stolen = virtual_ancestor ? orig : src1; |
| 49 | |
| 50 | result->ptr = stolen->ptr; |
| 51 | result->size = stolen->size; |
| 52 | stolen->ptr = NULL; |
| 53 | return 1; |
| 54 | } |
| 55 | |
| 56 | static int ll_xdl_merge(const struct ll_merge_driver *drv_unused, |
| 57 | mmbuffer_t *result, |
| Martin Renold | 606475f | 2009-07-01 20:18:04 | [diff] [blame] | 58 | const char *path, |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 59 | mmfile_t *orig, |
| 60 | mmfile_t *src1, const char *name1, |
| 61 | mmfile_t *src2, const char *name2, |
| 62 | int virtual_ancestor) |
| 63 | { |
| 64 | xpparam_t xpp; |
| Junio C Hamano | c236bcd | 2008-08-29 17:59:16 | [diff] [blame] | 65 | int style = 0; |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 66 | |
| 67 | if (buffer_is_binary(orig->ptr, orig->size) || |
| 68 | buffer_is_binary(src1->ptr, src1->size) || |
| 69 | buffer_is_binary(src2->ptr, src2->size)) { |
| Martin Renold | 606475f | 2009-07-01 20:18:04 | [diff] [blame] | 70 | warning("Cannot merge binary files: %s (%s vs. %s)\n", |
| 71 | path, name1, name2); |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 72 | return ll_binary_merge(drv_unused, result, |
| Martin Renold | 606475f | 2009-07-01 20:18:04 | [diff] [blame] | 73 | path, |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 74 | orig, src1, name1, |
| 75 | src2, name2, |
| 76 | virtual_ancestor); |
| 77 | } |
| 78 | |
| 79 | memset(&xpp, 0, sizeof(xpp)); |
| Junio C Hamano | c236bcd | 2008-08-29 17:59:16 | [diff] [blame] | 80 | if (git_xmerge_style >= 0) |
| 81 | style = git_xmerge_style; |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 82 | return xdl_merge(orig, |
| 83 | src1, name1, |
| 84 | src2, name2, |
| Junio C Hamano | c236bcd | 2008-08-29 17:59:16 | [diff] [blame] | 85 | &xpp, XDL_MERGE_ZEALOUS | style, |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 86 | result); |
| 87 | } |
| 88 | |
| 89 | static int ll_union_merge(const struct ll_merge_driver *drv_unused, |
| 90 | mmbuffer_t *result, |
| 91 | const char *path_unused, |
| 92 | mmfile_t *orig, |
| 93 | mmfile_t *src1, const char *name1, |
| 94 | mmfile_t *src2, const char *name2, |
| 95 | int virtual_ancestor) |
| 96 | { |
| 97 | char *src, *dst; |
| 98 | long size; |
| 99 | const int marker_size = 7; |
| Junio C Hamano | c236bcd | 2008-08-29 17:59:16 | [diff] [blame] | 100 | int status, saved_style; |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 101 | |
| Junio C Hamano | c236bcd | 2008-08-29 17:59:16 | [diff] [blame] | 102 | /* We have to force the RCS "merge" style */ |
| 103 | saved_style = git_xmerge_style; |
| 104 | git_xmerge_style = 0; |
| 105 | status = ll_xdl_merge(drv_unused, result, path_unused, |
| 106 | orig, src1, NULL, src2, NULL, |
| 107 | virtual_ancestor); |
| 108 | git_xmerge_style = saved_style; |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 109 | if (status <= 0) |
| 110 | return status; |
| 111 | size = result->size; |
| 112 | src = dst = result->ptr; |
| 113 | while (size) { |
| 114 | char ch; |
| 115 | if ((marker_size < size) && |
| 116 | (*src == '<' || *src == '=' || *src == '>')) { |
| 117 | int i; |
| 118 | ch = *src; |
| 119 | for (i = 0; i < marker_size; i++) |
| 120 | if (src[i] != ch) |
| 121 | goto not_a_marker; |
| 122 | if (src[marker_size] != '\n') |
| 123 | goto not_a_marker; |
| 124 | src += marker_size + 1; |
| 125 | size -= marker_size + 1; |
| 126 | continue; |
| 127 | } |
| 128 | not_a_marker: |
| 129 | do { |
| 130 | ch = *src++; |
| 131 | *dst++ = ch; |
| 132 | size--; |
| 133 | } while (ch != '\n' && size); |
| 134 | } |
| 135 | result->size = dst - result->ptr; |
| 136 | return 0; |
| 137 | } |
| 138 | |
| 139 | #define LL_BINARY_MERGE 0 |
| 140 | #define LL_TEXT_MERGE 1 |
| 141 | #define LL_UNION_MERGE 2 |
| 142 | static struct ll_merge_driver ll_merge_drv[] = { |
| 143 | { "binary", "built-in binary merge", ll_binary_merge }, |
| 144 | { "text", "built-in 3-way text merge", ll_xdl_merge }, |
| 145 | { "union", "built-in union merge", ll_union_merge }, |
| 146 | }; |
| 147 | |
| 148 | static void create_temp(mmfile_t *src, char *path) |
| 149 | { |
| 150 | int fd; |
| 151 | |
| 152 | strcpy(path, ".merge_file_XXXXXX"); |
| 153 | fd = xmkstemp(path); |
| 154 | if (write_in_full(fd, src->ptr, src->size) != src->size) |
| Thomas Rast | 0721c31 | 2009-06-27 15:58:47 | [diff] [blame] | 155 | die_errno("unable to write temp-file"); |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 156 | close(fd); |
| 157 | } |
| 158 | |
| 159 | /* |
| 160 | * User defined low-level merge driver support. |
| 161 | */ |
| 162 | static int ll_ext_merge(const struct ll_merge_driver *fn, |
| 163 | mmbuffer_t *result, |
| 164 | const char *path, |
| 165 | mmfile_t *orig, |
| 166 | mmfile_t *src1, const char *name1, |
| 167 | mmfile_t *src2, const char *name2, |
| 168 | int virtual_ancestor) |
| 169 | { |
| 170 | char temp[3][50]; |
| René Scharfe | ced621b | 2008-11-22 23:13:00 | [diff] [blame] | 171 | struct strbuf cmd = STRBUF_INIT; |
| 172 | struct strbuf_expand_dict_entry dict[] = { |
| 173 | { "O", temp[0] }, |
| 174 | { "A", temp[1] }, |
| 175 | { "B", temp[2] }, |
| 176 | { NULL } |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 177 | }; |
| Johannes Sixt | 0077138 | 2009-06-08 20:34:29 | [diff] [blame] | 178 | const char *args[] = { "sh", "-c", NULL, NULL }; |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 179 | int status, fd, i; |
| 180 | struct stat st; |
| 181 | |
| 182 | if (fn->cmdline == NULL) |
| 183 | die("custom merge driver %s lacks command line.", fn->name); |
| 184 | |
| 185 | result->ptr = NULL; |
| 186 | result->size = 0; |
| 187 | create_temp(orig, temp[0]); |
| 188 | create_temp(src1, temp[1]); |
| 189 | create_temp(src2, temp[2]); |
| 190 | |
| René Scharfe | ced621b | 2008-11-22 23:13:00 | [diff] [blame] | 191 | strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict); |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 192 | |
| René Scharfe | ced621b | 2008-11-22 23:13:00 | [diff] [blame] | 193 | args[2] = cmd.buf; |
| Johannes Sixt | 0077138 | 2009-06-08 20:34:29 | [diff] [blame] | 194 | status = run_command_v_opt(args, 0); |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 195 | fd = open(temp[1], O_RDONLY); |
| 196 | if (fd < 0) |
| 197 | goto bad; |
| 198 | if (fstat(fd, &st)) |
| 199 | goto close_bad; |
| 200 | result->size = st.st_size; |
| 201 | result->ptr = xmalloc(result->size + 1); |
| 202 | if (read_in_full(fd, result->ptr, result->size) != result->size) { |
| 203 | free(result->ptr); |
| 204 | result->ptr = NULL; |
| 205 | result->size = 0; |
| 206 | } |
| 207 | close_bad: |
| 208 | close(fd); |
| 209 | bad: |
| 210 | for (i = 0; i < 3; i++) |
| Alex Riesen | 691f1a2 | 2009-04-29 21:22:56 | [diff] [blame] | 211 | unlink_or_warn(temp[i]); |
| René Scharfe | ced621b | 2008-11-22 23:13:00 | [diff] [blame] | 212 | strbuf_release(&cmd); |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 213 | return status; |
| 214 | } |
| 215 | |
| 216 | /* |
| 217 | * merge.default and merge.driver configuration items |
| 218 | */ |
| 219 | static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail; |
| 220 | static const char *default_ll_merge; |
| 221 | |
| Johannes Schindelin | ef90d6d | 2008-05-14 17:46:53 | [diff] [blame] | 222 | static int read_merge_config(const char *var, const char *value, void *cb) |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 223 | { |
| 224 | struct ll_merge_driver *fn; |
| 225 | const char *ep, *name; |
| 226 | int namelen; |
| 227 | |
| 228 | if (!strcmp(var, "merge.default")) { |
| 229 | if (value) |
| Jim Meyering | 90dce51 | 2009-06-14 19:47:54 | [diff] [blame] | 230 | default_ll_merge = xstrdup(value); |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 231 | return 0; |
| 232 | } |
| 233 | |
| 234 | /* |
| 235 | * We are not interested in anything but "merge.<name>.variable"; |
| 236 | * especially, we do not want to look at variables such as |
| 237 | * "merge.summary", "merge.tool", and "merge.verbosity". |
| 238 | */ |
| 239 | if (prefixcmp(var, "merge.") || (ep = strrchr(var, '.')) == var + 5) |
| 240 | return 0; |
| 241 | |
| 242 | /* |
| 243 | * Find existing one as we might be processing merge.<name>.var2 |
| 244 | * after seeing merge.<name>.var1. |
| 245 | */ |
| 246 | name = var + 6; |
| 247 | namelen = ep - name; |
| 248 | for (fn = ll_user_merge; fn; fn = fn->next) |
| 249 | if (!strncmp(fn->name, name, namelen) && !fn->name[namelen]) |
| 250 | break; |
| 251 | if (!fn) { |
| 252 | fn = xcalloc(1, sizeof(struct ll_merge_driver)); |
| 253 | fn->name = xmemdupz(name, namelen); |
| 254 | fn->fn = ll_ext_merge; |
| 255 | *ll_user_merge_tail = fn; |
| 256 | ll_user_merge_tail = &(fn->next); |
| 257 | } |
| 258 | |
| 259 | ep++; |
| 260 | |
| 261 | if (!strcmp("name", ep)) { |
| 262 | if (!value) |
| 263 | return error("%s: lacks value", var); |
| Jim Meyering | 90dce51 | 2009-06-14 19:47:54 | [diff] [blame] | 264 | fn->description = xstrdup(value); |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 265 | return 0; |
| 266 | } |
| 267 | |
| 268 | if (!strcmp("driver", ep)) { |
| 269 | if (!value) |
| 270 | return error("%s: lacks value", var); |
| 271 | /* |
| 272 | * merge.<name>.driver specifies the command line: |
| 273 | * |
| 274 | * command-line |
| 275 | * |
| 276 | * The command-line will be interpolated with the following |
| 277 | * tokens and is given to the shell: |
| 278 | * |
| 279 | * %O - temporary file name for the merge base. |
| 280 | * %A - temporary file name for our version. |
| 281 | * %B - temporary file name for the other branches' version. |
| 282 | * |
| 283 | * The external merge driver should write the results in the |
| 284 | * file named by %A, and signal that it has done with zero exit |
| 285 | * status. |
| 286 | */ |
| Jim Meyering | 90dce51 | 2009-06-14 19:47:54 | [diff] [blame] | 287 | fn->cmdline = xstrdup(value); |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 288 | return 0; |
| 289 | } |
| 290 | |
| 291 | if (!strcmp("recursive", ep)) { |
| 292 | if (!value) |
| 293 | return error("%s: lacks value", var); |
| Jim Meyering | 90dce51 | 2009-06-14 19:47:54 | [diff] [blame] | 294 | fn->recursive = xstrdup(value); |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 295 | return 0; |
| 296 | } |
| 297 | |
| 298 | return 0; |
| 299 | } |
| 300 | |
| 301 | static void initialize_ll_merge(void) |
| 302 | { |
| 303 | if (ll_user_merge_tail) |
| 304 | return; |
| 305 | ll_user_merge_tail = &ll_user_merge; |
| Johannes Schindelin | ef90d6d | 2008-05-14 17:46:53 | [diff] [blame] | 306 | git_config(read_merge_config, NULL); |
| Junio C Hamano | 525ab63 | 2007-12-24 08:36:00 | [diff] [blame] | 307 | } |
| 308 | |
| 309 | static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr) |
| 310 | { |
| 311 | struct ll_merge_driver *fn; |
| 312 | const char *name; |
| 313 | int i; |
| 314 | |
| 315 | initialize_ll_merge(); |
| 316 | |
| 317 | if (ATTR_TRUE(merge_attr)) |
| 318 | return &ll_merge_drv[LL_TEXT_MERGE]; |
| 319 | else if (ATTR_FALSE(merge_attr)) |
| 320 | return &ll_merge_drv[LL_BINARY_MERGE]; |
| 321 | else if (ATTR_UNSET(merge_attr)) { |
| 322 | if (!default_ll_merge) |
| 323 | return &ll_merge_drv[LL_TEXT_MERGE]; |
| 324 | else |
| 325 | name = default_ll_merge; |
| 326 | } |
| 327 | else |
| 328 | name = merge_attr; |
| 329 | |
| 330 | for (fn = ll_user_merge; fn; fn = fn->next) |
| 331 | if (!strcmp(fn->name, name)) |
| 332 | return fn; |
| 333 | |
| 334 | for (i = 0; i < ARRAY_SIZE(ll_merge_drv); i++) |
| 335 | if (!strcmp(ll_merge_drv[i].name, name)) |
| 336 | return &ll_merge_drv[i]; |
| 337 | |
| 338 | /* default to the 3-way */ |
| 339 | return &ll_merge_drv[LL_TEXT_MERGE]; |
| 340 | } |
| 341 | |
| 342 | static const char *git_path_check_merge(const char *path) |
| 343 | { |
| 344 | static struct git_attr_check attr_merge_check; |
| 345 | |
| 346 | if (!attr_merge_check.attr) |
| 347 | attr_merge_check.attr = git_attr("merge", 5); |
| 348 | |
| 349 | if (git_checkattr(path, 1, &attr_merge_check)) |
| 350 | return NULL; |
| 351 | return attr_merge_check.value; |
| 352 | } |
| 353 | |
| 354 | int ll_merge(mmbuffer_t *result_buf, |
| 355 | const char *path, |
| 356 | mmfile_t *ancestor, |
| 357 | mmfile_t *ours, const char *our_label, |
| 358 | mmfile_t *theirs, const char *their_label, |
| 359 | int virtual_ancestor) |
| 360 | { |
| 361 | const char *ll_driver_name; |
| 362 | const struct ll_merge_driver *driver; |
| 363 | |
| 364 | ll_driver_name = git_path_check_merge(path); |
| 365 | driver = find_ll_merge_driver(ll_driver_name); |
| 366 | |
| 367 | if (virtual_ancestor && driver->recursive) |
| 368 | driver = find_ll_merge_driver(driver->recursive); |
| 369 | return driver->fn(driver, result_buf, path, |
| 370 | ancestor, |
| 371 | ours, our_label, |
| 372 | theirs, their_label, virtual_ancestor); |
| 373 | } |