Last active
January 16, 2025 16:39
-
-
Save electricessence/0a48e283f585837b0c39ff6762b9c804 to your computer and use it in GitHub Desktop.
Git-Worktree Function for Powershell (add to $PROFILE)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Git-Worktree-List { | |
git worktree list | |
} | |
function Copy-UntrackedFiles { | |
param ( | |
[string]$SourcePath, | |
[string]$TargetPath | |
) | |
Write-Host '' | |
Write-Host $SourcePath | |
# Check for changed, untracked and ignored files | |
$changes = git -C $SourcePath status --porcelain --ignored | ForEach-Object { $_.Substring(3) } | |
if (-not $changes) { | |
Write-Host "No uncommitted or untracked files." -ForegroundColor Green | |
return | |
} | |
# Get the count of untracked and modified files | |
$count = $changes.Count | |
Write-Host "Copying [$count] uncommitted or untracked folders/files." -ForegroundColor Yellow | |
# Copy untracked and modified files | |
$changes | ForEach-Object -Parallel { | |
$sourceFile = Join-Path $using:SourcePath $_ | |
$targetFile = Join-Path $using:TargetPath $_ | |
$targetDir = Split-Path -Parent $targetFile | |
if (-not (Test-Path -Path $targetDir)) { | |
New-Item -ItemType Directory -Path $targetDir -Force | Out-Null | |
} | |
Copy-Item -Path $sourceFile -Destination $targetFile -Force | |
Write-Host $_ # Comment this out if you don't want to see the copied folders/files. | |
} | |
Write-Host "Done." -ForegroundColor Green | |
# Recurse through each submodule | |
git -C $SourcePath submodule --quiet foreach 'echo $sm_path' | ForEach-Object { | |
$sp = Join-Path $SourcePath $_ | |
$tp = Join-Path $TargetPath $_ | |
Copy-UntrackedFiles $sp $tp | |
} | |
} | |
function Git-Worktree { | |
[CmdletBinding()] | |
Param( | |
[Parameter(Mandatory=$true)] | |
[string]$WorktreeName, | |
[Parameter(Mandatory=$false)] | |
[switch]$Mirror, | |
[Parameter(Mandatory=$false)] | |
[switch]$Delete, | |
[Parameter(Mandatory=$false)] | |
[switch]$Force | |
) | |
# Find the root of the git repository | |
$dotGitpath = git rev-parse --path-format=absolute --git-common-dir #includes '.git' at the end | |
$gitRoot = Split-Path -Path $dotGitpath -Parent | |
if ($LASTEXITCODE -ne 0 -or -not $gitRoot) { | |
Write-Error "Not inside a git repository or unable to find the git repository root." | |
return | |
} | |
# Get the branch name that the root is on. | |
$currentRootBranch = git -C $gitRoot rev-parse --abbrev-ref HEAD | |
if($WorktreeName -eq $currentRootBranch) { | |
Write-Host "The root is already on branch '$WorktreeName'." | |
# Check if we are on the root. If not, switch to the root. | |
if ($PWD.Path -ne $gitRoot) { | |
Set-Location -Path $gitRoot | |
Write-Host '===> Switched to the root.' -ForegroundColor Yellow | |
} | |
return | |
} | |
# Before potentially switching from one worktree to another, get the current worktree path | |
$currentWorktreePath = git rev-parse --show-toplevel | |
$currentWorktreePath = $currentWorktreePath -replace '\\', '/' | |
# Create the worktree path | |
$worktreePath = Join-Path $gitRoot '.git' '.wt' $WorktreeName | |
# Normalize the path to compare with existing worktree paths | |
$normalizedWorktreePath = $worktreePath -replace '\\', '/' | |
if ($Delete) { | |
if ($currentWorktreePath -like $normalizedWorktreePath) { | |
Set-Location -Path $gitRoot | |
Write-Host '===> Switched to root' -ForegroundColor Yellow | |
} | |
try { | |
if (Test-Path -Path $worktreePath) { | |
if($Force) { | |
git worktree remove $worktreePath --force | |
} | |
else { | |
$changes = git -C $worktreePath status --porcelain | |
if ($changes) { | |
Write-Host 'Uncommitted changes found.' -ForegroundColor Red | |
Write-Host 'Use -Force if you are sure you want to delete.' -ForegroundColor Red | |
return | |
} | |
git worktree remove $worktreePath | |
} | |
} | |
else { | |
if($Force) { | |
git worktree remove $WorktreeName --force | |
} | |
else { | |
$changes = git -C $worktreePath status --porcelain | |
if ($changes) { | |
Write-Host 'Uncommitted changes found.' -ForegroundColor Red | |
Write-Host 'Use -Force if you are sure you want to delete.' -ForegroundColor Red | |
return | |
} | |
git worktree remove $WorktreeName | |
} | |
} | |
if ($LASTEXITCODE -ne 0) { | |
Write-Host 'Could not remove.' -ForegroundColor Red | |
return | |
} | |
Write-Host "Worktree '$WorktreeName' has been removed." -ForegroundColor Yellow | |
} catch { | |
Write-Error "Error: Failed to remove worktree for branch '$WorktreeName' at '$worktreePath'." | |
} | |
return | |
} | |
# Check to see if the current worktree is already on the branch | |
$activeBranch = git rev-parse --abbrev-ref HEAD | |
if ($activeBranch -like $WorktreeName) { | |
Write-Host "Already on branch: $activeBranch" | |
return | |
} | |
# Get the list of existing worktrees | |
$existingWorktreePaths = git worktree list --porcelain | Where-Object { $_ -match '^worktree (.+)' } | ForEach-Object { | |
$Matches[1] -replace '\\', '/' | |
} | |
$switched = $false | |
foreach ($existingWorktreePath in $existingWorktreePaths) { | |
# Matching worktree? | |
if ($existingWorktreePath -like $normalizedWorktreePath) { | |
Set-Location -Path $existingWorktreePath | |
Write-Host "===> Switched to worktree: $WorktreeName" -ForegroundColor Yellow | |
$worktreePath = $existingWorktreePath | |
$switched = $true | |
break | |
} | |
$wtBranch = git -C $existingWorktreePath rev-parse --abbrev-ref HEAD | |
if ($LASTEXITCODE -ne 0) { | |
continue | |
} | |
# Matching branch? | |
if($wtBranch -like $WorktreeName) { | |
Write-Host "Found branch '$WorktreeName' in worktree:" | |
Set-Location -Path $existingWorktreePath | |
Write-Host "===> Switched to: $existingWorktreePath" -ForegroundColor Yellow | |
$worktreePath = $existingWorktreePath | |
return | |
} | |
} | |
if (-not $switched) { | |
try { | |
git worktree add $WorktreePath | |
if ($LASTEXITCODE -ne 0) { | |
throw "Could not add new worktree for branch '$WorktreeName' at '$WorktreePath'." | |
} | |
Write-Host "Worktree for branch '$WorktreeName' successfully created:" -ForegroundColor Yellow | |
Write-Host $worktreePath -ForegroundColor Yellow | |
Set-Location -Path $worktreePath | |
} | |
catch { | |
Write-Host "Error: Failed to create worktree for branch '$WorktreeName' and path '$worktreePath'." -ForegroundColor Red | |
return | |
} | |
} | |
if ($Mirror) { | |
Copy-UntrackedFiles $currentWorktreePath $worktreePath -Verbose | |
} | |
} | |
Set-Alias worktree Git-Worktree | |
Set-Alias worktree-list Git-Worktree-List |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment