From: Junio C Hamano Date: Mon, 26 Aug 2024 18:32:24 +0000 (-0700) Subject: Merge branch 'ds/for-each-ref-is-base' X-Git-Tag: v2.47.0-rc0~92 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3222718ad7d9dfc50e31037bf2f3977d2071fb75;p=thirdparty%2Fgit.git Merge branch 'ds/for-each-ref-is-base' 'git for-each-ref' learned a new "--format" atom to find the branch that the history leading to a given commit "%(is-base:)" 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 --- 3222718ad7d9dfc50e31037bf2f3977d2071fb75 diff --cc commit-reach.c index 02f8218b8e,74c20845b5..c3518aa360 --- a/commit-reach.c +++ b/commit-reach.c @@@ -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; + }