From: Miklos Vajna <vmiklos@collabora.com>
To: gitster@pobox.com
Cc: szeder.dev@gmail.com, ps@pks.im, git@vger.kernel.org
Subject: [PATCH v5 0/5] log: speed up --follow following renames for non-linear history
Date: Mon, 9 Nov 2026 09:03:33 +0100 [thread overview]
Message-ID: <cover.1794078554.git.vmiklos@collabora.com> (raw)
In-Reply-To: <cover.1793298331.git.vmiklos@collabora.com>
Hi Patrick,
Thanks for the reviews of v4. Changes in v5:
- The fix of the old 6/6 is now squashed into the speedup patch (5/5),
so no commit in the series loses history, and its test is moved to
1/5. While at it, I simplified 5/5: instead of two passes of diffs
against the parents, it first finds a parent where the path is
unchanged, and then only diffs against the other parents if they
still need renames to be followed. Because of the squash, the
range-diff pairs the new 5/5 with the old 6/6.
- 5/5 now also follows renames when a parent has no merge base with
the parent where the path is unchanged, e.g. a subtree merge of
unrelated histories. Added a test for this to 1/5.
- 5/5 now also follows copies, not only renames, when the merge dropped
the edit of the copy source. Added your test for this to 1/5. With
this, the linux.git case takes about 42s, instead of 37s.
Thanks,
Miklos
Miklos Vajna (5):
t4219: test more --follow cases across merges
log: extract showing the remerge/combined diff of a merge commit
log: fix -m --follow losing the history of later parents
log: simplify --follow following renames for non-linear history
log: speed up --follow following renames for non-linear history
log-tree.c | 258 +++++++++++++++++++++++++++++-------
t/t4219-log-follow-merge.sh | 230 ++++++++++++++++++++++++++++++++
2 files changed, 438 insertions(+), 50 deletions(-)
Range-diff against v4:
1: aabf5652c9 ! 1: 5512aa682f t4219: test more --follow cases across merges
@@ Commit message
t4219: test more --follow cases across merges
A later commit in this series will speed up 'git log --follow' for
- merge commits. Add tests for cases which already work, but had no
+ merge commits. Add tests for cases which already work, but have no
test coverage, so it's easy to break them while optimizing:
+ - a subtree merge adding the history of a file that was copied before
- --follow through a fork with renames on both sides, with -c, --cc
and --remerge-diff
- a --no-ff merge taking a rename from the second parent, in both commit
@@ Commit message
- a merge keeping the path, while the other side renamed it
- --diff-merges=first-parent, which diffs merges only against their
first parent, but still walks all parents
+ - a merge keeping the renamed path, while the other side edited the old
+ one
+ - a merge keeping the copied path, while the other side edited the
+ original
+ Helped-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Miklos Vajna <vmiklos@collabora.com>
## t/t4219-log-follow-merge.sh ##
+@@ t/t4219-log-follow-merge.sh: test_expect_success '--follow finds the pre-merge commit through a subtree merge
+ test_cmp expect actual
+ '
+
++test_expect_success 'setup subtree merge adding the history of a copied file' '
++ git init graft &&
++ echo outer >graft/outer.txt &&
++ git -C graft add outer.txt &&
++ test_tick &&
++ git -C graft commit -m "outer init" &&
++ mkdir graft/inner &&
++ cp inner/inner.txt graft/inner/ &&
++ git -C graft add inner &&
++ test_tick &&
++ git -C graft commit -m "copy inner.txt to inner/" &&
++
++ git -C graft fetch ../inner master &&
++ test_tick &&
++ git -C graft merge -s ours --allow-unrelated-histories \
++ -m "Merge the history of inner/" FETCH_HEAD
++'
++
++test_expect_success '--follow finds the history of a copied file through a subtree merge' '
++ git -C graft log --follow --pretty=tformat:%s inner/inner.txt >actual &&
++ cat >expect <<-\EOF &&
++ copy inner.txt to inner/
++ inner init
++ EOF
++ test_cmp expect actual
++'
++
+ test_expect_success 'setup merge of two branches that both renamed a file to README' '
+ git init foo &&
+ mkdir foo/foo &&
@@ t/t4219-log-follow-merge.sh: test_expect_success '--follow follows renames through a fork in a single history
test_cmp expect actual.sorted
'
@@ t/t4219-log-follow-merge.sh: test_expect_success '--follow follows renames throu
+ EOF
+ test_cmp expect actual
+'
++
++test_expect_success 'setup merge keeping the renamed path, other side edited the old one' '
++ git init keep-renamed &&
++ printf "line 1\nline 2\nline 3\nline 4\nline 5\n" >keep-renamed/F &&
++ git -C keep-renamed add F &&
++ test_tick &&
++ git -C keep-renamed commit -m "O: add F" &&
++ git -C keep-renamed branch B &&
++ git -C keep-renamed mv F G &&
++ test_tick &&
++ git -C keep-renamed commit -m "A1: rename F to G on master" &&
++
++ git -C keep-renamed checkout B &&
++ printf "line 1\nline 2\nline 3\nline 4 T\nline 5\n" >keep-renamed/F &&
++ test_tick &&
++ git -C keep-renamed commit -am "B2: update F on B" &&
++
++ git -C keep-renamed checkout master &&
++ test_tick &&
++ git -C keep-renamed merge -s ours -m "M: merge B, keep G, drop F" B
++'
++
++test_expect_success '--follow follows an edit on the side the merge did not take' '
++ git -C keep-renamed log --follow --pretty=tformat:%s G >actual &&
++ cat >expect <<-\EOF &&
++ B2: update F on B
++ A1: rename F to G on master
++ O: add F
++ EOF
++ test_cmp expect actual
++'
++
++test_expect_success 'setup merge keeping the copied path, other side edited the original' '
++ git init keep-copied &&
++ printf "line 1\nline 2\nline 3\nline 4\nline 5\n" >keep-copied/F &&
++ git -C keep-copied add F &&
++ test_tick &&
++ git -C keep-copied commit -m "O: add F" &&
++ git -C keep-copied branch B &&
++ cp keep-copied/F keep-copied/G &&
++ git -C keep-copied add G &&
++ test_tick &&
++ git -C keep-copied commit -m "A1: copy F to G on master" &&
++
++ git -C keep-copied checkout B &&
++ printf "line 1\nline 2\nline 3\nline 4 T\nline 5\n" >keep-copied/F &&
++ test_tick &&
++ git -C keep-copied commit -am "B2: update F on B" &&
++
++ git -C keep-copied checkout master &&
++ test_tick &&
++ git -C keep-copied merge -s ours -m "M: merge B, keep F and G" B
++'
++
++test_expect_success '--follow follows an edit of the copy source on the side the merge did not take' '
++ git -C keep-copied log --follow --pretty=tformat:%s G >actual &&
++ cat >expect <<-\EOF &&
++ B2: update F on B
++ A1: copy F to G on master
++ O: add F
++ EOF
++ test_cmp expect actual
++'
+
test_done
2: b172890bf4 = 2: f36129714b log: extract showing the remerge/combined diff of a merge commit
3: 6586e8f004 = 3: 85b6067fbd log: fix -m --follow losing the history of later parents
4: b903152bc4 ! 4: 5ba93e49cf log: simplify --follow following renames for non-linear history
@@ Commit message
log: simplify --follow following renames for non-linear history
Code added in 304812ed33 (log: improve --follow following renames for
- non-linear history, 2026-06-22) implemented the feature by introducing a
+ non-linear history, 2026-06-22) implements the feature by introducing a
new propagate_follow_pathspec_to_parent() function which adds a new call
to diff_tree_oid(). Also it introduces a new loop to record what path
each parent of a merge commit should use when following a path.
- This was working, but it caused a performance regression: e.g. 'git log
- --stat --follow -- include/linux/rsc_table.h' in linux.git went from 28s
- to 6m43s. The new code diffs each merge against each of its parents with
- rename detection, which is expensive when a parent doesn't have the
- followed path yet. Fixing this would require adding even more custom
- code, instead of just extending the existing code in log_tree_diff(). So
- first simplify the code here, and fix the performance regression in a
- follow-up commit.
+ This works, but it causes a performance regression: e.g. 'git log
+ --stat --follow -- include/linux/rsc_table.h' in linux.git takes 6m43s,
+ while it took 28s before that commit. The new code diffs each merge
+ against each of its parents with rename detection, which is expensive
+ when a parent doesn't have the followed path yet. Fixing this would
+ require adding even more custom code, instead of just extending the
+ existing code in log_tree_diff(). So first simplify the code here, and
+ fix the performance regression in a follow-up commit.
Remove propagate_follow_pathspec_to_parent() and the loop calling it
for each parent. Instead, record the path to follow for each parent in
- the parent loop of log_tree_diff(), right after diff_tree_oid() updated
+ the parent loop of log_tree_diff(), right after diff_tree_oid() updates
the pathspec in case of a rename. Merges are not diffed by default, so
with --follow, run the same loop for them as well, but without showing
anything. With -c, --cc or --remerge-diff, show the merge diff after the
5: 072d90b865 < -: ---------- log: speed up --follow following renames for non-linear history
6: 7be8cfb3ba ! 5: ddcac2115e log: fix --follow following an edit on a side the merge did not take
@@ Metadata
Author: Miklos Vajna <vmiklos@collabora.com>
## Commit message ##
- log: fix --follow following an edit on a side the merge did not take
+ log: speed up --follow following renames for non-linear history
- Take a history where F is renamed to G on master, F is edited on a side
- branch, and the side branch is merged with '-s ours', so the merge keeps
- G and drops F. Here 'git log --follow G' doesn't show the edit of F on
- the side branch, while it did before the previous commit.
+ Test the performance of 'git log --stat --follow --
+ include/linux/rsc_table.h' on linux.git: since commit 304812ed33 (log:
+ improve --follow following renames for non-linear history, 2026-06-22),
+ it takes 6m43s, while it took about 28 seconds before.
- The problem is that follow_needs_renames() only searches for renames in
- a parent without the path if the merge base still has the path, which
- covers the case when the parent renamed the path. Here the other side
- of the merge did the rename, so the merge base only has the old name.
- The parent is then told to follow G, which it doesn't have, and the
- history of F on its side is lost. Searching for renames in each parent
- that lacks the path would fix this, but that is the expensive search
- that the previous commit avoids, e.g. 148 merges hit this case for
- include/linux/rsc_table.h in linux.git.
+ What happens is that log_tree_diff() diffs each merge against all of its
+ parents with rename following. diff_tree_oid() only does the expensive
+ rename detection when the path is added relative to a parent, but that
+ is common for merges, e.g. when a branch forked before the path was
+ created merges mainline, and this slows down 'git log --follow'.
- Fix the problem by also searching for renames when the merge base
- doesn't have the path, but the parent changed a file since the merge
- base that the merge no longer has: only such a file can be the old name
- of the path that still needs to be followed in the parent. This check
- is cheap, since it only diffs the changes of the side branch, and it
- rules out 146 of the 148 merges in linux.git. Then do a plain rename
- detection from the parent to the merge, which only looks at the files
- deleted by the merge, not at all files of the parent like --follow does.
+ Fix the problem by first diffing the merge against its parents without
+ rename following, to find a parent where the path is unchanged. If there
+ is such a parent, then skip the diff against the other parents and just
+ follow the same path there. The exception is a parent that doesn't have
+ the path, but still needs renames to be followed:
+ - The merge base has the path, i.e. that side renamed or removed it, or
+ there is no merge base, e.g. a subtree merge of unrelated histories.
+ - The merge base doesn't have the path, but the parent changed a file
+ since then, and the merge dropped that change. A copy detection also
+ finds that file as the source of the path, i.e. the other side renamed
+ or copied the path from that file.
+ If the path changed relative to all parents, then diff against them with
+ rename following, as before. Given that this way a parent may be asked
+ to follow a path that it doesn't have, also adjust
+ remember_follow_pathspec(): if children disagree on what should be the
+ path for a commit, then prefer the path that exists in that commit.
- Reported-by: Patrick Steinhardt <ps@pks.im>
+ Now the above use case takes 42s, somewhat close to the original cost.
+
+ Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
+ Helped-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Miklos Vajna <vmiklos@collabora.com>
## log-tree.c ##
+@@
+ #include "range-diff.h"
+ #include "strmap.h"
+ #include "tree.h"
++#include "tree-walk.h"
+ #include "wildmatch.h"
+ #include "write-or-die.h"
+ #include "pager.h"
@@ log-tree.c: static void set_pathspec_to_single_path(struct pathspec *ps, const char *path)
PATHSPEC_LITERAL_PATH, "", paths);
}
--static int commit_has_path(struct rev_info *opt, struct commit *c,
-- const char *path)
+static int tree_has_path(struct rev_info *opt, struct object_id *tree,
+ const char *path)
- {
- struct object_id oid;
- unsigned short mode;
-
++{
++ struct object_id oid;
++ unsigned short mode;
++
+ return !get_tree_entry(opt->diffopt.repo, tree, path, &oid, &mode);
+}
+
+static int commit_has_path(struct rev_info *opt, struct commit *c,
+ const char *path)
+{
- parse_commit_or_die(c);
-- return !get_tree_entry(opt->diffopt.repo, get_commit_tree_oid(c), path,
-- &oid, &mode);
++ parse_commit_or_die(c);
+ return tree_has_path(opt, get_commit_tree_oid(c), path);
- }
-
++}
++
static void remember_follow_pathspec(struct rev_info *opt,
+ struct commit *c, const char *path)
+ {
+@@ log-tree.c: static void remember_follow_pathspec(struct rev_info *opt,
+ init_follow_pathspec_slab(opt->follow_pathspec_slab);
+ }
+ slot = follow_pathspec_slab_at(opt->follow_pathspec_slab, c);
+- if (*slot && !strcmp(*slot, path))
+- return;
++ /* If children of 'c' disagree on the path, prefer the one 'c' has. */
++ if (*slot) {
++ if (!strcmp(*slot, path) || commit_has_path(opt, c, *slot) ||
++ !commit_has_path(opt, c, path))
++ return;
++ }
+ free(*slot);
+ *slot = xstrdup(path);
+ }
@@ log-tree.c: void release_follow_pathspec_slab(struct rev_info *opt)
FREE_AND_NULL(opt->follow_pathspec_slab);
}
++/* Find a parent where 'path' is the same as in the merge. */
++static struct commit *follow_same_parent(struct rev_info *opt,
++ struct commit_list *parents,
++ struct object_id *merge_tree)
++{
++ struct commit *same = NULL;
++
++ opt->diffopt.flags.follow_renames = 0;
++ for (; parents && !same; parents = parents->next) {
++ parse_commit_or_die(parents->item);
++ diff_tree_oid(get_commit_tree_oid(parents->item), merge_tree,
++ "", &opt->diffopt);
++ if (!diff_queued_diff.nr)
++ same = parents->item;
++ diff_queue_clear(&diff_queued_diff);
++ }
++ opt->diffopt.flags.follow_renames = 1;
++ return same;
++}
++
+/*
-+ * Did 'parent' change a file since 'base' that the merge (with tree 'oid') no
-+ * longer has? Only such a file can be the source of a rename on the other side
-+ * of the merge that still needs to be followed in 'parent'.
++ * Did 'parent' change a file since 'base', but the merge tree dropped that
++ * change? I.e. the merge doesn't have the file or it's the same as in 'same'.
+ */
+static int follow_parent_changed_dropped_file(struct rev_info *opt,
+ struct commit *base,
+ struct commit *parent,
-+ struct object_id *oid)
++ struct commit *same,
++ struct object_id *merge_tree)
+{
+ struct diff_options diff_opts;
+ int i, ret = 0;
@@ log-tree.c: void release_follow_pathspec_slab(struct rev_info *opt)
+ "", &diff_opts);
+ for (i = 0; i < diff_queued_diff.nr && !ret; i++) {
+ struct diff_filepair *p = diff_queued_diff.queue[i];
++ struct object_id merge_oid, same_oid;
++ unsigned short mode;
+
-+ ret = DIFF_FILE_VALID(p->two) &&
-+ !tree_has_path(opt, oid, p->two->path);
++ if (!DIFF_FILE_VALID(p->two))
++ continue;
++ if (get_tree_entry(opt->diffopt.repo, merge_tree, p->two->path,
++ &merge_oid, &mode))
++ ret = 1;
++ else
++ ret = !oideq(&merge_oid, &p->two->oid) &&
++ !get_tree_entry(opt->diffopt.repo,
++ get_commit_tree_oid(same),
++ p->two->path, &same_oid, &mode) &&
++ oideq(&merge_oid, &same_oid);
+ }
+ diff_queue_clear(&diff_queued_diff);
+ diff_free(&diff_opts);
+ return ret;
+}
+
-+/*
-+ * Is 'path' in the merge (with tree 'oid') renamed from a file of 'parent'?
-+ * Only look at the files that the merge deleted, not at all files of 'parent'
-+ * like --follow does.
-+ */
++/* Is 'path' in the merge tree renamed or copied from a file of 'parent'? */
+static int follow_has_rename_source(struct rev_info *opt,
+ struct commit *parent,
-+ struct object_id *oid, const char *path)
++ struct object_id *merge_tree,
++ const char *path)
+{
+ struct diff_options diff_opts;
+ int i, ret = 0;
+
+ repo_diff_setup(opt->diffopt.repo, &diff_opts);
+ diff_opts.flags.recursive = 1;
-+ diff_opts.detect_rename = DIFF_DETECT_RENAME;
++ diff_opts.detect_rename = DIFF_DETECT_COPY;
+ diff_opts.rename_score = opt->diffopt.rename_score;
+ diff_opts.single_follow = path;
+ diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+ diff_setup_done(&diff_opts);
-+ diff_tree_oid(get_commit_tree_oid(parent), oid, "", &diff_opts);
++ diff_tree_oid(get_commit_tree_oid(parent), merge_tree, "", &diff_opts);
+ diffcore_std(&diff_opts);
+ for (i = 0; i < diff_queued_diff.nr && !ret; i++) {
+ struct diff_filepair *p = diff_queued_diff.queue[i];
+
-+ ret = p->status == DIFF_STATUS_RENAMED &&
++ ret = (p->status == DIFF_STATUS_RENAMED ||
++ p->status == DIFF_STATUS_COPIED) &&
+ !strcmp(p->two->path, path);
+ }
+ diff_queue_clear(&diff_queued_diff);
@@ log-tree.c: void release_follow_pathspec_slab(struct rev_info *opt)
+ return ret;
+}
+
- /*
- * When the path in a merge is the same as in 'same', does the path still need
- * renames to be followed in 'parent'? Only if the merge has the path, 'parent'
-- * doesn't have it, but its fork point with 'same' had it, e.g. 'parent'
-- * renamed the path.
-+ * doesn't have it, and either:
-+ *
-+ * - its fork point with 'same' had it, e.g. 'parent' renamed the path, or
-+ * - 'same' renamed the path from a file that 'parent' changed.
- */
- static int follow_needs_renames(struct rev_info *opt, struct commit *parent,
-- struct commit *same, const char *path)
-+ struct commit *same, struct object_id *oid,
-+ const char *path)
- {
- struct commit_list *bases = NULL, *base;
-- int ret = 0;
++/*
++ * When the path in a merge tree is the same as in 'same', does the path still
++ * need renames to be followed in 'parent'?
++ */
++static int follow_needs_renames(struct rev_info *opt, struct commit *parent,
++ struct commit *same,
++ struct object_id *merge_tree, const char *path)
++{
++ struct commit_list *bases = NULL, *base;
+ int ret = 0, changed_dropped = 0;
-
- if (commit_has_path(opt, parent, path) ||
- !commit_has_path(opt, same, path))
- return 0;
- if (repo_get_merge_bases(opt->diffopt.repo, parent, same, &bases) < 0)
- exit(128);
-- for (base = bases; base && !ret; base = base->next)
++
++ if (parent == same || commit_has_path(opt, parent, path) ||
++ !commit_has_path(opt, same, path))
++ return 0;
++ if (repo_get_merge_bases(opt->diffopt.repo, parent, same, &bases) < 0)
++ exit(128);
++ /* Unrelated histories, e.g. a subtree merge */
++ ret = !bases;
+ for (base = bases; base && !ret; base = base->next) {
- ret = commit_has_path(opt, base->item, path);
++ ret = commit_has_path(opt, base->item, path);
+ if (!ret && !changed_dropped)
+ changed_dropped = follow_parent_changed_dropped_file(
-+ opt, base->item, parent, oid);
++ opt, base->item, parent, same, merge_tree);
+ }
- commit_list_free(bases);
++ commit_list_free(bases);
+ if (!ret && changed_dropped)
-+ ret = follow_has_rename_source(opt, parent, oid, path);
- return ret;
- }
++ ret = follow_has_rename_source(opt, parent, merge_tree, path);
++ return ret;
++}
++
+ /* Show the remerge or combined diff of a merge commit */
+ static int log_tree_merge_diff(struct rev_info *opt, struct commit *commit,
+ struct commit_list *parents,
+@@ log-tree.c: static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
+ int all_need_diff = opt->diff || opt->diffopt.flags.exit_with_status;
+ int follow_only = 0;
+ char *path = NULL;
++ struct commit *same = NULL;
+ if (!all_need_diff && !opt->merges_need_diff)
+ return 0;
@@ log-tree.c: static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
- parents = parents->next) {
- if (parents->item != parent &&
- follow_needs_renames(opt, parents->item,
-- parent, path))
-+ parent, oid, path))
- tail = &commit_list_insert(parents->item,
- tail)->next;
- else
-
- ## t/t4219-log-follow-merge.sh ##
-@@ t/t4219-log-follow-merge.sh: test_expect_success '--follow with --diff-merges=first-parent follows all parent
- test_cmp expect actual
- '
+ if (opt->diffopt.flags.follow_renames)
+ path = xstrdup_or_null(
+ pathspec_single_path(&opt->diffopt.pathspec));
++ if (path && is_merge && (follow_only || opt->first_parent_merges))
++ same = follow_same_parent(opt, parents, oid);
-+test_expect_success 'setup merge keeping the renamed path, other side edited the old one' '
-+ git init keep-renamed &&
-+ printf "line 1\nline 2\nline 3\nline 4\nline 5\n" >keep-renamed/F &&
-+ git -C keep-renamed add F &&
-+ test_tick &&
-+ git -C keep-renamed commit -m "O: add F" &&
-+ git -C keep-renamed branch B &&
-+ git -C keep-renamed mv F G &&
-+ test_tick &&
-+ git -C keep-renamed commit -m "A1: rename F to G on master" &&
-+
-+ git -C keep-renamed checkout B &&
-+ printf "line 1\nline 2\nline 3\nline 4 T\nline 5\n" >keep-renamed/F &&
-+ test_tick &&
-+ git -C keep-renamed commit -am "B2: update F on B" &&
-+
-+ git -C keep-renamed checkout master &&
-+ test_tick &&
-+ git -C keep-renamed merge -s ours -m "M: merge B, keep G, drop F" B
-+'
-+
-+test_expect_success '--follow follows an edit on the side the merge did not take' '
-+ git -C keep-renamed log --follow --pretty=tformat:%s G >actual &&
-+ cat >expect <<-\EOF &&
-+ B2: update F on B
-+ A1: rename F to G on master
-+ O: add F
-+ EOF
-+ test_cmp expect actual
-+'
-+
- test_done
+ showed_log = 0;
+ all_parents = parents;
+@@ log-tree.c: static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
+ struct commit *parent = parents->item;
+
+ parse_commit_or_die(parent);
+- diff_tree_oid(get_commit_tree_oid(parent),
+- oid, "", &opt->diffopt);
++ /*
++ * Same as in a parent: follow the path as-is, unless renames
++ * still need to be followed in this parent.
++ */
++ if (!follow_only || !same ||
++ follow_needs_renames(opt, parent, same, oid, path))
++ diff_tree_oid(get_commit_tree_oid(parent),
++ oid, "", &opt->diffopt);
+ if (follow_only) {
+ diff_queue_clear(&diff_queued_diff);
+ opt->diffopt.found_follow = 0;
--
2.51.0
next prev parent reply other threads:[~2026-11-09 8:03 UTC|newest]
Thread overview: 42+ 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
2026-10-15 14:48 ` [PATCH] log: speed up --follow following renames for non-linear history Miklos Vajna
2026-10-15 16:57 ` Junio C Hamano
2026-10-19 6:10 ` [PATCH v2] " Miklos Vajna
2026-10-22 20:45 ` [PATCH v3 0/4] " Miklos Vajna
2026-10-22 20:45 ` [PATCH v3 1/4] t4219: test more --follow cases across merges Miklos Vajna
2026-10-22 20:46 ` [PATCH v3 2/4] log: extract showing the remerge/combined diff of a merge commit Miklos Vajna
2026-10-22 20:46 ` [PATCH v3 3/4] log: simplify --follow following renames for non-linear history Miklos Vajna
2026-10-26 11:40 ` Patrick Steinhardt
2026-10-22 20:47 ` [PATCH v3 4/4] log: speed up " Miklos Vajna
2026-10-26 11:40 ` Patrick Steinhardt
2026-10-26 11:42 ` [PATCH v3 0/4] " Patrick Steinhardt
2026-10-30 20:07 ` [PATCH v4 0/6] " Miklos Vajna
2026-10-30 20:07 ` [PATCH v4 1/6] t4219: test more --follow cases across merges Miklos Vajna
2026-10-30 20:07 ` [PATCH v4 2/6] log: extract showing the remerge/combined diff of a merge commit Miklos Vajna
2026-10-30 20:08 ` [PATCH v4 3/6] log: fix -m --follow losing the history of later parents Miklos Vajna
2026-10-30 20:08 ` [PATCH v4 4/6] log: simplify --follow following renames for non-linear history Miklos Vajna
2026-10-30 20:09 ` [PATCH v4 5/6] log: speed up " Miklos Vajna
2026-11-06 14:14 ` Patrick Steinhardt
2026-11-09 8:02 ` Miklos Vajna
2026-10-30 20:10 ` [PATCH v4 6/6] log: fix --follow following an edit on a side the merge did not take Miklos Vajna
2026-11-06 14:14 ` Patrick Steinhardt
2026-11-09 8:03 ` Miklos Vajna
2026-11-06 14:14 ` [PATCH v4 0/6] log: speed up --follow following renames for non-linear history Patrick Steinhardt
2026-11-09 8:01 ` Miklos Vajna
2026-11-09 8:03 ` Miklos Vajna [this message]
2026-11-09 8:04 ` [PATCH v5 1/5] t4219: test more --follow cases across merges Miklos Vajna
2026-11-10 23:06 ` Justin Tobler
2026-11-09 8:05 ` [PATCH v5 2/5] log: extract showing the remerge/combined diff of a merge commit Miklos Vajna
2026-11-09 8:05 ` [PATCH v5 3/5] log: fix -m --follow losing the history of later parents Miklos Vajna
2026-11-09 8:05 ` [PATCH v5 4/5] log: simplify --follow following renames for non-linear history Miklos Vajna
2026-11-10 16:45 ` Toon Claes
2026-11-09 8:06 ` [PATCH v5 5/5] log: speed up " Miklos Vajna
2026-11-11 0:40 ` [PATCH v5 0/5] " Justin Tobler
2026-11-11 10:43 ` Patrick Steinhardt
2026-11-11 12:41 ` Miklos Vajna
2026-11-11 13:29 ` Patrick Steinhardt
2026-11-12 9:05 ` [PATCH] log: make --follow following renames for non-linear history optional Miklos Vajna
2026-11-11 16:44 ` [PATCH v5 0/5] log: speed up --follow following renames for non-linear history Junio C Hamano
2026-10-16 20:37 ` [PATCH] " Junio C Hamano
2026-10-14 6:16 ` Performance regression in `git log --follow` Miklos Vajna
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=cover.1794078554.git.vmiklos@collabora.com \
--to=vmiklos@collabora.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=ps@pks.im \
--cc=szeder.dev@gmail.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