]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'ds/for-each-ref-is-base'
authorJunio C Hamano <gitster@pobox.com>
Mon, 26 Aug 2024 18:32:24 +0000 (11:32 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 26 Aug 2024 18:32:24 +0000 (11:32 -0700)
'git for-each-ref' learned a new "--format" atom to find the branch
that the history leading to a given commit "%(is-base:<commit>)" is
likely based on.

* ds/for-each-ref-is-base:
  p1500: add is-base performance tests
  for-each-ref: add 'is-base' token
  commit: add gentle reference lookup method
  commit-reach: add get_branch_base_for_tip

1  2 
commit-reach.c
commit.c
ref-filter.c

diff --cc commit-reach.c
index 02f8218b8e192bdf63f4067f7f4430ad14c0abb8,74c20845b5620459762789dd13e8c997a0d0d4bb..c3518aa3606f36d872ae096e6c3231d7dc6062a6
@@@ -1227,5 -1227,130 +1227,131 @@@ void tips_reachable_from_bases(struct r
  done:
        free(commits);
        repo_clear_commit_marks(r, SEEN);
 +      free_commit_list(stack);
  }
+ /*
+  * This slab initializes integers to zero, so use "-1" for "tip is best" and
+  * "i + 1" for "bases[i] is best".
+  */
+ define_commit_slab(best_branch_base, int);
+ static struct best_branch_base best_branch_base;
+ #define get_best(c) (*best_branch_base_at(&best_branch_base, (c)))
+ #define set_best(c,v) (*best_branch_base_at(&best_branch_base, (c)) = (v))
+ int get_branch_base_for_tip(struct repository *r,
+                           struct commit *tip,
+                           struct commit **bases,
+                           size_t bases_nr)
+ {
+       int best_index = -1;
+       struct commit *branch_point = NULL;
+       struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
+       int found_missing_gen = 0;
+       if (!bases_nr)
+               return -1;
+       repo_parse_commit(r, tip);
+       if (commit_graph_generation(tip) == GENERATION_NUMBER_INFINITY)
+               found_missing_gen = 1;
+       /* Check for missing generation numbers. */
+       for (size_t i = 0; i < bases_nr; i++) {
+               struct commit *c = bases[i];
+               repo_parse_commit(r, c);
+               if (commit_graph_generation(c) == GENERATION_NUMBER_INFINITY)
+                       found_missing_gen = 1;
+       }
+       if (found_missing_gen) {
+               struct commit **commits;
+               size_t commits_nr = bases_nr + 1;
+               CALLOC_ARRAY(commits, commits_nr);
+               COPY_ARRAY(commits, bases, bases_nr);
+               commits[bases_nr] = tip;
+               ensure_generations_valid(r, commits, commits_nr);
+               free(commits);
+       }
+       /* Initialize queue and slab now that generations are guaranteed. */
+       init_best_branch_base(&best_branch_base);
+       set_best(tip, -1);
+       prio_queue_put(&queue, tip);
+       for (size_t i = 0; i < bases_nr; i++) {
+               struct commit *c = bases[i];
+               int best = get_best(c);
+               /* Has this already been marked as best by another commit? */
+               if (best) {
+                       if (best == -1) {
+                               /* We agree at this position. Stop now. */
+                               best_index = i + 1;
+                               goto cleanup;
+                       }
+                       continue;
+               }
+               set_best(c, i + 1);
+               prio_queue_put(&queue, c);
+       }
+       while (queue.nr) {
+               struct commit *c = prio_queue_get(&queue);
+               int best_for_c = get_best(c);
+               int best_for_p, positive;
+               struct commit *parent;
+               /* Have we reached a known branch point? It's optimal. */
+               if (c == branch_point)
+                       break;
+               repo_parse_commit(r, c);
+               if (!c->parents)
+                       continue;
+               parent = c->parents->item;
+               repo_parse_commit(r, parent);
+               best_for_p = get_best(parent);
+               if (!best_for_p) {
+                       /* 'parent' is new, so pass along best_for_c. */
+                       set_best(parent, best_for_c);
+                       prio_queue_put(&queue, parent);
+                       continue;
+               }
+               if (best_for_p > 0 && best_for_c > 0) {
+                       /* Collision among bases. Minimize. */
+                       if (best_for_c < best_for_p)
+                               set_best(parent, best_for_c);
+                       continue;
+               }
+               /*
+                * At this point, we have reached a commit that is reachable
+                * from the tip, either from 'c' or from an earlier commit to
+                * have 'parent' as its first parent.
+                *
+                * Update 'best_index' to match the minimum of all base indices
+                * to reach 'parent'.
+                */
+               /* Exactly one is positive due to initial conditions. */
+               positive = (best_for_c < 0) ? best_for_p : best_for_c;
+               if (best_index < 0 || positive < best_index)
+                       best_index = positive;
+               /* No matter what, track that the parent is reachable from tip. */
+               set_best(parent, -1);
+               branch_point = parent;
+       }
+ cleanup:
+       clear_best_branch_base(&best_branch_base);
+       clear_prio_queue(&queue);
+       return best_index > 0 ? best_index - 1 : -1;
+ }
diff --cc commit.c
Simple merge
diff --cc ref-filter.c
Simple merge