git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH 0/4] Speed up git tag --contains

From
Jeff King <peff@peff.net>
Date
Mar 3, 2018, 05:15 UTC
Message-ID
<20180303051516.GE27689@sigill.intra.peff.net>
In-Reply-To
<E1ea4Ui-0005qJ-3s@theory.stanford.edu>
On Fri, Jan 12, 2018 at 10:56:00AM -0800, csilvers wrote:
Show 13 quoted lines
> > This is a resubmission of Jeff King's patch series to speed up git tag
> > --contains with some changes. It's been cooking for a while as:
> 
> Replying to this 6-year-old thread:
> 
> Is there any chance this could be resurrected?  We are using
> phabricator, which uses `git branch --contains` as part of its
> workflow.  Our repo has ~1000 branches on it, and the contains
> operation is eating up all our CPU (and time).  It would be very
> helpful to us to make this faster!
> 
> (The original thread is at
> https://public-inbox.org/git/E1OU82h-0001xY-3b@closure.thunk.org/
Sorry, this got thrown on my "to respond" pile and languished.
There are actually three things that make "git branch --contains" slow.

First, if you're filtering 1000 branches, we'll run 1000 merge-base traversals, which may walk over the same commits multiple times.

These days "tag --contains" uses a different algorithm that can look at all heads in a single traversal. But the downside is that it's depth-first, so it tends to walk down to the roots. That's generally OK for tags, since you often have ancient tags that mean getting close to the roots anyway.

But for branches, they're more likely to be recent, and you can get away without going very deep into the history.

So it's a tradeoff. There's no run-time switch to flip between them, but a patch like this:

diff --git a/builtin/branch.c b/builtin/branch.c
index 8dcc2ed058..4d674e86d5 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -404,6 +404,7 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
 
 	memset(&array, 0, sizeof(array));
 
+	filter->with_commit_tag_algo = 1;
 	filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
 
 	if (filter->verbose)

drops my run of "git branch -a --contains HEAD~100" from 8.6s to
0.4s on a repo with ~1800 branches. That sounds good, but on a repo with
a smaller number of branches, we may actually end up slower (because we
dig further down in history, and don't benefit from the multiple-branch
speedup).

I tried to do a "best of both" algorithm in:

 https://public-inbox.org/git/20140625233429.GA20457@sigill.intra.peff.net/

which finds arbitrary numbers of merge bases in a single traversal.  It
did seem to work, but I felt uneasy about some of the corner cases.
I've been meaning to revisit it, but obviously have never gotten around
to it.

The second slow thing is that during the traversal we load each commit
object from disk. The solution there is to keep the parent information
in a faster cache. I had a few proposals over the years, but I won't
even bother to dig them up, because there's quite recent and promising
work in this area from Derrick Stolee:

  https://public-inbox.org/git/1519698787-190494-1-git-send-email-dstolee@microsoft.com/

And finally, the thing that the patches you linked are referencing is
about using commit timestamps as a proxy for generation numbers. And
Stolee's patches actually leave room for real, trustable generation
numbers.

Once we have the serialized commit graph and generation numbers, think
the final step would just be to teach the "tag --contains" algorithm to
stop walking down unproductive lines of history. And in fact, I think we
can forget about the best-of-both multi-tip merge-base idea entirely.
Because if you can use the generation numbers to avoid going too deep,
then a depth-first approach is fine. And we'd just want to flip
git-branch over to using that algorithm by default.

-Peff
Previous: csilversNext: csilvers
Message 25 of 28 in “Speed up git tag --contains”
  1. 0/4 Speed up git tag --containsÆvar Arnfjörð Bjarmason, Jun 11, 2011
  2. 1/4 tag: speed up --contains calculationÆvar Arnfjörð Bjarmason, Jun 11, 2011
  3. 2/4 limit "contains" traversals based on commit timestampÆvar Arnfjörð Bjarmason, Jun 11, 2011
  4. 3/4 default core.clockskew variable to one dayÆvar Arnfjörð Bjarmason, Jun 11, 2011
  5. 4/4 Why is "git tag --contains" so slow?Ævar Arnfjörð Bjarmason, Jun 11, 2011
  6. Jeff KingJul 6, 2011
  7. Jeff KingJul 6, 2011
  8. Clemens BuchacherJul 6, 2011
  9. Jonathan NiederJul 6, 2011
  10. Jeff KingJul 6, 2011
  11. Jakub NarebskiJul 6, 2011
  12. Ted Ts'oJul 6, 2011
  13. Jeff KingJul 6, 2011
  14. Jakub NarebskiJul 6, 2011
  15. Jeff KingJul 7, 2011
  16. Junio C HamanoJul 7, 2011
  17. Jakub NarebskiJul 7, 2011
  18. A Large Angry SCMJul 7, 2011
  19. Junio C HamanoJul 8, 2011
  20. Jeff KingJul 8, 2011
  21. Junio C HamanoJul 6, 2011
  22. Jeff KingJul 7, 2011
  23. Jakub NarebskiJul 7, 2011
  24. csilversJan 12, 2018
  25. Jeff KingMar 3, 2018
  26. csilversMar 8, 2018
  27. Derrick StoleeMar 12, 2018
  28. Jeff KingMar 12, 2018

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.