Git fork
at reftables-rust 470 lines 13 kB view raw
1/* 2 * Low level 3-way in-core file merge. 3 * 4 * Copyright (c) 2007 Junio C Hamano 5 */ 6 7#define USE_THE_REPOSITORY_VARIABLE 8#define DISABLE_SIGN_COMPARE_WARNINGS 9 10#include "git-compat-util.h" 11#include "config.h" 12#include "convert.h" 13#include "attr.h" 14#include "xdiff-interface.h" 15#include "run-command.h" 16#include "merge-ll.h" 17#include "quote.h" 18#include "strbuf.h" 19#include "gettext.h" 20 21struct ll_merge_driver; 22 23typedef enum ll_merge_result (*ll_merge_fn)(const struct ll_merge_driver *, 24 mmbuffer_t *result, 25 const char *path, 26 mmfile_t *orig, const char *orig_name, 27 mmfile_t *src1, const char *name1, 28 mmfile_t *src2, const char *name2, 29 const struct ll_merge_options *opts, 30 int marker_size); 31 32struct ll_merge_driver { 33 const char *name; 34 const char *description; 35 ll_merge_fn fn; 36 char *recursive; 37 struct ll_merge_driver *next; 38 char *cmdline; 39}; 40 41static struct attr_check *merge_attributes; 42static struct attr_check *load_merge_attributes(void) 43{ 44 if (!merge_attributes) 45 merge_attributes = attr_check_initl("merge", "conflict-marker-size", NULL); 46 return merge_attributes; 47} 48 49void reset_merge_attributes(void) 50{ 51 attr_check_free(merge_attributes); 52 merge_attributes = NULL; 53} 54 55/* 56 * Built-in low-levels 57 */ 58static enum ll_merge_result ll_binary_merge(const struct ll_merge_driver *drv UNUSED, 59 mmbuffer_t *result, 60 const char *path UNUSED, 61 mmfile_t *orig, const char *orig_name UNUSED, 62 mmfile_t *src1, const char *name1 UNUSED, 63 mmfile_t *src2, const char *name2 UNUSED, 64 const struct ll_merge_options *opts, 65 int marker_size UNUSED) 66{ 67 enum ll_merge_result ret; 68 mmfile_t *stolen; 69 assert(opts); 70 71 /* 72 * The tentative merge result is the common ancestor for an 73 * internal merge. For the final merge, it is "ours" by 74 * default but -Xours/-Xtheirs can tweak the choice. 75 */ 76 if (opts->virtual_ancestor) { 77 stolen = orig; 78 ret = LL_MERGE_OK; 79 } else { 80 switch (opts->variant) { 81 default: 82 ret = LL_MERGE_BINARY_CONFLICT; 83 stolen = src1; 84 break; 85 case XDL_MERGE_FAVOR_OURS: 86 ret = LL_MERGE_OK; 87 stolen = src1; 88 break; 89 case XDL_MERGE_FAVOR_THEIRS: 90 ret = LL_MERGE_OK; 91 stolen = src2; 92 break; 93 } 94 } 95 96 result->ptr = stolen->ptr; 97 result->size = stolen->size; 98 stolen->ptr = NULL; 99 100 return ret; 101} 102 103static enum ll_merge_result ll_xdl_merge(const struct ll_merge_driver *drv_unused, 104 mmbuffer_t *result, 105 const char *path, 106 mmfile_t *orig, const char *orig_name, 107 mmfile_t *src1, const char *name1, 108 mmfile_t *src2, const char *name2, 109 const struct ll_merge_options *opts, 110 int marker_size) 111{ 112 enum ll_merge_result ret; 113 xmparam_t xmp; 114 int status; 115 assert(opts); 116 117 if (orig->size > MAX_XDIFF_SIZE || 118 src1->size > MAX_XDIFF_SIZE || 119 src2->size > MAX_XDIFF_SIZE || 120 buffer_is_binary(orig->ptr, orig->size) || 121 buffer_is_binary(src1->ptr, src1->size) || 122 buffer_is_binary(src2->ptr, src2->size)) { 123 return ll_binary_merge(drv_unused, result, 124 path, 125 orig, orig_name, 126 src1, name1, 127 src2, name2, 128 opts, marker_size); 129 } 130 131 memset(&xmp, 0, sizeof(xmp)); 132 xmp.level = XDL_MERGE_ZEALOUS; 133 xmp.favor = opts->variant; 134 xmp.xpp.flags = opts->xdl_opts; 135 if (opts->conflict_style >= 0) 136 xmp.style = opts->conflict_style; 137 else if (git_xmerge_style >= 0) 138 xmp.style = git_xmerge_style; 139 if (marker_size > 0) 140 xmp.marker_size = marker_size; 141 xmp.ancestor = orig_name; 142 xmp.file1 = name1; 143 xmp.file2 = name2; 144 status = xdl_merge(orig, src1, src2, &xmp, result); 145 ret = (status > 0) ? LL_MERGE_CONFLICT : status; 146 return ret; 147} 148 149static enum ll_merge_result ll_union_merge(const struct ll_merge_driver *drv_unused, 150 mmbuffer_t *result, 151 const char *path, 152 mmfile_t *orig, const char *orig_name, 153 mmfile_t *src1, const char *name1, 154 mmfile_t *src2, const char *name2, 155 const struct ll_merge_options *opts, 156 int marker_size) 157{ 158 /* Use union favor */ 159 struct ll_merge_options o; 160 assert(opts); 161 o = *opts; 162 o.variant = XDL_MERGE_FAVOR_UNION; 163 return ll_xdl_merge(drv_unused, result, path, 164 orig, orig_name, src1, name1, src2, name2, 165 &o, marker_size); 166} 167 168#define LL_BINARY_MERGE 0 169#define LL_TEXT_MERGE 1 170#define LL_UNION_MERGE 2 171static struct ll_merge_driver ll_merge_drv[] = { 172 { "binary", "built-in binary merge", ll_binary_merge }, 173 { "text", "built-in 3-way text merge", ll_xdl_merge }, 174 { "union", "built-in union merge", ll_union_merge }, 175}; 176 177static void create_temp(mmfile_t *src, char *path, size_t len) 178{ 179 int fd; 180 181 xsnprintf(path, len, ".merge_file_XXXXXX"); 182 fd = xmkstemp(path); 183 if (write_in_full(fd, src->ptr, src->size) < 0) 184 die_errno("unable to write temp-file"); 185 close(fd); 186} 187 188/* 189 * User defined low-level merge driver support. 190 */ 191static enum ll_merge_result ll_ext_merge(const struct ll_merge_driver *fn, 192 mmbuffer_t *result, 193 const char *path, 194 mmfile_t *orig, const char *orig_name, 195 mmfile_t *src1, const char *name1, 196 mmfile_t *src2, const char *name2, 197 const struct ll_merge_options *opts, 198 int marker_size) 199{ 200 char temp[3][50]; 201 struct strbuf cmd = STRBUF_INIT; 202 const char *format = fn->cmdline; 203 struct child_process child = CHILD_PROCESS_INIT; 204 int status, fd, i; 205 struct stat st; 206 enum ll_merge_result ret; 207 assert(opts); 208 209 if (!fn->cmdline) 210 die("custom merge driver %s lacks command line.", fn->name); 211 212 result->ptr = NULL; 213 result->size = 0; 214 create_temp(orig, temp[0], sizeof(temp[0])); 215 create_temp(src1, temp[1], sizeof(temp[1])); 216 create_temp(src2, temp[2], sizeof(temp[2])); 217 218 while (strbuf_expand_step(&cmd, &format)) { 219 if (skip_prefix(format, "%", &format)) 220 strbuf_addch(&cmd, '%'); 221 else if (skip_prefix(format, "O", &format)) 222 strbuf_addstr(&cmd, temp[0]); 223 else if (skip_prefix(format, "A", &format)) 224 strbuf_addstr(&cmd, temp[1]); 225 else if (skip_prefix(format, "B", &format)) 226 strbuf_addstr(&cmd, temp[2]); 227 else if (skip_prefix(format, "L", &format)) 228 strbuf_addf(&cmd, "%d", marker_size); 229 else if (skip_prefix(format, "P", &format)) 230 sq_quote_buf(&cmd, path); 231 else if (skip_prefix(format, "S", &format)) 232 sq_quote_buf(&cmd, orig_name ? orig_name : ""); 233 else if (skip_prefix(format, "X", &format)) 234 sq_quote_buf(&cmd, name1 ? name1 : ""); 235 else if (skip_prefix(format, "Y", &format)) 236 sq_quote_buf(&cmd, name2 ? name2 : ""); 237 else 238 strbuf_addch(&cmd, '%'); 239 } 240 241 child.use_shell = 1; 242 strvec_push(&child.args, cmd.buf); 243 status = run_command(&child); 244 fd = open(temp[1], O_RDONLY); 245 if (fd < 0) 246 goto bad; 247 if (fstat(fd, &st)) 248 goto close_bad; 249 result->size = st.st_size; 250 result->ptr = xmallocz(result->size); 251 if (read_in_full(fd, result->ptr, result->size) != result->size) { 252 FREE_AND_NULL(result->ptr); 253 result->size = 0; 254 } 255 close_bad: 256 close(fd); 257 bad: 258 for (i = 0; i < 3; i++) 259 unlink_or_warn(temp[i]); 260 strbuf_release(&cmd); 261 if (!status) 262 ret = LL_MERGE_OK; 263 else if (status <= 128) 264 ret = LL_MERGE_CONFLICT; 265 else 266 /* died due to a signal: WTERMSIG(status) + 128 */ 267 ret = LL_MERGE_ERROR; 268 return ret; 269} 270 271/* 272 * merge.default and merge.driver configuration items 273 */ 274static struct ll_merge_driver *ll_user_merge, **ll_user_merge_tail; 275static char *default_ll_merge; 276 277static int read_merge_config(const char *var, const char *value, 278 const struct config_context *ctx UNUSED, 279 void *cb UNUSED) 280{ 281 struct ll_merge_driver *fn; 282 const char *key, *name; 283 size_t namelen; 284 285 if (!strcmp(var, "merge.default")) 286 return git_config_string(&default_ll_merge, var, value); 287 288 /* 289 * We are not interested in anything but "merge.<name>.variable"; 290 * especially, we do not want to look at variables such as 291 * "merge.summary", "merge.tool", and "merge.verbosity". 292 */ 293 if (parse_config_key(var, "merge", &name, &namelen, &key) < 0 || !name) 294 return 0; 295 296 /* 297 * Find existing one as we might be processing merge.<name>.var2 298 * after seeing merge.<name>.var1. 299 */ 300 for (fn = ll_user_merge; fn; fn = fn->next) 301 if (!xstrncmpz(fn->name, name, namelen)) 302 break; 303 if (!fn) { 304 CALLOC_ARRAY(fn, 1); 305 fn->name = xmemdupz(name, namelen); 306 fn->fn = ll_ext_merge; 307 *ll_user_merge_tail = fn; 308 ll_user_merge_tail = &(fn->next); 309 } 310 311 if (!strcmp("name", key)) { 312 /* 313 * The description is leaking, but that's okay as we want to 314 * keep around the merge drivers anyway. 315 */ 316 return git_config_string((char **) &fn->description, var, value); 317 } 318 319 if (!strcmp("driver", key)) { 320 if (!value) 321 return config_error_nonbool(var); 322 /* 323 * merge.<name>.driver specifies the command line: 324 * 325 * command-line 326 * 327 * The command-line will be interpolated with the following 328 * tokens and is given to the shell: 329 * 330 * %O - temporary file name for the merge base. 331 * %A - temporary file name for our version. 332 * %B - temporary file name for the other branches' version. 333 * %L - conflict marker length 334 * %P - the original path (safely quoted for the shell) 335 * %S - the revision for the merge base 336 * %X - the revision for our version 337 * %Y - the revision for their version 338 * 339 * If the file is not named identically in all versions, then each 340 * revision is joined with the corresponding path, separated by a colon. 341 * The external merge driver should write the results in the 342 * file named by %A, and signal that it has done with zero exit 343 * status. 344 */ 345 fn->cmdline = xstrdup(value); 346 return 0; 347 } 348 349 if (!strcmp("recursive", key)) 350 return git_config_string(&fn->recursive, var, value); 351 352 return 0; 353} 354 355static void initialize_ll_merge(void) 356{ 357 if (ll_user_merge_tail) 358 return; 359 ll_user_merge_tail = &ll_user_merge; 360 repo_config(the_repository, read_merge_config, NULL); 361} 362 363static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr) 364{ 365 struct ll_merge_driver *fn; 366 const char *name; 367 int i; 368 369 initialize_ll_merge(); 370 371 if (ATTR_TRUE(merge_attr)) 372 return &ll_merge_drv[LL_TEXT_MERGE]; 373 else if (ATTR_FALSE(merge_attr)) 374 return &ll_merge_drv[LL_BINARY_MERGE]; 375 else if (ATTR_UNSET(merge_attr)) { 376 if (!default_ll_merge) 377 return &ll_merge_drv[LL_TEXT_MERGE]; 378 else 379 name = default_ll_merge; 380 } 381 else 382 name = merge_attr; 383 384 for (fn = ll_user_merge; fn; fn = fn->next) 385 if (!strcmp(fn->name, name)) 386 return fn; 387 388 for (i = 0; i < ARRAY_SIZE(ll_merge_drv); i++) 389 if (!strcmp(ll_merge_drv[i].name, name)) 390 return &ll_merge_drv[i]; 391 392 /* default to the 3-way */ 393 return &ll_merge_drv[LL_TEXT_MERGE]; 394} 395 396static void normalize_file(mmfile_t *mm, const char *path, struct index_state *istate) 397{ 398 struct strbuf strbuf = STRBUF_INIT; 399 if (renormalize_buffer(istate, path, mm->ptr, mm->size, &strbuf)) { 400 free(mm->ptr); 401 mm->size = strbuf.len; 402 mm->ptr = strbuf_detach(&strbuf, NULL); 403 } 404} 405 406enum ll_merge_result ll_merge(mmbuffer_t *result_buf, 407 const char *path, 408 mmfile_t *ancestor, const char *ancestor_label, 409 mmfile_t *ours, const char *our_label, 410 mmfile_t *theirs, const char *their_label, 411 struct index_state *istate, 412 const struct ll_merge_options *opts) 413{ 414 struct attr_check *check = load_merge_attributes(); 415 static const struct ll_merge_options default_opts = LL_MERGE_OPTIONS_INIT; 416 const char *ll_driver_name = NULL; 417 int marker_size = DEFAULT_CONFLICT_MARKER_SIZE; 418 const struct ll_merge_driver *driver; 419 420 if (!opts) 421 opts = &default_opts; 422 423 if (opts->renormalize) { 424 normalize_file(ancestor, path, istate); 425 normalize_file(ours, path, istate); 426 normalize_file(theirs, path, istate); 427 } 428 429 git_check_attr(istate, path, check); 430 ll_driver_name = check->items[0].value; 431 if (check->items[1].value) { 432 if (strtol_i(check->items[1].value, 10, &marker_size)) { 433 marker_size = DEFAULT_CONFLICT_MARKER_SIZE; 434 warning(_("invalid marker-size '%s', expecting an integer"), check->items[1].value); 435 } 436 if (marker_size <= 0) 437 marker_size = DEFAULT_CONFLICT_MARKER_SIZE; 438 } 439 driver = find_ll_merge_driver(ll_driver_name); 440 441 if (opts->virtual_ancestor) { 442 if (driver->recursive) 443 driver = find_ll_merge_driver(driver->recursive); 444 } 445 if (opts->extra_marker_size) { 446 marker_size += opts->extra_marker_size; 447 } 448 return driver->fn(driver, result_buf, path, ancestor, ancestor_label, 449 ours, our_label, theirs, their_label, 450 opts, marker_size); 451} 452 453int ll_merge_marker_size(struct index_state *istate, const char *path) 454{ 455 static struct attr_check *check; 456 int marker_size = DEFAULT_CONFLICT_MARKER_SIZE; 457 458 if (!check) 459 check = attr_check_initl("conflict-marker-size", NULL); 460 git_check_attr(istate, path, check); 461 if (check->items[0].value) { 462 if (strtol_i(check->items[0].value, 10, &marker_size)) { 463 marker_size = DEFAULT_CONFLICT_MARKER_SIZE; 464 warning(_("invalid marker-size '%s', expecting an integer"), check->items[0].value); 465 } 466 if (marker_size <= 0) 467 marker_size = DEFAULT_CONFLICT_MARKER_SIZE; 468 } 469 return marker_size; 470}