commit.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 files changed, 41 insertions(+), 1 deletions(-) diff --git a/commit.c b/commit.c index ac337c7d7dc1..0d33c33a6520 100644 --- a/commit.c +++ b/commit.c @@ -737,16 +737,56 @@ struct commit_list *get_merge_bases(struct commit *one, struct commit *two, return get_merge_bases_many(one, 1, &two, cleanup); } +#define VISITED (1 << 16) + +static int is_recursive_descendant(struct commit *commit, struct commit *target) +{ + int slop = 5; + parse_commit(target); + for (;;) { + struct commit_list *parents; + if (commit == target) + return 1; + if (commit->object.flags & VISITED) + return 0; + commit->object.flags |= VISITED; + parse_commit(commit); + if (commit->date + 5*24*60*60 < target->date) { + if (--slop <= 0) + return 0; + } else + slop = 5; + parents = commit->parents; + if (!parents) + return 0; + commit = parents->item; + parents = parents->next; + while (parents) { + if (is_recursive_descendant(parents->item, target)) + return 1; + parents = parents->next; + } + } +} + +static int is_descendant(struct commit *commit, struct commit *target) +{ + int ret = is_recursive_descendant(commit, target); + clear_commit_marks(commit, VISITED); + return ret; +} + int is_descendant_of(struct commit *commit, struct commit_list *with_commit) { if (!with_commit) return 1; + while (with_commit) { struct commit *other; other = with_commit->item; with_commit = with_commit->next; - if (in_merge_bases(other, &commit, 1)) + if (is_descendant(commit, other)) return 1; } return 0;