GitAliases.psm1

function Get-GitCommands {
  <#
  .Synopsis
   List all git subcommmands.
  #>


  git --help -av | Select-String -CaseSensitive -Pattern '^ +([-a-z0-9.]+)' | ForEach-Object { $_.Matches[0].Groups[1].Value }
}

function Enable-GitAliases([switch] $AllowClobber) {
  <#
  .Synopsis
   Make alias functions for the all git subcommands (collected by `Get-Command') to omit `git'.
  .Switch AllowClobber
   If specified, create an alias function even if the command is already defined (e.g. rm, mv).

  .Notes
   Even with -AllowClobber,
   if some aliases whose name conflict with git's subcommands (e.g. `git rm', `git diff', etc),
   You have to remove the alias to clobber them:

   Remove-Item -Force alias:rm
   Remove-Item -Force alias:mv
   Enable-GitAliases -AllowClobber # Then define `rm' function to wrap `git rm'

   Ref: https://stackoverflow.com/questions/24882210/how-do-i-remove-or-replace-a-built-in-alias-in-powershell
  #>


  $gitCommands = Get-GitCommands
  $targetCommands =
    if ($AllowClobber) {
      $gitCommands
    } else {
      $existingCommands = (Get-Command -ErrorAction SilentlyContinue (Get-GitCommands)).name -replace ".exe", ""
      # TODO: https://stackoverflow.com/questions/8609204/union-and-intersection-in-powershell
      $gitCommands | Where-Object { -not ($existingCommands -contains $_) }
    }
  $targetCommands | ForEach-Object { Enable-GitAlias $_ }
}

$script:enabledAliases = @{}
function Enable-GitAlias([string] $commandName) {
  <#
  .Synopsis
   Make an alias function for the specified git subcommand to omit `git'.

  .Parameter commandName
   Name of the git subcommand to make an alias function for.

  .Example
   Enable-GitAlias "status"
   > status
   On branch master
   Your branch is up to date with 'origin/master'.
   ...
  #>


  # Ref. https://serverfault.com/questions/165598/can-i-hook-powershell-to-call-a-function-each-time-i-execute-a-command
  $wrapper = @'
    function global:{0} {{
      git {0} $args
    }}
'@
 -f $commandName
  Invoke-Expression $wrapper
  $script:enabledAliases.Set_Item($commandName, $true)
}

if (Test-Path Function:\TabExpansion) {
  Rename-Item Function:\TabExpansion TabExpansionBackup
}

function TabExpansion($line, $lastWord) {
  <#
  .Synopsis
   Provides tab-expansion for functions configured with `Enable-GitAlias`
  #>


  # Ref: https://github.com/dahlbyk/posh-git/blob/da71c1ea091d7682362e4fa69f7dedb9aa9f36a7/src/GitTabExpansion.ps1#L415-L422
  $lastBlock = [regex]::Split($line, '[|;]')[-1].TrimStart()
  $matched = $lastBlock -match '^\S+'
  $commandName = $matches[0]

  if ($matched -and $script:enabledAliases.ContainsKey($commandName)) {
    Expand-GitCommand "git $lastBlock"
  } elseif (Test-Path Function:\TabExpansionBackup) {
    TabExpansionBackup $line $lastWord
  }
}

Export-ModuleMember -function Enable-GitAlias,Enable-GitAliases,Get-GitCommands,TabExpansion