diff --git a/source/Public/Assert-GitLocalChanges.ps1 b/source/Public/Assert-GitLocalChanges.ps1 new file mode 100644 index 0000000..e158cf7 --- /dev/null +++ b/source/Public/Assert-GitLocalChanges.ps1 @@ -0,0 +1,35 @@ +<# + .SYNOPSIS + Asserts that there are no unstaged or staged changes in the local Git branch. + + .DESCRIPTION + The Assert-GitLocalChanges command checks whether there are any unstaged + or staged changes in the local Git branch. If there are any staged or + unstaged changes, it throws a terminating error. + + .EXAMPLE + Assert-GitLocalChanges + + This example demonstrates how to use the Assert-GitLocalChanges command + to ensure that there are no local changes in the Git repository. +#> +function Assert-GitLocalChanges +{ + [CmdletBinding()] + param () + + $hasChanges = Test-GitLocalChanges + + if ($hasChanges) + { + # cSpell:ignore unstaged + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + ($script:localizedData.Assert_GitLocalChanges_FailedUnstagedChanges), + 'AGLC0001', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidResult, + 'Staged or unstaged changes' + ) + ) + } +} diff --git a/source/Public/Assert-GitRemote.ps1 b/source/Public/Assert-GitRemote.ps1 new file mode 100644 index 0000000..cff2a78 --- /dev/null +++ b/source/Public/Assert-GitRemote.ps1 @@ -0,0 +1,50 @@ +<# + .SYNOPSIS + Checks if the specified Git remote exists locally and throws an error if it doesn't. + + .DESCRIPTION + The `Assert-GitRemote` command checks if the remote specified in the `$RemoteName` + parameter exists locally. If the remote doesn't exist, it throws an error. + + .PARAMETER RemoteName + Specifies the name of the Git remote to check. + + .EXAMPLE + PS> Assert-GitRemote -RemoteName "origin" + + This example checks if the Git remote named "origin" exists locally. + + .INPUTS + None. + + .OUTPUTS + None. +#> +function Assert-GitRemote +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true, Position = 0)] + [System.String] + $RemoteName + ) + + <# + Check if the remote specified in $UpstreamRemoteName exists locally and + throw an error if it doesn't. + #> + $remoteExists = Test-GitRemote -RemoteName $RemoteName + + if (-not $remoteExists) + { + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + ($script:localizedData.New_SamplerGitHubReleaseTag_RemoteMissing -f $UpstreamRemoteName), + 'AGR0001', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::ObjectNotFound, + $DatabaseName + ) + ) + } +} diff --git a/source/Public/Get-GitBranchCommit.ps1 b/source/Public/Get-GitBranchCommit.ps1 new file mode 100644 index 0000000..dea3865 --- /dev/null +++ b/source/Public/Get-GitBranchCommit.ps1 @@ -0,0 +1,148 @@ +<# + .SYNOPSIS + Retrieves the commit ID(s) for a specified Git branch. + + .DESCRIPTION + The Get-GitBranchCommit command retrieves the commit ID(s) for a specified + Git branch. It provides options to retrieve the latest commit ID, a specific + number of latest commit IDs, or the first X number of commit IDs. + + .PARAMETER BranchName + Specifies the name of the Git branch. If not provided, the current branch + will be used. + + .PARAMETER Latest + Retrieves only the latest commit ID. + + .PARAMETER Last + Retrieves the specified number of latest commit IDs. The order will be from + the newest to the oldest commit. + + .PARAMETER First + Retrieves the first X number of commit IDs. The order will be from the + oldest to the newest commit. + + .OUTPUTS + System.String + + The commit ID(s) for the specified Git branch. + + .EXAMPLE + Get-GitBranchCommit -BranchName 'feature/branch' + + Retrieves all commit IDs for the 'feature/branch' Git branch. + + .EXAMPLE + Get-GitBranchCommit -Latest + + Retrieves only the latest commit ID for the current Git branch. + + .EXAMPLE + Get-GitBranchCommit -Last 5 + + Retrieves the 5 latest commit IDs for the current Git branch. + + .EXAMPLE + Get-GitBranchCommit -First 3 + + Retrieves the first 3 commit IDs for the current Git branch. +#> +function Get-GitBranchCommit +{ + [CmdletBinding(DefaultParameterSetName = 'NoParameter')] + [OutputType([System.String])] + param + ( + [Parameter(ParameterSetName = 'NoParameter')] + [Parameter(ParameterSetName = 'Latest')] + [Parameter(ParameterSetName = 'Last')] + [Parameter(ParameterSetName = 'First')] + [System.String] + $BranchName, + + [Parameter(ParameterSetName = 'Latest')] + [System.Management.Automation.SwitchParameter] + $Latest, + + [Parameter(ParameterSetName = 'Last')] + [System.UInt32] + $Last, + + [Parameter(ParameterSetName = 'First')] + [System.UInt32] + $First + ) + + $commitId = $null + + $argument = @() + + if ($PSBoundParameters.ContainsKey('BranchName')) + { + if ($BranchName -eq '.') + { + $BranchName = Get-GitLocalBranchName -Current + } + + $argument += @( + $BranchName + ) + } + + if ($Latest.IsPresent) + { + # Return only the latest commit ID. + $commitId = git rev-parse HEAD @argument + } + elseif ($Last) + { + # Return the latest X number of commits. + $commitId = git log -n $Last --pretty=format:"%H" @argument + } + elseif ($First) + { + if (-not $PSBoundParameters.ContainsKey('BranchName')) + { + $BranchName = Get-GitLocalBranchName -Current + } + + # Count the total number of commits in the branch. + $totalCommits = git rev-list --count $BranchName + + # Calculate the number of commits to skip. + $skipCommits = $totalCommits - $First + + # Return the first X number of commits. + $commitId = git log --skip $skipCommits --reverse -n $First --pretty=format:"%H" $BranchName + } + else + { + # Return all commit IDs. + $commitId = git log --pretty=format:"%H" @argument + } + + # TODO: Should handle LASTEXITCODE above too + + if ($LASTEXITCODE -ne 0) # cSpell: ignore LASTEXITCODE + { + if($PSBoundParameters.ContainsKey('BranchName')) + { + $errorMessage = $script:localizedData.Get_GitBranchCommit_FailedFromBranch -f $BranchName + } + else + { + $errorMessage = $script:localizedData.Get_GitBranchCommit_FailedFromCurrent + } + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + $errorMessage, + 'GGLBN0001', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::ObjectNotFound, + $branchName + ) + ) + } + + return $commitId +} diff --git a/source/Public/Get-GitLocalBranchName.ps1 b/source/Public/Get-GitLocalBranchName.ps1 new file mode 100644 index 0000000..fe703e3 --- /dev/null +++ b/source/Public/Get-GitLocalBranchName.ps1 @@ -0,0 +1,70 @@ +<# + .SYNOPSIS + Retrieves the name of the local Git branch. + + .DESCRIPTION + The Get-GitLocalBranchName command is used to retrieve the name of the + current local Git branch. It uses the `git rev-parse --abbrev-ref HEAD` + command to get the branch name. + + .OUTPUTS + [System.String] + + The function returns the name of the current local Git branch as a string. + + .EXAMPLE + PS C:\> Get-GitLocalBranchName -Current + + Returns the name of the current local Git branch. +#> +function Get-GitLocalBranchName +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'ShouldProcess is implemented without ShouldProcess/ShouldContinue.')] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')] + [OutputType([System.String])] + param + ( + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Current + ) + + $branchName = $null + + if ($WhatIfPreference) + { + if ($Current.IsPresent) + { + Write-Information -MessageData 'What if: Getting current local branch name' -InformationAction Continue + } + else + { + Write-Information -MessageData 'What if: Getting local branch names.' -InformationAction Continue + } + } + else + { + if ($Current.IsPresent) + { + $branchName = git rev-parse --abbrev-ref HEAD + } + else + { + $branchName = git branch --format='%(refname:short)' --list + } + + if ($LASTEXITCODE -ne 0) # cSpell: ignore LASTEXITCODE + { + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + $script:localizedData.Get_GitLocalBranchName_Failed, + 'GGLBN0001', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::ObjectNotFound, + $branchName + ) + ) + } + } + + return $branchName +} diff --git a/source/Public/New-SamplerGitHubReleaseTag.ps1 b/source/Public/New-SamplerGitHubReleaseTag.ps1 index 600e09c..5c78d82 100644 --- a/source/Public/New-SamplerGitHubReleaseTag.ps1 +++ b/source/Public/New-SamplerGitHubReleaseTag.ps1 @@ -91,58 +91,19 @@ function New-SamplerGitHubReleaseTag $ConfirmPreference = 'None' } - # Check if the remote specified in $UpstreamRemoteName exists locally and throw an error if it doesn't. - $remoteExists = git remote | Where-Object -FilterScript { $_ -eq $UpstreamRemoteName } - - if (-not $remoteExists) + if ($WhatIfPreference -eq $false) { - $PSCmdlet.ThrowTerminatingError( - [System.Management.Automation.ErrorRecord]::new( - ($script:localizedData.New_SamplerGitHubReleaseTag_RemoteMissing -f $UpstreamRemoteName), - 'NSGRT0001', # cspell: disable-line - [System.Management.Automation.ErrorCategory]::ObjectNotFound, - $DatabaseName - ) - ) - } + Assert-GitRemote -RemoteName $UpstreamRemoteName - $verboseDescriptionMessage = $script:localizedData.New_SamplerGitHubReleaseTag_FetchUpstream_ShouldProcessVerboseDescription -f $DefaultBranchName, $UpstreamRemoteName - $verboseWarningMessage = $script:localizedData.New_SamplerGitHubReleaseTag_FetchUpstream_ShouldProcessVerboseWarning -f $DefaultBranchName, $UpstreamRemoteName - $captionMessage = $script:localizedData.New_SamplerGitHubReleaseTag_FetchUpstream_ShouldProcessCaption - - if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) - { - # Fetch $DefaultBranchName from upstream and throw an error if it doesn't exist. - git fetch $UpstreamRemoteName $DefaultBranchName - - if ($LASTEXITCODE -ne 0) # cSpell: ignore LASTEXITCODE - { - $PSCmdlet.ThrowTerminatingError( - [System.Management.Automation.ErrorRecord]::new( - ($script:localizedData.New_SamplerGitHubReleaseTag_FailedFetchBranchFromRemote -f $DefaultBranchName, $UpstreamRemoteName), - 'NSGRT0002', # cspell: disable-line - [System.Management.Automation.ErrorCategory]::InvalidOperation, - $DatabaseName - ) - ) - } + Assert-GitLocalChanges } + # Fetch $DefaultBranchName from upstream and throw an error if it doesn't exist. + Update-RemoteTrackingBranch -RemoteName $UpstreamRemoteName -BranchName $DefaultBranchName + if ($CheckoutOriginalBranch.IsPresent) { - $currentLocalBranchName = git rev-parse --abbrev-ref HEAD - - if ($LASTEXITCODE -ne 0) - { - $PSCmdlet.ThrowTerminatingError( - [System.Management.Automation.ErrorRecord]::new( - $script:localizedData.New_SamplerGitHubReleaseTag_FailedGetLocalBranchName, - 'NSGRT0003', # cspell: disable-line - [System.Management.Automation.ErrorCategory]::InvalidOperation, - $DatabaseName - ) - ) - } + $currentLocalBranchName = Get-GitLocalBranchName -Current } $continueProcessing = $true @@ -154,14 +115,7 @@ function New-SamplerGitHubReleaseTag if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) { - git checkout $DefaultBranchName - - if ($LASTEXITCODE -ne 0) - { - $continueProcessing = $false - $errorMessage = $script:localizedData.New_SamplerGitHubReleaseTag_FailedCheckoutLocalBranch -f $DefaultBranchName - $errorCode = 'NSGRT0004' # cspell: disable-line - } + Switch-GitLocalBranch -BranchName $DefaultBranchName $switchedToDefaultBranch = $true @@ -169,7 +123,7 @@ function New-SamplerGitHubReleaseTag { git rebase $UpstreamRemoteName/$DefaultBranchName - if ($LASTEXITCODE -ne 0) + if ($LASTEXITCODE -ne 0) # cSpell: ignore LASTEXITCODE { $continueProcessing = $false $errorMessage = $script:localizedData.New_SamplerGitHubReleaseTag_FailedRebaseLocalDefaultBranch -f $DefaultBranchName, $UpstreamRemoteName @@ -178,14 +132,7 @@ function New-SamplerGitHubReleaseTag if ($continueProcessing) { - $headCommitId = git rev-parse HEAD - - if ($LASTEXITCODE -ne 0) - { - $continueProcessing = $false - $errorMessage = $script:localizedData.New_SamplerGitHubReleaseTag_FailedGetLastCommitId -f $DefaultBranchName - $errorCode = 'NSGRT0006' # cspell: disable-line - } + $headCommitId = Get-GitBranchCommit -Latest } } @@ -194,7 +141,7 @@ function New-SamplerGitHubReleaseTag # If something failed, revert back to the previous branch if requested. if ($CheckoutOriginalBranch.IsPresent -and $switchedToDefaultBranch) { - git checkout $currentLocalBranchName + Switch-GitLocalBranch -BranchName $currentLocalBranchName } $PSCmdlet.ThrowTerminatingError( @@ -220,7 +167,7 @@ function New-SamplerGitHubReleaseTag { if ($CheckoutOriginalBranch.IsPresent -and $switchedToDefaultBranch) { - git checkout $currentLocalBranchName + Switch-GitLocalBranch -BranchName $currentLocalBranchName } $PSCmdlet.ThrowTerminatingError( @@ -277,7 +224,7 @@ function New-SamplerGitHubReleaseTag { if ($CheckoutOriginalBranch.IsPresent -and $switchedToDefaultBranch) { - git checkout $currentLocalBranchName + Switch-GitLocalBranch -BranchName $currentLocalBranchName } $PSCmdlet.ThrowTerminatingError( @@ -329,19 +276,7 @@ function New-SamplerGitHubReleaseTag if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) { - git checkout $currentLocalBranchName - - if ($LASTEXITCODE -ne 0) - { - $PSCmdlet.ThrowTerminatingError( - [System.Management.Automation.ErrorRecord]::new( - ($script:localizedData.New_SamplerGitHubReleaseTag_FailedCheckoutPreviousBranch -f $currentLocalBranchName), - 'NSGRT0011', # cspell: disable-line - [System.Management.Automation.ErrorCategory]::InvalidOperation, - $DatabaseName - ) - ) - } + Switch-GitLocalBranch -BranchName $currentLocalBranchName } } } diff --git a/source/Public/Switch-GitLocalBranch.ps1 b/source/Public/Switch-GitLocalBranch.ps1 new file mode 100644 index 0000000..7465335 --- /dev/null +++ b/source/Public/Switch-GitLocalBranch.ps1 @@ -0,0 +1,50 @@ +<# + .SYNOPSIS + Switches to the specified local Git branch. + + .DESCRIPTION + The Switch-GitLocalBranch command is used to switch to the specified local + Git branch. It checks if the branch exists and performs the checkout operation. + If the checkout fails, it throws an error. + + .PARAMETER BranchName + The name of the branch to switch to. + + .EXAMPLE + Switch-GitLocalBranch -BranchName "feature/branch" + + This example switches to the "feature/branch" local Git branch. +#> +function Switch-GitLocalBranch +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'ShouldProcess is implemented without ShouldProcess/ShouldContinue.')] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')] + param + ( + [Parameter(Mandatory = $true, Position = 0)] + [System.String] + $BranchName + ) + + + if ($WhatIfPreference) + { + Write-Information -MessageData ('What if: Checking out local branch ''{0}''.' -f $BranchName) -InformationAction Continue + } + else + { + git checkout $BranchName + + if ($LASTEXITCODE -ne 0) # cSpell: ignore LASTEXITCODE + { + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + ($script:localizedData.Switch_GitLocalBranch_FailedCheckoutLocalBranch -f $BranchName), + 'SGLB0001', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $BranchName + ) + ) + } + } +} diff --git a/source/Public/Test-GitLocalChanges.ps1 b/source/Public/Test-GitLocalChanges.ps1 new file mode 100644 index 0000000..3ebe058 --- /dev/null +++ b/source/Public/Test-GitLocalChanges.ps1 @@ -0,0 +1,37 @@ +<# + .SYNOPSIS + Checks for unstaged or staged changes in the current local Git branch. + + .DESCRIPTION + The Test-GitLocalChanges command checks whether there are any unstaged or + staged changes in the current local Git branch. + + .OUTPUTS + System.Boolean + + Returns $true if there are unstaged or staged changes, otherwise returns $false. + + .EXAMPLE + PS> Test-GitLocalChanges + + This example demonstrates how to use the Test-GitLocalChanges function to + check for unstaged or staged changes in the current local Git branch. +#> +function Test-GitLocalChanges +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param () + + # Check for unstaged or staged changes + $status = git status --porcelain # cSpell: ignore unstaged + + $result = $false + + if ($status) + { + $result = $true + } + + return $result +} diff --git a/source/Public/Test-GitRemote.ps1 b/source/Public/Test-GitRemote.ps1 new file mode 100644 index 0000000..4903933 --- /dev/null +++ b/source/Public/Test-GitRemote.ps1 @@ -0,0 +1,45 @@ +<# + .SYNOPSIS + Tests if a Git remote exists locally. + + .DESCRIPTION + The Test-GitRemote command checks if the specified Git remote exists locally + and returns a boolean value indicating its existence. + + .PARAMETER RemoteName + Specifies the name of the Git remote to be tested. + + .EXAMPLE + Test-GitRemote -RemoteName "origin" + + Returns $true if the "origin" remote exists locally, otherwise returns $false. + + .NOTES + This function requires Git to be installed and accessible from the command line. +#> +function Test-GitRemote +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true, Position = 0)] + [System.String] + $RemoteName + ) + + # Check if the remote specified in $UpstreamRemoteName exists locally and throw an error if it doesn't. + $remoteExists = git remote | + Where-Object -FilterScript { + $_ -eq $RemoteName + } + + $result = $false + + if ($remoteExists) + { + $result = $true + } + + return $result +} diff --git a/source/Public/Test-GitRemoteBranch.ps1 b/source/Public/Test-GitRemoteBranch.ps1 new file mode 100644 index 0000000..777bb12 --- /dev/null +++ b/source/Public/Test-GitRemoteBranch.ps1 @@ -0,0 +1,59 @@ +<# + .SYNOPSIS + Tests if a remote branch exists in a Git repository. + + .DESCRIPTION + The Test-GitRemoteBranch command checks if a specified branch exists in a + remote Git repository. + + .PARAMETER BranchName + Specifies the name of the branch to check. + + .PARAMETER RemoteName + Specifies the name of the remote repository. + + .EXAMPLE + Test-GitRemoteBranch -BranchName "feature/branch" -RemoteName "origin" + + This example tests if the branch "feature/branch" exists in the remote repository + named "origin". + + .INPUTS + None. You cannot pipe input to this function. + + .OUTPUTS + System.Boolean + + This function returns a Boolean value indicating whether the branch exists in + the remote repository. +#> +function Test-GitRemoteBranch +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true, Position = 0)] + [System.String] + $BranchName, + + [Parameter(Mandatory = $true, Position = 1)] + [System.String] + $RemoteName + ) + + # Get the remote branches + $branch = ls-remote --branches $RemoteName $BranchName + + $oid, $heads = $branch -split "`t" + + $result = $false + + # Check if the branch exists + if ($heads -match "^refs/heads/$BranchName") + { + $result = $true + } + + return $result +} diff --git a/source/Public/Update-GitBranch.ps1 b/source/Public/Update-GitBranch.ps1 deleted file mode 100644 index 5be9bff..0000000 --- a/source/Public/Update-GitBranch.ps1 +++ /dev/null @@ -1,133 +0,0 @@ -<# - .SYNOPSIS - Updates the specified Git branch by pulling or rebasing from the upstream - branch. - - .DESCRIPTION - This function checks out the specified local branch and either pulls the - latest changes or rebases it with the upstream branch. - - .PARAMETER BranchName - Specifies the local branch name. Default is 'main'. - - .PARAMETER UpstreamBranchName - Specifies the upstream branch name. Default is 'main'. - - .PARAMETER RemoteName - Specifies the remote name. Default is 'origin'. - - .PARAMETER Rebase - Specifies that the local branch should be rebased with the upstream branch. - - .PARAMETER CheckoutOriginalBranch - If specified, switches back to the original branch after performing the - pull or rebase. - - .EXAMPLE - Update-GitBranch - - Checks out the 'main' branch and pulls the latest changes. - - .EXAMPLE - Update-GitBranch -BranchName 'feature-branch' - - Checks out the 'feature-branch' and pulls the latest changes. - - .EXAMPLE - Update-GitBranch -BranchName 'feature-branch' -UpstreamBranchName 'develop' -Rebase - - Checks out the 'feature-branch' and rebases it with the 'develop' branch. - - .EXAMPLE - Update-GitBranch -BranchName 'feature-branch' -RemoteName 'upstream' - - Checks out the 'feature-branch' and pulls the latest changes from the - 'upstream' remote. - - .EXAMPLE - Update-GitBranch -BranchName . - - Pulls the latest changes into the current branch. - - .EXAMPLE - Update-GitBranch -CheckoutOriginalBranch - - Checks out the 'main' branch, pulls the latest changes, and switches back - to the original branch. -#> -function Update-GitBranch -{ - [CmdletBinding()] - param - ( - [Parameter()] - [System.String] - $BranchName = 'main', - - [Parameter()] - [System.String] - $UpstreamBranchName = 'main', - - [Parameter()] - [System.String] - $RemoteName = 'origin', - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $Rebase, - - [Parameter()] - [System.Management.Automation.SwitchParameter] - $CheckoutOriginalBranch - ) - - # Check for unstaged or staged changes - $status = git status --porcelain - - if ($status) - { - # cSpell:ignore unstaged - $PSCmdlet.ThrowTerminatingError( - [System.Management.Automation.ErrorRecord]::new( - ($script:localizedData.Update_GitBranch_FailedUnstagedChanges), - 'UGB0001', # cspell: disable-line - [System.Management.Automation.ErrorCategory]::InvalidOperation, - ($status -join ', ') - ) - ) - } - - if ($BranchName -eq '.') - { - $BranchName = git rev-parse --abbrev-ref HEAD - - Write-Debug -Message ('Using the current branch ''{0}''.' -f $BranchName) - } - - # Capture the current branch name only if CheckoutOriginalBranch is specified. - if ($CheckoutOriginalBranch) - { - $currentBranch = git rev-parse --abbrev-ref HEAD - } - - # Checkout the specified branch - git checkout $BranchName - - if ($Rebase) - { - # Fetch the upstream branch and rebase the local branch - git fetch $RemoteName $UpstreamBranchName - git rebase $RemoteName/$UpstreamBranchName - } - else - { - # Run git pull with the specified remote and upstream branch - git pull $RemoteName $UpstreamBranchName - } - - # Switch back to the original branch if specified - if ($CheckoutOriginalBranch) - { - git checkout $currentBranch - } -} diff --git a/source/Public/Update-GitLocalBranch.ps1 b/source/Public/Update-GitLocalBranch.ps1 new file mode 100644 index 0000000..aeb834c --- /dev/null +++ b/source/Public/Update-GitLocalBranch.ps1 @@ -0,0 +1,146 @@ +<# + .SYNOPSIS + Updates the specified Git branch by pulling or rebasing from the upstream + branch. + + .DESCRIPTION + This function checks out the specified local branch and either pulls the + latest changes or rebases it with the upstream branch. + + .PARAMETER BranchName + Specifies the local branch name. Default is 'main'. + + .PARAMETER UpstreamBranchName + Specifies the upstream branch name. If not specified the value in BranchName + will be used. + + .PARAMETER RemoteName + Specifies the remote name. Default is 'origin'. + + .PARAMETER Rebase + Specifies that the local branch should be rebased with the upstream branch. + + .PARAMETER CheckoutOriginalBranch + If specified, switches back to the original branch after performing the + pull or rebase. + + .EXAMPLE + Update-GitLocalBranch + + Checks out the 'main' branch and pulls the latest changes. + + .EXAMPLE + Update-GitLocalBranch -BranchName 'feature-branch' + + Checks out the 'feature-branch' and pulls the latest changes. + + .EXAMPLE + Update-GitLocalBranch -BranchName 'feature-branch' -UpstreamBranchName 'develop' -Rebase + + Checks out the 'feature-branch' and rebases it with the 'develop' branch. + + .EXAMPLE + Update-GitLocalBranch -BranchName 'feature-branch' -RemoteName 'upstream' + + Checks out the 'feature-branch' and pulls the latest changes from the + 'upstream' remote. + + .EXAMPLE + Update-GitLocalBranch -BranchName . + + Pulls the latest changes into the current branch. + + .EXAMPLE + Update-GitLocalBranch -CheckoutOriginalBranch + + Checks out the 'main' branch, pulls the latest changes, and switches back + to the original branch. +#> +function Update-GitLocalBranch +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'ShouldProcess is implemented correctly.')] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] + param + ( + [Parameter()] + [System.String] + $BranchName = 'main', + + [Parameter()] + [System.String] + $UpstreamBranchName, + + [Parameter()] + [System.String] + $RemoteName = 'origin', + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $Rebase, + + [Parameter()] + [System.Management.Automation.SwitchParameter] + $CheckoutOriginalBranch + ) + + Assert-GitRemote -RemoteName $RemoteName + + if ($BranchName -eq '.') + { + $BranchName = Get-GitLocalBranchName -Current + + Write-Debug -Message ('Using the current branch ''{0}''.' -f $BranchName) + } + + if (-not $UpstreamBranchName) + { + $UpstreamBranchName = $BranchName + } + + if ($WhatIfPreference -eq $false) + { + Assert-GitLocalChanges + } + + # Capture the current branch name only if CheckoutOriginalBranch is specified. + if ($CheckoutOriginalBranch) + { + $currentLocalBranch = Get-GitLocalBranchName -Current + } + + # Fetch the upstream branch + Update-RemoteTrackingBranch -RemoteName $RemoteName -BranchName $UpstreamBranchName + + Switch-GitLocalBranch -BranchName $BranchName + + if ($Rebase) + { + if ($WhatIfPreference) + { + Write-Information -MessageData ('What if: Rebasing the local branch ''{0}'' using tracking branch ''{1}/{0}''.' -f $UpstreamBranchName, $RemoteName) -InformationAction Continue + } + else + { + # Rebase the local branch + git rebase $RemoteName/$UpstreamBranchName + } + } + else + { + if ($WhatIfPreference) + { + Write-Information -MessageData ('What if: Updating the local branch ''{0}'' by pulling from tracking branch ''{1}/{0}''.' -f $UpstreamBranchName, $RemoteName) -InformationAction Continue + } + else + { + # Run git pull with the specified remote and upstream branch + git pull $RemoteName $UpstreamBranchName + } + } + + # Switch back to the original branch if specified + if ($CheckoutOriginalBranch -and $WhatIfPreference -eq $false) + { + Switch-GitLocalBranch -BranchName $currentLocalBranch + } +} diff --git a/source/Public/Update-RemoteTrackingBranch.ps1 b/source/Public/Update-RemoteTrackingBranch.ps1 new file mode 100644 index 0000000..83d60ff --- /dev/null +++ b/source/Public/Update-RemoteTrackingBranch.ps1 @@ -0,0 +1,76 @@ +<# + .SYNOPSIS + Updates the remote tracking branch in the local git repository. + + .DESCRIPTION + The Update-RemoteTrackingBranch command fetches updates from the specified + remote and branch in Git. It is used to keep the local tracking branch up + to date with the remote branch. + + .PARAMETER RemoteName + Specifies the name of the remote. + + .PARAMETER BranchName + Specifies the name of the branch to update. If not provided, all branches + will be updated. + + .EXAMPLE + Update-RemoteTrackingBranch -RemoteName 'origin' -BranchName 'main' + + Fetches updates from the 'origin' remote repository for the 'main' branch. + + .EXAMPLE + Update-RemoteTrackingBranch -RemoteName 'upstream' + + Fetches updates from the 'upstream' remote repository for all branches. + + .NOTES + This function requires Git to be installed and accessible from the command line. +#> +function Update-RemoteTrackingBranch +{ + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] + param + ( + [Parameter(Mandatory = $true, Position = 0)] + [System.String] + $RemoteName, + + [Parameter(Position = 1)] + [System.String] + $BranchName + ) + + $arguments = @( + $RemoteName + ) + + if ($PSBoundParameters.ContainsKey('BranchName')) + { + $arguments += @( + $BranchName + ) + } + + $verboseDescriptionMessage = $script:localizedData.Update_RemoteTrackingBranch_FetchUpstream_ShouldProcessVerboseDescription -f $BranchName, $RemoteName + $verboseWarningMessage = $script:localizedData.Update_RemoteTrackingBranch_FetchUpstream_ShouldProcessVerboseWarning -f $BranchName, $RemoteName + $captionMessage = $script:localizedData.Update_RemoteTrackingBranch_FetchUpstream_ShouldProcessCaption + + if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) + { + # Fetch the updates from the specified remote and branch + git fetch @arguments + + if ($LASTEXITCODE -ne 0) # cSpell: ignore LASTEXITCODE + { + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + ($script:localizedData.Update_RemoteTrackingBranch_FailedFetchBranchFromRemote -f ($arguments -join ' ')), + 'URTB0001', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $DatabaseName + ) + ) + } + } +} diff --git a/source/en-US/Viscalyx.Common.strings.psd1 b/source/en-US/Viscalyx.Common.strings.psd1 index 1c1e96a..2c0ad2e 100644 --- a/source/en-US/Viscalyx.Common.strings.psd1 +++ b/source/en-US/Viscalyx.Common.strings.psd1 @@ -7,6 +7,16 @@ # cSpell: ignore unstaged ConvertFrom-StringData @' + ## Assert-GitLocalChanges + Assert_GitLocalChanges_FailedUnstagedChanges = There are unstaged or staged changes. Please commit or stash your changes before proceeding. + + ## Get-GitLocalBranchName + Get_GitLocalBranchName_Failed = Failed to get the name of the local branch. Make sure git repository is accessible. + + ## Get-GitBranchCommit + Get_GitBranchCommit_FailedFromBranch = Failed to retrieve commits. Make sure the branch '{0}' exists and is accessible. + Get_GitBranchCommit_FailedFromCurrent = Failed to retrieve commits from current branch. + ## Remove-History Convert_PesterSyntax_ShouldProcessVerboseDescription = Converting the script file '{0}'. Convert_PesterSyntax_ShouldProcessVerboseWarning = Are you sure you want to convert the script file '{0}'? @@ -15,22 +25,13 @@ ConvertFrom-StringData @' ## New-SamplerGitHubReleaseTag New_SamplerGitHubReleaseTag_RemoteMissing = The remote '{0}' does not exist in the local git repository. Please add the remote before proceeding. - New_SamplerGitHubReleaseTag_FailedFetchBranchFromRemote = Failed to fetch branch '{0}' from the remote '{1}'. Make sure the branch exists in the remote git repository and the remote is accessible. - New_SamplerGitHubReleaseTag_FailedGetLocalBranchName = Failed to get the name of the local branch. Make sure the local branch exists and is accessible. - New_SamplerGitHubReleaseTag_FailedCheckoutLocalBranch = Failed to checkout the local branch '{0}'. Make sure the branch exists and is accessible. New_SamplerGitHubReleaseTag_FailedRebaseLocalDefaultBranch = Failed to rebase the local default branch '{0}' using '{1}/{0}'. Make sure the branch exists and is accessible. - New_SamplerGitHubReleaseTag_FailedGetLastCommitId = Failed to get the last commit id of the local branch '{0}'. Make sure the branch exists and is accessible. New_SamplerGitHubReleaseTag_FailedFetchTagsFromUpstreamRemote = Failed to fetch tags from the upstream remote '{0}'. Make sure the remote exists and is accessible. New_SamplerGitHubReleaseTag_FailedGetTagsOrMissingTagsInLocalRepository = Failed to get tags from the local repository or the tags are missing. Make sure that at least one preview tag exist in the local repository, or specify a release tag. New_SamplerGitHubReleaseTag_FailedDescribeTags = Failed to describe the tags. Make sure the tags exist in the local repository. New_SamplerGitHubReleaseTag_LatestTagIsNotPreview = The latest tag '{0}' is not a preview tag or not a correctly formatted preview tag. Make sure the latest tag is a preview tag, or specify a release tag. New_SamplerGitHubReleaseTag_FailedCheckoutPreviousBranch = Failed to checkout the previous branch '{0}'. - New_SamplerGitHubReleaseTag_FetchUpstream_ShouldProcessVerboseDescription = Fetching branch '{0}' from the upstream remote '{1}'. - New_SamplerGitHubReleaseTag_FetchUpstream_ShouldProcessVerboseWarning = Are you sure you want to fetch branch '{0}' from the upstream remote '{1}'? - # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. - New_SamplerGitHubReleaseTag_FetchUpstream_ShouldProcessCaption = Fetch upstream branch - New_SamplerGitHubReleaseTag_Rebase_ShouldProcessVerboseDescription = Switching to and rebasing the local default branch '{0}' using the upstream branch '{1}/{0}'. New_SamplerGitHubReleaseTag_Rebase_ShouldProcessVerboseWarning = Are you sure you want switch to and rebase the local branch '{0}'? # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. @@ -52,6 +53,13 @@ ConvertFrom-StringData @' # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. New_SamplerGitHubReleaseTag_SwitchBack_ShouldProcessCaption = Switch to previous branch - ## Update-GitBranch - Update_GitBranch_FailedUnstagedChanges = There are unstaged or staged changes. Please commit or stash your changes before proceeding. + ## Switch-GitLocalBranch + Switch_GitLocalBranch_FailedCheckoutLocalBranch = Failed to checkout the local branch '{0}'. Make sure the branch exists and is accessible. + + ## Update-RemoteTrackingBranch + Update_RemoteTrackingBranch_FailedFetchBranchFromRemote = Failed to fetch from '{0}'. Make sure the branch exists in the remote git repository and the remote is accessible. + Update_RemoteTrackingBranch_FetchUpstream_ShouldProcessVerboseDescription = Fetching branch '{0}' from the upstream remote '{1}'. + Update_RemoteTrackingBranch_FetchUpstream_ShouldProcessVerboseWarning = Are you sure you want to fetch branch '{0}' from the upstream remote '{1}'? + # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. + Update_RemoteTrackingBranch_FetchUpstream_ShouldProcessCaption = Fetch upstream branch '@