A worktree is neither a symlink nor a clone
Files are copies, the repository is shared
Anyone who starts using worktrees hits the same question once. Is this a symlink, or is it fully separate like a clone?
Neither. The working files sit on disk separately, while the repository is shared.
.git is not a directory
Open .git inside a linked worktree and you get one line of text. Not a symlink either.
$ file ../wt/.git
../wt/.git: ASCII text
$ cat ../wt/.git
gitdir: /Users/me/repo/.git/worktrees/wt
Git reads that line and walks back to the main repository. That single line is the whole link.
What is shared and what is not
No need to guess. Ask git from inside the worktree.
$ git rev-parse --git-path HEAD
/Users/me/repo/.git/worktrees/wt/HEAD
$ git rev-parse --git-path index
/Users/me/repo/.git/worktrees/wt/index
$ git rev-parse --git-path refs/heads
/Users/me/repo/.git/refs/heads
$ git rev-parse --git-path objects
/Users/me/repo/.git/objects
HEAD and index resolve to the worktree's own directory. refs and objects resolve to the main .git. The docs call the first $GIT_DIR and the second $GIT_COMMON_DIR. The only exceptions are refs/bisect and refs/worktree, which stay per-worktree.
Separate HEAD and index is exactly why two worktrees can sit on different branches at once.
Commits cross over without a fetch
One set of refs and objects makes this inevitable.
# in the worktree
$ git commit -m "sentinel"
[wt/demo c48eba9] sentinel
# in the main checkout, no fetch
$ git log wt/demo --oneline -1
c48eba9 sentinel
Two clones would need a push and a fetch first. Most of the day-to-day difference comes from this.
The disk savings claim is only half true
"It saves space compared to a clone" gets repeated a lot, but against a local clone it does not hold.
On the same filesystem git clone hardlinks pack files instead of copying them.
$ stat -f '%l links %N' repo/.git/objects/pack/*.pack
2 links .../pack-1eb982d2.pack
2 links .../clone/.git/.../pack-1eb982d2.pack # same inode
Pass --no-hardlinks and the link count drops to 1, which confirms hardlinking is the default.
In this repository (a 439MB .git), measuring both paths in a single du invocation shows the clone adding 1.2MB. Measure it separately and you see 365MB, but that is the same inode counted twice.
Working files get copied either way. 431MB here.
So against a local clone, the win is not disk. It is one set of refs and objects.
Deleting the folder leaves git holding on
This is the trap people hit most. rm -rf on the directory leaves the registration behind.
$ rm -rf ../wt
$ git worktree list
/private/tmp/wt c48eba9 [wt/demo] prunable
$ git worktree add ../wt2 wt/demo
fatal: 'wt/demo' is already checked out at '/private/tmp/wt'
$ git branch -d wt/demo
error: Cannot delete branch 'wt/demo' checked out at '/private/tmp/wt'
The directory is gone, yet the branch can be neither re-checked-out nor deleted. git worktree prune clears it. Use git worktree remove and it never comes up.
gitignored files do not come along
A new worktree checks out only what git tracks. In a Rails project that leaves out .env, config/master.key, node_modules/, and the SQLite files under storage/. When the server refuses to boot in a fresh worktree, this is usually why.
Measured on git 2.39.5 (Apple Git-154), macOS.
How It Works
`git worktree add ../wt -b feature` checks out tracked files and drops a `.git` text file beside them
The `gitdir:` line inside `.git` points at `.git/worktrees/<name>/` in the main repository
HEAD and index come from that directory (`$GIT_DIR`); refs and objects come from the main `.git` (`$GIT_COMMON_DIR`)
A commit writes objects and refs straight into the main repository, visible from the other side with no fetch
`git worktree remove ../wt` deletes the files and the registration together; deleting only the folder leaves a `prune` to do
Pros
- ✓ One set of refs and objects means commits cross over with no fetch, and a single fetch serves every worktree
- ✓ Git refuses to check out one branch in two places, so you cannot accidentally diverge
Cons
- ✗ Gitignored files do not follow. `.env`, master.key and node_modules have to be supplied every time
- ✗ Deleting only the folder leaves the registration behind, blocking both branch deletion and re-checkout
- ✗ Working files are real copies, so disk grows accordingly; there is no space win over a local clone