-
-
Save astrolemonade/bb34704d716aed8afeea5ce236fe91d1 to your computer and use it in GitHub Desktop.
Interactive Git commands for Far Manager + FarNet.PowerShellFar
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| Interactive Git commands for Far Manager + FarNet.PowerShellFar | |
| <https://gist.github.com/nightroman/1d4806e4bcd2fae1b852> | |
| HOW TO USE | |
| Create a directory FarGit in any of $env:PSModulePath. As far as it is for | |
| PowerShellFar only consider %FARHOME%\FarNet\Modules\PowerShellFar\Modules | |
| Download FarGit.psm1 to the created directory FarGit. | |
| Import the module and call its commands in PowerShellFar. | |
| Or add these commands to your Far Manager user menu: | |
| h: Show help (user menu needs Far 3.0.4330+) | |
| ps: Import-Module FarGit; Show-FarGitHelp # | |
| s: Show status | |
| ps: Import-Module FarGit; Show-FarGitStatus # | |
| r: Go to root | |
| ps: Import-Module FarGit; Set-FarGitRoot # | |
| e: Edit config | |
| ps: Import-Module FarGit; Edit-FarGitConfig # | |
| : Branch | |
| { | |
| b: Switch branch | |
| ps: Import-Module FarGit; Invoke-FarGitCheckoutBranch # | |
| c: Create branch | |
| ps: Import-Module FarGit; Invoke-FarGitBranchCreate # | |
| m: Merge branch | |
| ps: Import-Module FarGit; Invoke-FarGitMergeBranch # | |
| d: Delete branch (safe) | |
| ps: Import-Module FarGit; Invoke-FarGitBranchDelete # | |
| k: Delete branch (force) | |
| ps: Import-Module FarGit; Invoke-FarGitBranchDelete -Force # | |
| r: Rename current branch | |
| ps: Import-Module FarGit; Invoke-FarGitBranchRename # | |
| } | |
| : Stash | |
| { | |
| a: Apply stash | |
| ps: Import-Module FarGit; Invoke-FarGitStashApply # | |
| p: Pop stash | |
| ps: Import-Module FarGit; Invoke-FarGitStashPop # | |
| d: Drop stash | |
| ps: Import-Module FarGit; Invoke-FarGitStashDrop # | |
| s: Show stash | |
| ps: Import-Module FarGit; Invoke-FarGitStashShow # | |
| } | |
| #> | |
| $ErrorActionPreference = 'Stop' | |
| ### Exported commands. | |
| <# | |
| .Synopsis | |
| Shows git help for the git command. | |
| .Description | |
| The command shows HTML help the git command found in the editor, dialog, or | |
| command line. If 'git command' is not found then the main page is shown. | |
| #> | |
| function Show-FarGitHelp { | |
| if ($Far.Line.Text -match '\bgit\s+(\S+)') { | |
| $null = Invoke-Git {git ($matches[1]) --help} | |
| } | |
| else { | |
| Invoke-Item -LiteralPath "$(Invoke-Git {git --html-path})\index.html" | |
| } | |
| } | |
| <# | |
| .Synopsis | |
| Opens the current config file in the editor. | |
| #> | |
| function Edit-FarGitConfig { | |
| try { | |
| Assert-Git | |
| $root = Invoke-Git {git rev-parse --git-dir} | |
| Open-FarEditor $root\config | |
| } | |
| catch { | |
| Show-Error | |
| } | |
| } | |
| <# | |
| .Synopsis | |
| Sets the repository root current in the panel. | |
| #> | |
| function Set-FarGitRoot { | |
| try { | |
| Assert-Git | |
| $root = Invoke-Git {git rev-parse --show-toplevel} | |
| $Far.Panel.CurrentDirectory = $root | |
| } | |
| catch { | |
| Show-Error | |
| } | |
| } | |
| <# | |
| .Synopsis | |
| Shows changed files and navigates to a selected. | |
| .Description | |
| The command shows the list of changed files. | |
| Select a file to navigate to in the active panel. | |
| #> | |
| function Show-FarGitStatus { | |
| try { | |
| Assert-Git | |
| $info = Invoke-Git {git status --porcelain} | ConvertFrom-Octet | .{process{ | |
| ($_ -replace '^ ', '-') -replace '^(.) ', '$1-' | |
| }} | Out-FarList -Title "On branch $(Get-BranchCurrent)" | |
| if (!$info) {return} | |
| if ($info -notmatch '^(..)\s+"?(.+?)"?(?:\s+->\s+"?(.+?)"?)?$') {throw "Unexpected status format: $info"} | |
| $path = if ($matches[3]) {$matches[3]} else {$matches[2]} | |
| $Far.Panel.GoToPath("$(Invoke-Git {git rev-parse --show-toplevel})\$path") | |
| } | |
| catch { | |
| Show-Error | |
| } | |
| } | |
| <# | |
| .Synopsis | |
| Switches to another branch. | |
| .Description | |
| The command shows the branch list. | |
| Select a branch to be set the current. | |
| If there are no branches the command prompts to create a new branch. | |
| #> | |
| function Invoke-FarGitCheckoutBranch { | |
| try { | |
| Assert-Git | |
| if (!($branch = Get-BranchOther)) { | |
| if (0 -eq (Show-FarMessage 'No other branches. Create?' -Buttons YesNo)) { | |
| Invoke-FarGitBranchCreate | |
| } | |
| return | |
| } | |
| $branch = $branch | Out-FarList -Title "Switch from $(Get-BranchCurrent)" | |
| if ($branch) { | |
| Invoke-Git {git checkout $branch} | |
| } | |
| } | |
| catch { | |
| Show-Error | |
| } | |
| } | |
| <# | |
| .Synopsis | |
| Merges another branch into the current. | |
| .Description | |
| The command shows the branch list. | |
| Select a branch to merge into the current. | |
| #> | |
| function Invoke-FarGitMergeBranch { | |
| try { | |
| Assert-Git | |
| if (!($branch = Get-BranchOther)) { | |
| Show-FarMessage 'No other branches.' | |
| return | |
| } | |
| $branch = $branch | Out-FarList -Title "Merge to $(Get-BranchCurrent)" | |
| if ($branch) { | |
| Invoke-Git {git merge $branch} | |
| } | |
| } | |
| catch { | |
| Show-Error | |
| } | |
| } | |
| <# | |
| .Synopsis | |
| Deletes another branch. | |
| .Parameter Force | |
| Tells to delete a branch irrespective of its merged status. | |
| .Description | |
| The command shows the branch list. | |
| Select a branch to be deleted. | |
| By default the branch must be fully merged in its upstream branch. Use the | |
| switch Force in order to delete a branch irrespective of its merged status. | |
| #> | |
| function Invoke-FarGitBranchDelete { | |
| [CmdletBinding()] | |
| param( | |
| [switch]$Force | |
| ) | |
| try { | |
| Assert-Git | |
| if (!($branch = Get-BranchOther)) { | |
| Show-FarMessage 'No other branches.' | |
| return | |
| } | |
| $branch = $branch | Out-FarList -Title 'Delete branch' | |
| if (!$branch) {return} | |
| #?? | |
| if ($branch -ceq 'master') { | |
| Show-FarMessage 'Cannot delete master.' | |
| return | |
| } | |
| $message = if ($Force) {"Delete branch $branch (force)"} else {"Delete branch $branch"} | |
| if (0 -ne (Show-FarMessage $message -Buttons YesNo -IsWarning:$Force)) {return} | |
| if ($Force) { | |
| Invoke-Git {git branch -D $branch} | |
| } | |
| else { | |
| Invoke-Git {git branch -d $branch} | |
| } | |
| } | |
| catch { | |
| Show-Error | |
| } | |
| } | |
| <# | |
| .Synopsis | |
| Creates a new branch and switches to it. | |
| .Description | |
| The command shows the input box. | |
| Enter the new branch name and press enter. | |
| #> | |
| function Invoke-FarGitBranchCreate { | |
| try { | |
| Assert-Git | |
| $currentBranch = Get-BranchCurrent | |
| $newBranch = if ($currentBranch -eq 'master') {''} else {$currentBranch} | |
| $newBranch = $Far.Input('Branch name', 'GitBranch', "New branch from $currentBranch", $newBranch) | |
| if ($newBranch) { | |
| Invoke-Git {git checkout -b $newBranch} | |
| } | |
| } | |
| catch { | |
| Show-Error | |
| } | |
| } | |
| <# | |
| .Synopsis | |
| Renames the current branch. | |
| .Description | |
| The command shows the input box with the current branch name. | |
| Enter the new branch name and press enter. | |
| #> | |
| function Invoke-FarGitBranchRename { | |
| param ( | |
| [switch]$DatePrefix | |
| ) | |
| try { | |
| Assert-Git | |
| $currentBranch = Get-BranchCurrent | |
| $newBranch = if ($DatePrefix -and $currentBranch -notmatch '^\d\d\d\d\d\d-') { | |
| '{0:yyMMdd}-{1}' -f (Get-Date), $currentBranch | |
| } | |
| else { | |
| $currentBranch | |
| } | |
| $newBranch = $Far.Input('Branch name', 'GitBranch', "Rename branch $currentBranch", $newBranch) | |
| if ($newBranch) { | |
| Invoke-Git {git branch -m $newBranch} | |
| } | |
| } | |
| catch { | |
| Show-Error | |
| } | |
| } | |
| <# | |
| .Synopsis | |
| Invokes git stash apply. | |
| .Description | |
| The command shows the stash list. | |
| Select a stash to be applied. | |
| #> | |
| function Invoke-FarGitStashApply { | |
| try { | |
| Assert-Git | |
| if ($stash = Select-StashName 'Apply stash') { | |
| Invoke-Git {git stash apply $stash} | |
| } | |
| } | |
| catch { | |
| Show-Error | |
| } | |
| } | |
| <# | |
| .Synopsis | |
| Invokes git stash drop. | |
| .Description | |
| The command shows the stash list. | |
| Select a stash to be dropped. | |
| #> | |
| function Invoke-FarGitStashDrop { | |
| try { | |
| Assert-Git | |
| if ($stash = Select-StashName 'Drop stash') { | |
| Invoke-Git {git stash drop $stash} | |
| } | |
| } | |
| catch { | |
| Show-Error | |
| } | |
| } | |
| <# | |
| .Synopsis | |
| Invokes git stash pop. | |
| .Description | |
| The command shows the stash list. | |
| Select a stash to be popped. | |
| #> | |
| function Invoke-FarGitStashPop { | |
| try { | |
| Assert-Git | |
| if ($stash = Select-StashName 'Pop stash') { | |
| Invoke-Git {git stash pop $stash} | |
| } | |
| } | |
| catch { | |
| Show-Error | |
| } | |
| } | |
| <# | |
| .Synopsis | |
| Invokes git stash show. | |
| .Description | |
| The command shows the stash list. | |
| Select a stash to show its patch details. | |
| #> | |
| function Invoke-FarGitStashShow { | |
| try { | |
| Assert-Git | |
| if ($stash = Select-StashName 'Show stash') { | |
| Invoke-Git {git stash show -p $stash} | |
| } | |
| } | |
| catch { | |
| Show-Error | |
| } | |
| } | |
| ### Internal commands. | |
| # Decodes git strings with octets. | |
| filter ConvertFrom-Octet { | |
| if (!$_.Contains('\')) { | |
| return $_ | |
| } | |
| $bytes = [System.Collections.ArrayList]@() | |
| foreach($m in ([regex]'\\\d\d\d|.').Matches($_)) { | |
| $t = $m.ToString() | |
| if (($b = $t[0]) -eq '\') { | |
| $b = [Convert]::ToInt32($t.Substring(1), 8) | |
| } | |
| $null = $bytes.Add($b) | |
| } | |
| [System.Text.Encoding]::UTF8.GetString($bytes) | |
| } | |
| # Gets the current branch name. | |
| function Get-BranchCurrent { | |
| foreach($branch in Invoke-Git {git branch --list --quiet}) { | |
| if ($branch -notmatch '^(\*)?\s*(\S+)') {throw "Unexpected branch format: '$branch'."} | |
| if ($matches[1]) { | |
| return $matches[2] | |
| } | |
| } | |
| } | |
| # Gets branch names except the current. | |
| function Get-BranchOther { | |
| foreach($branch in Invoke-Git {git branch --list --quiet}) { | |
| if ($branch -notmatch '^(\*)?\s*(\S+)') {throw "Unexpected branch format: '$branch'."} | |
| if (!$matches[1]) { | |
| $matches[2] | |
| } | |
| } | |
| } | |
| # Gets stash strings. | |
| function Get-StashText { | |
| Invoke-Git {git stash list} | |
| } | |
| # Gets the stash name from a string. | |
| function Get-StashName { | |
| [CmdletBinding()] | |
| param( | |
| $Stash | |
| ) | |
| if ($Stash -notmatch '^(stash@{\d+})') {throw "Unexpected stash format: $Stash."} | |
| $matches[1] | |
| } | |
| # Shows the stash list and gets the selected stash name. | |
| function Select-StashName { | |
| [CmdletBinding()] | |
| param( | |
| $Title | |
| ) | |
| if (!($stash = Get-StashText)) { | |
| Show-FarMessage 'There are no stashes.' | |
| return | |
| } | |
| $stash = $stash | Out-FarList -Title $Title | |
| if (!$stash) {return} | |
| Get-StashName $stash | |
| } | |
| # Invokes a native command and checks for $LASTEXITCODE. | |
| function Invoke-Git($Command) { | |
| ${*OutputEncoding} = [Console]::OutputEncoding | |
| [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 | |
| try { | |
| . $Command | |
| if ($LASTEXITCODE) {Write-Error "Exit code: $LASTEXITCODE. Command: $Command" -ErrorAction 1} | |
| } | |
| finally { | |
| [Console]::OutputEncoding = ${*OutputEncoding} | |
| } | |
| } | |
| # Shows the error $_. | |
| function Show-Error { | |
| Write-Host $_ -ForegroundColor Red | |
| } | |
| # Throw if not a repo. | |
| function Assert-Git { | |
| [CmdletBinding()]param() | |
| $path = $PSCmdlet.GetUnresolvedProviderPathFromPSPath('.') | |
| do { | |
| if (Test-Path "$path\.git") {return} | |
| } while($Path = Split-Path $Path) | |
| throw 'Not a git repository.' | |
| } | |
| Export-ModuleMember -Function *-FarGit* |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment