functions/Set-GitBranch.ps1
function Set-GitBranch { <# .SYNOPSIS Checks out the specified branches for the specified repository. .DESCRIPTION Checks out the specified branches for the specified repository. An optional script block may be passed in containing git commands to be executed against each specified branch after it has been checked out. The script will be split by semicolon (default) or a user-defined separator, and each command run in turn. The output of the command has three components: header (in the form "<RepoName> | <BranchName>"), command, and results. Each section can be directed to the host, the pipeline, or suppressed. By default, the header, command, and results are only written when GitScript is used. .PARAMETER Repo The name of a git repository, or the path or a substring of the path of a repository directory or any of its subdirectories or files. If the Repo parameter is omitted, the current repository will be used if currently inside a repository; otherwise, nothing is returned. For examples of using the Repo parameter, refer to the help text for Get-GitRepo. .PARAMETER BranchName The names of the branches to be checked out. Wildcard characters are allowed. The pattern will match against existing branches in the specified repository. A warning will be generated for any values that do not match the name of an existing branch. .PARAMETER Force Checks out the specified branches even when a matching filter in $Powdrgit.BranchExcludes exists. .PARAMETER SetLocation Sets the working directory to the top-level directory of the specified repository. In the case where multiple Repo values are passed in, the location will reflect the repository that was specified last. .PARAMETER GitScript Used to provide git commands that will be executed against the specified branches. The default command separator is the semicolon (";"). An alternative separator can be specified with the GitScriptSeparator parameter or by setting $Powdrgit.DefaultGitScriptSeparator. A literal separator character can be specified with a backtick escape e.g. "`;". .PARAMETER HeaderOut Used to suppress, direct, or color the background of the header output. When the value is 'None', the header will be suppressed. When the value is 'Pipe', the header will be sent to the pipeline. When the value is a standard Powershell color, the header will be written to the host with a background in that color. When GitScript is provided, default is 'DarkGray'; otherwise it is 'None'. .PARAMETER CommandOut Used to suppress, direct, or color the command output. When the value is 'None', the command will be suppressed. When the value is 'Pipe', the command will be sent to the pipeline. When the value is a standard Powershell color, the command will be written to the host in that color. Default is 'Green'. .PARAMETER ResultsOut Used to suppress, direct, or color the results output. When the value is 'None', the results will be suppressed. When the value is 'Pipe', the results will be sent to the pipeline. When the value is 'Native', the results will be written to the host using both native git colors and git output streams. When the value is a standard Powershell color, the results will be written to the host in that color. Default is 'DarkGray'. .PARAMETER GitScriptSeparator An alternative separator for splitting commands passed in to the GitScript parameter. If an empty string ('') or $null is passed in, no splitting will occur i.e. the script will execute as a single statement. The default separator is a semicolon (";"). An empty of null value will treat the GitScript value as a single statement. The GitScriptSeparator parameter will override the value in $Powdrgit.DefaultGitScriptSeparator. .EXAMPLE ## Check out a branch from outside a repository without naming a repository ## PS C:\> $Powdrgit.Path = 'C:\PowdrgitExamples\MyToolbox;C:\PowdrgitExamples\Project1' # to ensure the repository paths are defined PS C:\> Set-GitBranch -BranchName main # Nothing was returned because the current location is not inside a repository. .EXAMPLE ## Call from outside a repository for non-existent repository ## PS C:\> $Powdrgit.Path = 'C:\PowdrgitExamples\MyToolbox;C:\PowdrgitExamples\Project1' # to ensure the repository paths are defined PS C:\> $Powdrgit.ShowWarnings = $true # to ensure warnings are visible PS C:\> Set-GitBranch -Repo NonExistentRepo -BranchName main WARNING: [Set-GitBranch]Repository 'NonExistentRepo' not found. Check the repository directory exists and has been added to the $Powdrgit.Path module variable. .EXAMPLE ## Check out a branch from outside a repository by naming a repository ## PS C:\> $Powdrgit.Path = 'C:\PowdrgitExamples\MyToolbox;C:\PowdrgitExamples\Project1' # to ensure the repository paths are defined PS C:\> Set-GitBranch -Repo MyToolbox -BranchName feature1 # Nothing was returned, but the specified branch is now checked out for the specified repository. # To confirm the checkout: PS C:\> Get-GitBranch -Repo MyToolbox -Current | Format-Table -Property RepoName,BranchName,IsCheckedOut,IsRemote RepoName BranchName IsCheckedOut IsRemote -------- ---------- ------------ -------- MyToolbox feature1 True False # Checkout main branch: PS C:\> Set-GitBranch -Repo MyToolbox -BranchName main # To confirm the checkout: PS C:\> Get-GitBranch -Repo MyToolbox -Current | Format-Table -Property RepoName,BranchName,IsCheckedOut,IsRemote RepoName BranchName IsCheckedOut IsRemote -------- ---------- ------------ -------- MyToolbox main True False .EXAMPLE ## Check out a branch from outside a repository and use SetLocation parameter ## PS C:\> $Powdrgit.Path = 'C:\PowdrgitExamples\MyToolbox;C:\PowdrgitExamples\Project1' # to ensure the repository paths are defined PS C:\> Set-GitBranch -Repo MyToolbox -BranchName main -SetLocation PS C:\PowdrgitExamples\MyToolbox> # Nothing was returned, but the specified branch is now checked out for the specified repository # Also, because the SetLocation switch was used, the current location (reflected in the prompt) changed to the repository's top-level directory. .EXAMPLE ## Check out a branch from outside a repository and use GitScript to run a git command against the branch ## PS C:\> $Powdrgit.Path = 'C:\PowdrgitExamples\MyToolbox;C:\PowdrgitExamples\Project1' # to ensure the repository paths are defined PS C:\> Set-GitBranch -Repo MyToolbox -BranchName main -GitScript 'git pull' MyToolbox | main git pull Already up to date. .EXAMPLE ## Use GitScript to run a git command against a branch and capture the only the git output in a variable ## PS C:\> $Powdrgit.Path = 'C:\PowdrgitExamples\MyToolbox;C:\PowdrgitExamples\Project1' # to ensure the repository paths are defined PS C:\> $gitPullOutput = $null PS C:\> $gitPullOutput = Set-GitBranch -Repo MyToolbox -BranchName main -GitScript 'git pull' -ResultsOut Pipe MyToolbox | main git pull PS C:\> $gitPullOutput Already up to date. # The header and command output were still seen in the host as they were not suppressed with the HeaderOut and CommandOut parameters. .EXAMPLE ## Use GitScript to run a git command against a branch and suppress all output ## PS C:\> $Powdrgit.Path = 'C:\PowdrgitExamples\MyToolbox;C:\PowdrgitExamples\Project1' # to ensure the repository paths are defined PS C:\> Set-GitBranch -Repo MyToolbox -BranchName main -GitScript 'git pull' -HeaderOut None -CommandOut None -ResultsOut None PS C:\> # No output was seen in the host as it was suppressed with the HeaderOut, CommandOut, and ResultsOut parameters. .EXAMPLE ## Run a git command against multiple branches ## PS C:\> $Powdrgit.Path = 'C:\PowdrgitExamples\MyToolbox;C:\PowdrgitExamples\Project1' # to ensure the repository paths are defined PS C:\> $branchesToPull = Get-GitBranch -Repo MyToolbox | Where-Object BranchName -in 'feature1','release' | Select-Object -ExpandProperty BranchName PS C:\> Set-GitBranch -Repo MyToolbox -BranchName $branchesToPull -GitScript 'git pull' MyToolbox | feature1 git pull Already up to date. MyToolbox | release git pull Already up to date. PS C:\> # The command passed to the script block parameter was executed against each branch stored in the $branchesToPull variable. .EXAMPLE ## Run a git command against multiple branches in multiple repositories ## PS C:\> $Powdrgit.Path = 'C:\PowdrgitExamples\MyToolbox;C:\PowdrgitExamples\Project1' # to ensure the repository paths are defined PS C:\> Get-GitRepo | Get-GitBranch | Set-GitBranch -GitScript 'git status' MyToolbox | feature1 git status On branch feature1 Your branch is ahead of 'origin/feature1' by 1 commit. (use "git push" to publish your local commits) nothing to commit, working tree clean MyToolbox | feature3 git status On branch feature3 nothing to commit, working tree clean MyToolbox | main git status On branch main Your branch is ahead of 'origin/main' by 3 commits. (use "git push" to publish your local commits) nothing to commit, working tree clean MyToolbox | release git status On branch release Your branch is up to date with 'origin/release'. nothing to commit, working tree clean Project1 | main git status On branch main nothing to commit, working tree clean Project1 | newfeature git status On branch newfeature nothing to commit, working tree clean # By piping the results of Get-GitRepo | Get-GitBranch into Set-GitBranch, we can see the status of all branches in all repositories in a single command. .EXAMPLE ## Check out a branch usually hidden from results with $Powdrgit.BranchExcludes ## PS C:\> $Powdrgit.Path = 'C:\PowdrgitExamples\MyToolbox;C:\PowdrgitExamples\Project1' # to ensure the repository paths are defined PS C:\> $Powdrgit.ShowWarnings = $true # to ensure warnings are visible PS C:\> $Powdrgit.BranchExcludesNoWarn = $false # to ensure warnings are visible PS C:\> $Powdrgit.BranchExcludes = [PSCustomObject]@{ RepoPattern = '.*'; BranchPattern = 'feature\d'; ApplyTo = 'Global' } PS C:\> Set-GitBranch -Repo MyToolbox -BranchName feature1 -Force PS C:\> Get-GitBranch -Repo MyToolbox -Current | Format-Table -Property RepoName,BranchName,IsCheckedOut,IsRemote RepoName BranchName IsCheckedOut IsRemote -------- ---------- ------------ -------- MyToolbox feature1 True False .INPUTS [System.String[]] Accepts string objects via the Repo parameter. The output of Get-GitBranch can be piped into Set-GitTag. .OUTPUTS [System.String] When output is present, returns String objects. .NOTES Author : nmbell .LINK Get-GitBranch .LINK Get-GitRepo .LINK Set-GitRepo .LINK Get-GitLog .LINK Invoke-GitExpression .LINK about_powdrgit .LINK https://github.com/nmbell/powdrgit/blob/main/help/about_powdrgit.md #> # Function alias [Alias('sgb')] # Use cmdlet binding [CmdletBinding( SupportsShouldProcess = $true , ConfirmImpact = 'Medium' , HelpURI = 'https://github.com/nmbell/powdrgit/blob/main/help/Set-GitBranch.md' )] # Declare output type [OutputType([System.String])] # Declare parameters Param( [Parameter( Mandatory = $false , Position = 0 , ValueFromPipeline = $false , ValueFromPipelineByPropertyName = $true )] # [ArgumentCompleter()] [Alias('RepoName','RepoPath')] [String[]] $Repo , [Parameter( Mandatory = $true , HelpMessage = 'Enter the name of a branch to be checked out. Wildcard characters are allowed.' , Position = 1 , ValueFromPipeline = $false , ValueFromPipelineByPropertyName = $true )] # [ArgumentCompleter()] [String[]] $BranchName = '*' , [Switch] $Force , [Switch] $SetLocation , [ValidateNotNullOrEmpty()] [String] $GitScript , # [ArgumentCompleter()] [String] $HeaderOut , # [ArgumentCompleter()] [String] $CommandOut = 'Green' , # [ArgumentCompleter()] [String] $ResultsOut = 'DarkGray' , [String] $GitScriptSeparator ) BEGIN { $bk = 'B' # Common BEGIN: Set-StrictMode -Version 3.0 $thisFunctionName = $MyInvocation.MyCommand $start = Get-Date $indent = ($Powdrgit.DebugIndentChar[0]+' ')*($PowdrgitCallDepth++) $PSDefaultParameterValues += @{ '*:Verbose' = $(If ($DebugPreference -notin 'Ignore','SilentlyContinue') { $DebugPreference } Else { $VerbosePreference }) } # turn on Verbose with Debug $warn = $Powdrgit.ShowWarnings -and !($PSBoundParameters.ContainsKey('WarningAction') -and $PSBoundParameters.WarningAction -eq 'Ignore') # because -WarningAction:Ignore is not implemented correctly Write-Debug " $(ts)$indent[$thisFunctionName][$bk]Start: $($start.ToString('yyyy-MM-dd HH:mm:ss.fff'))" # Function BEGIN: Write-Debug " $(ts)$indent[$thisFunctionName][$bk]Storing current location" $startLocation = $PWD.Path If (!$HeaderOut) { $HeaderOut = 'None' If ($GitScript) { $HeaderOut = 'DarkGray' } } } PROCESS { $bk = 'P' # Find the repository name from current location If (!$PSBoundParameters.ContainsKey('Repo')) { $Repo = Get-GitRepo -Current | Select-Object -ExpandProperty RepoPath } # Get the repository info $validRepos = Get-ValidRepo -Repo $Repo # Set the branches ForEach ($validRepo in $validRepos) { # Move to the repository directory Write-Debug " $(ts)$indent[$thisFunctionName][$bk]Moving to the repository directory: $($validRepo.RepoPath)" Set-GitRepo -Repo $validRepo.RepoPath -WarningAction Ignore # Get matching branches $validBranches = Get-GitBranch -Repo $validRepo.RepoPath -BranchName $BranchName -IncludeRemote -Force:$Force -Verbose:$false -Debug:$false -WarningAction Ignore #| Select-Object -ExpandProperty BranchName # Set branches ForEach ($validBranch in $validBranches) { $localBranchName = If ($validBranch.IsRemote) { $validBranch.BranchName -replace "^$($validBranch.RemoteName)/",'' } Else { $validBranch.BranchName } Write-Verbose "$(ts)$indent[$thisFunctionName][$bk]Checking out branch $localBranchName" $gitCommand = "git checkout $localBranchName" $gitResults = Invoke-GitExpression -Command $gitCommand $currentBranch = Get-GitBranch -Current -Verbose:$false -Debug:$false -WarningAction Ignore | Select-Object -ExpandProperty BranchName Write-Debug " $(ts)$indent[$thisFunctionName][$bk]Current branch is $currentBranch" If ($currentBranch -ne $localBranchName) { $gitResults | Out-String | Write-Host If ($warn) { Write-Warning "[$thisFunctionName]Failed to checkout branch $localBranchName`:" } } Else { Write-Debug " $(ts)$indent[$thisFunctionName][$bk]Writing header" Write-GitOut -OutputType Header -OutputValue "`r`n$($validRepo.RepoName) | $localBranchName" -OutputStream $HeaderOut If ($GitScript) { Write-Debug " $(ts)$indent[$thisFunctionName][$bk]Executing script block" If (!$PSBoundParameters.ContainsKey('GitScriptSeparator')) { $GitScriptSeparator = $Powdrgit.DefaultGitScriptSeparator } If (!$GitScriptSeparator) { $GitScriptSeparator = 'z%G1+$jNMuU%XUCASoPf312osOOjMHCOnh+kn3Ke' } # some unlikely-to-occur string Write-Debug " $(ts)$indent[$thisFunctionName][$bk]Separator: $GitScriptSeparator" $GitScript = $GitScript.Replace('`'+$GitScriptSeparator,'<separator>') # to preserve escaped separators $gitScriptLines = $GitScript.Split($GitScriptSeparator) ForEach ($line in $gitScriptLines | Where-Object { $_.Trim() }) { $line = $line.Replace('<separator>',$GitScriptSeparator).Trim() Write-Debug " $(ts)$indent[$thisFunctionName][$bk]Writing command" Write-GitOut -OutputType Body -OutputValue $line -OutputStream $CommandOut # results $shouldText = $line # If ($WhatIfPreference) { Write-Host "What if: $shouldText" } # handled by ShouldProcess If ($PSCmdlet.ShouldProcess($shouldText,$null,$null)) { Write-Verbose "$(ts)$indent[$thisFunctionName][$bk]$shouldText" If (!$ResultsOut -or $ResultsOut -eq 'Pipe') { Invoke-GitExpression -Command $line } ElseIf ($ResultsOut -eq 'None') { Invoke-GitExpression -Command $line | Out-Null } ElseIf ($ResultsOut -eq 'Native') { Invoke-Expression -Command $line } Else { Invoke-GitExpression -Command $line | Write-Host -ForegroundColor $ResultsOut } } } } } } } } END { $bk = 'E' # Function END: If (!$SetLocation) { Write-Debug " $(ts)$indent[$thisFunctionName][$bk]Setting location to original directory" Set-Location -Path $startLocation } # Common END: $end = Get-Date $duration = New-TimeSpan -Start $start -End $end Write-Debug " $(ts)$indent[$thisFunctionName][$bk]Finish: $($end.ToString('yyyy-MM-dd HH:mm:ss.fff')) ($($duration.ToString('d\d\ hh\:mm\:ss\.fff')))" $PowdrgitCallDepth-- } } |