]> git.ipfire.org Git - thirdparty/git.git/blame - bisect.c
bisect--helper: implement "git bisect--helper"
[thirdparty/git.git] / bisect.c
CommitLineData
a2ad79ce
CC
1#include "cache.h"
2#include "commit.h"
3#include "diff.h"
4#include "revision.h"
1bf072e3
CC
5#include "refs.h"
6#include "list-objects.h"
4eb5b646 7#include "sha1-lookup.h"
a2ad79ce
CC
8#include "bisect.h"
9
95188648
CC
10static unsigned char (*skipped_sha1)[20];
11static int skipped_sha1_nr;
1bf072e3
CC
12static int skipped_sha1_alloc;
13
14static const char **rev_argv;
15static int rev_argv_nr;
16static int rev_argv_alloc;
95188648 17
a2ad79ce
CC
18/* bits #0-15 in revision.h */
19
20#define COUNTED (1u<<16)
21
22/*
23 * This is a truly stupid algorithm, but it's only
24 * used for bisection, and we just don't care enough.
25 *
26 * We care just barely enough to avoid recursing for
27 * non-merge entries.
28 */
29static int count_distance(struct commit_list *entry)
30{
31 int nr = 0;
32
33 while (entry) {
34 struct commit *commit = entry->item;
35 struct commit_list *p;
36
37 if (commit->object.flags & (UNINTERESTING | COUNTED))
38 break;
39 if (!(commit->object.flags & TREESAME))
40 nr++;
41 commit->object.flags |= COUNTED;
42 p = commit->parents;
43 entry = p;
44 if (p) {
45 p = p->next;
46 while (p) {
47 nr += count_distance(p);
48 p = p->next;
49 }
50 }
51 }
52
53 return nr;
54}
55
56static void clear_distance(struct commit_list *list)
57{
58 while (list) {
59 struct commit *commit = list->item;
60 commit->object.flags &= ~COUNTED;
61 list = list->next;
62 }
63}
64
65#define DEBUG_BISECT 0
66
67static inline int weight(struct commit_list *elem)
68{
69 return *((int*)(elem->item->util));
70}
71
72static inline void weight_set(struct commit_list *elem, int weight)
73{
74 *((int*)(elem->item->util)) = weight;
75}
76
77static int count_interesting_parents(struct commit *commit)
78{
79 struct commit_list *p;
80 int count;
81
82 for (count = 0, p = commit->parents; p; p = p->next) {
83 if (p->item->object.flags & UNINTERESTING)
84 continue;
85 count++;
86 }
87 return count;
88}
89
90static inline int halfway(struct commit_list *p, int nr)
91{
92 /*
93 * Don't short-cut something we are not going to return!
94 */
95 if (p->item->object.flags & TREESAME)
96 return 0;
97 if (DEBUG_BISECT)
98 return 0;
99 /*
100 * 2 and 3 are halfway of 5.
101 * 3 is halfway of 6 but 2 and 4 are not.
102 */
103 switch (2 * weight(p) - nr) {
104 case -1: case 0: case 1:
105 return 1;
106 default:
107 return 0;
108 }
109}
110
111#if !DEBUG_BISECT
112#define show_list(a,b,c,d) do { ; } while (0)
113#else
114static void show_list(const char *debug, int counted, int nr,
115 struct commit_list *list)
116{
117 struct commit_list *p;
118
119 fprintf(stderr, "%s (%d/%d)\n", debug, counted, nr);
120
121 for (p = list; p; p = p->next) {
122 struct commit_list *pp;
123 struct commit *commit = p->item;
124 unsigned flags = commit->object.flags;
125 enum object_type type;
126 unsigned long size;
127 char *buf = read_sha1_file(commit->object.sha1, &type, &size);
128 char *ep, *sp;
129
130 fprintf(stderr, "%c%c%c ",
131 (flags & TREESAME) ? ' ' : 'T',
132 (flags & UNINTERESTING) ? 'U' : ' ',
133 (flags & COUNTED) ? 'C' : ' ');
134 if (commit->util)
135 fprintf(stderr, "%3d", weight(p));
136 else
137 fprintf(stderr, "---");
138 fprintf(stderr, " %.*s", 8, sha1_to_hex(commit->object.sha1));
139 for (pp = commit->parents; pp; pp = pp->next)
140 fprintf(stderr, " %.*s", 8,
141 sha1_to_hex(pp->item->object.sha1));
142
143 sp = strstr(buf, "\n\n");
144 if (sp) {
145 sp += 2;
146 for (ep = sp; *ep && *ep != '\n'; ep++)
147 ;
148 fprintf(stderr, " %.*s", (int)(ep - sp), sp);
149 }
150 fprintf(stderr, "\n");
151 }
152}
153#endif /* DEBUG_BISECT */
154
155static struct commit_list *best_bisection(struct commit_list *list, int nr)
156{
157 struct commit_list *p, *best;
158 int best_distance = -1;
159
160 best = list;
161 for (p = list; p; p = p->next) {
162 int distance;
163 unsigned flags = p->item->object.flags;
164
165 if (flags & TREESAME)
166 continue;
167 distance = weight(p);
168 if (nr - distance < distance)
169 distance = nr - distance;
170 if (distance > best_distance) {
171 best = p;
172 best_distance = distance;
173 }
174 }
175
176 return best;
177}
178
179struct commit_dist {
180 struct commit *commit;
181 int distance;
182};
183
184static int compare_commit_dist(const void *a_, const void *b_)
185{
186 struct commit_dist *a, *b;
187
188 a = (struct commit_dist *)a_;
189 b = (struct commit_dist *)b_;
190 if (a->distance != b->distance)
191 return b->distance - a->distance; /* desc sort */
192 return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
193}
194
195static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
196{
197 struct commit_list *p;
198 struct commit_dist *array = xcalloc(nr, sizeof(*array));
199 int cnt, i;
200
201 for (p = list, cnt = 0; p; p = p->next) {
202 int distance;
203 unsigned flags = p->item->object.flags;
204
205 if (flags & TREESAME)
206 continue;
207 distance = weight(p);
208 if (nr - distance < distance)
209 distance = nr - distance;
210 array[cnt].commit = p->item;
211 array[cnt].distance = distance;
212 cnt++;
213 }
214 qsort(array, cnt, sizeof(*array), compare_commit_dist);
215 for (p = list, i = 0; i < cnt; i++) {
216 struct name_decoration *r = xmalloc(sizeof(*r) + 100);
217 struct object *obj = &(array[i].commit->object);
218
219 sprintf(r->name, "dist=%d", array[i].distance);
220 r->next = add_decoration(&name_decoration, obj, r);
221 p->item = array[i].commit;
222 p = p->next;
223 }
224 if (p)
225 p->next = NULL;
226 free(array);
227 return list;
228}
229
230/*
231 * zero or positive weight is the number of interesting commits it can
232 * reach, including itself. Especially, weight = 0 means it does not
233 * reach any tree-changing commits (e.g. just above uninteresting one
234 * but traversal is with pathspec).
235 *
236 * weight = -1 means it has one parent and its distance is yet to
237 * be computed.
238 *
239 * weight = -2 means it has more than one parent and its distance is
240 * unknown. After running count_distance() first, they will get zero
241 * or positive distance.
242 */
243static struct commit_list *do_find_bisection(struct commit_list *list,
244 int nr, int *weights,
245 int find_all)
246{
247 int n, counted;
248 struct commit_list *p;
249
250 counted = 0;
251
252 for (n = 0, p = list; p; p = p->next) {
253 struct commit *commit = p->item;
254 unsigned flags = commit->object.flags;
255
256 p->item->util = &weights[n++];
257 switch (count_interesting_parents(commit)) {
258 case 0:
259 if (!(flags & TREESAME)) {
260 weight_set(p, 1);
261 counted++;
262 show_list("bisection 2 count one",
263 counted, nr, list);
264 }
265 /*
266 * otherwise, it is known not to reach any
267 * tree-changing commit and gets weight 0.
268 */
269 break;
270 case 1:
271 weight_set(p, -1);
272 break;
273 default:
274 weight_set(p, -2);
275 break;
276 }
277 }
278
279 show_list("bisection 2 initialize", counted, nr, list);
280
281 /*
282 * If you have only one parent in the resulting set
283 * then you can reach one commit more than that parent
284 * can reach. So we do not have to run the expensive
285 * count_distance() for single strand of pearls.
286 *
287 * However, if you have more than one parents, you cannot
288 * just add their distance and one for yourself, since
289 * they usually reach the same ancestor and you would
290 * end up counting them twice that way.
291 *
292 * So we will first count distance of merges the usual
293 * way, and then fill the blanks using cheaper algorithm.
294 */
295 for (p = list; p; p = p->next) {
296 if (p->item->object.flags & UNINTERESTING)
297 continue;
298 if (weight(p) != -2)
299 continue;
300 weight_set(p, count_distance(p));
301 clear_distance(list);
302
303 /* Does it happen to be at exactly half-way? */
304 if (!find_all && halfway(p, nr))
305 return p;
306 counted++;
307 }
308
309 show_list("bisection 2 count_distance", counted, nr, list);
310
311 while (counted < nr) {
312 for (p = list; p; p = p->next) {
313 struct commit_list *q;
314 unsigned flags = p->item->object.flags;
315
316 if (0 <= weight(p))
317 continue;
318 for (q = p->item->parents; q; q = q->next) {
319 if (q->item->object.flags & UNINTERESTING)
320 continue;
321 if (0 <= weight(q))
322 break;
323 }
324 if (!q)
325 continue;
326
327 /*
328 * weight for p is unknown but q is known.
329 * add one for p itself if p is to be counted,
330 * otherwise inherit it from q directly.
331 */
332 if (!(flags & TREESAME)) {
333 weight_set(p, weight(q)+1);
334 counted++;
335 show_list("bisection 2 count one",
336 counted, nr, list);
337 }
338 else
339 weight_set(p, weight(q));
340
341 /* Does it happen to be at exactly half-way? */
342 if (!find_all && halfway(p, nr))
343 return p;
344 }
345 }
346
347 show_list("bisection 2 counted all", counted, nr, list);
348
349 if (!find_all)
350 return best_bisection(list, nr);
351 else
352 return best_bisection_sorted(list, nr);
353}
354
355struct commit_list *find_bisection(struct commit_list *list,
356 int *reaches, int *all,
357 int find_all)
358{
359 int nr, on_list;
360 struct commit_list *p, *best, *next, *last;
361 int *weights;
362
363 show_list("bisection 2 entry", 0, 0, list);
364
365 /*
366 * Count the number of total and tree-changing items on the
367 * list, while reversing the list.
368 */
369 for (nr = on_list = 0, last = NULL, p = list;
370 p;
371 p = next) {
372 unsigned flags = p->item->object.flags;
373
374 next = p->next;
375 if (flags & UNINTERESTING)
376 continue;
377 p->next = last;
378 last = p;
379 if (!(flags & TREESAME))
380 nr++;
381 on_list++;
382 }
383 list = last;
384 show_list("bisection 2 sorted", 0, nr, list);
385
386 *all = nr;
387 weights = xcalloc(on_list, sizeof(*weights));
388
389 /* Do the real work of finding bisection commit. */
390 best = do_find_bisection(list, nr, weights, find_all);
391 if (best) {
392 if (!find_all)
393 best->next = NULL;
394 *reaches = weight(best);
395 }
396 free(weights);
397 return best;
398}
399
1bf072e3
CC
400static int register_ref(const char *refname, const unsigned char *sha1,
401 int flags, void *cb_data)
402{
403 if (!strcmp(refname, "bad")) {
404 ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
405 rev_argv[rev_argv_nr++] = xstrdup(sha1_to_hex(sha1));
406 } else if (!prefixcmp(refname, "good-")) {
407 const char *hex = sha1_to_hex(sha1);
408 char *good = xmalloc(strlen(hex) + 2);
409 *good = '^';
410 memcpy(good + 1, hex, strlen(hex) + 1);
411 ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
412 rev_argv[rev_argv_nr++] = good;
413 } else if (!prefixcmp(refname, "skip-")) {
414 ALLOC_GROW(skipped_sha1, skipped_sha1_nr + 1,
415 skipped_sha1_alloc);
416 hashcpy(skipped_sha1[skipped_sha1_nr++], sha1);
417 }
418
419 return 0;
420}
421
422static int read_bisect_refs(void)
423{
424 return for_each_ref_in("refs/bisect/", register_ref, NULL);
425}
426
95188648
CC
427static int skipcmp(const void *a, const void *b)
428{
429 return hashcmp(a, b);
430}
431
432static void prepare_skipped(void)
433{
434 qsort(skipped_sha1, skipped_sha1_nr, sizeof(*skipped_sha1), skipcmp);
435}
436
4eb5b646
CC
437static const unsigned char *skipped_sha1_access(size_t index, void *table)
438{
439 unsigned char (*skipped)[20] = table;
440 return skipped[index];
441}
442
95188648
CC
443static int lookup_skipped(unsigned char *sha1)
444{
4eb5b646
CC
445 return sha1_pos(sha1, skipped_sha1, skipped_sha1_nr,
446 skipped_sha1_access);
95188648
CC
447}
448
449struct commit_list *filter_skipped(struct commit_list *list,
450 struct commit_list **tried,
451 int show_all)
452{
453 struct commit_list *filtered = NULL, **f = &filtered;
454
455 *tried = NULL;
456
457 if (!skipped_sha1_nr)
458 return list;
459
460 prepare_skipped();
461
462 while (list) {
463 struct commit_list *next = list->next;
464 list->next = NULL;
465 if (0 <= lookup_skipped(list->item->object.sha1)) {
466 /* Move current to tried list */
467 *tried = list;
468 tried = &list->next;
469 } else {
470 if (!show_all)
471 return list;
472 /* Move current to filtered list */
473 *f = list;
474 f = &list->next;
475 }
476 list = next;
477 }
478
479 return filtered;
480}
1bf072e3
CC
481
482int bisect_next_vars(const char *prefix)
483{
484 struct rev_info revs;
485 int reaches = 0, all = 0;
486
487 init_revisions(&revs, prefix);
488 revs.abbrev = 0;
489 revs.commit_format = CMIT_FMT_UNSPECIFIED;
490
491 /* argv[0] will be ignored by setup_revisions */
492 ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
493 rev_argv[rev_argv_nr++] = xstrdup("bisect_rev_setup");
494
495 if (read_bisect_refs())
496 die("reading bisect refs failed");
497
498 ALLOC_GROW(rev_argv, rev_argv_nr + 1, rev_argv_alloc);
499 rev_argv[rev_argv_nr++] = xstrdup("--");
500
501 setup_revisions(rev_argv_nr, rev_argv, &revs, NULL);
502
503 revs.limited = 1;
504
505 if (prepare_revision_walk(&revs))
506 die("revision walk setup failed");
507 if (revs.tree_objects)
508 mark_edges_uninteresting(revs.commits, &revs, NULL);
509
510 revs.commits = find_bisection(revs.commits, &reaches, &all,
511 !!skipped_sha1_nr);
512
513 return show_bisect_vars(&revs, reaches, all, 0, 1);
514}