'How to get the path of a file relative to the Git repository root?

Example:

$ cd lib
$ git absolute-path test.c # how to do this?
lib/test.c


Solution 1:[1]

Pasting the following into your bash terminal will work, regardless of whether "test.c" currently exists or not. You can copy the git-absolute-path function into your .bashrc file for future convenience.

git-absolute-path () {
    fullpath=$([[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}")
    gitroot="$(git rev-parse --show-toplevel)" || return 1
    [[ "$fullpath" =~ "$gitroot" ]] && echo "${fullpath/$gitroot\//}"
}

git-absolute-path test.c

Solution 2:[2]

In order to get the path of the current directory, relative to the git root, I ended up doing this:

if gitroot=$(git rev-parse --show-toplevel 2>/dev/null); then
    directory=$(realpath --relative-to="$gitroot" .)
fi

(I'm assuming Bash and I do not know how portable this is.)

Solution 3:[3]

I would like to improve on @gmatht's answer by making it work in an corner-case, by resolving the git root differently:

git-absolute-path () {
    fullpath=$([[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}")
    gitroot="$(echo $(cd $(git rev-parse --show-cdup) .; pwd))" || return 1
    [[ "$fullpath" =~ "$gitroot" ]] && echo "${fullpath/$gitroot\//}"
}

The corner-case I'm referring to is when your git repo is in /tmp and you're on Windows. /tmp seems to be a special case: it refers to your Windows user's temp folder i.e. C:/Users/<user>/AppData/Local/Temp. (Not sure "how" it refers to that, it doesn't appear to be a symlink. Like I said, a special case). In any case, fullpath can be like /tmp/your-temp-repo but gitroot can be like C:/Users/<user>/AppData/Local/Temp/your-temp-repo but then they're not equal and git-absolute-path returns nothing incorrectly.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 YoungFrog
Solution 3 JBSnorro