From: "SZEDER Gábor" <szeder.dev@gmail.com>
To: Patrick Steinhardt <ps@pks.im>
Cc: git@vger.kernel.org, Miklos Vajna <vmiklos@collabora.com>
Subject: Re: Performance regression in `git log --follow`
Date: Tue, 13 Oct 2026 23:24:41 +0200 [thread overview]
Message-ID: <as6hmUG5AaL5iLbO@szeder.dev> (raw)
In-Reply-To: <as44NVKZA9C9S5ae@pks.im>
On Tue, Oct 13, 2026 at 03:55:01PM +0200, Patrick Steinhardt wrote:
> Hi,
>
> a user on IRC reported a performance regression in linux.git:
>
> $ git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
> $ git -C linux log --stat --follow -- include/linux/rsc_table.h
>
> I was able to reproduce the performance regression: while this command
> takes roughly 33 seconds with Git 2.55, it takes almost 8 minutes with
> Git 2.56.
>
> I've bisected the regression to 304812ed33 (log: improve --follow
> following renames for non-linear history, 2026-06-22). I don't have time
> right now to debug any further, so I decided to just forward the report
> to the mailing list. I've also Cc'd the original author of the above
> commit.
The performance regression is clearly noticable in the git repository,
though the slowdown is not as pronounced as in linux.git:
Benchmark 1: ./304812ed33^/bin/git --no-pager log --oneline --no-patch --follow v2.56.0 t/valgrind/valgrind.sh
Time (mean ± σ): 3.275 s ± 0.022 s [User: 3.157 s, System: 0.116 s]
Range (min … max): 3.240 s … 3.313 s 10 runs
Benchmark 2: ./304812ed33/bin/git --no-pager log --oneline --no-patch --follow v2.56.0 t/valgrind/valgrind.sh
Time (mean ± σ): 8.825 s ± 0.029 s [User: 8.554 s, System: 0.264 s]
Range (min … max): 8.769 s … 8.864 s 10 runs
Summary
'./304812ed33^/bin/git --no-pager log --oneline --no-patch --follow v2.56.0 t/valgrind/valgrind.sh' ran
2.69 ± 0.02 times faster than './304812ed33/bin/git --no-pager log --oneline --no-patch --follow v2.56.0 t/valgrind/valgrind.sh'
That's 5.5 seconds of runtime difference, and with a bit of trace
performance hack[1] it turns out that most of it is spent in a call to
propagate_follow_pathspec_to_parent() to compare the current commit
with its second...N-th parent:
$ GIT_TRACE_PERFORMANCE=1 ./304812ed33/bin/git --no-pager log --follow --oneline v2.56.0 t/valgrind/valgrind.sh >/dev/null
21:59:30.465592 read-cache.c:2364 performance: 0.000626524 s: read cache .git/index
21:59:39.246697 log-tree.c:1158 performance: 1.374980154 s: first parent propagate_follow_pathspec_to_parent()
21:59:39.247164 log-tree.c:1159 performance: 4.448692273 s: N-th parents propagate_follow_pathspec_to_parent()
21:59:39.247192 trace.c:416 performance: 8.799236172 s: git command: ./304812ed33/bin/git --no-pager log --follow --oneline v2.56.0 t/valgrind/valgrind.sh
Interestingly, although line-level log performs its own rename
tracking, it's still notably faster than "--follow" even before this
perf regression (that "-L1,40" means "track the whole file"):
Benchmark 1: ./304812ed33^/bin/git --no-pager log --oneline --no-patch --follow v2.56.0 t/valgrind/valgrind.sh
Time (mean ± σ): 3.260 s ± 0.019 s [User: 3.140 s, System: 0.118 s]
Range (min … max): 3.219 s … 3.284 s 10 runs
Benchmark 2: ./304812ed33^/bin/git --no-pager log --oneline --no-patch -L1,40:t/valgrind/valgrind.sh v2.56.0
Time (mean ± σ): 1.454 s ± 0.014 s [User: 1.369 s, System: 0.084 s]
Range (min … max): 1.441 s … 1.481 s 10 runs
Summary
'./304812ed33^/bin/git --no-pager log --oneline --no-patch -L1,40:t/valgrind/valgrind.sh v2.56.0' ran
2.24 ± 0.02 times faster than './304812ed33^/bin/git --no-pager log --oneline --no-patch --follow v2.56.0 t/valgrind/valgrind.sh'
The reason for this might be:
- If line-level log finds that the interesting path is not modified
between a merge commit and its first parent, then it doesn't
perform a diff between that merge commit and its second..N-th
parent.
"--follow" seems to performs a diff between a merge commit and all
its parents.
- Line-level log checks for renames only when the path is not
present in the parent commit.
"--follow" seems to always look for renames.
[1] It really is just a hack:
diff --git a/log-tree.c b/log-tree.c
index 83a3c4bf9b..fd4824fd3e 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -34,6 +34,7 @@
#include "wildmatch.h"
#include "write-or-die.h"
#include "pager.h"
+#include "trace.h"
static struct decoration name_decoration = { "object names" };
static int decoration_loaded;
@@ -1144,6 +1145,8 @@ static void free_follow_pathspec_slot(char **slot)
FREE_AND_NULL(*slot);
}
+static uint64_t t_first = 0, t_nth = 0;
+
void release_follow_pathspec_slab(struct rev_info *opt)
{
if (!opt->follow_pathspec_slab)
@@ -1151,6 +1154,9 @@ void release_follow_pathspec_slab(struct rev_info *opt)
deep_clear_follow_pathspec_slab(opt->follow_pathspec_slab,
free_follow_pathspec_slot);
FREE_AND_NULL(opt->follow_pathspec_slab);
+
+ trace_performance(t_first, "first parent propagate_follow_pathspec_to_parent()");
+ trace_performance(t_nth, "N-th parents propagate_follow_pathspec_to_parent()");
}
/* Compute a path to follow in parent, if there is one */
@@ -1304,9 +1310,18 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)
struct commit_list *parents = get_saved_parents(opt, commit);
if (parents && parents->next) {
struct commit_list *p;
- for (p = parents; p; p = p->next)
+ for (p = parents; p; p = p->next) {
+ if (p == parents)
+ t_first -= getnanotime();
+ else
+ t_nth -= getnanotime();
propagate_follow_pathspec_to_parent(opt, commit,
p->item);
+ if (p == parents)
+ t_first += getnanotime();
+ else
+ t_nth += getnanotime();
+ }
} else if (parents) {
remember_follow_pathspec(opt, parents->item,
pathspec_single_path(&opt->diffopt.pathspec));
prev parent reply other threads:[~2026-10-13 21:25 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-13 13:55 Performance regression in `git log --follow` Patrick Steinhardt
2026-10-13 21:24 ` SZEDER Gábor [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=as6hmUG5AaL5iLbO@szeder.dev \
--to=szeder.dev@gmail.com \
--cc=git@vger.kernel.org \
--cc=ps@pks.im \
--cc=vmiklos@collabora.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox