Fundamentals 11 min read

Master Git Worktrees: Seamlessly Manage Multiple Branches Without Cloning

This guide explains why traditional stash or multiple‑repo cloning fall short, introduces Git's worktree feature, and walks through adding, listing, removing, and pruning worktrees so developers can handle hotfixes and features in parallel without disrupting their main workflow.

macrozheng
macrozheng
macrozheng
Master Git Worktrees: Seamlessly Manage Multiple Branches Without Cloning

Background

As programmers we often have to switch between feature development and urgent hotfixes, and the usual solutions are either committing unfinished work or using

git stash | git stash pop

. The stash approach has limitations in several scenarios.

Scenarios We Face

Running long tests on the main branch; switching to a hotfix or feature interrupts testing.

Large projects make frequent index switching costly.

Older releases have different settings, causing IDE re‑structure overhead.

Branch switching requires resetting environment variables (dev/qa/prod).

Need to switch to a colleague’s code to reproduce bugs.

Why Not Clone Multiple Repos?

State synchronization between repos is difficult; cherry‑picking across repos is cumbersome.

Each repo repeats the full

.git

history, consuming large disk space.

Managing many repositories for a single project is error‑prone.

git‑worktree

Git has supported worktrees since 2015, but many developers are unaware of it. It allows a single repository to have multiple working directories that can be on different branches without interfering with each other.

Maintain one repo while working on several branches simultaneously, without interference.

Common commands (most frequently used):

<code>git worktree add [-f] [--detach] [--checkout] [--lock] [-b &lt;new-branch&gt;] &lt;path&gt; [&lt;commit-ish&gt;]
git worktree list [--porcelain]
git worktree remove [-f] &lt;worktree&gt;
git worktree prune [-n] [-v] [--expire &lt;expire&gt;]
</code>

Two Git Knowledge Points You Might Miss

When you run

git init

or

git clone

, the repository initially has only one worktree, called the

main worktree

.

In a working directory Git either has a

.git

directory or a

.git

file that points to the actual

.git

folder.

git worktree add

Assume the project root is

amend-crash-demo

:

<code>.
└── amend-crash-demo

1 directory
</code>

Run the following commands:

<code>cd amend-crash-demo
git worktree add ../feature/feature2
</code>

Output:

<code>➜  amend-crash-demo git:(main) git worktree add ../feature/feature2
Preparing worktree (new branch 'feature2')
HEAD is now at 82b8711 add main file
</code>

Directory structure becomes:

<code>.
├── amend-crash-demo
└── feature
    └── feature2

3 directories
</code>

The command creates a new branch

feature2

at the current HEAD and places its working tree at the specified path.

Inside the new worktree there is a

.git

file that points back to the main repository:

<code>gitdir: /Users/rgyb/Documents/projects/amend-crash-demo/.git/worktrees/feature2
</code>

Now you can work on

feature2

(add/commit/pull/push) without affecting the main worktree.

If your branch naming convention includes a slash (e.g.,

feature/JIRAID-Title

), the slash would be interpreted as a directory separator. Use the

-b

option to create the branch while keeping the desired name:

<code>git worktree add -b "hotfix/JIRA234-fix-naming" ../hotfix/JIRA234-fix-naming
</code>

Resulting directory layout:

<code>.
├── amend-crash-demo
├── feature
│   └── feature2
└── hotfix
    └── hotfix
        └── JIRA234-fix-naming

6 directories
</code>

After adding the

-b

flag, the structure is tidy:

<code>.
├── amend-crash-demo
├── feature
│   └── feature2
└── hotfix
    ├── JIRA234-fix-naming
    └── hotfix
        └── JIRA234-fix-naming

7 directories
</code>

git worktree list

From any worktree you can list all existing worktrees:

<code>git worktree list
</code>

Example output shows the main worktree and the additional ones:

<code>/Users/rgyb/Documents/projects/amend-crash-demo               82b8711 [main]
/Users/rgyb/Documents/projects/chore/chore                     8782898 (detached HEAD)
/Users/rgyb/Documents/projects/feature/feature2               82b8711 [feature2]
/Users/rgyb/Documents/projects/hotfix/hotfix/JIRA234-fix-naming 82b8711 [JIRA234-fix-naming]
/Users/rgyb/Documents/projects/hotfix/JIRA234-fix-naming      82b8711 [hotfix/JIRA234-fix-naming]
</code>

git worktree remove

Remove a worktree by name:

<code>git worktree remove hotfix/hotfix/JIRA234-fix-naming
</code>

If the worktree has uncommitted changes, force removal with

-f

:

<code>git worktree remove -f hotfix/JIRA234-fix-naming
</code>

git worktree prune

Use

git worktree prune

as a cleanup fallback to delete stale administrative files left after worktree removal.

Summary

The complete workflow consists of four commands:

<code>git worktree add
git worktree list
git worktree remove
git worktree prune
</code>

Using a single repository with multiple worktrees avoids the drawbacks of cloning many repos and keeps the workflow smooth.

My practice: I keep all feature worktrees under a feature directory and all hotfix worktrees under a hotfix directory to maintain a tidy disk layout.

Soul Questions

Can the main worktree be deleted? Why?

Do you understand how the

.git/worktrees

directory changes when repeatedly creating and deleting worktrees?

software developmentgitversion controlbranch managementworktree
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.