Search code examples
gitpathgit-show

using "git show" with an absolute path


When I run git show HEAD^:file.txt git displays the content of this file at revision HEAD^. But when I run this command with the absolute path, i.e.: git show HEAD^:/home/me/repo/file.txt it fails (exit code 128) with the message

fatal: path '/home/me/repo/file.txt' exists on disk, but not in 'HEAD^'

What would be an elegant way to show my file at revision HEAD^ when all my script has is the full path?


More details:

  • I'm trying to get my git show command to work in a script. All I know is that the current working directory (from which the script is invoked) is in the git repo, but I don't have more clues about the layout of this repo
  • My script should work on all my teammates laptops. I can't make a lot of assumptions on their environment so I cannot use trick like leveraging Perl of Python to convert the absolute path to a local one
  • Overall I'm pretty surprised because other git commands seem to handle absolute paths just fine (for instance git diff -- /home/me/repo/file.txt behaves correctly)

Solution

  • You can use git rev-parse --show-toplevel to find the path to the worktree of any repo, then compute the path of your file relative to this worktree directory.

    filepath=$1
    
    worktree=$(git rev-parse --show-toplevel)
    relpath=$(realpath -s --relative-to="$worktree" "$filepath")
    
    git show -C "$worktree" HEAD^:"$relpath"
    

    If you want to do this with any revision (not just HEAD^), you should either have your script take the revision and the file path as 2 separate arguments, or split the unique argument revision:filepath in 2 parts yourself.


    One element about why <HEAD>:<abspath> doesn't work as you expect:

    <revision>:<path> is a pretty low level notation to point at anything under a tree. If you feed it to git rev-parse or git cat-file for example, you will see the hash/type/content of the blob or subtree that is pointed to:

    $ git rev-parse HEAD:path/to/file
    7bb43a8cc90a1accbb6a167f9ed7c62af3f15b92
    $ git cat-file -t HEAD:path/to/file
    blob
    $ git cat-file -p HEAD:path/to/file
    -- ... content ... --
    

    It is actually not restricted to commits, <revision> can also be any tree :

    $ git rev-parse HEAD:path/to
    eacf32b...
    $ git rev-parse eacf32b:file.txt
    7bb43a8c..
    

    Converting the string <revision>:<path> into the id of a git object is one of the first action that is taken (it is not specific to the git show command for example).

    And this action is implemented by just looking at paths that exist within the git database.