Martin Vegter
Martin Vegter

Reputation: 527

git show: confusing behavior when showing past version of a file

I want to see a historical version of a given file: .zshrc

This will show me all commits where this file changed:

git log .zshrc
...
...

Now for given commit, I want to see the whole file. But the following actually does not show the file. It shows a diff between that commit and previous commit:

git show bbac89168a2fbbaaa6ef31dc50616dd236c8b5d4 .zshrc

This does not work at all:

git show bbac89168a2fbbaaa6ef31dc50616dd236c8b5d4:.zshrc  

fatal: Path 'test/.zshrc' exists, but not '.zshrc'.
Did you mean 'bbac89168a2fbbaaa6ef31dc50616dd236c8b5d4:test/.zshrc' aka '2c0989168a2fbbaaa6ef31dc50616dd236c8b5d4:./.zshrc'?

And finally, this behaves as expected. It will "cat" given version of file

git show bbac89168a2fbbaaa6ef31dc50616dd236c8b5d4:./.zshrc

But why is the behavior of git show so illogical and confusing?

Why is git show bbac89168a2fbbaaa6ef31dc50616dd236c8b5d4 .zshrc different from git show bbac89168a2fbbaaa6ef31dc50616dd236c8b5d4:.zshrc

Also, why does git show bbac89168a2fbbaaa6ef31dc50616dd236c8b5d4:.zshrc not work, even though git clearly understand, and suggests, I should use: bbac89168a2fbbaaa6ef31dc50616dd236c8b5d4:test/.zshrc ?

Why can't I use relative path :.zshrc and instead must use completely unnecessary construct :./.zshrc ?

Upvotes: 0

Views: 82

Answers (2)

LeGEC
LeGEC

Reputation: 52111

When you use the notation <commit hash>:<path>, you may think of <path> as an absolute path starting from the root of the repository.

This will work in any context : in a bare repository, when working with external worktrees ...


Starting your path with ./ is one way to name relative paths, it actually indicates to git : "try to make out what tree to inspect in the commit from my current working directory" -- which does have an intuitive meaning when you have a regular clone of a repo, but may fail in other contexts.


Note that the behavior of git show <commit> <path> is also very different from git show <commit>:<path> :

  • git show <commit> <path> will display a diff :
    the diff brought by commit <commit>, limited to only what affects <path>. It will also display information about commit <commit>.
  • git show <commit>:<path> will display a content :
    it will display the full content of what git stored at <path> for commit <commit>.
    If <path> points to a file ("a blob" in git terms), you will get the full content of that file ; if <path> points to a directory ("a tree" in git terms), you will get the listing for that directory.

Note that git does have illogical and contradictory choices in how two different commands interpret their arguments, or how one same command interprets different arguments.

Most of these choices are the result of :

  • a command was first implemented that way,
  • once a command has been published, it is very important to avoid breaking existing scripts or workflows that rely on its behavior.

Upvotes: 2

larsks
larsks

Reputation: 312410

Why is git show bbac89168a2fbbaaa6ef31dc50616dd236c8b5d4 .zshrc different from git show bbac89168a2fbbaaa6ef31dc50616dd236c8b5d4:.zshrc

Because you're running git show <thing>, and in the first case <thing> is just a commit. The filename acts as a filter on the commit diff, so you only see the diff for that particular file.

In the second case (<commit>:<path>), <thing> is a file in a commit, so you get that instead.

Also, why does git show bbac89168a2fbbaaa6ef31dc50616dd236c8b5d4:.zshrc not work...

I can't reproduce that particular problem (with git 2.31.1). If I have a file named .zshrc at the top level of the repository, I can run git show <commit>:.zshrc and git shows me the file contents.

But why is the behavior of git show so illogical and confusing?

Some of these commands have been around for a long time and people have grown to rely on the behavior, confusing though it may be. There have been efforts in recent years to offer more modern alternatives to some commands (see e.g. git switch, which tries to provide a more obvious interface for some actions that were previously part of git checkout. I don't know if git show has been the target of this sort of work.

Upvotes: 1

Related Questions