]>
Commit | Line | Data |
---|---|---|
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 ET |
17 | |
18 | int git_merge__cleanup(git_repository *repo) | |
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 || | |
29 | git_buf_joinpath(&merge_mode_path, repo->path_repository, GIT_MERGE_MODE_FILE) < 0) | |
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 | ||
43 | cleanup: | |
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 |
51 | int 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 | ||
97 | cleanup: | |
98 | git_commit_list_free(&result); | |
99 | git_revwalk_free(walk); | |
100 | git_vector_free(&list); | |
101 | return error; | |
102 | } | |
103 | ||
104 | int 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 | ||
144 | on_error: | |
145 | git_revwalk_free(walk); | |
146 | return -1; | |
147 | } | |
148 | ||
149 | static 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 | ||
162 | int 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 | } |