Introduction
When working on a project, many times we have to switch to a different branch to help a colleague, fix a bug, or to work on another feature (because of a change in priorities or blocks).
In these situations, we have some options:
Clone again to another folder: This was the option that I used up until some time ago, but if you are working on a big code base, it may take some time to download the remote repository and it will use more space in the disk because you will end up with one copy of the repository for each branch;
Stash or commit changes and checkout the other branch: This is ok, but it takes more steps and doesn’t allow for multiple branches checked out in parallel;
Add a new working tree: This is what I prefer to do because I can have only one local repository shared between the branches.
In this post, I’ll show how to use Git working trees to make those branch switches easier.
Basics of how a Git repository works
When we use the git clone
command, Git creates two things in the destination: a working tree and a copy of the remote repository (in a .git
folder inside the working tree directory).
βΉοΈ
git clone --bare
clones only the repository in the root folder, without the working tree.
Repository
The Git repository is a structured directory where Git stores its objects, branches, and other components used to control the versions of our files.
Working tree
The working tree is where the actual files we work on are stored. When we use git checkout
, Git changes all the files in the working tree to reflect the branch we are working on.
Example
git clone https://github.com/dgenezini/MyProject.git MyProject
Within the repository, there are a lot of files and folders, but, for the scope of this post, these are the most important ones:
- objects = Directory storing blobs (files), trees (directories), and commits;
- refs = Directory storing pointers to the commits that are the heads of each branch or tag in the repository;
- HEAD = File pointing to the branch or tag that is checked in in the working tree;
- index = File used to control what is staged.
Why use Git Worktree?
Using the git worktree
command, we can create multiple working trees pointing to the same local repository, sharing most of the repository between them.
Instead of a .git
directory, the additional working trees have a .git
file with a pointer to a working tree folder inside the local repository.
In the working tree folder, we have the Git components that are not shared with the other working trees. Note that most of the repository, including the objects
folder (files, directories and commits), is shared.
These are the main components in the working tree folders:
- HEAD = File pointing to the branch that is checked out in the working tree;
- index = File used to control what is staged in the working tree;
- commondir = File pointing to the local Git repository.
Using Git Worktree
I like to have all the working trees as subfolders, so I start by creating a folder with the name of the repository and cloning the default branch to a folder with the name of the branch (in my case, main
).
mkdir MyProject && cd MyProject
git clone https://github.com/dgenezini/MyProject.git main
This is the result I want:
MyProject/ <-- My repo name
βββ main <-- Branch name
βββ .git <-- Local repository
βββ README.md
βΉοΈ Some people use
git clone --bare
to pull the repository without a working tree, but the--bare
option does not map the branches to their remote origins, so I prefer to clone my default branch (in this example, themain
branch), because it is a long living branch, that way I don’t have to manually map the remote origins for every working tree created.
Adding a working tree
Inside the main
directory, use the git worktree add
command:
git worktree add [path] [branch]
Example:
cd main
git worktree add ../featureA featureA
This is will be the result:
MyProject/ <-- My repo name
βββ featureA <-- Branch name
βΒ Β βββ .git <-- File pointing to ../main/.git
βΒ Β βββ README.md
βββ main <-- Branch name
Β Β βββ .git <-- Git local repository
βββ README.md
βΉοΈ You can use the
git worktree add
and other Git commands inside any working tree directory.
β οΈ If you are using windows, change the slash on the path from
../featureA
to..\featureA
.
Changing to a working tree
Just change the directory you are working on:
cd ../featureA
Removing a working tree
Inside the main
directory, use the git worktree remove
command:
git worktree remove [branch]
Example:
git worktree remove featureA
or just delete the working tree folder (featureA
in this example), then use git worktree prune
to clean the invalid working trees.
Git Worktrees extension for Visual Studio Code
Git Worktrees is a free extension for Visual Studio Code that helps us work with Git working trees.
Adding a working tree
Open the Command Palette (Ctrl+Shift+P
) and type worktree add
Changing to a working tree
Open the Command Palette (Ctrl+Shift+P
), search for worktree list
and select Git Worktree: List
.
Select the branch you want to work on and VS Code will open another window in that working tree.
Removing a working tree
Open the Command Palette (Ctrl+Shift+P
), search for worktree remove
and select Git Worktree: Remove
.
Select the branch you want to remove.
β οΈ You can’t remove the working tree you have currently open in VS Code.
Changing the working tree from Visual Studio 2022
Visual Studio is my favorite IDE for working with .NET, so it is important that I can change easily between working trees from within it.
Once you open the project from the working tree for the first time, Visual Studio will keep track of the working tree as a repository in the status bar. Just change it from there and it will load the project.