Restore-GitStash.ps1
|
function Restore-GitStash { <# .SYNOPSIS Restore a stash entry using git stash pop. .DESCRIPTION Applies the selected stash to the current or specified working tree and removes that entry only when native git stash pop succeeds. Git retains the stash on an apply failure or conflict; conflicts can leave partially restored files and an unmerged index for the caller to resolve. Does not force an overwrite, resolve conflicts, or retry with apply/drop. Successful operations produce no pipeline output. .PARAMETER Path Literal directory inside the working tree. Defaults to the current location. Accepts pipeline paths and objects with Path, RepositoryPath or RepoPath. Pipeline properties select only the repository, not a stash object ID. Objects carrying ObjectId (including Save-GitStash results) require an explicit Stash selector to avoid silently restoring a different entry. Explicit empty paths are rejected rather than falling back to the caller. .PARAMETER Stash Exact stash reflog selector, such as stash@{0} or stash@{1}. Defaults to stash@{0}, the newest entry when Git runs. Bare numeric indices, object IDs, revision expressions and wildcards are not supported. The index must be a canonical nonnegative 32-bit integer. .OUTPUTS None. Native failures are PowerShell errors; use -ErrorAction Stop to terminate. The error's TargetObject contains Git's exit code and streams. .NOTES Selectors are mutable positions, not immutable stash identities. Avoid concurrent stash writers (including other worktrees of this repository) while confirming or running this command. It uses Git's native pop semantics and does not add transactional or concurrent-writer protection. The caller's location and LASTEXITCODE are unchanged. Like default git stash pop, it does not request --index to reinstate the staged state. .EXAMPLE Restore-GitStash Pops the newest stash in the current repository. .EXAMPLE Restore-GitStash -Path ../project -Stash 'stash@{1}' Pops the second-newest stash in that repository without changing location. .EXAMPLE Restore-GitStash -Path ../project -WhatIf Previews the pop without changing files, the index or the stash stack. .LINK https://git-scm.com/docs/git-stash #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('RepositoryPath', 'RepoPath')] [ValidateNotNullOrWhiteSpace()] [string]$Path, [ValidateScript({ $index = 0 $_ -cmatch '\Astash@\{(?:0|[1-9][0-9]*)\}\z' -and [int]::TryParse($_.Substring(7, $_.Length - 8), [ref]$index) }, ErrorMessage = 'Stash must be an exact stash@{n} selector with a canonical nonnegative 32-bit index, not an object ID or Git option.')] [string]$Stash = 'stash@{0}' ) process { if ($MyInvocation.ExpectingInput -and $null -ne $PSItem -and ($PSItem.PSObject.Properties['ObjectId'] -or $PSItem.PSObject.TypeNames -contains 'GitStash') -and -not $PSBoundParameters.ContainsKey('Stash')) { $PSCmdlet.WriteError([System.Management.Automation.ErrorRecord]::new( [System.ArgumentException]::new('Stash objects require an explicit -Stash ''stash@{n}'' selector. ObjectId is not a native stash pop selector.'), 'GitStashSelectorRequired', [System.Management.Automation.ErrorCategory]::InvalidArgument, $PSItem)) return } $repositoryPath = Resolve-GitRepositoryPath -Path $Path if (-not $repositoryPath) { return } if ($PSCmdlet.ShouldProcess($repositoryPath, "Pop git stash '$Stash' (apply changes and remove on success)")) { $result = Invoke-Git -Path $repositoryPath -Arguments @('stash', 'pop', '--', $Stash) if ($null -ne $result) { Write-Verbose "Restored '$Stash' in '$repositoryPath'." } } } } |