0

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 ?

400 the Cat
  • 266
  • 3
  • 23
  • Have you tried using a (decent) Git GUI, like SourceTree or GitKraken? – Dai Sep 09 '21 at 03:13
  • There probably *should* be a config option for this, as there now is for `git status`, which has the opposite default behavior (it would show `../file.ext` when top level `file.ext` is modified and you're in `sub`; there's now a flag to make it use top level consistently). Get yourself a current version of the repo—there's a read-only, up-to-date copy in github.com/git/git—and add the config option and send it in as an enhancement request. – torek Sep 09 '21 at 16:24

2 Answers2

2

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.

larsks
  • 277,717
  • 41
  • 399
  • 399
  • in my case `.zshrc` is not at the top level of the repository. The repository starts at parent directory. So `test/.zshrc`. But when I am in test/, I cannot use `.zshrc`. I have to use either `test/.zshrc` or `./.zshrc`. Is there any way to change this behaviour using some configuration option ? – 400 the Cat Sep 09 '21 at 03:31
2

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.
LeGEC
  • 46,477
  • 5
  • 57
  • 104