Skip to content

Instantly share code, notes, and snippets.

@nightroman nightroman/FarGit.psm1
Last active Mar 26, 2019

Embed
What would you like to do?
Interactive Git commands for Far Manager + FarNet.PowerShellFar
<#
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}
$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
$branch = $Far.Input('Branch name', 'GitBranch', 'New branch')
if ($branch) {
Invoke-Git {git checkout -b $branch}
}
}
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 {
try {
Assert-Git
$branch = $Far.Input('Branch name', 'GitBranch', 'Rename branch', (Get-BranchCurrent))
if ($branch) {
Invoke-Git {git branch -m $branch}
}
}
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
You can’t perform that action at this time.