On Tue, Apr 03, 2018 at 02:29:01PM -0400, Derrick Stolee wrote: > If we have generic "can X reach Y?" queries, then we can also use generation > numbers there to great effect (by not walking commits Z with gen(Z) <= > gen(Y)). Perhaps I should look at that "git branch --contains" thread for > ideas. I think the gist of it is the patch below. Which I hastily adapted from the patch we run at GitHub that uses timestamps as a proxy. So it's possible I completely flubbed the logic. I'm assuming unavailable generation numbers are set to 0; the logic is actually a bit simpler if they end up as (uint32_t)-1. Assuming it works, that would cover for-each-ref and tag. You'd probably want to drop the "with_commit_tag_algo" flag in ref-filter.h, and just use always use it by default (and that would cover "git branch"). --- diff --git a/ref-filter.c b/ref-filter.c index 45fc56216a..6bea6173d1 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1584,7 +1584,8 @@ static int in_commit_list(const struct commit_list *want, struct commit *c) */ static enum contains_result contains_test(struct commit *candidate, const struct commit_list *want, - struct contains_cache *cache) + struct contains_cache *cache, + uint32_t cutoff) { enum contains_result *cached = contains_cache_at(cache, candidate); @@ -1598,8 +1599,11 @@ static enum contains_result contains_test(struct commit *candidate, return CONTAINS_YES; } - /* Otherwise, we don't know; prepare to recurse */ parse_commit_or_die(candidate); + + if (candidate->generation && candidate->generation < cutoff) + return CONTAINS_NO; + return CONTAINS_UNKNOWN; } @@ -1615,8 +1619,20 @@ static enum contains_result contains_tag_algo(struct commit *candidate, struct contains_cache *cache) { struct contains_stack contains_stack = { 0, 0, NULL }; - enum contains_result result = contains_test(candidate, want, cache); + enum contains_result result; + uint32_t cutoff = -1; + const struct commit_list *p; + + for (p = want; p; p = p->next) { + struct commit *c = p->item; + parse_commit_or_die(c); + if (c->generation && c->generation < cutoff ) + cutoff = c->generation; + } + if (cutoff == -1) + cutoff = 0; + result = contains_test(candidate, want, cache, cutoff); if (result != CONTAINS_UNKNOWN) return result; @@ -1634,7 +1650,7 @@ static enum contains_result contains_tag_algo(struct commit *candidate, * If we just popped the stack, parents->item has been marked, * therefore contains_test will return a meaningful yes/no. */ - else switch (contains_test(parents->item, want, cache)) { + else switch (contains_test(parents->item, want, cache, cutoff)) { case CONTAINS_YES: *contains_cache_at(cache, commit) = CONTAINS_YES; contains_stack.nr--; @@ -1648,7 +1664,7 @@ static enum contains_result contains_tag_algo(struct commit *candidate, } } free(contains_stack.contains_stack); - return contains_test(candidate, want, cache); + return contains_test(candidate, want, cache, cutoff); } static int commit_contains(struct ref_filter *filter, struct commit *commit,