From c2a3befce4e0e4d9edcdfad79414f2929d580227 Mon Sep 17 00:00:00 2001 From: Derrick Stolee Date: Wed, 13 Jun 2018 14:10:26 -0400 Subject: [PATCH] commit: add generation to pop_most_recent_commit() The method pop_most_recent_commit() is confusingly named, in that it pops the most-recent commit, but also adds that commit's parents to the list. This is used by a few commit walks, especially the one in ref_newer(). 'git push' uses ref_newer() to check if a force-push is necessary, and in the case of a force-push being needed, the current algorithm walks every reachable commit. This is especially severe in the case of an amended commit: they have the same parent, but we still walk to the very end of the graph! Add a 'min_generation' parameter to pop_most_recent_commit() to limit the commits that are walked to those with generation number at least 'min_generation'. This greatly reduces the number of commits walked by a force-push. There may be more work to improve this algorithm in the future, but for now this is enough for most cases. This direction has the benefit that it does not affect the non-force-push case at all. Future directions should consider improving that case as well. Signed-off-by: Derrick Stolee --- commit.c | 7 +++++-- commit.h | 6 +++++- fetch-pack.c | 3 ++- sha1-name.c | 3 ++- walker.c | 3 ++- 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/commit.c b/commit.c index a5333c7ac6c373..74b7260e2d85a3 100644 --- a/commit.c +++ b/commit.c @@ -590,7 +590,8 @@ void commit_list_sort_by_date(struct commit_list **list) } struct commit *pop_most_recent_commit(struct commit_list **list, - unsigned int mark) + unsigned int mark, + uint32_t min_generation) { struct commit *ret = pop_commit(list); struct commit_list *parents = ret->parents; @@ -599,7 +600,9 @@ struct commit *pop_most_recent_commit(struct commit_list **list, struct commit *commit = parents->item; if (!parse_commit(commit) && !(commit->object.flags & mark)) { commit->object.flags |= mark; - commit_list_insert_by_date(commit, list); + + if (commit->generation >= min_generation) + commit_list_insert_by_date(commit, list); } parents = parents->next; } diff --git a/commit.h b/commit.h index 42728c2906608a..e582020059e051 100644 --- a/commit.h +++ b/commit.h @@ -193,9 +193,13 @@ extern const char *skip_blank_lines(const char *msg); /** Removes the first commit from a list sorted by date, and adds all * of its parents. + * + * The parents are not added if their generation number is strictly + * lower than min_generation. **/ struct commit *pop_most_recent_commit(struct commit_list **list, - unsigned int mark); + unsigned int mark, + uint32_t min_generation); struct commit *pop_commit(struct commit_list **stack); diff --git a/fetch-pack.c b/fetch-pack.c index 9e65a240ac3e5d..1a7ea2ae7ad84d 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -532,7 +532,8 @@ static void mark_recent_complete_commits(struct fetch_pack_args *args, while (complete && cutoff <= complete->item->date) { print_verbose(args, _("Marking %s as complete"), oid_to_hex(&complete->item->object.oid)); - pop_most_recent_commit(&complete, COMPLETE); + pop_most_recent_commit(&complete, COMPLETE, + GENERATION_NUMBER_ZERO); } } diff --git a/sha1-name.c b/sha1-name.c index 375fba94a1ec46..0fe715b51ef3d4 100644 --- a/sha1-name.c +++ b/sha1-name.c @@ -1193,7 +1193,8 @@ static int get_oid_oneline(const char *prefix, struct object_id *oid, struct commit *commit; int matches; - commit = pop_most_recent_commit(&list, ONELINE_SEEN); + commit = pop_most_recent_commit(&list, ONELINE_SEEN, + GENERATION_NUMBER_ZERO); if (!parse_object(the_repository, &commit->object.oid)) continue; buf = get_commit_buffer(commit, NULL); diff --git a/walker.c b/walker.c index d74ae59c77fb36..bf911b8456301c 100644 --- a/walker.c +++ b/walker.c @@ -82,7 +82,8 @@ static int process_commit(struct walker *walker, struct commit *commit) return -1; while (complete && complete->item->date >= commit->date) { - pop_most_recent_commit(&complete, COMPLETE); + pop_most_recent_commit(&complete, COMPLETE, + GENERATION_NUMBER_ZERO); } if (commit->object.flags & COMPLETE)