Skip to content

Commit 5227c38

Browse files
derrickstoleegitster
authored andcommitted
commit-reach: move walk methods from commit.c
There are several commit walks in the codebase. Group them together into a new commit-reach.c file and corresponding header. After we group these walks into one place, we can reduce duplicate logic by calling equivalent methods. The method declarations in commit.h are not touched by this commit and will be moved in a following commit. Many consumers need to point to commit-reach.h and that would bloat this commit. Signed-off-by: Derrick Stolee <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent dade47c commit 5227c38

File tree

5 files changed

+404
-359
lines changed

5 files changed

+404
-359
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,7 @@ LIB_OBJS += column.o
829829
LIB_OBJS += combine-diff.o
830830
LIB_OBJS += commit.o
831831
LIB_OBJS += commit-graph.o
832+
LIB_OBJS += commit-reach.o
832833
LIB_OBJS += compat/obstack.o
833834
LIB_OBJS += compat/terminal.o
834835
LIB_OBJS += config.o

commit-reach.c

Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
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+
}

0 commit comments

Comments
 (0)