With Git worktrees, a Git repository can support multiple working trees. This allows you to check out more than one branch at a time.
Main Vs. Linked Worktrees
Git supports two kinds of worktrees:
- Main worktree: Created when you run
git clone <some_repo>orgit init, as long as the repository is not a Bare Repository. - Linked worktree: Created by running
git worktree add <worktree_path>.
A repository has one main worktree if it is not a bare repository, and zero or more linked worktrees.
Worktree Commands
Create Worktrees
Use git worktree add to create a new linked worktree:
git worktree add <worktree_path>The <worktree_path> can point almost anywhere:
- Another folder on the same computer
- A mounted drive
- A portable device
This command will either create a new branch (with the same name as
<worktree_path>) or, if that branch already exists, check out the existing branch in the newly created worktree.
You can also explicitly specify the branch to check out in the new linked worktree:
git worktree add <worktree_path> <branch_name>However, this works only if the following conditions are met:
<branch_name>already exists.- The branch is not currently checked out in another worktree.
Otherwise, Git will return an error.
List Worktrees
Run this from inside any worktree to see all worktrees connected to the same repository:
git worktree listUse --porcelain when you want a more structured output. It prints each worktree attribute on its own line:
git worktree list --porcelainRemove Worktrees
Use git worktree remove to delete a linked worktree and its files:
git worktree remove <worktree_path>This removes the working tree folder, but it does not delete the branch that was created or checked out for that worktree.
Fiddling with Worktree Commands
We will use this dummy repo as a sandbox for testing worktree commands and observing how Git behaves.
The repository has two useful details for this walkthrough:
- The default branch name is
master. - The default branch,
master, contains only a singleREADMEfile.
Default Clone
Start with a normal clone:
git clone git@github.com:ishahroz/Hello-World.gitMove into the repository and list the files:
cd Hello-World
ls
# .git READMENow list the worktrees:
git worktree list --porcelain
# worktree ~/Hello-World
# HEAD XXXXa60b01f91b31XXXXX5a4e4d4e80d8edXXXX
# branch refs/heads/masterThe normal clone creates the main worktree, checked out on the master branch.
Now create a linked worktree:
git worktree add Hello-Test1Because the path is Hello-Test1, Git creates:
- A new folder named
Hello-Test1 - A new branch named
Hello-Test1
Since we ran the command from inside the main repository, the new worktree is created inside the main repository folder:
ls
# .git Hello-Test1 READMEList the worktrees again:
git worktree list --porcelain
# worktree ~/Hello-World
# HEAD XXXXa60b01f91b31XXXXX5a4e4d4e80d8edXXXX
# branch refs/heads/master
# worktree ~/Hello-World/Hello-Test1
# HEAD XXXXa60b01f91b31XXXXX5a4e4d4e80d8edXXXX
# branch refs/heads/Hello-Test1Move into the linked worktree and check its status:
cd Hello-Test1 && git status
# On branch Hello-Test1
# nothing to commit, working tree cleanThe linked worktree is on the new Hello-Test1 branch that Git created for it.
Now go back to the main repository and check its status:
cd .. && git statusOn branch master
Your branch is up to date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
Hello-Test1/
nothing added to commit but untracked files present (use "git add" to track)Notice two things:
- The main worktree is still on
master. - The
Hello-Test1/folder appears as untracked content because it was created inside the main repository.
For this reason, linked worktrees are usually easier to manage when they live outside the main repository folder.
Remove the linked worktree:
git worktree remove Hello-Test1After removal, the folder is gone and only the main worktree remains:
ls
# .git README
git worktree list --porcelain
# worktree ~/Hello-World
# HEAD XXXXa60b01f91b31XXXXX5a4e4d4e80d8edXXXX
# branch refs/heads/masterNow create the same linked worktree one level outside the main repository:
git worktree add ../Hello-Test1ls
# .git README
git worktree list --porcelain
# worktree ~/Hello-World
# HEAD XXXXa60b01f91b31XXXXX5a4e4d4e80d8edXXXX
# branch refs/heads/master
# worktree ~/Hello-Test1
# HEAD XXXXa60b01f91b31XXXXX5a4e4d4e80d8edXXXX
# branch refs/heads/Hello-Test1This time, the main repository stays clean because the linked worktree lives beside it, not inside it.
cd ../Hello-Test1 && git status
# On branch Hello-Test1
# nothing to commit, working tree clean
ls
# .git READMEInside the linked worktree, .git is not a metadata folder. It is a file that points back to the main repository’s Git metadata:
cat .git
# gitdir: ~/Hello-World/.git/worktrees/Hello-Test1That reference is how Git connects this linked worktree back to the Hello-World repository.
You can still list all worktrees from inside the linked worktree:
git worktree list --porcelain
# worktree ~/Hello-World
# HEAD XXXXa60b01f91b31XXXXX5a4e4d4e80d8edXXXX
# branch refs/heads/master
# worktree ~/Hello-Test1
# HEAD XXXXa60b01f91b31XXXXX5a4e4d4e80d8edXXXX
# branch refs/heads/Hello-Test1Create a new file called TestFile.md and commit it (don’t worry—we’ll come back later to why we created this file):
touch TestFile.md
git add . && git commit -m "Add TestFile.md"Remove the linked worktree again, then check the main repository status:
git worktree remove Hello-Test1
cd ../Hello-World && git status
# On branch master
# Your branch is up to date with 'origin/master'.
# nothing to commit, working tree clean
git worktree list --porcelain
# worktree ~/Hello-World
# HEAD XXXXa60b01f91b31XXXXX5a4e4d4e80d8edXXXX
# branch refs/heads/masterRemoving a linked worktree deletes the working tree files, but it does not delete the branch.
git branch
# master
# Hello-Test1Let’s check out the Hello-Test1 branch again:
git checkout Hello-Test1
ls
# .git README TestFile.mdWe can see that TestFile.md, which we committed earlier, is still present in the branch. The branch itself was not deleted.
This means we can still create a new working tree and check out the Hello-Test1 branch there, even if the previous working tree with the same name was removed.
Bare Repository (Recommended Way)
Create a directory with the same name as the repository:
mkdir Hello-World && cd Hello-WorldClone the dummy repository as a Bare Repository:
git clone --bare git@github.com:ishahroz/Hello-World.git .gitThis command downloads only the Git metadata and creates only the .git folder:
ls
# .git
ls .git
# config description HEAD hooks info objects packed-refs refsThis .git directory is the same folder that Git creates when you perform a normal clone, except that no working files are checked out.
Let’s print the worktrees:
git worktree list --porcelain
# worktree ~/Hello-World
# bareAt this point, there is not a single linked worktree.
If we try to run git status or git checkout, Git will throw an error because these commands must be executed within a valid worktree:
git status
# fatal: this operation must be run in a work tree
git checkout master
# fatal: this operation must be run in a work treeIt is time to create a worktree with the path master and the master branch checked out:
git worktree add masterLet’s list the folder contents again. You can now see a new folder named master, which is a linked worktree:
ls
# .git masterLet’s move to the master folder and run git status again:
cd master
git status
# On branch master
# nothing to commit, working tree cleanSince we are now inside a valid worktree, Git commands work as expected.
Let’s print the worktree list again:
cd .. && git worktree list --porcelain
# worktree ~/Hello-World
# bare
# worktree ~/Hello-World/master
# HEAD xxxxa60b01f91b314f5xxxxa4e4d4e80d8edxxxx
# branch refs/heads/masterWe can now see the linked worktree associated with the master branch.
We can even remove the worktree and create it again later:
git worktree remove master
ls
# .git