commit-reach.con commit upload-pack: generalize commit date cutoff (118be57)
   1#include "cache.h"
   2#include "commit.h"
   3#include "commit-graph.h"
   4#include "decorate.h"
   5#include "prio-queue.h"
   6#include "tree.h"
   7#include "ref-filter.c"
   8#include "revision.h"
   9#include "tag.h"
  10#include "commit-reach.h"
  11
  12/* Remember to update object flag allocation in object.h */
  13#define PARENT1         (1u<<16)
  14#define PARENT2         (1u<<17)
  15#define STALE           (1u<<18)
  16#define RESULT          (1u<<19)
  17
  18static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
  19
  20static int queue_has_nonstale(struct prio_queue *queue)
  21{
  22        int i;
  23        for (i = 0; i < queue->nr; i++) {
  24                struct commit *commit = queue->array[i].data;
  25                if (!(commit->object.flags & STALE))
  26                        return 1;
  27        }
  28        return 0;
  29}
  30
  31/* all input commits in one and twos[] must have been parsed! */
  32static struct commit_list *paint_down_to_common(struct commit *one, int n,
  33                                                struct commit **twos,
  34                                                int min_generation)
  35{
  36        struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
  37        struct commit_list *result = NULL;
  38        int i;
  39        uint32_t last_gen = GENERATION_NUMBER_INFINITY;
  40
  41        one->object.flags |= PARENT1;
  42        if (!n) {
  43                commit_list_append(one, &result);
  44                return result;
  45        }
  46        prio_queue_put(&queue, one);
  47
  48        for (i = 0; i < n; i++) {
  49                twos[i]->object.flags |= PARENT2;
  50                prio_queue_put(&queue, twos[i]);
  51        }
  52
  53        while (queue_has_nonstale(&queue)) {
  54                struct commit *commit = prio_queue_get(&queue);
  55                struct commit_list *parents;
  56                int flags;
  57
  58                if (commit->generation > last_gen)
  59                        BUG("bad generation skip %8x > %8x at %s",
  60                            commit->generation, last_gen,
  61                            oid_to_hex(&commit->object.oid));
  62                last_gen = commit->generation;
  63
  64                if (commit->generation < min_generation)
  65                        break;
  66
  67                flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
  68                if (flags == (PARENT1 | PARENT2)) {
  69                        if (!(commit->object.flags & RESULT)) {
  70                                commit->object.flags |= RESULT;
  71                                commit_list_insert_by_date(commit, &result);
  72                        }
  73                        /* Mark parents of a found merge stale */
  74                        flags |= STALE;
  75                }
  76                parents = commit->parents;
  77                while (parents) {
  78                        struct commit *p = parents->item;
  79                        parents = parents->next;
  80                        if ((p->object.flags & flags) == flags)
  81                                continue;
  82                        if (parse_commit(p))
  83                                return NULL;
  84                        p->object.flags |= flags;
  85                        prio_queue_put(&queue, p);
  86                }
  87        }
  88
  89        clear_prio_queue(&queue);
  90        return result;
  91}
  92
  93static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos)
  94{
  95        struct commit_list *list = NULL;
  96        struct commit_list *result = NULL;
  97        int i;
  98
  99        for (i = 0; i < n; i++) {
 100                if (one == twos[i])
 101                        /*
 102                         * We do not mark this even with RESULT so we do not
 103                         * have to clean it up.
 104                         */
 105                        return commit_list_insert(one, &result);
 106        }
 107
 108        if (parse_commit(one))
 109                return NULL;
 110        for (i = 0; i < n; i++) {
 111                if (parse_commit(twos[i]))
 112                        return NULL;
 113        }
 114
 115        list = paint_down_to_common(one, n, twos, 0);
 116
 117        while (list) {
 118                struct commit *commit = pop_commit(&list);
 119                if (!(commit->object.flags & STALE))
 120                        commit_list_insert_by_date(commit, &result);
 121        }
 122        return result;
 123}
 124
 125struct commit_list *get_octopus_merge_bases(struct commit_list *in)
 126{
 127        struct commit_list *i, *j, *k, *ret = NULL;
 128
 129        if (!in)
 130                return ret;
 131
 132        commit_list_insert(in->item, &ret);
 133
 134        for (i = in->next; i; i = i->next) {
 135                struct commit_list *new_commits = NULL, *end = NULL;
 136
 137                for (j = ret; j; j = j->next) {
 138                        struct commit_list *bases;
 139                        bases = get_merge_bases(i->item, j->item);
 140                        if (!new_commits)
 141                                new_commits = bases;
 142                        else
 143                                end->next = bases;
 144                        for (k = bases; k; k = k->next)
 145                                end = k;
 146                }
 147                ret = new_commits;
 148        }
 149        return ret;
 150}
 151
 152static int remove_redundant(struct commit **array, int cnt)
 153{
 154        /*
 155         * Some commit in the array may be an ancestor of
 156         * another commit.  Move such commit to the end of
 157         * the array, and return the number of commits that
 158         * are independent from each other.
 159         */
 160        struct commit **work;
 161        unsigned char *redundant;
 162        int *filled_index;
 163        int i, j, filled;
 164
 165        work = xcalloc(cnt, sizeof(*work));
 166        redundant = xcalloc(cnt, 1);
 167        ALLOC_ARRAY(filled_index, cnt - 1);
 168
 169        for (i = 0; i < cnt; i++)
 170                parse_commit(array[i]);
 171        for (i = 0; i < cnt; i++) {
 172                struct commit_list *common;
 173                uint32_t min_generation = array[i]->generation;
 174
 175                if (redundant[i])
 176                        continue;
 177                for (j = filled = 0; j < cnt; j++) {
 178                        if (i == j || redundant[j])
 179                                continue;
 180                        filled_index[filled] = j;
 181                        work[filled++] = array[j];
 182
 183                        if (array[j]->generation < min_generation)
 184                                min_generation = array[j]->generation;
 185                }
 186                common = paint_down_to_common(array[i], filled, work,
 187                                              min_generation);
 188                if (array[i]->object.flags & PARENT2)
 189                        redundant[i] = 1;
 190                for (j = 0; j < filled; j++)
 191                        if (work[j]->object.flags & PARENT1)
 192                                redundant[filled_index[j]] = 1;
 193                clear_commit_marks(array[i], all_flags);
 194                clear_commit_marks_many(filled, work, all_flags);
 195                free_commit_list(common);
 196        }
 197
 198        /* Now collect the result */
 199        COPY_ARRAY(work, array, cnt);
 200        for (i = filled = 0; i < cnt; i++)
 201                if (!redundant[i])
 202                        array[filled++] = work[i];
 203        for (j = filled, i = 0; i < cnt; i++)
 204                if (redundant[i])
 205                        array[j++] = work[i];
 206        free(work);
 207        free(redundant);
 208        free(filled_index);
 209        return filled;
 210}
 211
 212static struct commit_list *get_merge_bases_many_0(struct commit *one,
 213                                                  int n,
 214                                                  struct commit **twos,
 215                                                  int cleanup)
 216{
 217        struct commit_list *list;
 218        struct commit **rslt;
 219        struct commit_list *result;
 220        int cnt, i;
 221
 222        result = merge_bases_many(one, n, twos);
 223        for (i = 0; i < n; i++) {
 224                if (one == twos[i])
 225                        return result;
 226        }
 227        if (!result || !result->next) {
 228                if (cleanup) {
 229                        clear_commit_marks(one, all_flags);
 230                        clear_commit_marks_many(n, twos, all_flags);
 231                }
 232                return result;
 233        }
 234
 235        /* There are more than one */
 236        cnt = commit_list_count(result);
 237        rslt = xcalloc(cnt, sizeof(*rslt));
 238        for (list = result, i = 0; list; list = list->next)
 239                rslt[i++] = list->item;
 240        free_commit_list(result);
 241
 242        clear_commit_marks(one, all_flags);
 243        clear_commit_marks_many(n, twos, all_flags);
 244
 245        cnt = remove_redundant(rslt, cnt);
 246        result = NULL;
 247        for (i = 0; i < cnt; i++)
 248                commit_list_insert_by_date(rslt[i], &result);
 249        free(rslt);
 250        return result;
 251}
 252
 253struct commit_list *get_merge_bases_many(struct commit *one,
 254                                         int n,
 255                                         struct commit **twos)
 256{
 257        return get_merge_bases_many_0(one, n, twos, 1);
 258}
 259
 260struct commit_list *get_merge_bases_many_dirty(struct commit *one,
 261                                               int n,
 262                                               struct commit **twos)
 263{
 264        return get_merge_bases_many_0(one, n, twos, 0);
 265}
 266
 267struct commit_list *get_merge_bases(struct commit *one, struct commit *two)
 268{
 269        return get_merge_bases_many_0(one, 1, &two, 1);
 270}
 271
 272/*
 273 * Is "commit" a descendant of one of the elements on the "with_commit" list?
 274 */
 275int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
 276{
 277        if (!with_commit)
 278                return 1;
 279        while (with_commit) {
 280                struct commit *other;
 281
 282                other = with_commit->item;
 283                with_commit = with_commit->next;
 284                if (in_merge_bases(other, commit))
 285                        return 1;
 286        }
 287        return 0;
 288}
 289
 290/*
 291 * Is "commit" an ancestor of one of the "references"?
 292 */
 293int in_merge_bases_many(struct commit *commit, int nr_reference, struct commit **reference)
 294{
 295        struct commit_list *bases;
 296        int ret = 0, i;
 297        uint32_t min_generation = GENERATION_NUMBER_INFINITY;
 298
 299        if (parse_commit(commit))
 300                return ret;
 301        for (i = 0; i < nr_reference; i++) {
 302                if (parse_commit(reference[i]))
 303                        return ret;
 304                if (reference[i]->generation < min_generation)
 305                        min_generation = reference[i]->generation;
 306        }
 307
 308        if (commit->generation > min_generation)
 309                return ret;
 310
 311        bases = paint_down_to_common(commit, nr_reference, reference, commit->generation);
 312        if (commit->object.flags & PARENT2)
 313                ret = 1;
 314        clear_commit_marks(commit, all_flags);
 315        clear_commit_marks_many(nr_reference, reference, all_flags);
 316        free_commit_list(bases);
 317        return ret;
 318}
 319
 320/*
 321 * Is "commit" an ancestor of (i.e. reachable from) the "reference"?
 322 */
 323int in_merge_bases(struct commit *commit, struct commit *reference)
 324{
 325        return in_merge_bases_many(commit, 1, &reference);
 326}
 327
 328struct commit_list *reduce_heads(struct commit_list *heads)
 329{
 330        struct commit_list *p;
 331        struct commit_list *result = NULL, **tail = &result;
 332        struct commit **array;
 333        int num_head, i;
 334
 335        if (!heads)
 336                return NULL;
 337
 338        /* Uniquify */
 339        for (p = heads; p; p = p->next)
 340                p->item->object.flags &= ~STALE;
 341        for (p = heads, num_head = 0; p; p = p->next) {
 342                if (p->item->object.flags & STALE)
 343                        continue;
 344                p->item->object.flags |= STALE;
 345                num_head++;
 346        }
 347        array = xcalloc(num_head, sizeof(*array));
 348        for (p = heads, i = 0; p; p = p->next) {
 349                if (p->item->object.flags & STALE) {
 350                        array[i++] = p->item;
 351                        p->item->object.flags &= ~STALE;
 352                }
 353        }
 354        num_head = remove_redundant(array, num_head);
 355        for (i = 0; i < num_head; i++)
 356                tail = &commit_list_insert(array[i], tail)->next;
 357        free(array);
 358        return result;
 359}
 360
 361void reduce_heads_replace(struct commit_list **heads)
 362{
 363        struct commit_list *result = reduce_heads(*heads);
 364        free_commit_list(*heads);
 365        *heads = result;
 366}
 367
 368static void unmark_and_free(struct commit_list *list, unsigned int mark)
 369{
 370        while (list) {
 371                struct commit *commit = pop_commit(&list);
 372                commit->object.flags &= ~mark;
 373        }
 374}
 375
 376int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
 377{
 378        struct object *o;
 379        struct commit *old_commit, *new_commit;
 380        struct commit_list *list, *used;
 381        int found = 0;
 382
 383        /*
 384         * Both new_commit and old_commit must be commit-ish and new_commit is descendant of
 385         * old_commit.  Otherwise we require --force.
 386         */
 387        o = deref_tag(the_repository, parse_object(the_repository, old_oid),
 388                      NULL, 0);
 389        if (!o || o->type != OBJ_COMMIT)
 390                return 0;
 391        old_commit = (struct commit *) o;
 392
 393        o = deref_tag(the_repository, parse_object(the_repository, new_oid),
 394                      NULL, 0);
 395        if (!o || o->type != OBJ_COMMIT)
 396                return 0;
 397        new_commit = (struct commit *) o;
 398
 399        if (parse_commit(new_commit) < 0)
 400                return 0;
 401
 402        used = list = NULL;
 403        commit_list_insert(new_commit, &list);
 404        while (list) {
 405                new_commit = pop_most_recent_commit(&list, TMP_MARK);
 406                commit_list_insert(new_commit, &used);
 407                if (new_commit == old_commit) {
 408                        found = 1;
 409                        break;
 410                }
 411        }
 412        unmark_and_free(list, TMP_MARK);
 413        unmark_and_free(used, TMP_MARK);
 414        return found;
 415}
 416
 417/*
 418 * Mimicking the real stack, this stack lives on the heap, avoiding stack
 419 * overflows.
 420 *
 421 * At each recursion step, the stack items points to the commits whose
 422 * ancestors are to be inspected.
 423 */
 424struct contains_stack {
 425        int nr, alloc;
 426        struct contains_stack_entry {
 427                struct commit *commit;
 428                struct commit_list *parents;
 429        } *contains_stack;
 430};
 431
 432static int in_commit_list(const struct commit_list *want, struct commit *c)
 433{
 434        for (; want; want = want->next)
 435                if (!oidcmp(&want->item->object.oid, &c->object.oid))
 436                        return 1;
 437        return 0;
 438}
 439
 440/*
 441 * Test whether the candidate is contained in the list.
 442 * Do not recurse to find out, though, but return -1 if inconclusive.
 443 */
 444static enum contains_result contains_test(struct commit *candidate,
 445                                          const struct commit_list *want,
 446                                          struct contains_cache *cache,
 447                                          uint32_t cutoff)
 448{
 449        enum contains_result *cached = contains_cache_at(cache, candidate);
 450
 451        /* If we already have the answer cached, return that. */
 452        if (*cached)
 453                return *cached;
 454
 455        /* or are we it? */
 456        if (in_commit_list(want, candidate)) {
 457                *cached = CONTAINS_YES;
 458                return CONTAINS_YES;
 459        }
 460
 461        /* Otherwise, we don't know; prepare to recurse */
 462        parse_commit_or_die(candidate);
 463
 464        if (candidate->generation < cutoff)
 465                return CONTAINS_NO;
 466
 467        return CONTAINS_UNKNOWN;
 468}
 469
 470static void push_to_contains_stack(struct commit *candidate, struct contains_stack *contains_stack)
 471{
 472        ALLOC_GROW(contains_stack->contains_stack, contains_stack->nr + 1, contains_stack->alloc);
 473        contains_stack->contains_stack[contains_stack->nr].commit = candidate;
 474        contains_stack->contains_stack[contains_stack->nr++].parents = candidate->parents;
 475}
 476
 477static enum contains_result contains_tag_algo(struct commit *candidate,
 478                                              const struct commit_list *want,
 479                                              struct contains_cache *cache)
 480{
 481        struct contains_stack contains_stack = { 0, 0, NULL };
 482        enum contains_result result;
 483        uint32_t cutoff = GENERATION_NUMBER_INFINITY;
 484        const struct commit_list *p;
 485
 486        for (p = want; p; p = p->next) {
 487                struct commit *c = p->item;
 488                load_commit_graph_info(the_repository, c);
 489                if (c->generation < cutoff)
 490                        cutoff = c->generation;
 491        }
 492
 493        result = contains_test(candidate, want, cache, cutoff);
 494        if (result != CONTAINS_UNKNOWN)
 495                return result;
 496
 497        push_to_contains_stack(candidate, &contains_stack);
 498        while (contains_stack.nr) {
 499                struct contains_stack_entry *entry = &contains_stack.contains_stack[contains_stack.nr - 1];
 500                struct commit *commit = entry->commit;
 501                struct commit_list *parents = entry->parents;
 502
 503                if (!parents) {
 504                        *contains_cache_at(cache, commit) = CONTAINS_NO;
 505                        contains_stack.nr--;
 506                }
 507                /*
 508                 * If we just popped the stack, parents->item has been marked,
 509                 * therefore contains_test will return a meaningful yes/no.
 510                 */
 511                else switch (contains_test(parents->item, want, cache, cutoff)) {
 512                case CONTAINS_YES:
 513                        *contains_cache_at(cache, commit) = CONTAINS_YES;
 514                        contains_stack.nr--;
 515                        break;
 516                case CONTAINS_NO:
 517                        entry->parents = parents->next;
 518                        break;
 519                case CONTAINS_UNKNOWN:
 520                        push_to_contains_stack(parents->item, &contains_stack);
 521                        break;
 522                }
 523        }
 524        free(contains_stack.contains_stack);
 525        return contains_test(candidate, want, cache, cutoff);
 526}
 527
 528int commit_contains(struct ref_filter *filter, struct commit *commit,
 529                    struct commit_list *list, struct contains_cache *cache)
 530{
 531        if (filter->with_commit_tag_algo)
 532                return contains_tag_algo(commit, list, cache) == CONTAINS_YES;
 533        return is_descendant_of(commit, list);
 534}