* [PATCH] log: let --follow follow renames in merge commits
@ 2026-05-05 7:42 Miklos Vajna
0 siblings, 0 replies; 5+ 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] 5+ messages in thread
* [PATCH] log: let --follow follow renames in merge commits
@ 2026-05-12 7:21 Miklos Vajna
2026-05-19 6:17 ` Miklos Vajna
0 siblings, 1 reply; 5+ 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] 5+ messages in thread
* Re: [PATCH] log: let --follow follow renames in merge commits
2026-05-12 7:21 [PATCH] log: let --follow follow renames in merge commits Miklos Vajna
@ 2026-05-19 6:17 ` Miklos Vajna
2026-05-19 6:37 ` Junio C Hamano
0 siblings, 1 reply; 5+ messages in thread
From: Miklos Vajna @ 2026-05-19 6:17 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Patrick Steinhardt, brian m. carlson
Hi Junio,
On Tue, May 12, 2026 at 09:21:17AM +0200, Miklos Vajna <vmiklos@collabora.com> wrote:
> 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?
I'm a bit confused regarding what can be a next step here. I
understanding you were away for 3 weeks, so there is a lot to process.
:-) Should I just wait more or should I resend this?
Thanks,
Miklos
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] log: let --follow follow renames in merge commits
2026-05-19 6:17 ` Miklos Vajna
@ 2026-05-19 6:37 ` Junio C Hamano
2026-05-19 8:14 ` Junio C Hamano
0 siblings, 1 reply; 5+ messages in thread
From: Junio C Hamano @ 2026-05-19 6:37 UTC (permalink / raw)
To: Miklos Vajna; +Cc: git, Patrick Steinhardt, brian m. carlson
Miklos Vajna <vmiklos@collabora.com> writes:
> Hi Junio,
>
> On Tue, May 12, 2026 at 09:21:17AM +0200, Miklos Vajna <vmiklos@collabora.com> wrote:
>> 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.
The patch collecting no comments is just that--nobody so far is
interested enough to drop other things they were doing to give
supporting code reviews---and "no news" does not mean a good news.
>> So this is a resend, this time to you, CC'ing the list, rather than the
>> other way around.
>>
>> Could you please review this?
>
> I'm a bit confused regarding what can be a next step here. I
> understanding you were away for 3 weeks, so there is a lot to process.
> :-) Should I just wait more or should I resend this?
Rather, ask other reviewers; when I do not comment on a patch, I
often am not interested, or too busy and the change does not look
interesting enough to me to make me drop what I am doing.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] log: let --follow follow renames in merge commits
2026-05-19 6:37 ` Junio C Hamano
@ 2026-05-19 8:14 ` Junio C Hamano
0 siblings, 0 replies; 5+ messages in thread
From: Junio C Hamano @ 2026-05-19 8:14 UTC (permalink / raw)
To: Miklos Vajna; +Cc: git, Patrick Steinhardt, brian m. carlson
Junio C Hamano <gitster@pobox.com> writes:
>>> Could you please review this?
>>
>> I'm a bit confused regarding what can be a next step here. I
>> understanding you were away for 3 weeks, so there is a lot to process.
>> :-) Should I just wait more or should I resend this?
>
> Rather, ask other reviewers; when I do not comment on a patch, I
> often am not interested, or too busy and the change does not look
> interesting enough to me to make me drop what I am doing.
Addendum. As I said in
https://lore.kernel.org/git/xmqqqzni967o.fsf@gitster.g/
and the subsequent discussion concluded, the "==follow" checkbox
feature is meant to work well only in a linear history, and that is
inherent to the way it "follows" the single path.
It does not follow different pathname(s) while following a set of
different histories merged, e.g., in a history like this (as usual
time flows from left to right)
----o----A----o
\
M----o----o----o
/
----x----B----x
you may start following path F at the HEAD, and after crossing the
merge M, one history may find out that path F came from path G.
The traveral starts with "F" as the sole element in the pathspec,
but once the traversal hits that commit (say, A), the traversal
switches to use "G" as the sole element in the pathspec and follows
the history down. Even if the other history (i.e., 'x' on the lower
history) had path F all along, once the pathspec is swapped to
follow "G" on the upper lineage of the history, traversal of the
lower lineage that happens after the traversal passes 'A" _will_ try
to follow "G" that may not exist at all. Or 'x' may have done the
same rename from "G" to "F" at "B". Depending on the order in which
"A" and any of these commits on the lower history are visited, the
commit that is a child of "B" (which has the path at "F") may be
visited after "A", in which case the path in question "F" will not
be looked for in it.
A minor "tweak" that does not solve this inherent design issue does
not interest me, so...
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-05-19 8:14 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-12 7:21 [PATCH] log: let --follow follow renames in merge commits Miklos Vajna
2026-05-19 6:17 ` Miklos Vajna
2026-05-19 6:37 ` Junio C Hamano
2026-05-19 8:14 ` Junio C Hamano
-- strict thread matches above, loose matches on Subject: below --
2026-05-05 7:42 Miklos Vajna
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox