bisect.con commit bisect--helper: implement "git bisect--helper" (1bf072e)
   1#include "cache.h"
   2#include "commit.h"
   3#include "diff.h"
   4#include "revision.h"
   5#include "refs.h"
   6#include "list-objects.h"
   7#include "sha1-lookup.h"
   8#include "bisect.h"
   9
  10static unsigned char (*skipped_sha1)[20];
  11static int skipped_sha1_nr;
  12static int skipped_sha1_alloc;
  13
  14static const char **rev_argv;
  15static int rev_argv_nr;
  16static int rev_argv_alloc;
  17
  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
 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
 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
 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
 443static int lookup_skipped(unsigned char *sha1)
 444{
 445        return sha1_pos(sha1, skipped_sha1, skipped_sha1_nr,
 446                        skipped_sha1_access);
 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}
 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}