Coverage Report

Created: 2024-09-08 06:23

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