diff --git a/builtin/tag.c b/builtin/tag.c index cef27263bc..68efca949c 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -12,6 +12,8 @@ #include "tag.h" #include "run-command.h" #include "parse-options.h" +#include "diff.h" +#include "revision.h" static const char * const git_tag_usage[] = { "git tag [-a|-s|-u ] [-f] [-m |-F ] []", @@ -23,6 +25,8 @@ static const char * const git_tag_usage[] = { static char signingkey[1000]; +static int core_clock_skew = 86400; + struct tag_filter { const char **patterns; int lines; @@ -40,6 +44,68 @@ static int match_pattern(const char **patterns, const char *ref) return 0; } +static int in_commit_list(const struct commit_list *want, struct commit *c) +{ + for (; want; want = want->next) + if (!hashcmp(want->item->object.sha1, c->object.sha1)) + return 1; + return 0; +} + +static int contains_recurse(struct commit *candidate, + const struct commit_list *want, + unsigned long cutoff) +{ + struct commit_list *p; + + /* was it previously marked as containing a want commit? */ + if (candidate->object.flags & TMP_MARK) + return 1; + /* or marked as not possibly containing a want commit? */ + if (candidate->object.flags & UNINTERESTING) + return 0; + /* or are we it? */ + if (in_commit_list(want, candidate)) + return 1; + + if (parse_commit(candidate) < 0) + return 0; + + /* stop searching if we go too far back in time */ + if (candidate->date < cutoff) + return 0; + + /* Otherwise recurse and mark ourselves for future traversals. */ + for (p = candidate->parents; p; p = p->next) { + if (contains_recurse(p->item, want, cutoff)) { + candidate->object.flags |= TMP_MARK; + return 1; + } + } + candidate->object.flags |= UNINTERESTING; + return 0; +} + +static int contains(struct commit *candidate, const struct commit_list *want) +{ + unsigned long cutoff = 0; + + if (core_clock_skew >= 0) { + const struct commit_list *c; + unsigned long min_date = ULONG_MAX; + for (c = want; c; c = c->next) { + if (parse_commit(c->item) < 0) + continue; + if (c->item->date < min_date) + min_date = c->item->date; + } + if (min_date > core_clock_skew) + cutoff = min_date - core_clock_skew; + } + + return contains_recurse(candidate, want, cutoff); +} + static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { @@ -58,7 +124,7 @@ static int show_reference(const char *refname, const unsigned char *sha1, commit = lookup_commit_reference_gently(sha1, 1); if (!commit) return 0; - if (!is_descendant_of(commit, filter->with_commit)) + if (!contains(commit, filter->with_commit)) return 0; } @@ -241,6 +307,14 @@ static int git_tag_config(const char *var, const char *value, void *cb) return 0; } + if (!strcmp(var, "core.clockskew")) { + if (!value || !strcmp(value, "none")) + core_clock_skew = -1; + else + core_clock_skew = git_config_int(var, value); + return 0; + } + return git_default_config(var, value, cb); }