Signed-off-by: Derrick Stolee <dstolee@xxxxxxxxxxxxx> --- commit-reach.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ commit-reach.h | 8 +++++++ upload-pack.c | 62 +++----------------------------------------------- 3 files changed, 72 insertions(+), 59 deletions(-) diff --git a/commit-reach.c b/commit-reach.c index 6cfd7379ce..44c09669ec 100644 --- a/commit-reach.c +++ b/commit-reach.c @@ -1,5 +1,6 @@ #include "cache.h" #include "commit.h" +#include "commit-graph.h" #include "decorate.h" #include "prio-queue.h" #include "tree.h" @@ -528,3 +529,63 @@ int commit_contains(struct ref_filter *filter, struct commit *commit, return contains_tag_algo(commit, list, cache) == CONTAINS_YES; return is_descendant_of(commit, list); } + +static int reachable(struct commit *from, int with_flag, int assign_flag, time_t min_commit_date) +{ + struct prio_queue work = { compare_commits_by_commit_date }; + + prio_queue_put(&work, from); + while (work.nr) { + struct commit_list *list; + struct commit *commit = prio_queue_get(&work); + + if (commit->object.flags & with_flag) { + from->object.flags |= assign_flag; + break; + } + if (!commit->object.parsed) + parse_object(&commit->object.oid); + if (commit->object.flags & TMP_MARK) + continue; + commit->object.flags |= TMP_MARK; + if (commit->date < min_commit_date) + continue; + for (list = commit->parents; list; list = list->next) { + struct commit *parent = list->item; + if (!(parent->object.flags & TMP_MARK)) + prio_queue_put(&work, parent); + } + } + from->object.flags |= TMP_MARK; + clear_commit_marks(from, TMP_MARK); + clear_prio_queue(&work); + return (from->object.flags & assign_flag); +} + +int can_all_from_reach_with_flag(struct object_array from, + int with_flag, int assign_flag, + time_t min_commit_date) +{ + int i; + + for (i = 0; i < from.nr; i++) { + struct object *from_one = from.objects[i].item; + + if (from_one->flags & assign_flag) + continue; + from_one = deref_tag(from_one, "a from object", 0); + if (!from_one || from_one->type != OBJ_COMMIT) { + /* no way to tell if this is reachable by + * looking at the ancestry chain alone, so + * leave a note to ourselves not to worry about + * this object anymore. + */ + from.objects[i].item->flags |= assign_flag; + continue; + } + if (!reachable((struct commit *)from_one, with_flag, + assign_flag, min_commit_date)) + return 0; + } + return 1; +} diff --git a/commit-reach.h b/commit-reach.h index 986fb388d5..c3da8488eb 100644 --- a/commit-reach.h +++ b/commit-reach.h @@ -22,4 +22,12 @@ define_commit_slab(contains_cache, enum contains_result); int commit_contains(struct ref_filter *filter, struct commit *commit, struct commit_list *list, struct contains_cache *cache); +/* + * Can every commit or tag in the 'from' array reach a commit that has + * the 'with_flag' bit on? Mark the commits in 'from' that can reach + * such commits with 'assign_flag'. + */ +int can_all_from_reach_with_flag(struct object_array from, int with_flag, + int assign_flag, time_t min_commit_date); + #endif diff --git a/upload-pack.c b/upload-pack.c index 2f09cadbc0..7c58cb8f5e 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -22,6 +22,7 @@ #include "quote.h" #include "upload-pack.h" #include "serve.h" +#include "commit-reach.h" /* Remember to update object flag allocation in object.h */ #define THEY_HAVE (1u << 11) @@ -333,70 +334,13 @@ static int got_oid(const char *hex, struct object_id *oid) return 0; } -static int reachable(struct commit *from, int with_flag, int assign_flag) -{ - struct prio_queue work = { compare_commits_by_commit_date }; - - prio_queue_put(&work, from); - while (work.nr) { - struct commit_list *list; - struct commit *commit = prio_queue_get(&work); - - if (commit->object.flags & with_flag) { - from->object.flags |= assign_flag; - break; - } - if (!commit->object.parsed) - parse_object(&commit->object.oid); - if (commit->object.flags & TMP_MARK) - continue; - commit->object.flags |= TMP_MARK; - if (commit->date < oldest_have) - continue; - for (list = commit->parents; list; list = list->next) { - struct commit *parent = list->item; - if (!(parent->object.flags & TMP_MARK)) - prio_queue_put(&work, parent); - } - } - from->object.flags |= TMP_MARK; - clear_commit_marks(from, TMP_MARK); - clear_prio_queue(&work); - return (from->object.flags & assign_flag); -} - -static int can_all_from_reach_with_flag(struct object_array from, - int with_flag, int assign_flag) -{ - int i; - - for (i = 0; i < from.nr; i++) { - struct object *from_one = from.objects[i].item; - - if (from_one->flags & assign_flag) - continue; - from_one = deref_tag(from_one, "a from object", 0); - if (!from_one || from_one->type != OBJ_COMMIT) { - /* no way to tell if this is reachable by - * looking at the ancestry chain alone, so - * leave a note to ourselves not to worry about - * this object anymore. - */ - from.objects[i].item->flags |= assign_flag; - continue; - } - if (!reachable((struct commit *)from_one, with_flag, assign_flag)) - return 0; - } - return 1; -} - static int ok_to_give_up(void) { if (!have_obj.nr) return 0; - return can_all_from_reach_with_flag(want_obj, THEY_HAVE, COMMON_KNOWN); + return can_all_from_reach_with_flag(want_obj, THEY_HAVE, + COMMON_KNOWN, oldest_have); } static int get_common_commits(void) -- 2.18.0.118.gd4f65b8d14