]> git.proxmox.com Git - libgit2.git/blame - src/merge.c
Merge pull request #1204 from arrbee/diff-blob-to-buffer
[libgit2.git] / src / merge.c
CommitLineData
632d8b23
ET
1/*
2 * Copyright (C) 2009-2012 the libgit2 contributors
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
7
8#include "repository.h"
4ff192d3 9#include "revwalk.h"
632d8b23
ET
10#include "buffer.h"
11#include "merge.h"
12#include "refs.h"
13#include "git2/repository.h"
14#include "git2/merge.h"
15#include "git2/reset.h"
4ff192d3 16#include "commit_list.h"
632d8b23 17
ad2bc32f 18int git_repository_merge_cleanup(git_repository *repo)
632d8b23
ET
19{
20 int error = 0;
21 git_buf merge_head_path = GIT_BUF_INIT,
22 merge_mode_path = GIT_BUF_INIT,
23 merge_msg_path = GIT_BUF_INIT;
24
25 assert(repo);
26
27 if (git_buf_joinpath(&merge_head_path, repo->path_repository, GIT_MERGE_HEAD_FILE) < 0 ||
28 git_buf_joinpath(&merge_mode_path, repo->path_repository, GIT_MERGE_MODE_FILE) < 0 ||
c31ae146 29 git_buf_joinpath(&merge_msg_path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
632d8b23
ET
30 return -1;
31
32 if (git_path_isfile(merge_head_path.ptr)) {
33 if ((error = p_unlink(merge_head_path.ptr)) < 0)
34 goto cleanup;
35 }
36
37 if (git_path_isfile(merge_mode_path.ptr))
38 (void)p_unlink(merge_mode_path.ptr);
39
40 if (git_path_isfile(merge_msg_path.ptr))
41 (void)p_unlink(merge_msg_path.ptr);
42
43cleanup:
44 git_buf_free(&merge_msg_path);
45 git_buf_free(&merge_mode_path);
46 git_buf_free(&merge_head_path);
47
48 return error;
49}
50
4ff192d3
BS
51int git_merge_base_many(git_oid *out, git_repository *repo, const git_oid input_array[], size_t length)
52{
53 git_revwalk *walk;
54 git_vector list;
55 git_commit_list *result = NULL;
56 int error = -1;
57 unsigned int i;
58 git_commit_list_node *commit;
59
60 assert(out && repo && input_array);
61
62 if (length < 2) {
63 giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length);
64 return -1;
65 }
66
67 if (git_vector_init(&list, length - 1, NULL) < 0)
68 return -1;
69
70 if (git_revwalk_new(&walk, repo) < 0)
71 goto cleanup;
72
73 for (i = 1; i < length; i++) {
d5e44d84 74 commit = git_revwalk__commit_lookup(walk, &input_array[i]);
4ff192d3
BS
75 if (commit == NULL)
76 goto cleanup;
77
78 git_vector_insert(&list, commit);
79 }
80
d5e44d84 81 commit = git_revwalk__commit_lookup(walk, &input_array[0]);
4ff192d3
BS
82 if (commit == NULL)
83 goto cleanup;
84
85 if (git_merge__bases_many(&result, walk, commit, &list) < 0)
86 goto cleanup;
87
88 if (!result) {
89 error = GIT_ENOTFOUND;
90 goto cleanup;
91 }
92
93 git_oid_cpy(out, &result->item->oid);
94
95 error = 0;
96
97cleanup:
98 git_commit_list_free(&result);
99 git_revwalk_free(walk);
100 git_vector_free(&list);
101 return error;
102}
103
104int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two)
105{
106 git_revwalk *walk;
107 git_vector list;
108 git_commit_list *result = NULL;
109 git_commit_list_node *commit;
110 void *contents[1];
111
112 if (git_revwalk_new(&walk, repo) < 0)
113 return -1;
114
d5e44d84 115 commit = git_revwalk__commit_lookup(walk, two);
4ff192d3
BS
116 if (commit == NULL)
117 goto on_error;
118
119 /* This is just one value, so we can do it on the stack */
120 memset(&list, 0x0, sizeof(git_vector));
121 contents[0] = commit;
122 list.length = 1;
123 list.contents = contents;
124
d5e44d84 125 commit = git_revwalk__commit_lookup(walk, one);
4ff192d3
BS
126 if (commit == NULL)
127 goto on_error;
128
129 if (git_merge__bases_many(&result, walk, commit, &list) < 0)
130 goto on_error;
131
132 if (!result) {
133 git_revwalk_free(walk);
134 giterr_clear();
135 return GIT_ENOTFOUND;
136 }
137
138 git_oid_cpy(out, &result->item->oid);
139 git_commit_list_free(&result);
140 git_revwalk_free(walk);
141
142 return 0;
143
144on_error:
145 git_revwalk_free(walk);
146 return -1;
147}
148
149static int interesting(git_pqueue *list)
150{
151 unsigned int i;
152 /* element 0 isn't used - we need to start at 1 */
153 for (i = 1; i < list->size; i++) {
154 git_commit_list_node *commit = list->d[i];
155 if ((commit->flags & STALE) == 0)
156 return 1;
157 }
158
159 return 0;
160}
161
162int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
163{
164 int error;
165 unsigned int i;
166 git_commit_list_node *two;
167 git_commit_list *result = NULL, *tmp = NULL;
168 git_pqueue list;
169
170 /* if the commit is repeated, we have a our merge base already */
171 git_vector_foreach(twos, i, two) {
172 if (one == two)
173 return git_commit_list_insert(one, out) ? 0 : -1;
174 }
175
176 if (git_pqueue_init(&list, twos->length * 2, git_commit_list_time_cmp) < 0)
177 return -1;
178
179 if (git_commit_list_parse(walk, one) < 0)
180 return -1;
181
182 one->flags |= PARENT1;
183 if (git_pqueue_insert(&list, one) < 0)
184 return -1;
185
186 git_vector_foreach(twos, i, two) {
187 git_commit_list_parse(walk, two);
188 two->flags |= PARENT2;
189 if (git_pqueue_insert(&list, two) < 0)
190 return -1;
191 }
192
193 /* as long as there are non-STALE commits */
194 while (interesting(&list)) {
195 git_commit_list_node *commit;
196 int flags;
197
198 commit = git_pqueue_pop(&list);
199
200 flags = commit->flags & (PARENT1 | PARENT2 | STALE);
201 if (flags == (PARENT1 | PARENT2)) {
202 if (!(commit->flags & RESULT)) {
203 commit->flags |= RESULT;
204 if (git_commit_list_insert(commit, &result) == NULL)
205 return -1;
206 }
207 /* we mark the parents of a merge stale */
208 flags |= STALE;
209 }
210
211 for (i = 0; i < commit->out_degree; i++) {
212 git_commit_list_node *p = commit->parents[i];
213 if ((p->flags & flags) == flags)
214 continue;
215
216 if ((error = git_commit_list_parse(walk, p)) < 0)
217 return error;
218
219 p->flags |= flags;
220 if (git_pqueue_insert(&list, p) < 0)
221 return -1;
222 }
223 }
224
225 git_pqueue_free(&list);
226
227 /* filter out any stale commits in the results */
228 tmp = result;
229 result = NULL;
230
231 while (tmp) {
232 struct git_commit_list *next = tmp->next;
233 if (!(tmp->item->flags & STALE))
234 if (git_commit_list_insert_by_date(tmp->item, &result) == NULL)
235 return -1;
236
237 git__free(tmp);
238 tmp = next;
239 }
240
241 *out = result;
242 return 0;
243}
42e50b5e
ET
244
245int git_repository_mergehead_foreach(git_repository *repo,
246 git_repository_mergehead_foreach_cb cb,
247 void *payload)
248{
249 git_buf merge_head_path = GIT_BUF_INIT, merge_head_file = GIT_BUF_INIT;
250 char *buffer, *line;
251 size_t line_num = 1;
252 git_oid oid;
253 int error = 0;
254
255 assert(repo && cb);
256
257 if ((error = git_buf_joinpath(&merge_head_path, repo->path_repository,
258 GIT_MERGE_HEAD_FILE)) < 0)
259 return error;
260
261 if ((error = git_futils_readbuffer(&merge_head_file,
262 git_buf_cstr(&merge_head_path))) < 0)
263 goto cleanup;
264
265 buffer = merge_head_file.ptr;
266
267 while ((line = git__strsep(&buffer, "\n")) != NULL) {
268 if (strlen(line) != GIT_OID_HEXSZ) {
269 giterr_set(GITERR_INVALID, "Unable to parse OID - invalid length");
270 error = -1;
271 goto cleanup;
272 }
273
274 if ((error = git_oid_fromstr(&oid, line)) < 0)
275 goto cleanup;
276
277 if (cb(&oid, payload) < 0) {
278 error = GIT_EUSER;
279 goto cleanup;
280 }
281
282 ++line_num;
283 }
284
285 if (*buffer) {
286 giterr_set(GITERR_MERGE, "No EOL at line %d", line_num);
287 error = -1;
288 goto cleanup;
289 }
290
291cleanup:
292 git_buf_free(&merge_head_path);
293 git_buf_free(&merge_head_file);
294
295 return error;
296}