Git Worktree
One useful but often overlooked feature of Git is git worktree
.
This feature allows you to maintain separate working directories for different branches of your Git repository at the same time.
This can help keep your development environment organized and efficient, particularly when working on multiple features or projects simultaneously.
With git worktree
, you no longer need to stash or commit your current changes before switching branches. However, managing multiple worktrees can be cumbersome. This is where my PowerShell script, inspired by Bill Mill's approach to using git worktrees, comes into play.
What the script does
The PowerShell script, named Git-Worktree
simplifies working with git worktree
by automating the creation, deletion, and navigation of worktrees directly from your PowerShell terminal. The tool simplifies the process of setting up worktrees by providing easy-to-use commands.
For your convenience, aliases
worktree
andgw
are also set up to call theGit-Worktree
function
Here's a breakdown of its capabilities:
-
Listing worktrees: Running
worktree list
provides a list of all existing worktrees within your Git repository. -
Creating worktrees: Running
worktree mybranch
, the script allows you to create new worktrees for specific branches. The first parameter should specify the branch name. If the branch does not exist it will be created. If the branch already exists, a worktree will be created for it. If both the branch and worktree already exist, you will be switched to that worktree. All worktrees will be created inside.worktrees
repository for keeping your project directory clean. -
Deleting worktrees: The script can delete existing worktrees. The
-Delete
parameter is used to initiate the deletion process. -
Switching to the
master
branch: Usingworktree master
switches you to the root directory of the repository for working with themaster
branch.
Using the script
Here are some examples of how you can leverage the script in your workflow:
1. Creating/switching a worktree for a branch
To create a new worktree for a branch or switch to existing, run:
worktree bugfix-header-typo
worktree task-123
By default every new branch is checked-out from master
branch, not current. If you want to checkout from current branch, you need to add -Current
parameter:
worktree task-123 -Current
2. Deleting a worktree
To delete a worktree when you're done with the branch:
worktree bugfix-header-typo -Delete
worktree task-123 -Delete
3. Switching to the root directory (master)
To navigate back to the main repo worktree for the master
branch:
worktree master
4. Listing existing worktrees
To list all your current worktrees:
worktree list
Summary
By using this script, you simplify the context-switching process that typically hampers productivity when you need to work on multiple branches. The script also helps you maintain a clean work environment by organizing worktrees systematically.
The Git-Worktree
script handles the boilerplate of worktree management, so you can retain your focus on what really matters: coding and contributing valuable features to your projects.
To get started, integrate the script into your PowerShell $profile
file (run notepad $profile
) and use the aliased commands worktree
or gw
to take your branch management to the next level. Enjoy seamless multitasking across your Git repositories and say goodbye to the old hassles of branch switching.
The script
function Git-Worktree {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string]$BranchName,
[Parameter(Mandatory=$false)]
[switch]$Delete,
[Parameter(Mandatory=$false)]
[switch]$Current
)
function Resolve-GitRoot {
$dotGitpath = git rev-parse --path-format=absolute --git-common-dir #includes '.git' at the end
$gitTopLevel = Split-Path -Path $dotGitpath -Parent
if ($LASTEXITCODE -eq 0 -and $gitTopLevel) {
return $gitTopLevel
} else {
Write-Error "Not inside a git repository or unable to find the git repository root."
throw "Not a git repository (or any of the parent directories)"
}
}
try {
$gitRoot = Resolve-GitRoot
}
catch {
Write-Error "Error: $($_.Exception.Message)"
}
if ($BranchName -eq 'list') {
git worktree list
return;
}
if($BranchName -eq 'master') {
Write-Output "Switching to the root of the repository for main branch 'master' at '$gitRoot'"
Set-Location -Path $gitRoot
return
}
function Get-GitWorktrees {
$worktrees = git worktree list --porcelain | Where-Object { $_ -match '^worktree (.+)' } | ForEach-Object {
$Matches[1].Replace('\', '/')
}
return $worktrees
}
$existingWorktreePaths = Get-GitWorktrees
$worktreePath = Join-Path $gitRoot '.worktrees' $BranchName
if ($Delete) {
if (Test-Path -Path $worktreePath) {
try {
Write-Output "Switching to root"
Set-Location -Path $gitRoot
git worktree remove $worktreePath
if ($LASTEXITCODE -ne 0) {
throw "Could not remove worktree"
}
if (Test-Path -Path $worktreePath){
Remove-Item $worktreePath -Recurse
Write-Host "'$worktreePath' folder removed successfully."
}
Write-Output "Worktree for branch '$BranchName' has been removed."
} catch {
Write-Error "Error: Failed to remove worktree for branch '$BranchName' at '$worktreePath'."
}
} else {
Write-Output "No worktree for branch '$BranchName' at '$worktreePath' exists to remove."
}
return
}
$normalizedWorktreePath = $worktreePath.Replace('\', '/')
if ($existingWorktreePaths -like "*$normalizedWorktreePath*") {
Write-Output "Worktree for branch '$BranchName' already exists at '$worktreePath'. Switching to that directory."
Set-Location -Path $worktreePath
return
}
function Create-GitWorktree {
param (
[string]$BranchName,
[string]$WorktreePath,
[switch]$Current
)
git show-ref --quiet -- "refs/heads/$BranchName"
$branchExists = $LASTEXITCODE -eq 0
if (-not $branchExists) {
git ls-remote --quiet --exit-code origin "refs/heads/$BranchName" > $null
$branchExists = $LASTEXITCODE -eq 0
}
if ($branchExists) {
git worktree add $worktreePath $branchName
} else {
$checkoutBranch = $Current ? $null : ($branchExists ? $BranchName : 'master')
git worktree add -b $branchName $worktreePath $checkoutBranch
}
if ($LASTEXITCODE -ne 0) {
throw "Could not add new worktree for branch '$BranchName' at '$worktreePath'."
}
}
try {
Create-GitWorktree -BranchName $BranchName -WorktreePath $worktreePath -Current $Current
Write-Output "Worktree for branch '$BranchName' successfully created at '$worktreePath'."
Set-Location -Path $worktreePath
}
catch {
Write-Error "Error: Failed to create worktree for branch '$BranchName' and path '$worktreePath'."
}
}
Set-Alias worktree Git-Worktree
Set-Alias gw Git-Worktree
Top comments (0)