Desk Clarity

A field guide to everyday digital problems.

Developer tools / Practical guide

Git Show-Toplevel Resolves a Symlink: Choose Logical or Physical Paths

Avoid assuming that Git preserves the shell path spelling used to enter a checkout.

By ON Lab2 min readSources checked November 10, 2026 (KST)

Git repository paths and a shell logical working directory can use different spellings for the same location. Decide whether a script needs the physical repository root or the logical path the user entered before combining paths.

Reproduce the boundary in a disposable fixture

Avoid assuming that Git preserves the shell path spelling used to enter a checkout.

Use a new empty temporary directory for this fixture. The example creates only local test data. Commands marked with example.invalid do not identify a real service.

git init -q real-repo
ln -s real-repo alias-repo
cd alias-repo
printf 'logical: %s
' "$PWD"
printf 'physical: %s
' "$(pwd -P)"
root=$(git rev-parse --show-toplevel)
printf 'git root: %s
' "$root"
# Compare filesystem identity rather than path strings.
python3 - "$root" "$PWD" <<'CHECK'
import os,sys
assert os.path.samefile(sys.argv[1],sys.argv[2])
print('same directory identity')
CHECK

Observed result

logical: /tmp/fixture/alias-repo
physical: /tmp/fixture/real-repo
git root: /tmp/fixture/real-repo
same directory identity
(Paths shortened for readability.)

What the result means

The shell logical path retains alias-repo, while a physical path resolves that symbolic link. Git reports the top-level working-tree directory and the identity check confirms that it refers to the same directory as the logical shell path. String inequality alone would not show that these are the same location, especially when path normalization or platform case handling also differs.

Choose the correction for your workflow

For reading repository files in a local script, obtain the root once and join repository-relative paths to that anchor, preserving proper quoting. If the application must display the user chosen logical path, keep that separately from a physical filesystem anchor. Do not try to reconstruct an arbitrary symlink path by substituting string prefixes into Git output. In cross-platform automation, pass paths to the runtime that understands their path syntax instead of mixing Windows, WSL and POSIX representations.

Limits and the next check

This fixture runs under Bash on macOS with a symbolic link; it is not a test of every Git version, WSL mount or case-insensitive filesystem. --show-toplevel can fail when there is no working tree, such as a bare repository. A directory path is not a persistent repository identity across moves or mounts. The fixture confirms current filesystem identity without changing global Git configuration or attempting to force a particular output spelling.

Related: Compare the related workflow boundary.

This isolated example was executed on macOS on November 10, 2026 (KST). Available tools: node v26.5.0, git git version 2.39.3 (Apple Git-145), npm 11.17.0, jq jq-1.7.1-apple. Shell fixtures use Bash and repository-path identity uses Python 3. This checks the illustrated contract, not your project, host service or every tool version.

Sources & further reading