Coverage Report

Created: 2024-09-08 06:23

/src/git/notes-merge.c
Line
Count
Source (jump to first uncovered line)
1
#define USE_THE_REPOSITORY_VARIABLE
2
3
#include "git-compat-util.h"
4
#include "advice.h"
5
#include "commit.h"
6
#include "gettext.h"
7
#include "refs.h"
8
#include "object-file.h"
9
#include "object-name.h"
10
#include "object-store-ll.h"
11
#include "path.h"
12
#include "repository.h"
13
#include "diff.h"
14
#include "diffcore.h"
15
#include "hex.h"
16
#include "xdiff-interface.h"
17
#include "merge-ll.h"
18
#include "dir.h"
19
#include "notes.h"
20
#include "notes-merge.h"
21
#include "strbuf.h"
22
#include "trace.h"
23
#include "notes-utils.h"
24
#include "commit-reach.h"
25
26
struct notes_merge_pair {
27
  struct object_id obj, base, local, remote;
28
};
29
30
void init_notes_merge_options(struct repository *r,
31
            struct notes_merge_options *o)
32
0
{
33
0
  memset(o, 0, sizeof(struct notes_merge_options));
34
0
  strbuf_init(&(o->commit_msg), 0);
35
0
  o->verbosity = NOTES_MERGE_VERBOSITY_DEFAULT;
36
0
  o->repo = r;
37
0
}
38
39
static int path_to_oid(const char *path, struct object_id *oid)
40
0
{
41
0
  char hex_oid[GIT_MAX_HEXSZ];
42
0
  int i = 0;
43
0
  while (*path && i < the_hash_algo->hexsz) {
44
0
    if (*path != '/')
45
0
      hex_oid[i++] = *path;
46
0
    path++;
47
0
  }
48
0
  if (*path || i != the_hash_algo->hexsz)
49
0
    return -1;
50
0
  return get_oid_hex(hex_oid, oid);
51
0
}
52
53
static int verify_notes_filepair(struct diff_filepair *p, struct object_id *oid)
54
0
{
55
0
  switch (p->status) {
56
0
  case DIFF_STATUS_MODIFIED:
57
0
    assert(p->one->mode == p->two->mode);
58
0
    assert(!is_null_oid(&p->one->oid));
59
0
    assert(!is_null_oid(&p->two->oid));
60
0
    break;
61
0
  case DIFF_STATUS_ADDED:
62
0
    assert(is_null_oid(&p->one->oid));
63
0
    break;
64
0
  case DIFF_STATUS_DELETED:
65
0
    assert(is_null_oid(&p->two->oid));
66
0
    break;
67
0
  default:
68
0
    return -1;
69
0
  }
70
0
  assert(!strcmp(p->one->path, p->two->path));
71
0
  return path_to_oid(p->one->path, oid);
72
0
}
73
74
static struct notes_merge_pair *find_notes_merge_pair_pos(
75
    struct notes_merge_pair *list, int len, struct object_id *obj,
76
    int insert_new, int *occupied)
77
0
{
78
  /*
79
   * Both diff_tree_remote() and diff_tree_local() tend to process
80
   * merge_pairs in ascending order. Therefore, cache last returned
81
   * index, and search sequentially from there until the appropriate
82
   * position is found.
83
   *
84
   * Since inserts only happen from diff_tree_remote() (which mainly
85
   * _appends_), we don't care that inserting into the middle of the
86
   * list is expensive (using memmove()).
87
   */
88
0
  static int last_index;
89
0
  int i = last_index < len ? last_index : len - 1;
90
0
  int prev_cmp = 0, cmp = -1;
91
0
  while (i >= 0 && i < len) {
92
0
    cmp = oidcmp(obj, &list[i].obj);
93
0
    if (!cmp) /* obj belongs @ i */
94
0
      break;
95
0
    else if (cmp < 0 && prev_cmp <= 0) /* obj belongs < i */
96
0
      i--;
97
0
    else if (cmp < 0) /* obj belongs between i-1 and i */
98
0
      break;
99
0
    else if (cmp > 0 && prev_cmp >= 0) /* obj belongs > i */
100
0
      i++;
101
0
    else /* if (cmp > 0) */ { /* obj belongs between i and i+1 */
102
0
      i++;
103
0
      break;
104
0
    }
105
0
    prev_cmp = cmp;
106
0
  }
107
0
  if (i < 0)
108
0
    i = 0;
109
  /* obj belongs at, or immediately preceding, index i (0 <= i <= len) */
110
111
0
  if (!cmp)
112
0
    *occupied = 1;
113
0
  else {
114
0
    *occupied = 0;
115
0
    if (insert_new && i < len) {
116
0
      MOVE_ARRAY(list + i + 1, list + i, len - i);
117
0
      memset(list + i, 0, sizeof(struct notes_merge_pair));
118
0
    }
119
0
  }
120
0
  last_index = i;
121
0
  return list + i;
122
0
}
123
124
static struct object_id uninitialized = {
125
  .hash =
126
  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
127
  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
128
};
129
130
static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o,
131
             const struct object_id *base,
132
             const struct object_id *remote,
133
             int *num_changes)
134
0
{
135
0
  struct diff_options opt;
136
0
  struct notes_merge_pair *changes;
137
0
  int i, len = 0;
138
139
0
  trace_printf("\tdiff_tree_remote(base = %.7s, remote = %.7s)\n",
140
0
         oid_to_hex(base), oid_to_hex(remote));
141
142
0
  repo_diff_setup(o->repo, &opt);
143
0
  opt.flags.recursive = 1;
144
0
  opt.output_format = DIFF_FORMAT_NO_OUTPUT;
145
0
  diff_setup_done(&opt);
146
0
  diff_tree_oid(base, remote, "", &opt);
147
0
  diffcore_std(&opt);
148
149
0
  CALLOC_ARRAY(changes, diff_queued_diff.nr);
150
151
0
  for (i = 0; i < diff_queued_diff.nr; i++) {
152
0
    struct diff_filepair *p = diff_queued_diff.queue[i];
153
0
    struct notes_merge_pair *mp;
154
0
    int occupied;
155
0
    struct object_id obj;
156
157
0
    if (verify_notes_filepair(p, &obj)) {
158
0
      trace_printf("\t\tCannot merge entry '%s' (%c): "
159
0
             "%.7s -> %.7s. Skipping!\n", p->one->path,
160
0
             p->status, oid_to_hex(&p->one->oid),
161
0
             oid_to_hex(&p->two->oid));
162
0
      continue;
163
0
    }
164
0
    mp = find_notes_merge_pair_pos(changes, len, &obj, 1, &occupied);
165
0
    if (occupied) {
166
      /* We've found an addition/deletion pair */
167
0
      assert(oideq(&mp->obj, &obj));
168
0
      if (is_null_oid(&p->one->oid)) { /* addition */
169
0
        assert(is_null_oid(&mp->remote));
170
0
        oidcpy(&mp->remote, &p->two->oid);
171
0
      } else if (is_null_oid(&p->two->oid)) { /* deletion */
172
0
        assert(is_null_oid(&mp->base));
173
0
        oidcpy(&mp->base, &p->one->oid);
174
0
      } else
175
0
        assert(!"Invalid existing change recorded");
176
0
    } else {
177
0
      oidcpy(&mp->obj, &obj);
178
0
      oidcpy(&mp->base, &p->one->oid);
179
0
      oidcpy(&mp->local, &uninitialized);
180
0
      oidcpy(&mp->remote, &p->two->oid);
181
0
      len++;
182
0
    }
183
0
    trace_printf("\t\tStored remote change for %s: %.7s -> %.7s\n",
184
0
           oid_to_hex(&mp->obj), oid_to_hex(&mp->base),
185
0
           oid_to_hex(&mp->remote));
186
0
  }
187
0
  diff_flush(&opt);
188
189
0
  *num_changes = len;
190
0
  return changes;
191
0
}
192
193
static void diff_tree_local(struct notes_merge_options *o,
194
          struct notes_merge_pair *changes, int len,
195
          const struct object_id *base,
196
          const struct object_id *local)
197
0
{
198
0
  struct diff_options opt;
199
0
  int i;
200
201
0
  trace_printf("\tdiff_tree_local(len = %i, base = %.7s, local = %.7s)\n",
202
0
         len, oid_to_hex(base), oid_to_hex(local));
203
204
0
  repo_diff_setup(o->repo, &opt);
205
0
  opt.flags.recursive = 1;
206
0
  opt.output_format = DIFF_FORMAT_NO_OUTPUT;
207
0
  diff_setup_done(&opt);
208
0
  diff_tree_oid(base, local, "", &opt);
209
0
  diffcore_std(&opt);
210
211
0
  for (i = 0; i < diff_queued_diff.nr; i++) {
212
0
    struct diff_filepair *p = diff_queued_diff.queue[i];
213
0
    struct notes_merge_pair *mp;
214
0
    int match;
215
0
    struct object_id obj;
216
217
0
    if (verify_notes_filepair(p, &obj)) {
218
0
      trace_printf("\t\tCannot merge entry '%s' (%c): "
219
0
             "%.7s -> %.7s. Skipping!\n", p->one->path,
220
0
             p->status, oid_to_hex(&p->one->oid),
221
0
             oid_to_hex(&p->two->oid));
222
0
      continue;
223
0
    }
224
0
    mp = find_notes_merge_pair_pos(changes, len, &obj, 0, &match);
225
0
    if (!match) {
226
0
      trace_printf("\t\tIgnoring local-only change for %s: "
227
0
             "%.7s -> %.7s\n", oid_to_hex(&obj),
228
0
             oid_to_hex(&p->one->oid),
229
0
             oid_to_hex(&p->two->oid));
230
0
      continue;
231
0
    }
232
233
0
    assert(oideq(&mp->obj, &obj));
234
0
    if (is_null_oid(&p->two->oid)) { /* deletion */
235
      /*
236
       * Either this is a true deletion (1), or it is part
237
       * of an A/D pair (2), or D/A pair (3):
238
       *
239
       * (1) mp->local is uninitialized; set it to null_sha1
240
       * (2) mp->local is not uninitialized; don't touch it
241
       * (3) mp->local is uninitialized; set it to null_sha1
242
       *     (will be overwritten by following addition)
243
       */
244
0
      if (oideq(&mp->local, &uninitialized))
245
0
        oidclr(&mp->local, the_repository->hash_algo);
246
0
    } else if (is_null_oid(&p->one->oid)) { /* addition */
247
      /*
248
       * Either this is a true addition (1), or it is part
249
       * of an A/D pair (2), or D/A pair (3):
250
       *
251
       * (1) mp->local is uninitialized; set to p->two->sha1
252
       * (2) mp->local is uninitialized; set to p->two->sha1
253
       * (3) mp->local is null_sha1;     set to p->two->sha1
254
       */
255
0
      assert(is_null_oid(&mp->local) ||
256
0
             oideq(&mp->local, &uninitialized));
257
0
      oidcpy(&mp->local, &p->two->oid);
258
0
    } else { /* modification */
259
      /*
260
       * This is a true modification. p->one->sha1 shall
261
       * match mp->base, and mp->local shall be uninitialized.
262
       * Set mp->local to p->two->sha1.
263
       */
264
0
      assert(oideq(&p->one->oid, &mp->base));
265
0
      assert(oideq(&mp->local, &uninitialized));
266
0
      oidcpy(&mp->local, &p->two->oid);
267
0
    }
268
0
    trace_printf("\t\tStored local change for %s: %.7s -> %.7s\n",
269
0
           oid_to_hex(&mp->obj), oid_to_hex(&mp->base),
270
0
           oid_to_hex(&mp->local));
271
0
  }
272
0
  diff_flush(&opt);
273
0
}
274
275
static void check_notes_merge_worktree(struct notes_merge_options *o)
276
0
{
277
0
  if (!o->has_worktree) {
278
    /*
279
     * Must establish NOTES_MERGE_WORKTREE.
280
     * Abort if NOTES_MERGE_WORKTREE already exists
281
     */
282
0
    if (file_exists(git_path(NOTES_MERGE_WORKTREE)) &&
283
0
        !is_empty_dir(git_path(NOTES_MERGE_WORKTREE))) {
284
0
      if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
285
0
        die(_("You have not concluded your previous "
286
0
            "notes merge (%s exists).\nPlease, use "
287
0
            "'git notes merge --commit' or 'git notes "
288
0
            "merge --abort' to commit/abort the "
289
0
            "previous merge before you start a new "
290
0
            "notes merge."), git_path("NOTES_MERGE_*"));
291
0
      else
292
0
        die(_("You have not concluded your notes merge "
293
0
            "(%s exists)."), git_path("NOTES_MERGE_*"));
294
0
    }
295
296
0
    if (safe_create_leading_directories_const(git_path(
297
0
        NOTES_MERGE_WORKTREE "/.test")))
298
0
      die_errno("unable to create directory %s",
299
0
          git_path(NOTES_MERGE_WORKTREE));
300
0
    o->has_worktree = 1;
301
0
  } else if (!file_exists(git_path(NOTES_MERGE_WORKTREE)))
302
    /* NOTES_MERGE_WORKTREE should already be established */
303
0
    die("missing '%s'. This should not happen",
304
0
        git_path(NOTES_MERGE_WORKTREE));
305
0
}
306
307
static void write_buf_to_worktree(const struct object_id *obj,
308
          const char *buf, unsigned long size)
309
0
{
310
0
  int fd;
311
0
  char *path = git_pathdup(NOTES_MERGE_WORKTREE "/%s", oid_to_hex(obj));
312
0
  if (safe_create_leading_directories_const(path))
313
0
    die_errno("unable to create directory for '%s'", path);
314
315
0
  fd = xopen(path, O_WRONLY | O_EXCL | O_CREAT, 0666);
316
317
0
  while (size > 0) {
318
0
    ssize_t ret = write_in_full(fd, buf, size);
319
0
    if (ret < 0) {
320
      /* Ignore epipe */
321
0
      if (errno == EPIPE)
322
0
        break;
323
0
      die_errno("notes-merge");
324
0
    }
325
0
    size -= ret;
326
0
    buf += ret;
327
0
  }
328
329
0
  close(fd);
330
0
  free(path);
331
0
}
332
333
static void write_note_to_worktree(const struct object_id *obj,
334
           const struct object_id *note)
335
0
{
336
0
  enum object_type type;
337
0
  unsigned long size;
338
0
  void *buf = repo_read_object_file(the_repository, note, &type, &size);
339
340
0
  if (!buf)
341
0
    die("cannot read note %s for object %s",
342
0
        oid_to_hex(note), oid_to_hex(obj));
343
0
  if (type != OBJ_BLOB)
344
0
    die("blob expected in note %s for object %s",
345
0
        oid_to_hex(note), oid_to_hex(obj));
346
0
  write_buf_to_worktree(obj, buf, size);
347
0
  free(buf);
348
0
}
349
350
static int ll_merge_in_worktree(struct notes_merge_options *o,
351
        struct notes_merge_pair *p)
352
0
{
353
0
  mmbuffer_t result_buf;
354
0
  mmfile_t base, local, remote;
355
0
  enum ll_merge_result status;
356
357
0
  read_mmblob(&base, &p->base);
358
0
  read_mmblob(&local, &p->local);
359
0
  read_mmblob(&remote, &p->remote);
360
361
0
  status = ll_merge(&result_buf, oid_to_hex(&p->obj), &base, NULL,
362
0
        &local, o->local_ref, &remote, o->remote_ref,
363
0
        o->repo->index, NULL);
364
365
0
  free(base.ptr);
366
0
  free(local.ptr);
367
0
  free(remote.ptr);
368
369
0
  if (status == LL_MERGE_BINARY_CONFLICT)
370
0
    warning("Cannot merge binary files: %s (%s vs. %s)",
371
0
      oid_to_hex(&p->obj), o->local_ref, o->remote_ref);
372
0
  if ((status < 0) || !result_buf.ptr)
373
0
    die("Failed to execute internal merge");
374
375
0
  write_buf_to_worktree(&p->obj, result_buf.ptr, result_buf.size);
376
0
  free(result_buf.ptr);
377
378
0
  return status;
379
0
}
380
381
static int merge_one_change_manual(struct notes_merge_options *o,
382
           struct notes_merge_pair *p,
383
           struct notes_tree *t)
384
0
{
385
0
  const char *lref = o->local_ref ? o->local_ref : "local version";
386
0
  const char *rref = o->remote_ref ? o->remote_ref : "remote version";
387
388
0
  trace_printf("\t\t\tmerge_one_change_manual(obj = %.7s, base = %.7s, "
389
0
         "local = %.7s, remote = %.7s)\n",
390
0
         oid_to_hex(&p->obj), oid_to_hex(&p->base),
391
0
         oid_to_hex(&p->local), oid_to_hex(&p->remote));
392
393
  /* add "Conflicts:" section to commit message first time through */
394
0
  if (!o->has_worktree)
395
0
    strbuf_addstr(&(o->commit_msg), "\n\nConflicts:\n");
396
397
0
  strbuf_addf(&(o->commit_msg), "\t%s\n", oid_to_hex(&p->obj));
398
399
0
  if (o->verbosity >= 2)
400
0
    printf("Auto-merging notes for %s\n", oid_to_hex(&p->obj));
401
0
  check_notes_merge_worktree(o);
402
0
  if (is_null_oid(&p->local)) {
403
    /* D/F conflict, checkout p->remote */
404
0
    assert(!is_null_oid(&p->remote));
405
0
    if (o->verbosity >= 1)
406
0
      printf("CONFLICT (delete/modify): Notes for object %s "
407
0
        "deleted in %s and modified in %s. Version from %s "
408
0
        "left in tree.\n",
409
0
        oid_to_hex(&p->obj), lref, rref, rref);
410
0
    write_note_to_worktree(&p->obj, &p->remote);
411
0
  } else if (is_null_oid(&p->remote)) {
412
    /* D/F conflict, checkout p->local */
413
0
    assert(!is_null_oid(&p->local));
414
0
    if (o->verbosity >= 1)
415
0
      printf("CONFLICT (delete/modify): Notes for object %s "
416
0
        "deleted in %s and modified in %s. Version from %s "
417
0
        "left in tree.\n",
418
0
        oid_to_hex(&p->obj), rref, lref, lref);
419
0
    write_note_to_worktree(&p->obj, &p->local);
420
0
  } else {
421
    /* "regular" conflict, checkout result of ll_merge() */
422
0
    const char *reason = "content";
423
0
    if (is_null_oid(&p->base))
424
0
      reason = "add/add";
425
0
    assert(!is_null_oid(&p->local));
426
0
    assert(!is_null_oid(&p->remote));
427
0
    if (o->verbosity >= 1)
428
0
      printf("CONFLICT (%s): Merge conflict in notes for "
429
0
        "object %s\n", reason,
430
0
        oid_to_hex(&p->obj));
431
0
    ll_merge_in_worktree(o, p);
432
0
  }
433
434
0
  trace_printf("\t\t\tremoving from partial merge result\n");
435
0
  remove_note(t, p->obj.hash);
436
437
0
  return 1;
438
0
}
439
440
static int merge_one_change(struct notes_merge_options *o,
441
          struct notes_merge_pair *p, struct notes_tree *t)
442
0
{
443
  /*
444
   * Return 0 if change is successfully resolved (stored in notes_tree).
445
   * Return 1 is change results in a conflict (NOT stored in notes_tree,
446
   * but instead written to NOTES_MERGE_WORKTREE with conflict markers).
447
   */
448
0
  switch (o->strategy) {
449
0
  case NOTES_MERGE_RESOLVE_MANUAL:
450
0
    return merge_one_change_manual(o, p, t);
451
0
  case NOTES_MERGE_RESOLVE_OURS:
452
0
    if (o->verbosity >= 2)
453
0
      printf("Using local notes for %s\n",
454
0
            oid_to_hex(&p->obj));
455
    /* nothing to do */
456
0
    return 0;
457
0
  case NOTES_MERGE_RESOLVE_THEIRS:
458
0
    if (o->verbosity >= 2)
459
0
      printf("Using remote notes for %s\n",
460
0
            oid_to_hex(&p->obj));
461
0
    if (add_note(t, &p->obj, &p->remote, combine_notes_overwrite))
462
0
      BUG("combine_notes_overwrite failed");
463
0
    return 0;
464
0
  case NOTES_MERGE_RESOLVE_UNION:
465
0
    if (o->verbosity >= 2)
466
0
      printf("Concatenating local and remote notes for %s\n",
467
0
              oid_to_hex(&p->obj));
468
0
    if (add_note(t, &p->obj, &p->remote, combine_notes_concatenate))
469
0
      die("failed to concatenate notes "
470
0
          "(combine_notes_concatenate)");
471
0
    return 0;
472
0
  case NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ:
473
0
    if (o->verbosity >= 2)
474
0
      printf("Concatenating unique lines in local and remote "
475
0
        "notes for %s\n", oid_to_hex(&p->obj));
476
0
    if (add_note(t, &p->obj, &p->remote, combine_notes_cat_sort_uniq))
477
0
      die("failed to concatenate notes "
478
0
          "(combine_notes_cat_sort_uniq)");
479
0
    return 0;
480
0
  }
481
0
  die("Unknown strategy (%i).", o->strategy);
482
0
}
483
484
static int merge_changes(struct notes_merge_options *o,
485
       struct notes_merge_pair *changes, int *num_changes,
486
       struct notes_tree *t)
487
0
{
488
0
  int i, conflicts = 0;
489
490
0
  trace_printf("\tmerge_changes(num_changes = %i)\n", *num_changes);
491
0
  for (i = 0; i < *num_changes; i++) {
492
0
    struct notes_merge_pair *p = changes + i;
493
0
    trace_printf("\t\t%.7s: %.7s -> %.7s/%.7s\n",
494
0
           oid_to_hex(&p->obj), oid_to_hex(&p->base),
495
0
           oid_to_hex(&p->local),
496
0
           oid_to_hex(&p->remote));
497
498
0
    if (oideq(&p->base, &p->remote)) {
499
      /* no remote change; nothing to do */
500
0
      trace_printf("\t\t\tskipping (no remote change)\n");
501
0
    } else if (oideq(&p->local, &p->remote)) {
502
      /* same change in local and remote; nothing to do */
503
0
      trace_printf("\t\t\tskipping (local == remote)\n");
504
0
    } else if (oideq(&p->local, &uninitialized) ||
505
0
         oideq(&p->local, &p->base)) {
506
      /* no local change; adopt remote change */
507
0
      trace_printf("\t\t\tno local change, adopted remote\n");
508
0
      if (add_note(t, &p->obj, &p->remote,
509
0
             combine_notes_overwrite))
510
0
        BUG("combine_notes_overwrite failed");
511
0
    } else {
512
      /* need file-level merge between local and remote */
513
0
      trace_printf("\t\t\tneed content-level merge\n");
514
0
      conflicts += merge_one_change(o, p, t);
515
0
    }
516
0
  }
517
518
0
  return conflicts;
519
0
}
520
521
static int merge_from_diffs(struct notes_merge_options *o,
522
          const struct object_id *base,
523
          const struct object_id *local,
524
          const struct object_id *remote,
525
          struct notes_tree *t)
526
0
{
527
0
  struct notes_merge_pair *changes;
528
0
  int num_changes, conflicts;
529
530
0
  trace_printf("\tmerge_from_diffs(base = %.7s, local = %.7s, "
531
0
         "remote = %.7s)\n", oid_to_hex(base), oid_to_hex(local),
532
0
         oid_to_hex(remote));
533
534
0
  changes = diff_tree_remote(o, base, remote, &num_changes);
535
0
  diff_tree_local(o, changes, num_changes, base, local);
536
537
0
  conflicts = merge_changes(o, changes, &num_changes, t);
538
0
  free(changes);
539
540
0
  if (o->verbosity >= 4)
541
0
    printf(t->dirty ?
542
0
           "Merge result: %i unmerged notes and a dirty notes tree\n" :
543
0
           "Merge result: %i unmerged notes and a clean notes tree\n",
544
0
           conflicts);
545
546
0
  return conflicts ? -1 : 1;
547
0
}
548
549
int notes_merge(struct notes_merge_options *o,
550
    struct notes_tree *local_tree,
551
    struct object_id *result_oid)
552
0
{
553
0
  struct object_id local_oid, remote_oid;
554
0
  struct commit *local, *remote;
555
0
  struct commit_list *bases = NULL;
556
0
  const struct object_id *base_oid, *base_tree_oid;
557
0
  int result = 0;
558
559
0
  assert(o->local_ref && o->remote_ref);
560
0
  assert(!strcmp(o->local_ref, local_tree->ref));
561
0
  oidclr(result_oid, the_repository->hash_algo);
562
563
0
  trace_printf("notes_merge(o->local_ref = %s, o->remote_ref = %s)\n",
564
0
         o->local_ref, o->remote_ref);
565
566
  /* Dereference o->local_ref into local_sha1 */
567
0
  if (refs_read_ref_full(get_main_ref_store(the_repository), o->local_ref, 0, &local_oid, NULL))
568
0
    die("Failed to resolve local notes ref '%s'", o->local_ref);
569
0
  else if (!check_refname_format(o->local_ref, 0) &&
570
0
    is_null_oid(&local_oid))
571
0
    local = NULL; /* local_oid == null_oid indicates unborn ref */
572
0
  else if (!(local = lookup_commit_reference(o->repo, &local_oid)))
573
0
    die("Could not parse local commit %s (%s)",
574
0
        oid_to_hex(&local_oid), o->local_ref);
575
0
  trace_printf("\tlocal commit: %.7s\n", oid_to_hex(&local_oid));
576
577
  /* Dereference o->remote_ref into remote_oid */
578
0
  if (repo_get_oid(the_repository, o->remote_ref, &remote_oid)) {
579
    /*
580
     * Failed to get remote_oid. If o->remote_ref looks like an
581
     * unborn ref, perform the merge using an empty notes tree.
582
     */
583
0
    if (!check_refname_format(o->remote_ref, 0)) {
584
0
      oidclr(&remote_oid, the_repository->hash_algo);
585
0
      remote = NULL;
586
0
    } else {
587
0
      die("Failed to resolve remote notes ref '%s'",
588
0
          o->remote_ref);
589
0
    }
590
0
  } else if (!(remote = lookup_commit_reference(o->repo, &remote_oid))) {
591
0
    die("Could not parse remote commit %s (%s)",
592
0
        oid_to_hex(&remote_oid), o->remote_ref);
593
0
  }
594
0
  trace_printf("\tremote commit: %.7s\n", oid_to_hex(&remote_oid));
595
596
0
  if (!local && !remote)
597
0
    die("Cannot merge empty notes ref (%s) into empty notes ref "
598
0
        "(%s)", o->remote_ref, o->local_ref);
599
0
  if (!local) {
600
    /* result == remote commit */
601
0
    oidcpy(result_oid, &remote_oid);
602
0
    goto found_result;
603
0
  }
604
0
  if (!remote) {
605
    /* result == local commit */
606
0
    oidcpy(result_oid, &local_oid);
607
0
    goto found_result;
608
0
  }
609
0
  assert(local && remote);
610
611
  /* Find merge bases */
612
0
  if (repo_get_merge_bases(the_repository, local, remote, &bases) < 0)
613
0
    exit(128);
614
0
  if (!bases) {
615
0
    base_oid = null_oid();
616
0
    base_tree_oid = the_hash_algo->empty_tree;
617
0
    if (o->verbosity >= 4)
618
0
      printf("No merge base found; doing history-less merge\n");
619
0
  } else if (!bases->next) {
620
0
    base_oid = &bases->item->object.oid;
621
0
    base_tree_oid = get_commit_tree_oid(bases->item);
622
0
    if (o->verbosity >= 4)
623
0
      printf("One merge base found (%.7s)\n",
624
0
             oid_to_hex(base_oid));
625
0
  } else {
626
    /* TODO: How to handle multiple merge-bases? */
627
0
    base_oid = &bases->item->object.oid;
628
0
    base_tree_oid = get_commit_tree_oid(bases->item);
629
0
    if (o->verbosity >= 3)
630
0
      printf("Multiple merge bases found. Using the first "
631
0
        "(%.7s)\n", oid_to_hex(base_oid));
632
0
  }
633
634
0
  if (o->verbosity >= 4)
635
0
    printf("Merging remote commit %.7s into local commit %.7s with "
636
0
      "merge-base %.7s\n", oid_to_hex(&remote->object.oid),
637
0
      oid_to_hex(&local->object.oid),
638
0
      oid_to_hex(base_oid));
639
640
0
  if (oideq(&remote->object.oid, base_oid)) {
641
    /* Already merged; result == local commit */
642
0
    if (o->verbosity >= 2)
643
0
      printf_ln("Already up to date.");
644
0
    oidcpy(result_oid, &local->object.oid);
645
0
    goto found_result;
646
0
  }
647
0
  if (oideq(&local->object.oid, base_oid)) {
648
    /* Fast-forward; result == remote commit */
649
0
    if (o->verbosity >= 2)
650
0
      printf("Fast-forward\n");
651
0
    oidcpy(result_oid, &remote->object.oid);
652
0
    goto found_result;
653
0
  }
654
655
0
  result = merge_from_diffs(o, base_tree_oid,
656
0
          get_commit_tree_oid(local),
657
0
          get_commit_tree_oid(remote), local_tree);
658
659
0
  if (result != 0) { /* non-trivial merge (with or without conflicts) */
660
    /* Commit (partial) result */
661
0
    struct commit_list *parents = NULL;
662
0
    commit_list_insert(remote, &parents); /* LIFO order */
663
0
    commit_list_insert(local, &parents);
664
0
    create_notes_commit(o->repo, local_tree, parents, o->commit_msg.buf,
665
0
            o->commit_msg.len, result_oid);
666
0
    free_commit_list(parents);
667
0
  }
668
669
0
found_result:
670
0
  free_commit_list(bases);
671
0
  strbuf_release(&(o->commit_msg));
672
0
  trace_printf("notes_merge(): result = %i, result_oid = %.7s\n",
673
0
         result, oid_to_hex(result_oid));
674
0
  return result;
675
0
}
676
677
int notes_merge_commit(struct notes_merge_options *o,
678
           struct notes_tree *partial_tree,
679
           struct commit *partial_commit,
680
           struct object_id *result_oid)
681
0
{
682
  /*
683
   * Iterate through files in .git/NOTES_MERGE_WORKTREE and add all
684
   * found notes to 'partial_tree'. Write the updated notes tree to
685
   * the DB, and commit the resulting tree object while reusing the
686
   * commit message and parents from 'partial_commit'.
687
   * Finally store the new commit object OID into 'result_oid'.
688
   */
689
0
  DIR *dir;
690
0
  struct dirent *e;
691
0
  struct strbuf path = STRBUF_INIT;
692
0
  const char *buffer = repo_get_commit_buffer(the_repository,
693
0
                partial_commit, NULL);
694
0
  const char *msg = strstr(buffer, "\n\n");
695
0
  int baselen;
696
697
0
  git_path_buf(&path, NOTES_MERGE_WORKTREE);
698
0
  if (o->verbosity >= 3)
699
0
    printf("Committing notes in notes merge worktree at %s\n",
700
0
      path.buf);
701
702
0
  if (!msg || msg[2] == '\0')
703
0
    die("partial notes commit has empty message");
704
0
  msg += 2;
705
706
0
  dir = opendir(path.buf);
707
0
  if (!dir)
708
0
    die_errno("could not open %s", path.buf);
709
710
0
  strbuf_addch(&path, '/');
711
0
  baselen = path.len;
712
0
  while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL) {
713
0
    struct stat st;
714
0
    struct object_id obj_oid, blob_oid;
715
716
0
    if (get_oid_hex(e->d_name, &obj_oid)) {
717
0
      if (o->verbosity >= 3)
718
0
        printf("Skipping non-SHA1 entry '%s%s'\n",
719
0
          path.buf, e->d_name);
720
0
      continue;
721
0
    }
722
723
0
    strbuf_addstr(&path, e->d_name);
724
    /* write file as blob, and add to partial_tree */
725
0
    if (stat(path.buf, &st))
726
0
      die_errno("Failed to stat '%s'", path.buf);
727
0
    if (index_path(o->repo->index, &blob_oid, path.buf, &st, HASH_WRITE_OBJECT))
728
0
      die("Failed to write blob object from '%s'", path.buf);
729
0
    if (add_note(partial_tree, &obj_oid, &blob_oid, NULL))
730
0
      die("Failed to add resolved note '%s' to notes tree",
731
0
          path.buf);
732
0
    if (o->verbosity >= 4)
733
0
      printf("Added resolved note for object %s: %s\n",
734
0
        oid_to_hex(&obj_oid), oid_to_hex(&blob_oid));
735
0
    strbuf_setlen(&path, baselen);
736
0
  }
737
738
0
  create_notes_commit(o->repo, partial_tree, partial_commit->parents, msg,
739
0
          strlen(msg), result_oid);
740
0
  repo_unuse_commit_buffer(the_repository, partial_commit, buffer);
741
0
  if (o->verbosity >= 4)
742
0
    printf("Finalized notes merge commit: %s\n",
743
0
      oid_to_hex(result_oid));
744
0
  strbuf_release(&path);
745
0
  closedir(dir);
746
0
  return 0;
747
0
}
748
749
int notes_merge_abort(struct notes_merge_options *o)
750
0
{
751
  /*
752
   * Remove all files within .git/NOTES_MERGE_WORKTREE. We do not remove
753
   * the .git/NOTES_MERGE_WORKTREE directory itself, since it might be
754
   * the current working directory of the user.
755
   */
756
0
  struct strbuf buf = STRBUF_INIT;
757
0
  int ret;
758
759
0
  git_path_buf(&buf, NOTES_MERGE_WORKTREE);
760
0
  if (o->verbosity >= 3)
761
0
    printf("Removing notes merge worktree at %s/*\n", buf.buf);
762
0
  ret = remove_dir_recursively(&buf, REMOVE_DIR_KEEP_TOPLEVEL);
763
0
  strbuf_release(&buf);
764
0
  return ret;
765
0
}