builtin/notes.c: Refactor creation of notes commits.
[git] / notes-merge.c
1 #include "cache.h"
2 #include "commit.h"
3 #include "refs.h"
4 #include "notes.h"
5 #include "notes-merge.h"
6
7 void init_notes_merge_options(struct notes_merge_options *o)
8 {
9         memset(o, 0, sizeof(struct notes_merge_options));
10         o->verbosity = NOTES_MERGE_VERBOSITY_DEFAULT;
11 }
12
13 #define OUTPUT(o, v, ...) \
14         do { \
15                 if ((o)->verbosity >= (v)) { \
16                         printf(__VA_ARGS__); \
17                         puts(""); \
18                 } \
19         } while (0)
20
21 void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
22                          const char *msg, unsigned char *result_sha1)
23 {
24         unsigned char tree_sha1[20];
25
26         assert(t->initialized);
27
28         if (write_notes_tree(t, tree_sha1))
29                 die("Failed to write notes tree to database");
30
31         if (!parents) {
32                 /* Deduce parent commit from t->ref */
33                 unsigned char parent_sha1[20];
34                 if (!read_ref(t->ref, parent_sha1)) {
35                         struct commit *parent = lookup_commit(parent_sha1);
36                         if (!parent || parse_commit(parent))
37                                 die("Failed to find/parse commit %s", t->ref);
38                         commit_list_insert(parent, &parents);
39                 }
40                 /* else: t->ref points to nothing, assume root/orphan commit */
41         }
42
43         if (commit_tree(msg, tree_sha1, parents, result_sha1, NULL))
44                 die("Failed to commit notes tree to database");
45 }
46
47 int notes_merge(struct notes_merge_options *o,
48                 unsigned char *result_sha1)
49 {
50         unsigned char local_sha1[20], remote_sha1[20];
51         struct commit *local, *remote;
52         struct commit_list *bases = NULL;
53         const unsigned char *base_sha1;
54         int result = 0;
55
56         assert(o->local_ref && o->remote_ref);
57         hashclr(result_sha1);
58
59         trace_printf("notes_merge(o->local_ref = %s, o->remote_ref = %s)\n",
60                o->local_ref, o->remote_ref);
61
62         /* Dereference o->local_ref into local_sha1 */
63         if (!resolve_ref(o->local_ref, local_sha1, 0, NULL))
64                 die("Failed to resolve local notes ref '%s'", o->local_ref);
65         else if (!check_ref_format(o->local_ref) && is_null_sha1(local_sha1))
66                 local = NULL; /* local_sha1 == null_sha1 indicates unborn ref */
67         else if (!(local = lookup_commit_reference(local_sha1)))
68                 die("Could not parse local commit %s (%s)",
69                     sha1_to_hex(local_sha1), o->local_ref);
70         trace_printf("\tlocal commit: %.7s\n", sha1_to_hex(local_sha1));
71
72         /* Dereference o->remote_ref into remote_sha1 */
73         if (get_sha1(o->remote_ref, remote_sha1)) {
74                 /*
75                  * Failed to get remote_sha1. If o->remote_ref looks like an
76                  * unborn ref, perform the merge using an empty notes tree.
77                  */
78                 if (!check_ref_format(o->remote_ref)) {
79                         hashclr(remote_sha1);
80                         remote = NULL;
81                 } else {
82                         die("Failed to resolve remote notes ref '%s'",
83                             o->remote_ref);
84                 }
85         } else if (!(remote = lookup_commit_reference(remote_sha1))) {
86                 die("Could not parse remote commit %s (%s)",
87                     sha1_to_hex(remote_sha1), o->remote_ref);
88         }
89         trace_printf("\tremote commit: %.7s\n", sha1_to_hex(remote_sha1));
90
91         if (!local && !remote)
92                 die("Cannot merge empty notes ref (%s) into empty notes ref "
93                     "(%s)", o->remote_ref, o->local_ref);
94         if (!local) {
95                 /* result == remote commit */
96                 hashcpy(result_sha1, remote_sha1);
97                 goto found_result;
98         }
99         if (!remote) {
100                 /* result == local commit */
101                 hashcpy(result_sha1, local_sha1);
102                 goto found_result;
103         }
104         assert(local && remote);
105
106         /* Find merge bases */
107         bases = get_merge_bases(local, remote, 1);
108         if (!bases) {
109                 base_sha1 = null_sha1;
110                 OUTPUT(o, 4, "No merge base found; doing history-less merge");
111         } else if (!bases->next) {
112                 base_sha1 = bases->item->object.sha1;
113                 OUTPUT(o, 4, "One merge base found (%.7s)",
114                        sha1_to_hex(base_sha1));
115         } else {
116                 /* TODO: How to handle multiple merge-bases? */
117                 base_sha1 = bases->item->object.sha1;
118                 OUTPUT(o, 3, "Multiple merge bases found. Using the first "
119                        "(%.7s)", sha1_to_hex(base_sha1));
120         }
121
122         OUTPUT(o, 4, "Merging remote commit %.7s into local commit %.7s with "
123                "merge-base %.7s", sha1_to_hex(remote->object.sha1),
124                sha1_to_hex(local->object.sha1), sha1_to_hex(base_sha1));
125
126         if (!hashcmp(remote->object.sha1, base_sha1)) {
127                 /* Already merged; result == local commit */
128                 OUTPUT(o, 2, "Already up-to-date!");
129                 hashcpy(result_sha1, local->object.sha1);
130                 goto found_result;
131         }
132         if (!hashcmp(local->object.sha1, base_sha1)) {
133                 /* Fast-forward; result == remote commit */
134                 OUTPUT(o, 2, "Fast-forward");
135                 hashcpy(result_sha1, remote->object.sha1);
136                 goto found_result;
137         }
138
139         /* TODO: */
140         result = error("notes_merge() cannot yet handle real merges.");
141
142 found_result:
143         free_commit_list(bases);
144         trace_printf("notes_merge(): result = %i, result_sha1 = %.7s\n",
145                result, sha1_to_hex(result_sha1));
146         return result;
147 }