Git-PsRadar.psm1
<#
.SYNOPSIS A heads up display for git. A port of https://github.com/michaeldfallen/git-radar .DESCRIPTION Provides an at-a-glance information about your git repo. .LINK https://github.com/vincpa/git-psradar #> $upDownArrow = ([Convert]::ToChar(23)) $upArrow = ([Convert]::ToChar(24)) $downArrow = ([Convert]::ToChar(25)) $rightArrow = ([Convert]::ToChar(26)) function Write-Chost($message = ""){ if ( $message ){ # predefined Color Array $colors = @("black","blue","cyan","darkblue","darkcyan","darkgray","darkgreen","darkmagenta","darkred","darkyellow","gray","green","magenta","red","white","yellow"); # Set CurrentColor to default Foreground Color $CurrentColor = $defaultFGColor # Split Messages $message = $message.split("#") # Iterate through splitted array foreach( $string in $message ){ if ($string) { # If a string between #-Tags is equal to any predefined color, and is equal to the defaultcolor: set current color if ( $colors -contains $string.tolower()){ $CurrentColor = $string }else{ # If string is a output message, than write string with current color (with no line break) if ($CurrentColor -ne $null -and $CurrentColor -ne -1) { write-host -nonewline -f $CurrentColor $string } else { write-host -nonewline $string } } } } } } function Get-StatusString($porcelainString) { $results = @{ Staged = @{ Modified = 0; Deleted = 0; Added = 0; Renamed = 0; Copied = 0; }; Unstaged = @{ Modified = 0; Deleted = 0; Renamed = 0; Copied = 0; }; Untracked = @{ Added = 0; }; Conflicted = @{ ConflictUs = 0; ConflictThem = 0; Conflict = 0; } } if ($porcelainString -ne '' -and $porcelainStatus -ne $null) { $porcelainString.Split([Environment]::NewLine) | % { if ($_[0] -eq 'R') { $results.Staged.Renamed++; } elseif ($_[0] -eq 'A') { $results.Staged.Added++ } elseif ($_[0] -eq 'D') { $results.Staged.Deleted++ } elseif ($_[0] -eq 'M') { $results.Staged.Modified++ } elseif ($_[0] -eq 'C') { $results.Staged.Copied++ } if ($_[1] -eq 'R') { $results.Unstaged.Renamed++ } elseif ($_[1] -eq 'D') { $results.Unstaged.Deleted++ } elseif ($_[1] -eq 'M') { $results.Unstaged.Modified++ } elseif ($_[1] -eq 'C') { $results.Unstaged.Copied++ } if($_[1] -eq '?') { $results.Untracked.Added++ } elseif ($_[1] -eq 'U') { $results.Conflicted.ConflictUs++ } elseif ($_[1] -eq 'T') { $results.Conflicted.ConflictThem++ } elseif ($_[1] -eq 'B') { $results.Conflicted.Conflict++ } } } return $results } function Get-Staged($seed, $status, $color, $showNewFiles, $onlyShowNewFiles) { $result = (Get-StatusCountFragment $seed $status.Added 'A' $color) $result = (Get-StatusCountFragment $result $status.Renamed 'R' $color) $result = (Get-StatusCountFragment $result $status.Deleted 'D' $color) $result = (Get-StatusCountFragment $result $status.Modified 'M' $color) $result = (Get-StatusCountFragment $result $status.Copied 'C' $color) $result = (Get-StatusCountFragment $result $status.ConflictUs 'U' $color) $result = (Get-StatusCountFragment $result $status.ConflictThem 'T' $color) $result = (Get-StatusCountFragment $result $status.Conflict 'B' $color) $result = (Get-StatusCountFragment $result $status.RemoteAhead $downArrow $color) $result = (Get-StatusCountFragment $result $status.LocalAhead $upArrow $color) if (-not [string]::IsNullOrWhiteSpace($result) -and $seed.Length -ne $result.Length) { $result += ' ' } return $result } function Get-StatusCountFragment($seed, $count, $symbol, $color) { if ($count -gt 0) { return "$seed#white#$count#$color#$symbol" } return $seed; } function Get-FilesStatus() { $porcelainStatus = git status --porcelain; $status = Get-StatusString $porcelainStatus $result = (Get-Staged "" $status.Conflicted Yellow) $result = (Get-Staged $result $status.Staged Green) $result = (Get-Staged $result $status.Unstaged Magenta) $result = (Get-Staged $result $status.Untracked Gray) return ' ' + $result } function Get-CommitStatus($currentBranch) { $remoteAheadCount = 0 $localAheadCount = 0 $remoteBranchName = $null # get remote name of the current branch, i.e. origin $remoteName = git config --get "branch.$currentBranch.remote" if ($remoteName -eq $null) { $remoteName = 'origin' # Still haven't found a way to get the remote name when on the master branch } $remoteBranchName = git config --get "branch.$currentBranch.merge" if ($remoteBranchName -eq $null -and $currentBranch -eq 'master') { $remoteBranchName = 'master' # Need to find out how to determine the remote branch name when on the master branch } if ($remoteBranchName -ne $null) { # We only need the remote branch name $remoteBranchName = $remoteBranchName.Substring($remoteBranchName.LastIndexOf('/') + 1) # Get remote commit count ahead of current branch $remoteAheadCount = git rev-list --left-only --count $remoteName'/'$remoteBranchName...HEAD $localAheadCount = git rev-list --right-only --count $remoteName'/'$remoteBranchName...HEAD $result = "" if ($remoteAheadCount -gt 0 -and $localAheadCount -gt 0) { $result = " #white#$remoteAheadCount#yellow#$downArrow$upArrow#white#$localAheadCount" } else { $remoteCounts = @{ RemoteAhead = $remoteAheadCount; } $result = Get-Staged " " $remoteCounts Green $remoteCounts = @{ LocalAhead = $localAheadCount; } $result = (Get-Staged $result $remoteCounts Magenta).TrimEnd() } } return "#darkgray#git:($currentBranch$result#darkgray#)" } # Does not raise an error when outside of a git repo function Test-GitRepo($location) { $directoryInfo = $location; if ($location -is [System.Management.Automation.PathInfo]) { if ($location.Provider.Name -eq 'FileSystem' -and (-not $location.ProviderPath.StartsWith('\\'))) { $directoryInfo = ([System.IO.DirectoryInfo]$location.Path) } } if ($directoryInfo -eq $null) { return } if ($directoryInfo -is [System.IO.DirectoryInfo]) { $gs = $directoryInfo.GetDirectories(".git"); if ($gs.Length -eq 0) { return Test-GitRepo($directoryInfo.Parent); } return $directoryInfo.FullName; } } function TimeToUpdate($lastUpdatePath) { if ((Test-Path $lastUpdatePath)){ return (Get-Date).Subtract((Get-Item $lastUpdatePath).LastWriteTime).TotalMinutes -gt 5 } else { return $true } return $false } function Begin-SilentFetch($gitRepoPath) { $lastUpdatePath = $gitRepoPath + '\.git\lastupdatetime' if (TimeToUpdate $lastUpdatePath) { echo $null > $lastUpdatePath Remove-Job -Name 'gitfetch' -Force -ErrorAction SilentlyContinue Start-Job -Name 'gitfetch' -ArgumentList $gitRepoPath, $lastUpdatePath -ScriptBlock { param($gitRepoPath, $lastUpdatePath) git -C $gitRepoPath fetch --quiet } } } function Show-PsRadar($gitRoot, $currentPath) { if($gitRoot -ne $null) { #Get current branch name $currentBranch = git symbolic-ref --short HEAD if ($currentBranch -ne $NULL) { $commitStatus = Get-CommitStatus $currentBranch $fileStatus = (Get-FilesStatus).TrimEnd() $repoName = ($gitRoot.Substring($gitRoot.LastIndexOf('\') + 1) + $currentPath.Substring($gitRoot.Length)).Replace('\', '/') Write-Host "$rightArrow " -NoNewline -ForegroundColor Green Write-Host "$repoName/ " -NoNewline -ForegroundColor DarkCyan Write-Chost $commitStatus Write-Chost $fileStatus Begin-SilentFetch $gitRepoPath return $true } } return $false } Export-ModuleMember -Function Show-GitPsRadar, Test-GitRepo -WarningAction SilentlyContinue -WarningVariable $null # Get the existing prompt function if ($Script:originalPrompt -eq $null) { $Script:originalPrompt = (Get-Item function:prompt).ScriptBlock } function global:prompt { $currentLocation = Get-Location $currentPath = $currentLocation.ProviderPath $gitRepoPath = Test-GitRepo $currentLocation # Change the prompt as soon as we enter a git repository if ($gitRepoPath -ne $null -and (Show-PsRadar $gitRepoPath $currentPath)) { return "> " } else { Invoke-Command $Script:originalPrompt } } |