Public/Invoke-GitCloneBare.ps1
|
<#
.SYNOPSIS Clones a git repository as a bare repo and sets up worktrees and config. .DESCRIPTION Clones <Url> into <DestinationPath>/<RepoName>.git as a bare repository, creates one worktree per entry in -Worktrees, and enforces a set of recommended git config values (long paths, auto branch push, pruning, etc). .EXAMPLE Invoke-GitCloneBare https://github.com/org/myrepo.git .EXAMPLE Invoke-GitCloneBare https://github.com/org/myrepo.git -Worktrees main,dev -Destination C:\repos #> function Invoke-GitCloneBare { [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory, Position = 0)] [ValidateNotNullOrEmpty()] [string] $Url, [Parameter(Position = 1)] [string[]] $Worktrees = @('master'), [Parameter()] [string] $Destination = (Get-Location).Path, [Parameter()] [switch] $SkipSafeDirectory, [Parameter()] [switch] $SkipGitConfig ) # Derive a repo name from the URL (strip .git suffix for the folder name basis) $repoName = [System.IO.Path]::GetFileNameWithoutExtension($Url.TrimEnd('/')) $bareDir = Join-Path $Destination "$repoName.git" if ($PSCmdlet.ShouldProcess($bareDir, "Clone bare repository from $Url")) { Write-Verbose "Cloning $Url -> $bareDir" git clone --bare $Url $bareDir if ($LASTEXITCODE -ne 0) { throw "git clone failed for: $Url" } # Fix the fetch refspec so `git fetch` retrieves remote-tracking refs git -C $bareDir config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*' if ($LASTEXITCODE -ne 0) { throw "Failed to set fetch refspec in $bareDir" } # Initial fetch to populate remotes/origin/* git -C $bareDir fetch origin if ($LASTEXITCODE -ne 0) { throw "git fetch failed in $bareDir" } if (-not $SkipGitConfig) { Write-Verbose "Applying recommended git config" git -C $bareDir config core.longpaths true git -C $bareDir config push.autoSetupRemote true git -C $bareDir config fetch.prune true } if (-not $SkipSafeDirectory) { Write-Verbose "Adding $bareDir to global git safe.directory" git config --global --add safe.directory $bareDir } # Create requested worktrees foreach ($wt in $Worktrees) { $wtPath = Join-Path $Destination $wt # Determine whether the branch exists locally or only on origin $localBranch = git -C $bareDir branch --list $wt 2>$null $remoteBranch = git -C $bareDir branch -r --list "origin/$wt" 2>$null if ($localBranch) { Write-Verbose "Adding worktree '$wt' from local branch" git -C $bareDir worktree add $wtPath $wt } elseif ($remoteBranch) { Write-Verbose "Adding worktree '$wt' tracking origin/$wt" git -C $bareDir worktree add --track -b $wt $wtPath "origin/$wt" } else { Write-Warning "Branch '$wt' not found locally or on origin — skipping worktree" continue } if ($LASTEXITCODE -ne 0) { Write-Warning "Failed to create worktree for branch '$wt'" } else { if (-not $SkipSafeDirectory) { git config --global --add safe.directory $wtPath } Write-Host "Worktree created: $wtPath" -ForegroundColor Green } } Write-Host "Bare clone ready: $bareDir" -ForegroundColor Cyan return $bareDir } } |