commit-reach: move walk methods from commit.c
[git] / commit-reach.c
1 #include "cache.h"
2 #include "prio-queue.h"
3 #include "commit.h"
4 #include "commit-reach.h"
5
6 /* Remember to update object flag allocation in object.h */
7 #define PARENT1         (1u<<16)
8 #define PARENT2         (1u<<17)
9 #define STALE           (1u<<18)
10 #define RESULT          (1u<<19)
11
12 static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
13
14 static int queue_has_nonstale(struct prio_queue *queue)
15 {
16         int i;
17         for (i = 0; i < queue->nr; i++) {
18                 struct commit *commit = queue->array[i].data;
19                 if (!(commit->object.flags & STALE))
20                         return 1;
21         }
22         return 0;
23 }
24
25 /* all input commits in one and twos[] must have been parsed! */
26 static struct commit_list *paint_down_to_common(struct commit *one, int n,
27                                                 struct commit **twos,
28                                                 int min_generation)
29 {
30         struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
31         struct commit_list *result = NULL;
32         int i;
33         uint32_t last_gen = GENERATION_NUMBER_INFINITY;
34
35         one->object.flags |= PARENT1;
36         if (!n) {
37                 commit_list_append(one, &result);
38                 return result;
39         }
40         prio_queue_put(&queue, one);
41
42         for (i = 0; i < n; i++) {
43                 twos[i]->object.flags |= PARENT2;
44                 prio_queue_put(&queue, twos[i]);
45         }
46
47         while (queue_has_nonstale(&queue)) {
48                 struct commit *commit = prio_queue_get(&queue);
49                 struct commit_list *parents;
50                 int flags;
51
52                 if (commit->generation > last_gen)
53                         BUG("bad generation skip %8x > %8x at %s",
54                             commit->generation, last_gen,
55                             oid_to_hex(&commit->object.oid));
56                 last_gen = commit->generation;
57
58                 if (commit->generation < min_generation)
59                         break;
60
61                 flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
62                 if (flags == (PARENT1 | PARENT2)) {
63                         if (!(commit->object.flags & RESULT)) {
64                                 commit->object.flags |= RESULT;
65                                 commit_list_insert_by_date(commit, &result);
66                         }
67                         /* Mark parents of a found merge stale */
68                         flags |= STALE;
69                 }
70                 parents = commit->parents;
71                 while (parents) {
72                         struct commit *p = parents->item;
73                         parents = parents->next;
74                         if ((p->object.flags & flags) == flags)
75                                 continue;
76                         if (parse_commit(p))
77                                 return NULL;
78                         p->object.flags |= flags;
79                         prio_queue_put(&queue, p);
80                 }
81         }
82
83         clear_prio_queue(&queue);
84         return result;
85 }
86
87 static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos)
88 {
89         struct commit_list *list = NULL;
90         struct commit_list *result = NULL;
91         int i;
92
93         for (i = 0; i < n; i++) {
94                 if (one == twos[i])
95                         /*
96                          * We do not mark this even with RESULT so we do not
97                          * have to clean it up.
98                          */
99                         return commit_list_insert(one, &result);
100         }
101
102         if (parse_commit(one))
103                 return NULL;
104         for (i = 0; i < n; i++) {
105                 if (parse_commit(twos[i]))
106                         return NULL;
107         }
108
109         list = paint_down_to_common(one, n, twos, 0);
110
111         while (list) {
112                 struct commit *commit = pop_commit(&list);
113                 if (!(commit->object.flags & STALE))
114                         commit_list_insert_by_date(commit, &result);
115         }
116         return result;
117 }
118
119 struct commit_list *get_octopus_merge_bases(struct commit_list *in)
120 {
121         struct commit_list *i, *j, *k, *ret = NULL;
122
123         if (!in)
124                 return ret;
125
126         commit_list_insert(in->item, &ret);
127
128         for (i = in->next; i; i = i->next) {
129                 struct commit_list *new_commits = NULL, *end = NULL;
130
131                 for (j = ret; j; j = j->next) {
132                         struct commit_list *bases;
133                         bases = get_merge_bases(i->item, j->item);
134                         if (!new_commits)
135                                 new_commits = bases;
136                         else
137                                 end->next = bases;
138                         for (k = bases; k; k = k->next)
139                                 end = k;
140                 }
141                 ret = new_commits;
142         }
143         return ret;
144 }
145
146 static int remove_redundant(struct commit **array, int cnt)
147 {
148         /*
149          * Some commit in the array may be an ancestor of
150          * another commit.  Move such commit to the end of
151          * the array, and return the number of commits that
152          * are independent from each other.
153          */
154         struct commit **work;
155         unsigned char *redundant;
156         int *filled_index;
157         int i, j, filled;
158
159         work = xcalloc(cnt, sizeof(*work));
160         redundant = xcalloc(cnt, 1);
161         ALLOC_ARRAY(filled_index, cnt - 1);
162
163         for (i = 0; i < cnt; i++)
164                 parse_commit(array[i]);
165         for (i = 0; i < cnt; i++) {
166                 struct commit_list *common;
167                 uint32_t min_generation = array[i]->generation;
168
169                 if (redundant[i])
170                         continue;
171                 for (j = filled = 0; j < cnt; j++) {
172                         if (i == j || redundant[j])
173                                 continue;
174                         filled_index[filled] = j;
175                         work[filled++] = array[j];
176
177                         if (array[j]->generation < min_generation)
178                                 min_generation = array[j]->generation;
179                 }
180                 common = paint_down_to_common(array[i], filled, work,
181                                               min_generation);
182                 if (array[i]->object.flags & PARENT2)
183                         redundant[i] = 1;
184                 for (j = 0; j < filled; j++)
185                         if (work[j]->object.flags & PARENT1)
186                                 redundant[filled_index[j]] = 1;
187                 clear_commit_marks(array[i], all_flags);
188                 clear_commit_marks_many(filled, work, all_flags);
189                 free_commit_list(common);
190         }
191
192         /* Now collect the result */
193         COPY_ARRAY(work, array, cnt);
194         for (i = filled = 0; i < cnt; i++)
195                 if (!redundant[i])
196                         array[filled++] = work[i];
197         for (j = filled, i = 0; i < cnt; i++)
198                 if (redundant[i])
199                         array[j++] = work[i];
200         free(work);
201         free(redundant);
202         free(filled_index);
203         return filled;
204 }
205
206 static struct commit_list *get_merge_bases_many_0(struct commit *one,
207                                                   int n,
208                                                   struct commit **twos,
209                                                   int cleanup)
210 {
211         struct commit_list *list;
212         struct commit **rslt;
213         struct commit_list *result;
214         int cnt, i;
215
216         result = merge_bases_many(one, n, twos);
217         for (i = 0; i < n; i++) {
218                 if (one == twos[i])
219                         return result;
220         }
221         if (!result || !result->next) {
222                 if (cleanup) {
223                         clear_commit_marks(one, all_flags);
224                         clear_commit_marks_many(n, twos, all_flags);
225                 }
226                 return result;
227         }
228
229         /* There are more than one */
230         cnt = commit_list_count(result);
231         rslt = xcalloc(cnt, sizeof(*rslt));
232         for (list = result, i = 0; list; list = list->next)
233                 rslt[i++] = list->item;
234         free_commit_list(result);
235
236         clear_commit_marks(one, all_flags);
237         clear_commit_marks_many(n, twos, all_flags);
238
239         cnt = remove_redundant(rslt, cnt);
240         result = NULL;
241         for (i = 0; i < cnt; i++)
242                 commit_list_insert_by_date(rslt[i], &result);
243         free(rslt);
244         return result;
245 }
246
247 struct commit_list *get_merge_bases_many(struct commit *one,
248                                          int n,
249                                          struct commit **twos)
250 {
251         return get_merge_bases_many_0(one, n, twos, 1);
252 }
253
254 struct commit_list *get_merge_bases_many_dirty(struct commit *one,
255                                                int n,
256                                                struct commit **twos)
257 {
258         return get_merge_bases_many_0(one, n, twos, 0);
259 }
260
261 struct commit_list *get_merge_bases(struct commit *one, struct commit *two)
262 {
263         return get_merge_bases_many_0(one, 1, &two, 1);
264 }
265
266 /*
267  * Is "commit" a descendant of one of the elements on the "with_commit" list?
268  */
269 int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
270 {
271         if (!with_commit)
272                 return 1;
273         while (with_commit) {
274                 struct commit *other;
275
276                 other = with_commit->item;
277                 with_commit = with_commit->next;
278                 if (in_merge_bases(other, commit))
279                         return 1;
280         }
281         return 0;
282 }
283
284 /*
285  * Is "commit" an ancestor of one of the "references"?
286  */
287 int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit **reference)
288 {
289         struct commit_list *bases;
290         int ret = 0, i;
291         uint32_t min_generation = GENERATION_NUMBER_INFINITY;
292
293         if (parse_commit(commit))
294                 return ret;
295         for (i = 0; i < nr_reference; i++) {
296                 if (parse_commit(reference[i]))
297                         return ret;
298                 if (reference[i]->generation < min_generation)
299                         min_generation = reference[i]->generation;
300         }
301
302         if (commit->generation > min_generation)
303                 return ret;
304
305         bases = paint_down_to_common(commit, nr_reference, reference, commit->generation);
306         if (commit->object.flags & PARENT2)
307                 ret = 1;
308         clear_commit_marks(commit, all_flags);
309         clear_commit_marks_many(nr_reference, reference, all_flags);
310         free_commit_list(bases);
311         return ret;
312 }
313
314 /*
315  * Is "commit" an ancestor of (i.e. reachable from) the "reference"?
316  */
317 int in_merge_bases(struct commit *commit, struct commit *reference)
318 {
319         return in_merge_bases_many(commit, 1, &reference);
320 }
321
322 struct commit_list *reduce_heads(struct commit_list *heads)
323 {
324         struct commit_list *p;
325         struct commit_list *result = NULL, **tail = &result;
326         struct commit **array;
327         int num_head, i;
328
329         if (!heads)
330                 return NULL;
331
332         /* Uniquify */
333         for (p = heads; p; p = p->next)
334                 p->item->object.flags &= ~STALE;
335         for (p = heads, num_head = 0; p; p = p->next) {
336                 if (p->item->object.flags & STALE)
337                         continue;
338                 p->item->object.flags |= STALE;
339                 num_head++;
340         }
341         array = xcalloc(num_head, sizeof(*array));
342         for (p = heads, i = 0; p; p = p->next) {
343                 if (p->item->object.flags & STALE) {
344                         array[i++] = p->item;
345                         p->item->object.flags &= ~STALE;
346                 }
347         }
348         num_head = remove_redundant(array, num_head);
349         for (i = 0; i < num_head; i++)
350                 tail = &commit_list_insert(array[i], tail)->next;
351         free(array);
352         return result;
353 }
354
355 void reduce_heads_replace(struct commit_list **heads)
356 {
357         struct commit_list *result = reduce_heads(*heads);
358         free_commit_list(*heads);
359         *heads = result;
360 }