* [PATCH] log: let --follow follow renames in merge commits
@ 2026-05-05 7:42 Miklos Vajna
0 siblings, 0 replies; 2+ messages in thread
From: Miklos Vajna @ 2026-05-05 7:42 UTC (permalink / raw)
To: git; +Cc: Patrick Steinhardt, Junio C Hamano, brian m. carlson
Have a repo with a subtree merge, do a 'git log --follow prefix/test.c',
the output only contains history in the outer repo, not commits that
were merged via a subtree merge.
This is inconsistent, since doing a 'git blame prefix/test.c' does find
the original commits. This works because find_rename() in blame.c is
invoked for each parent, and there diff_tree_oid() is used, which uses
try_to_follow_renames(). This means that in case a rename happens as
part of a merge commit, git blame can follow that rename.
Fix the problem in a similar way for the 'git log --follow' case: in
case log_tree_diff() finds a merge commit and it would return early,
then do some extra work in the follow_renames case first. Check each
parent, use diff_tree_oid() and if found_follow is set, then work with
that parent instead of returning.
This means that users examining the history of a repo with subtree
merges can see all commits to a file with a single 'git log --follow'
invocation, instead of one invocation for the outer repo and one for the
history before the subtree merge.
Signed-off-by: Miklos Vajna <vmiklos@collabora.com>
---
Hi,
I recently ran into a case where a subtree merge was used to do a
one-off import of one repo into an other one, into a subdirectory. Turns
out git blame finds original commits nicely, but git log --follow is
less great for this case. This is an improvement to also follow renames
when the rename happens as part of a merge commit.
Let me know if I managed to get some formality wrong, I haven't
contributed since 2022.
Could you please review this?
Thanks,
Miklos
log-tree.c | 20 ++++++++++++++++-
t/meson.build | 1 +
t/t4218-log-follow-subtree-merge.sh | 34 +++++++++++++++++++++++++++++
3 files changed, 54 insertions(+), 1 deletion(-)
create mode 100755 t/t4218-log-follow-subtree-merge.sh
diff --git a/log-tree.c b/log-tree.c
index 7e048701d0..bce09c7dac 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -1142,8 +1142,26 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
/* Show parent info for multiple diffs */
log->parent = parents->item;
}
- } else
+ } else {
+ if (opt->diffopt.flags.follow_renames) {
+ /*
+ * Detect a rename across one of the parents.
+ * Check each parent till we find a follow.
+ */
+ struct commit_list *p;
+ for (p = parents; p; p = p->next) {
+ parse_commit_or_die(p->item);
+ diff_tree_oid(get_commit_tree_oid(p->item),
+ oid, "", &opt->diffopt);
+ diff_queue_clear(&diff_queued_diff);
+ if (opt->diffopt.found_follow) {
+ opt->diffopt.found_follow = 0;
+ break;
+ }
+ }
+ }
return 0;
+ }
}
showed_log = 0;
diff --git a/t/meson.build b/t/meson.build
index 7528e5cda5..b4ae8d76d8 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -574,6 +574,7 @@ integration_tests = [
't4215-log-skewed-merges.sh',
't4216-log-bloom.sh',
't4217-log-limit.sh',
+ 't4218-log-follow-subtree-merge.sh',
't4252-am-options.sh',
't4253-am-keep-cr-dos.sh',
't4254-am-corrupt.sh',
diff --git a/t/t4218-log-follow-subtree-merge.sh b/t/t4218-log-follow-subtree-merge.sh
new file mode 100755
index 0000000000..7ca607cbb8
--- /dev/null
+++ b/t/t4218-log-follow-subtree-merge.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+test_description='Test --follow follows renames across subtree merges'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+test_expect_success 'setup subtree-merged repository' '
+ git init inner &&
+ echo inner >inner/inner.txt &&
+ git -C inner add inner.txt &&
+ git -C inner commit -m "inner init" &&
+
+ git init outer &&
+ echo outer >outer/outer.txt &&
+ git -C outer add outer.txt &&
+ git -C outer commit -m "outer init" &&
+
+ git -C outer fetch ../inner master &&
+ git -C outer merge -s ours --no-commit --allow-unrelated-histories \
+ FETCH_HEAD &&
+ git -C outer read-tree --prefix=inner/ -u FETCH_HEAD &&
+ git -C outer commit -m "Merge inner repo into inner/ subdirectory"
+'
+
+test_expect_success '--follow finds the pre-merge commit through a subtree merge' '
+ git -C outer log --follow --pretty=tformat:%s inner/inner.txt >actual &&
+ echo "inner init" >expect &&
+ test_cmp expect actual
+'
+
+test_done
--
2.51.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [PATCH] log: let --follow follow renames in merge commits
@ 2026-05-12 7:21 Miklos Vajna
0 siblings, 0 replies; 2+ messages in thread
From: Miklos Vajna @ 2026-05-12 7:21 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Patrick Steinhardt, brian m. carlson
Have a repo with a subtree merge, do a 'git log --follow prefix/test.c',
the output only contains history in the outer repo, not commits that
were merged via a subtree merge.
This is inconsistent, since doing a 'git blame prefix/test.c' does find
the original commits. This works because find_rename() in blame.c is
invoked for each parent, and there diff_tree_oid() is used, which uses
try_to_follow_renames(). This means that in case a rename happens as
part of a merge commit, git blame can follow that rename.
Fix the problem in a similar way for the 'git log --follow' case: in
case log_tree_diff() finds a merge commit and it would return early,
then do some extra work in the follow_renames case first. Check each
parent, use diff_tree_oid() and if found_follow is set, then work with
that parent instead of returning.
This means that users examining the history of a repo with subtree
merges can see all commits to a file with a single 'git log --follow'
invocation, instead of one invocation for the outer repo and one for the
history before the subtree merge.
Signed-off-by: Miklos Vajna <vmiklos@collabora.com>
---
Hi Junio,
I sent this out a week ago at
<https://lore.kernel.org/git/afmfSa-p-9vuDL3E@collabora.com/T/#u>, I
didn't get any reply to it -- so I'm somewhat optimistic that the patch
itself is a good idea, seeing no negative comments.
So this is a resend, this time to you, CC'ing the list, rather than the
other way around.
Could you please review this?
Thanks,
Miklos
log-tree.c | 20 ++++++++++++++++-
t/meson.build | 1 +
t/t4218-log-follow-subtree-merge.sh | 34 +++++++++++++++++++++++++++++
3 files changed, 54 insertions(+), 1 deletion(-)
create mode 100755 t/t4218-log-follow-subtree-merge.sh
diff --git a/log-tree.c b/log-tree.c
index 7e048701d0..bce09c7dac 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -1142,8 +1142,26 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
/* Show parent info for multiple diffs */
log->parent = parents->item;
}
- } else
+ } else {
+ if (opt->diffopt.flags.follow_renames) {
+ /*
+ * Detect a rename across one of the parents.
+ * Check each parent till we find a follow.
+ */
+ struct commit_list *p;
+ for (p = parents; p; p = p->next) {
+ parse_commit_or_die(p->item);
+ diff_tree_oid(get_commit_tree_oid(p->item),
+ oid, "", &opt->diffopt);
+ diff_queue_clear(&diff_queued_diff);
+ if (opt->diffopt.found_follow) {
+ opt->diffopt.found_follow = 0;
+ break;
+ }
+ }
+ }
return 0;
+ }
}
showed_log = 0;
diff --git a/t/meson.build b/t/meson.build
index 7528e5cda5..b4ae8d76d8 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -574,6 +574,7 @@ integration_tests = [
't4215-log-skewed-merges.sh',
't4216-log-bloom.sh',
't4217-log-limit.sh',
+ 't4218-log-follow-subtree-merge.sh',
't4252-am-options.sh',
't4253-am-keep-cr-dos.sh',
't4254-am-corrupt.sh',
diff --git a/t/t4218-log-follow-subtree-merge.sh b/t/t4218-log-follow-subtree-merge.sh
new file mode 100755
index 0000000000..7ca607cbb8
--- /dev/null
+++ b/t/t4218-log-follow-subtree-merge.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+test_description='Test --follow follows renames across subtree merges'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+test_expect_success 'setup subtree-merged repository' '
+ git init inner &&
+ echo inner >inner/inner.txt &&
+ git -C inner add inner.txt &&
+ git -C inner commit -m "inner init" &&
+
+ git init outer &&
+ echo outer >outer/outer.txt &&
+ git -C outer add outer.txt &&
+ git -C outer commit -m "outer init" &&
+
+ git -C outer fetch ../inner master &&
+ git -C outer merge -s ours --no-commit --allow-unrelated-histories \
+ FETCH_HEAD &&
+ git -C outer read-tree --prefix=inner/ -u FETCH_HEAD &&
+ git -C outer commit -m "Merge inner repo into inner/ subdirectory"
+'
+
+test_expect_success '--follow finds the pre-merge commit through a subtree merge' '
+ git -C outer log --follow --pretty=tformat:%s inner/inner.txt >actual &&
+ echo "inner init" >expect &&
+ test_cmp expect actual
+'
+
+test_done
--
2.51.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-05-12 7:21 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-05 7:42 [PATCH] log: let --follow follow renames in merge commits Miklos Vajna
-- strict thread matches above, loose matches on Subject: below --
2026-05-12 7:21 Miklos Vajna
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox