git

Форк
0
/
notes-merge.c 
765 строк · 23.2 Кб
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
{
33
	memset(o, 0, sizeof(struct notes_merge_options));
34
	strbuf_init(&(o->commit_msg), 0);
35
	o->verbosity = NOTES_MERGE_VERBOSITY_DEFAULT;
36
	o->repo = r;
37
}
38

39
static int path_to_oid(const char *path, struct object_id *oid)
40
{
41
	char hex_oid[GIT_MAX_HEXSZ];
42
	int i = 0;
43
	while (*path && i < the_hash_algo->hexsz) {
44
		if (*path != '/')
45
			hex_oid[i++] = *path;
46
		path++;
47
	}
48
	if (*path || i != the_hash_algo->hexsz)
49
		return -1;
50
	return get_oid_hex(hex_oid, oid);
51
}
52

53
static int verify_notes_filepair(struct diff_filepair *p, struct object_id *oid)
54
{
55
	switch (p->status) {
56
	case DIFF_STATUS_MODIFIED:
57
		assert(p->one->mode == p->two->mode);
58
		assert(!is_null_oid(&p->one->oid));
59
		assert(!is_null_oid(&p->two->oid));
60
		break;
61
	case DIFF_STATUS_ADDED:
62
		assert(is_null_oid(&p->one->oid));
63
		break;
64
	case DIFF_STATUS_DELETED:
65
		assert(is_null_oid(&p->two->oid));
66
		break;
67
	default:
68
		return -1;
69
	}
70
	assert(!strcmp(p->one->path, p->two->path));
71
	return path_to_oid(p->one->path, oid);
72
}
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
{
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
	static int last_index;
89
	int i = last_index < len ? last_index : len - 1;
90
	int prev_cmp = 0, cmp = -1;
91
	while (i >= 0 && i < len) {
92
		cmp = oidcmp(obj, &list[i].obj);
93
		if (!cmp) /* obj belongs @ i */
94
			break;
95
		else if (cmp < 0 && prev_cmp <= 0) /* obj belongs < i */
96
			i--;
97
		else if (cmp < 0) /* obj belongs between i-1 and i */
98
			break;
99
		else if (cmp > 0 && prev_cmp >= 0) /* obj belongs > i */
100
			i++;
101
		else /* if (cmp > 0) */ { /* obj belongs between i and i+1 */
102
			i++;
103
			break;
104
		}
105
		prev_cmp = cmp;
106
	}
107
	if (i < 0)
108
		i = 0;
109
	/* obj belongs at, or immediately preceding, index i (0 <= i <= len) */
110

111
	if (!cmp)
112
		*occupied = 1;
113
	else {
114
		*occupied = 0;
115
		if (insert_new && i < len) {
116
			MOVE_ARRAY(list + i + 1, list + i, len - i);
117
			memset(list + i, 0, sizeof(struct notes_merge_pair));
118
		}
119
	}
120
	last_index = i;
121
	return list + i;
122
}
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
{
135
	struct diff_options opt;
136
	struct notes_merge_pair *changes;
137
	int i, len = 0;
138

139
	trace_printf("\tdiff_tree_remote(base = %.7s, remote = %.7s)\n",
140
	       oid_to_hex(base), oid_to_hex(remote));
141

142
	repo_diff_setup(o->repo, &opt);
143
	opt.flags.recursive = 1;
144
	opt.output_format = DIFF_FORMAT_NO_OUTPUT;
145
	diff_setup_done(&opt);
146
	diff_tree_oid(base, remote, "", &opt);
147
	diffcore_std(&opt);
148

149
	CALLOC_ARRAY(changes, diff_queued_diff.nr);
150

151
	for (i = 0; i < diff_queued_diff.nr; i++) {
152
		struct diff_filepair *p = diff_queued_diff.queue[i];
153
		struct notes_merge_pair *mp;
154
		int occupied;
155
		struct object_id obj;
156

157
		if (verify_notes_filepair(p, &obj)) {
158
			trace_printf("\t\tCannot merge entry '%s' (%c): "
159
			       "%.7s -> %.7s. Skipping!\n", p->one->path,
160
			       p->status, oid_to_hex(&p->one->oid),
161
			       oid_to_hex(&p->two->oid));
162
			continue;
163
		}
164
		mp = find_notes_merge_pair_pos(changes, len, &obj, 1, &occupied);
165
		if (occupied) {
166
			/* We've found an addition/deletion pair */
167
			assert(oideq(&mp->obj, &obj));
168
			if (is_null_oid(&p->one->oid)) { /* addition */
169
				assert(is_null_oid(&mp->remote));
170
				oidcpy(&mp->remote, &p->two->oid);
171
			} else if (is_null_oid(&p->two->oid)) { /* deletion */
172
				assert(is_null_oid(&mp->base));
173
				oidcpy(&mp->base, &p->one->oid);
174
			} else
175
				assert(!"Invalid existing change recorded");
176
		} else {
177
			oidcpy(&mp->obj, &obj);
178
			oidcpy(&mp->base, &p->one->oid);
179
			oidcpy(&mp->local, &uninitialized);
180
			oidcpy(&mp->remote, &p->two->oid);
181
			len++;
182
		}
183
		trace_printf("\t\tStored remote change for %s: %.7s -> %.7s\n",
184
		       oid_to_hex(&mp->obj), oid_to_hex(&mp->base),
185
		       oid_to_hex(&mp->remote));
186
	}
187
	diff_flush(&opt);
188

189
	*num_changes = len;
190
	return changes;
191
}
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
{
198
	struct diff_options opt;
199
	int i;
200

201
	trace_printf("\tdiff_tree_local(len = %i, base = %.7s, local = %.7s)\n",
202
	       len, oid_to_hex(base), oid_to_hex(local));
203

204
	repo_diff_setup(o->repo, &opt);
205
	opt.flags.recursive = 1;
206
	opt.output_format = DIFF_FORMAT_NO_OUTPUT;
207
	diff_setup_done(&opt);
208
	diff_tree_oid(base, local, "", &opt);
209
	diffcore_std(&opt);
210

211
	for (i = 0; i < diff_queued_diff.nr; i++) {
212
		struct diff_filepair *p = diff_queued_diff.queue[i];
213
		struct notes_merge_pair *mp;
214
		int match;
215
		struct object_id obj;
216

217
		if (verify_notes_filepair(p, &obj)) {
218
			trace_printf("\t\tCannot merge entry '%s' (%c): "
219
			       "%.7s -> %.7s. Skipping!\n", p->one->path,
220
			       p->status, oid_to_hex(&p->one->oid),
221
			       oid_to_hex(&p->two->oid));
222
			continue;
223
		}
224
		mp = find_notes_merge_pair_pos(changes, len, &obj, 0, &match);
225
		if (!match) {
226
			trace_printf("\t\tIgnoring local-only change for %s: "
227
			       "%.7s -> %.7s\n", oid_to_hex(&obj),
228
			       oid_to_hex(&p->one->oid),
229
			       oid_to_hex(&p->two->oid));
230
			continue;
231
		}
232

233
		assert(oideq(&mp->obj, &obj));
234
		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
			if (oideq(&mp->local, &uninitialized))
245
				oidclr(&mp->local, the_repository->hash_algo);
246
		} 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
			assert(is_null_oid(&mp->local) ||
256
			       oideq(&mp->local, &uninitialized));
257
			oidcpy(&mp->local, &p->two->oid);
258
		} 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
			assert(oideq(&p->one->oid, &mp->base));
265
			assert(oideq(&mp->local, &uninitialized));
266
			oidcpy(&mp->local, &p->two->oid);
267
		}
268
		trace_printf("\t\tStored local change for %s: %.7s -> %.7s\n",
269
		       oid_to_hex(&mp->obj), oid_to_hex(&mp->base),
270
		       oid_to_hex(&mp->local));
271
	}
272
	diff_flush(&opt);
273
}
274

275
static void check_notes_merge_worktree(struct notes_merge_options *o)
276
{
277
	if (!o->has_worktree) {
278
		/*
279
		 * Must establish NOTES_MERGE_WORKTREE.
280
		 * Abort if NOTES_MERGE_WORKTREE already exists
281
		 */
282
		if (file_exists(git_path(NOTES_MERGE_WORKTREE)) &&
283
		    !is_empty_dir(git_path(NOTES_MERGE_WORKTREE))) {
284
			if (advice_enabled(ADVICE_RESOLVE_CONFLICT))
285
				die(_("You have not concluded your previous "
286
				    "notes merge (%s exists).\nPlease, use "
287
				    "'git notes merge --commit' or 'git notes "
288
				    "merge --abort' to commit/abort the "
289
				    "previous merge before you start a new "
290
				    "notes merge."), git_path("NOTES_MERGE_*"));
291
			else
292
				die(_("You have not concluded your notes merge "
293
				    "(%s exists)."), git_path("NOTES_MERGE_*"));
294
		}
295

296
		if (safe_create_leading_directories_const(git_path(
297
				NOTES_MERGE_WORKTREE "/.test")))
298
			die_errno("unable to create directory %s",
299
				  git_path(NOTES_MERGE_WORKTREE));
300
		o->has_worktree = 1;
301
	} else if (!file_exists(git_path(NOTES_MERGE_WORKTREE)))
302
		/* NOTES_MERGE_WORKTREE should already be established */
303
		die("missing '%s'. This should not happen",
304
		    git_path(NOTES_MERGE_WORKTREE));
305
}
306

307
static void write_buf_to_worktree(const struct object_id *obj,
308
				  const char *buf, unsigned long size)
309
{
310
	int fd;
311
	char *path = git_pathdup(NOTES_MERGE_WORKTREE "/%s", oid_to_hex(obj));
312
	if (safe_create_leading_directories_const(path))
313
		die_errno("unable to create directory for '%s'", path);
314

315
	fd = xopen(path, O_WRONLY | O_EXCL | O_CREAT, 0666);
316

317
	while (size > 0) {
318
		ssize_t ret = write_in_full(fd, buf, size);
319
		if (ret < 0) {
320
			/* Ignore epipe */
321
			if (errno == EPIPE)
322
				break;
323
			die_errno("notes-merge");
324
		}
325
		size -= ret;
326
		buf += ret;
327
	}
328

329
	close(fd);
330
	free(path);
331
}
332

333
static void write_note_to_worktree(const struct object_id *obj,
334
				   const struct object_id *note)
335
{
336
	enum object_type type;
337
	unsigned long size;
338
	void *buf = repo_read_object_file(the_repository, note, &type, &size);
339

340
	if (!buf)
341
		die("cannot read note %s for object %s",
342
		    oid_to_hex(note), oid_to_hex(obj));
343
	if (type != OBJ_BLOB)
344
		die("blob expected in note %s for object %s",
345
		    oid_to_hex(note), oid_to_hex(obj));
346
	write_buf_to_worktree(obj, buf, size);
347
	free(buf);
348
}
349

350
static int ll_merge_in_worktree(struct notes_merge_options *o,
351
				struct notes_merge_pair *p)
352
{
353
	mmbuffer_t result_buf;
354
	mmfile_t base, local, remote;
355
	enum ll_merge_result status;
356

357
	read_mmblob(&base, &p->base);
358
	read_mmblob(&local, &p->local);
359
	read_mmblob(&remote, &p->remote);
360

361
	status = ll_merge(&result_buf, oid_to_hex(&p->obj), &base, NULL,
362
			  &local, o->local_ref, &remote, o->remote_ref,
363
			  o->repo->index, NULL);
364

365
	free(base.ptr);
366
	free(local.ptr);
367
	free(remote.ptr);
368

369
	if (status == LL_MERGE_BINARY_CONFLICT)
370
		warning("Cannot merge binary files: %s (%s vs. %s)",
371
			oid_to_hex(&p->obj), o->local_ref, o->remote_ref);
372
	if ((status < 0) || !result_buf.ptr)
373
		die("Failed to execute internal merge");
374

375
	write_buf_to_worktree(&p->obj, result_buf.ptr, result_buf.size);
376
	free(result_buf.ptr);
377

378
	return status;
379
}
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
{
385
	const char *lref = o->local_ref ? o->local_ref : "local version";
386
	const char *rref = o->remote_ref ? o->remote_ref : "remote version";
387

388
	trace_printf("\t\t\tmerge_one_change_manual(obj = %.7s, base = %.7s, "
389
	       "local = %.7s, remote = %.7s)\n",
390
	       oid_to_hex(&p->obj), oid_to_hex(&p->base),
391
	       oid_to_hex(&p->local), oid_to_hex(&p->remote));
392

393
	/* add "Conflicts:" section to commit message first time through */
394
	if (!o->has_worktree)
395
		strbuf_addstr(&(o->commit_msg), "\n\nConflicts:\n");
396

397
	strbuf_addf(&(o->commit_msg), "\t%s\n", oid_to_hex(&p->obj));
398

399
	if (o->verbosity >= 2)
400
		printf("Auto-merging notes for %s\n", oid_to_hex(&p->obj));
401
	check_notes_merge_worktree(o);
402
	if (is_null_oid(&p->local)) {
403
		/* D/F conflict, checkout p->remote */
404
		assert(!is_null_oid(&p->remote));
405
		if (o->verbosity >= 1)
406
			printf("CONFLICT (delete/modify): Notes for object %s "
407
				"deleted in %s and modified in %s. Version from %s "
408
				"left in tree.\n",
409
				oid_to_hex(&p->obj), lref, rref, rref);
410
		write_note_to_worktree(&p->obj, &p->remote);
411
	} else if (is_null_oid(&p->remote)) {
412
		/* D/F conflict, checkout p->local */
413
		assert(!is_null_oid(&p->local));
414
		if (o->verbosity >= 1)
415
			printf("CONFLICT (delete/modify): Notes for object %s "
416
				"deleted in %s and modified in %s. Version from %s "
417
				"left in tree.\n",
418
				oid_to_hex(&p->obj), rref, lref, lref);
419
		write_note_to_worktree(&p->obj, &p->local);
420
	} else {
421
		/* "regular" conflict, checkout result of ll_merge() */
422
		const char *reason = "content";
423
		if (is_null_oid(&p->base))
424
			reason = "add/add";
425
		assert(!is_null_oid(&p->local));
426
		assert(!is_null_oid(&p->remote));
427
		if (o->verbosity >= 1)
428
			printf("CONFLICT (%s): Merge conflict in notes for "
429
				"object %s\n", reason,
430
				oid_to_hex(&p->obj));
431
		ll_merge_in_worktree(o, p);
432
	}
433

434
	trace_printf("\t\t\tremoving from partial merge result\n");
435
	remove_note(t, p->obj.hash);
436

437
	return 1;
438
}
439

440
static int merge_one_change(struct notes_merge_options *o,
441
			    struct notes_merge_pair *p, struct notes_tree *t)
442
{
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
	switch (o->strategy) {
449
	case NOTES_MERGE_RESOLVE_MANUAL:
450
		return merge_one_change_manual(o, p, t);
451
	case NOTES_MERGE_RESOLVE_OURS:
452
		if (o->verbosity >= 2)
453
			printf("Using local notes for %s\n",
454
						oid_to_hex(&p->obj));
455
		/* nothing to do */
456
		return 0;
457
	case NOTES_MERGE_RESOLVE_THEIRS:
458
		if (o->verbosity >= 2)
459
			printf("Using remote notes for %s\n",
460
						oid_to_hex(&p->obj));
461
		if (add_note(t, &p->obj, &p->remote, combine_notes_overwrite))
462
			BUG("combine_notes_overwrite failed");
463
		return 0;
464
	case NOTES_MERGE_RESOLVE_UNION:
465
		if (o->verbosity >= 2)
466
			printf("Concatenating local and remote notes for %s\n",
467
							oid_to_hex(&p->obj));
468
		if (add_note(t, &p->obj, &p->remote, combine_notes_concatenate))
469
			die("failed to concatenate notes "
470
			    "(combine_notes_concatenate)");
471
		return 0;
472
	case NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ:
473
		if (o->verbosity >= 2)
474
			printf("Concatenating unique lines in local and remote "
475
				"notes for %s\n", oid_to_hex(&p->obj));
476
		if (add_note(t, &p->obj, &p->remote, combine_notes_cat_sort_uniq))
477
			die("failed to concatenate notes "
478
			    "(combine_notes_cat_sort_uniq)");
479
		return 0;
480
	}
481
	die("Unknown strategy (%i).", o->strategy);
482
}
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
{
488
	int i, conflicts = 0;
489

490
	trace_printf("\tmerge_changes(num_changes = %i)\n", *num_changes);
491
	for (i = 0; i < *num_changes; i++) {
492
		struct notes_merge_pair *p = changes + i;
493
		trace_printf("\t\t%.7s: %.7s -> %.7s/%.7s\n",
494
		       oid_to_hex(&p->obj), oid_to_hex(&p->base),
495
		       oid_to_hex(&p->local),
496
		       oid_to_hex(&p->remote));
497

498
		if (oideq(&p->base, &p->remote)) {
499
			/* no remote change; nothing to do */
500
			trace_printf("\t\t\tskipping (no remote change)\n");
501
		} else if (oideq(&p->local, &p->remote)) {
502
			/* same change in local and remote; nothing to do */
503
			trace_printf("\t\t\tskipping (local == remote)\n");
504
		} else if (oideq(&p->local, &uninitialized) ||
505
			   oideq(&p->local, &p->base)) {
506
			/* no local change; adopt remote change */
507
			trace_printf("\t\t\tno local change, adopted remote\n");
508
			if (add_note(t, &p->obj, &p->remote,
509
				     combine_notes_overwrite))
510
				BUG("combine_notes_overwrite failed");
511
		} else {
512
			/* need file-level merge between local and remote */
513
			trace_printf("\t\t\tneed content-level merge\n");
514
			conflicts += merge_one_change(o, p, t);
515
		}
516
	}
517

518
	return conflicts;
519
}
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
{
527
	struct notes_merge_pair *changes;
528
	int num_changes, conflicts;
529

530
	trace_printf("\tmerge_from_diffs(base = %.7s, local = %.7s, "
531
	       "remote = %.7s)\n", oid_to_hex(base), oid_to_hex(local),
532
	       oid_to_hex(remote));
533

534
	changes = diff_tree_remote(o, base, remote, &num_changes);
535
	diff_tree_local(o, changes, num_changes, base, local);
536

537
	conflicts = merge_changes(o, changes, &num_changes, t);
538
	free(changes);
539

540
	if (o->verbosity >= 4)
541
		printf(t->dirty ?
542
		       "Merge result: %i unmerged notes and a dirty notes tree\n" :
543
		       "Merge result: %i unmerged notes and a clean notes tree\n",
544
		       conflicts);
545

546
	return conflicts ? -1 : 1;
547
}
548

549
int notes_merge(struct notes_merge_options *o,
550
		struct notes_tree *local_tree,
551
		struct object_id *result_oid)
552
{
553
	struct object_id local_oid, remote_oid;
554
	struct commit *local, *remote;
555
	struct commit_list *bases = NULL;
556
	const struct object_id *base_oid, *base_tree_oid;
557
	int result = 0;
558

559
	assert(o->local_ref && o->remote_ref);
560
	assert(!strcmp(o->local_ref, local_tree->ref));
561
	oidclr(result_oid, the_repository->hash_algo);
562

563
	trace_printf("notes_merge(o->local_ref = %s, o->remote_ref = %s)\n",
564
	       o->local_ref, o->remote_ref);
565

566
	/* Dereference o->local_ref into local_sha1 */
567
	if (refs_read_ref_full(get_main_ref_store(the_repository), o->local_ref, 0, &local_oid, NULL))
568
		die("Failed to resolve local notes ref '%s'", o->local_ref);
569
	else if (!check_refname_format(o->local_ref, 0) &&
570
		is_null_oid(&local_oid))
571
		local = NULL; /* local_oid == null_oid indicates unborn ref */
572
	else if (!(local = lookup_commit_reference(o->repo, &local_oid)))
573
		die("Could not parse local commit %s (%s)",
574
		    oid_to_hex(&local_oid), o->local_ref);
575
	trace_printf("\tlocal commit: %.7s\n", oid_to_hex(&local_oid));
576

577
	/* Dereference o->remote_ref into remote_oid */
578
	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
		if (!check_refname_format(o->remote_ref, 0)) {
584
			oidclr(&remote_oid, the_repository->hash_algo);
585
			remote = NULL;
586
		} else {
587
			die("Failed to resolve remote notes ref '%s'",
588
			    o->remote_ref);
589
		}
590
	} else if (!(remote = lookup_commit_reference(o->repo, &remote_oid))) {
591
		die("Could not parse remote commit %s (%s)",
592
		    oid_to_hex(&remote_oid), o->remote_ref);
593
	}
594
	trace_printf("\tremote commit: %.7s\n", oid_to_hex(&remote_oid));
595

596
	if (!local && !remote)
597
		die("Cannot merge empty notes ref (%s) into empty notes ref "
598
		    "(%s)", o->remote_ref, o->local_ref);
599
	if (!local) {
600
		/* result == remote commit */
601
		oidcpy(result_oid, &remote_oid);
602
		goto found_result;
603
	}
604
	if (!remote) {
605
		/* result == local commit */
606
		oidcpy(result_oid, &local_oid);
607
		goto found_result;
608
	}
609
	assert(local && remote);
610

611
	/* Find merge bases */
612
	if (repo_get_merge_bases(the_repository, local, remote, &bases) < 0)
613
		exit(128);
614
	if (!bases) {
615
		base_oid = null_oid();
616
		base_tree_oid = the_hash_algo->empty_tree;
617
		if (o->verbosity >= 4)
618
			printf("No merge base found; doing history-less merge\n");
619
	} else if (!bases->next) {
620
		base_oid = &bases->item->object.oid;
621
		base_tree_oid = get_commit_tree_oid(bases->item);
622
		if (o->verbosity >= 4)
623
			printf("One merge base found (%.7s)\n",
624
			       oid_to_hex(base_oid));
625
	} else {
626
		/* TODO: How to handle multiple merge-bases? */
627
		base_oid = &bases->item->object.oid;
628
		base_tree_oid = get_commit_tree_oid(bases->item);
629
		if (o->verbosity >= 3)
630
			printf("Multiple merge bases found. Using the first "
631
				"(%.7s)\n", oid_to_hex(base_oid));
632
	}
633

634
	if (o->verbosity >= 4)
635
		printf("Merging remote commit %.7s into local commit %.7s with "
636
			"merge-base %.7s\n", oid_to_hex(&remote->object.oid),
637
			oid_to_hex(&local->object.oid),
638
			oid_to_hex(base_oid));
639

640
	if (oideq(&remote->object.oid, base_oid)) {
641
		/* Already merged; result == local commit */
642
		if (o->verbosity >= 2)
643
			printf_ln("Already up to date.");
644
		oidcpy(result_oid, &local->object.oid);
645
		goto found_result;
646
	}
647
	if (oideq(&local->object.oid, base_oid)) {
648
		/* Fast-forward; result == remote commit */
649
		if (o->verbosity >= 2)
650
			printf("Fast-forward\n");
651
		oidcpy(result_oid, &remote->object.oid);
652
		goto found_result;
653
	}
654

655
	result = merge_from_diffs(o, base_tree_oid,
656
				  get_commit_tree_oid(local),
657
				  get_commit_tree_oid(remote), local_tree);
658

659
	if (result != 0) { /* non-trivial merge (with or without conflicts) */
660
		/* Commit (partial) result */
661
		struct commit_list *parents = NULL;
662
		commit_list_insert(remote, &parents); /* LIFO order */
663
		commit_list_insert(local, &parents);
664
		create_notes_commit(o->repo, local_tree, parents, o->commit_msg.buf,
665
				    o->commit_msg.len, result_oid);
666
		free_commit_list(parents);
667
	}
668

669
found_result:
670
	free_commit_list(bases);
671
	strbuf_release(&(o->commit_msg));
672
	trace_printf("notes_merge(): result = %i, result_oid = %.7s\n",
673
	       result, oid_to_hex(result_oid));
674
	return result;
675
}
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
{
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
	DIR *dir;
690
	struct dirent *e;
691
	struct strbuf path = STRBUF_INIT;
692
	const char *buffer = repo_get_commit_buffer(the_repository,
693
						    partial_commit, NULL);
694
	const char *msg = strstr(buffer, "\n\n");
695
	int baselen;
696

697
	git_path_buf(&path, NOTES_MERGE_WORKTREE);
698
	if (o->verbosity >= 3)
699
		printf("Committing notes in notes merge worktree at %s\n",
700
			path.buf);
701

702
	if (!msg || msg[2] == '\0')
703
		die("partial notes commit has empty message");
704
	msg += 2;
705

706
	dir = opendir(path.buf);
707
	if (!dir)
708
		die_errno("could not open %s", path.buf);
709

710
	strbuf_addch(&path, '/');
711
	baselen = path.len;
712
	while ((e = readdir_skip_dot_and_dotdot(dir)) != NULL) {
713
		struct stat st;
714
		struct object_id obj_oid, blob_oid;
715

716
		if (get_oid_hex(e->d_name, &obj_oid)) {
717
			if (o->verbosity >= 3)
718
				printf("Skipping non-SHA1 entry '%s%s'\n",
719
					path.buf, e->d_name);
720
			continue;
721
		}
722

723
		strbuf_addstr(&path, e->d_name);
724
		/* write file as blob, and add to partial_tree */
725
		if (stat(path.buf, &st))
726
			die_errno("Failed to stat '%s'", path.buf);
727
		if (index_path(o->repo->index, &blob_oid, path.buf, &st, HASH_WRITE_OBJECT))
728
			die("Failed to write blob object from '%s'", path.buf);
729
		if (add_note(partial_tree, &obj_oid, &blob_oid, NULL))
730
			die("Failed to add resolved note '%s' to notes tree",
731
			    path.buf);
732
		if (o->verbosity >= 4)
733
			printf("Added resolved note for object %s: %s\n",
734
				oid_to_hex(&obj_oid), oid_to_hex(&blob_oid));
735
		strbuf_setlen(&path, baselen);
736
	}
737

738
	create_notes_commit(o->repo, partial_tree, partial_commit->parents, msg,
739
			    strlen(msg), result_oid);
740
	repo_unuse_commit_buffer(the_repository, partial_commit, buffer);
741
	if (o->verbosity >= 4)
742
		printf("Finalized notes merge commit: %s\n",
743
			oid_to_hex(result_oid));
744
	strbuf_release(&path);
745
	closedir(dir);
746
	return 0;
747
}
748

749
int notes_merge_abort(struct notes_merge_options *o)
750
{
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
	struct strbuf buf = STRBUF_INIT;
757
	int ret;
758

759
	git_path_buf(&buf, NOTES_MERGE_WORKTREE);
760
	if (o->verbosity >= 3)
761
		printf("Removing notes merge worktree at %s/*\n", buf.buf);
762
	ret = remove_dir_recursively(&buf, REMOVE_DIR_KEEP_TOPLEVEL);
763
	strbuf_release(&buf);
764
	return ret;
765
}
766

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.