I happen to be using the git svn
bridge, I'm not sure if that's relevant. In my ".gitconfig" I have an alias for git lg
that's defined like this:
[alias]
lg = log --max-count=100 --branches --color --graph --pretty=format:'%Cred%h%Creset - %C(bold blue)<%an>%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)' --abbrev-commit
When I first cloned this repository, running git lg
would be near instantaneous, but this command seems to get slower and slower as time goes by. Now git lg
takes at least 30 seconds. What is causing this to be slow and how can I make it run faster?
Check if you have git gc
desactivated in background (as in here), like git config gc.auto
set to 0 or git config gc.autodetach
to false, or git config gc.pruneExpire
set to never.
You would not then need to use git gc
explicitly.
More generally, if git log becomes slow again, you can with recent version of Git (2.23, August 2019) invoque a perf tracing:
git -c trace2.perfTarget=~/perf.log log
You can then see what is still taking time.
Update 7 years later, with Git 2.31 (Q1 2021), some pretty-format specifiers do not need the data in commit object (e.g. "%H
"), but we were over-eager to load and parse it, which has been made even lazier.
That contributes making some git log --pretty
faster.
See commit 018b9de (28 Jan 2021) by Jeff King (peff
).
(Merged by Junio C Hamano -- gitster
-- in commit 938ecaa, 10 Feb 2021)
pretty
: lazy-load commit data when expanding user-formatReported-by: Michael Haggerty
Signed-off-by: Jeff King
When we expand a user-format, we try to avoid work that isn't necessary for the output.
For instance, we don't bother parsing the commit header until we know we need the author, subject, etc.But we do always load the commit object's contents from disk, even if the format doesn't require it (e.g., just "
%H
").
Traditionally this didn't matter much, because we'd have loaded it as part of the traversal anyway, and we'd typically have those bytes attached to the commit struct (or these days, cached in a commit-slab).But when we have a commit-graph, we might easily get to the point of pretty-printing a commit without ever having looked at the actual object contents.
We should push off that load (and reencoding) until we're certain that it's needed.I think the results of p4205 show the advantage pretty clearly (we serve parent and tree oids out of the commit struct itself, so they benefit as well):
# using git.git as the test repo Test HEAD^ HEAD ---------------------------------------------------------------------- 4205.1: log with %H 0.40(0.39+0.01) 0.03(0.02+0.01) -92.5% 4205.2: log with %h 0.45(0.44+0.01) 0.09(0.09+0.00) -80.0% 4205.3: log with %T 0.40(0.39+0.00) 0.04(0.04+0.00) -90.0% 4205.4: log with %t 0.46(0.46+0.00) 0.09(0.08+0.01) -80.4% 4205.5: log with %P 0.39(0.39+0.00) 0.03(0.03+0.00) -92.3% 4205.6: log with %p 0.46(0.46+0.00) 0.10(0.09+0.00) -78.3% 4205.7: log with %h-%h-%h 0.52(0.51+0.01) 0.15(0.14+0.00) -71.2% 4205.8: log with %an-%ae-%s 0.42(0.41+0.00) 0.42(0.41+0.01) +0.0% # using linux.git as the test repo Test HEAD^ HEAD ---------------------------------------------------------------------- 4205.1: log with %H 7.12(6.97+0.14) 0.76(0.65+0.11) -89.3% 4205.2: log with %h 7.35(7.19+0.16) 1.30(1.19+0.11) -82.3% 4205.3: log with %T 7.58(7.42+0.15) 1.02(0.94+0.08) -86.5% 4205.4: log with %t 8.05(7.89+0.15) 1.55(1.41+0.13) -80.7% 4205.5: log with %P 7.12(7.01+0.10) 0.76(0.69+0.07) -89.3% 4205.6: log with %p 7.38(7.27+0.10) 1.32(1.20+0.12) -82.1% 4205.7: log with %h-%h-%h 7.81(7.67+0.13) 1.79(1.67+0.12) -77.1% 4205.8: log with %an-%ae-%s 7.90(7.74+0.15) 7.81(7.66+0.15) -1.1%
I added the final test to show where we don't improve (the 1% there is just lucky noise), but also as a regression test to make sure we're not doing anything stupid like loading the commit multiple times when there are several placeholders that need it.
Another approach is to use %d, as mentioned in "How do I show tags in a custom git log format?".
This is faster with Git 2.33 (Q3 2021), which optimizes "git log
"(man) for cases where we wasted cycles to load ref decoration data that may not be needed.
See commit d1ed8d6 (14 Jul 2021), and commit 6afb265, commit 88473c8, commit 7463064, commit 542d6ab, commit b2086b5, commit 3c7e2e8 (22 Jun 2021) by Jeff King (peff
).
(Merged by Junio C Hamano -- gitster
-- in commit c9d6d8a, 28 Jul 2021)
load_ref_decorations()
: avoid parsing non-tag objectsSigned-off-by: Jeff King
When we load the ref decorations, we parse the object pointed to by each ref in order to get a "struct object".
This is unnecessarily expensive; we really only need the object struct, and don't even look at the parsed contents.
The exception is tags, which we do need to peel.We can improve this by looking up the object type first (which is much cheaper), and skipping the parse entirely for non-tags.
This increases the work slightly for annotated tags (which now do a type lookup and a parse), but decreases it a lot for other types.On balance, this seems to be a good tradeoff.
- In my git.git clone, with ~2k refs, most of which are branches, the time to run "
git log -1 --decorate
"(man) drops from 34ms to 11ms.- Even on my linux.
git clone
(man), which contains mostly tags and only a handful of branches, the time drops from 30ms to 19ms.- And on a more extreme real-world case with ~220k refs, mostly non-tags, the time drops from 2.6s to 650ms.
That command is a lop-sided example, of course, because it does as little non-loading work as possible.
But it does show the absolute time improvement.
Even in something like a full "git log --decorate
"(man) on that extreme repo, we'd still be saving 2s of CPU time.