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