Re: List all commits of a specified file in oldest to newest order
- From
Ævar Arnfjörð Bjarmason <avarab@gmail.com>
- Date
- Nov 5, 2021, 08:13 UTC
- Message-ID
- <211105.86a6ijhujg.gmgdl@evledraar.gmail.com>
- In-Reply-To
- <c3932b3c-323a-39d6-26a7-ba0c3d17378b@onenetbeyond.org>
On Fri, Nov 05 2021, Vipul Kumar wrote:
Show 6 quoted lines
> I want to list all commits (including renames) of a specified file in > oldest to newest order. But when I run "git log --follow --reverse -- > <path/to/the/file>" command, I'm getting a single commit, which points > to the "rename of the file". This behavior is weird to me because I > expected to get list of all commit in oldest to newest order, whereas > "git log --follow -- <path/to/the/file>" command works as expected.
This is a known caveat of how the history traversal works, but from a quick glance I couldn't see any explicit documentation for it, aside from this terse mention in git-log(1):
"[-M can be used] for following files across renames while
traversing history, see --follow.".The key thing being the "traversal", i.e. as we walk history we'll encounter a tree entry where b.txt was deleted, and see that it was moved from a.txt.
However, if we walk history from the beginning we have no idea of the relationship of a->b.txt, since we didn't encounter that commit yet, that's only something we see while walking the history.
Perhaps we should have some option like --buffer-then-reverse, which wouldn't change the walking order, but would only change the output, but we don't.
This caveat doesn't only apply to reverse, try to apply a move of b.txt on top of your history:
b.txt -> c.txt
And now do:
git log [--follow] -- b.txt
What should we output there? If we're arguing that we should first traverse the history to "look forward" that'll also apply to a non-reverse walk, since we're asking to follow b.txt.
But we haven't encountered the b->c.txt relationship yet (well, we run into the rename commit, but once you add a c->d.txt on top...). So maybe instead of --buffer-then-reverse we'd need a hypothetical --two-pass, which would also impact options other than --reverse whose behavior relies on traversal order.
Take all the above as an explanation for how it works now, not some defense of this being user-friendly. I've also often been annoyed at this behavior.
For small sets you can feed your into an alias like:
git show $(git -P log --pretty=format:"%h" --pretty=tformat:%H --follow -- b.txt | tac)
As a poor man's --buffer-then-reverse.