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
    }
}