summaryrefslogtreecommitdiff
path: root/exec-cmd.c
diff options
context:
space:
mode:
authorLibravatar Derrick Stolee <dstolee@microsoft.com>2021-02-19 12:34:07 +0000
committerLibravatar Junio C Hamano <gitster@pobox.com>2021-02-22 13:34:34 -0800
commitfbc21e3fbba982c50a3c2a273038770a249ed510 (patch)
tree192a706f005cdbbf49ae80a71077c8bc68855b42 /exec-cmd.c
parentcommit-reach: reduce requirements for remove_redundant() (diff)
downloadtgif-fbc21e3fbba982c50a3c2a273038770a249ed510.tar.xz
commit-reach: use one walk in remove_redundant()
The current implementation of remove_redundant() uses several calls to paint_down_to_common() to determine that commits are independent of each other. This leads to quadratic behavior when many inputs are passed to commands such as 'git merge-base'. For example, in the Linux kernel repository, I tested the performance by passing all tags: git merge-base --independent $(git for-each-ref refs/tags --format="$(refname)") (Note: I had to delete the tags v2.6.11-tree and v2.6.11 as they do not point to commits.) Here is the performance improvement introduced by this change: Before: 16.4s After: 1.1s This performance improvement requires the commit-graph file to be present. We keep the old algorithm around as remove_redundant_no_gen() and use it when generation_numbers_enabled() is false. This is similar to other algorithms within commit-reach.c. The new algorithm is implemented in remove_redundant_with_gen(). The basic approach is to do one commit walk instead of many. First, scan all commits in the list and mark their _parents_ with the STALE flag. This flag will indicate commits that are reachable from one of the inputs, except not including themselves. Then, walk commits until covering all commits up to the minimum generation number pushing the STALE flag throughout. At the end, we need to clear the STALE bit from all of the commits we walked. We move the non-stale commits in 'array' to the beginning of the list, and this might overwrite stale commits. However, we store an array of commits that started the walk, and use clear_commit_marks() on each of those starting commits. That method will walk the reachable commits with the STALE bit and clear them all. This makes the algorithm safe for re-entry or for other uses of those commits after this walk. This logic is covered by tests in t6600-test-reach.sh, so the behavior does not change. This is tested both in the case with a commit-graph and without. Signed-off-by: Derrick Stolee <dstolee@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'exec-cmd.c')
0 files changed, 0 insertions, 0 deletions