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: 43+ 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-12 19:09 ` Junio C Hamano
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.