Public/Undo-Changes.ps1
|
function Undo-Changes { <# .SYNOPSIS Throw away unsaved changes and return the working area to its last saved state. .DESCRIPTION Undo-Changes is the GitEasy-first way to abandon every unsaved edit. Because the operation is destructive, the command refuses to run without explicit confirmation: pass -Force, or accept the standard PowerShell -Confirm prompt. For a softer alternative, Save-Work -NoPush will save the current state locally first, so you can recover later if you change your mind. Each invocation writes a self-contained diagnostic log file. Successful runs log silently; failures throw a plain-English message and point at the log file with the technical detail. .PARAMETER Force Skip the confirmation prompt and discard unsaved changes immediately. .PARAMETER LogPath Override the directory where the diagnostic log for this run is written. .EXAMPLE Find-CodeChange; Undo-Changes -Force .EXAMPLE Save-Work 'checkpoint before undo' -NoPush; Undo-Changes -Force .NOTES Safety: - Always run Find-CodeChange first to see what will be discarded. - Use Save-Work -NoPush for a recoverable checkpoint before undoing. - Refuses to run during an unfinished merge, rebase, cherry-pick, revert, or bisect. - Refuses to run while there are unfinished conflicts. .LINK Find-CodeChange .LINK Restore-File .LINK Save-Work #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseSingularNouns', '', Justification = 'GitEasy uses plain-English plural names as a brand choice; singular form is exported as an alias.' )] [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] [OutputType([PSCustomObject])] [Alias('Undo-Change')] param( [Parameter()] [switch]$Force, [Parameter()] [string]$LogPath = '' ) $repoRoot = $null try { $rootProbe = Invoke-GEGit -ArgumentList @('rev-parse', '--show-toplevel') -AllowFailure if ($rootProbe.ExitCode -eq 0) { $repoRoot = $rootProbe.Output | Select-Object -First 1 } } catch { $repoRoot = $null } $session = Start-GELogSession -Command 'Undo-Changes' -Repository ([string]$repoRoot) -LogPath $LogPath $userMessageOnFailure = 'Could not undo changes.' try { Assert-GESafeSave -Path ([string]$repoRoot) -LogPath $session.Path | Out-Null if (-not $repoRoot) { $rootResult = Invoke-GEGit -ArgumentList @('rev-parse', '--show-toplevel') -LogPath $session.Path $repoRoot = $rootResult.Output | Select-Object -First 1 } $statusResult = Invoke-GEGit -ArgumentList @('status', '--porcelain=v1') -WorkingDirectory $repoRoot -LogPath $session.Path $statusLines = @($statusResult.Output | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }) if ($statusLines.Count -eq 0) { Write-Host 'Nothing to undo. The active working area is already clean.' $result = [PSCustomObject]@{ Repository = $repoRoot Discarded = 0 Message = 'Nothing to undo.' } Complete-GELogSession -Path $session.Path -Outcome 'SUCCESS' return $result } $hasExplicitOptIn = $Force -or $PSBoundParameters.ContainsKey('Confirm') -or $PSBoundParameters.ContainsKey('WhatIf') if (-not $hasExplicitOptIn) { throw "Refusing to discard unsaved changes without confirmation. Re-run with -Force to skip the prompt, pass -Confirm to be prompted explicitly, use -WhatIf to preview, or use Save-Work -NoPush first to keep a recoverable checkpoint." } if (-not $Force) { if (-not $PSCmdlet.ShouldProcess($repoRoot, "Discard $($statusLines.Count) unsaved change(s) - this cannot be reversed")) { Complete-GELogSession -Path $session.Path -Outcome 'SUCCESS' -UserMessage 'Skipped (Confirm declined or WhatIf).' return } } Invoke-GEGit -ArgumentList @('checkout', '--', '.') -WorkingDirectory $repoRoot -LogPath $session.Path | Out-Null Invoke-GEGit -ArgumentList @('clean', '-fd') -WorkingDirectory $repoRoot -LogPath $session.Path | Out-Null Write-Host "Discarded $($statusLines.Count) unsaved change(s)." $result = [PSCustomObject]@{ Repository = $repoRoot Discarded = $statusLines.Count Message = "Discarded $($statusLines.Count) unsaved change(s)." } Complete-GELogSession -Path $session.Path -Outcome 'SUCCESS' return $result } catch { $err = $_ $innerMessage = $err.Exception.Message if ($innerMessage -like 'git *') { $finalMsg = $userMessageOnFailure } else { $finalMsg = $innerMessage } Complete-GELogSession -Path $session.Path -Outcome 'FAILURE' -UserMessage $finalMsg -ErrorMessage $innerMessage throw "$finalMsg Details: $($session.Path)" } } |