Scripts/About.ps1

$ErrorActionPreference=1
. $PSScriptRoot\Basic.ps1
. $PSScriptRoot\New-FGBranchExplorer.ps1

<#
.Synopsis
    Opens panel with git branches.
 
.Description
    The current git branch is shown with (*).
    Remote branches are included as hidden.
    Keys and actions:
        [Enter]
            Checkout the current panel branch.
            If it is remote then a new local branch is created.
        [F7]
            Checkout a new branch from the current git branch.
            The current panel branch does not matter.
        [F6]
            Rename the current panel branch.
        [F8], [Del]
            Delete the current panel branch.
        [CtrlH]
            Show/hide remote branches.
#>

function Open-FarGitBranch {
    [CmdletBinding()]
    param(
        [string]$Path = '.'
    )
    $Path = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Path)
    $Root = Invoke-Error {git -C $Path rev-parse --show-toplevel}
    if ($LASTEXITCODE) {
        throw $Root
    }
    (New-FGBranchExplorer $Root).OpenPanel()
}

<#
.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-Native {git ($matches[1]) --help}
    }
    else {
        Invoke-Item -LiteralPath "$(Invoke-Native {git --html-path})\index.html"
    }
}

<#
.Synopsis
    Opens the current config file in the editor.
#>

function Edit-FarGitConfig {
    try {
        Assert-Git

        $root = Invoke-Native {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-Native {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-Native {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-Native {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-Native {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-Native {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-Native {git branch -D $branch}
        }
        else {
            Invoke-Native {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 (('main', 'master') -ccontains $currentBranch) {''} else {$currentBranch}
        $newBranch = $Far.Input('Branch name', 'GitBranch', "New branch from $currentBranch", $newBranch)
        if ($newBranch) {
            Invoke-Native {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-Native {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-Native {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-Native {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-Native {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-Native {git stash show -p $stash}
        }
    }
    catch {
        Show-Error
    }
}