functions/Format-GitDiff.ps1
function Format-GitDiff { <# .SYNOPSIS Formats GitCommitDiff objects as a readable diff. .DESCRIPTION Formats GitCommitDiff objects as a readable diff. The diff contains two parts: the summary and the diff lines. The summary contains the following: - An overall summary of all changed files, inserted lines, deleted lines e.g: 3 files changed, 78 insertions(+), 18 deletions(-) - A summary of each changed file in the format "<Type> : <delete lines count>- : <added lines count>+ : <filepath>", e.g.: Add : 0- : 55+ : MyFolder/MyFirstFile.txt Delete : 1- : 0+ : MyFolder/MySecondFile.txt Modify : 17- : 23+ : MyFolder/MyThirdFile.txt The diff lines contains the following for each file in the diff: - A summary of the changed file, similar to that included in the summary information - A series of lines showing the line changes for the file in the format "<old line number>→<new line number> <change type indicator> <line text>". Removed lines have an old line number but no new line number, and a "-" indicator. Added lines have a new line number but no old line number, and a "+" indicator. Changed lines are represented as a removed lined and an added line. E.g.: Modify : 2- : 2+ : MyFile.txt 1→1 The first line of text 2→ - This line needs to be updated 3→ - This line needs to be removed →2 + This line was updated 4→3 This line shouldn't be changed →4 + This line was added The colors that summary, old, new, and unchanged lines in the diff appear with when outputting to the console host can be controlled by setting the following module variable values: $Powdrgit.DiffLineColorNew = 'Cyan' $Powdrgit.DiffLineColorOld = 'Magenta' $Powdrgit.DiffLineColorSame = 'DarkGray' $Powdrgit.DiffLineColorSummary = 'Gray' Any color values recognized by Write-Host as a foreground color can be used. To output the text as string objects (e.g. to write to a file), use the PassThru switch parameter. .PARAMETER InputObject GitCommitDiff object from e.g. Get-GitDiff. .PARAMETER NoSummary Suppresses the diff summary information. .PARAMETER NoLines Suppresses the diff line information. .PARAMETER PassThru Outputs the formatted diff as an array of strings. .EXAMPLE ## Get formatted summary and line diff information for a diff ## PS C:\> $Powdrgit.Path = 'C:\PowdrgitExamples\Project2' # to ensure the repository paths are defined PS C:\> Set-GitBranch -Repo Project2 -BranchName main -SetLocation # move to the repository directory and checkout the main branch PS C:\PowdrgitExamples\Project2> $commitHash = Get-GitLog -NoMerges | Where-Object Subject -eq "Update Mary's bio" | Select-Object -ExpandProperty SHA1Hash # pick a commit from the log PS C:\PowdrgitExamples\Project2> $diff = Get-GitDiff -SHA1HashFrom $commitHash PS C:\PowdrgitExamples\Project2> $diff | Format-GitDiff 2 files changed, 2 insertions(+), 2 deletions(-) Add : 0- : 2+ : Jack.txt Delete : 2- : 0+ : Mary.txt Add : 0- : 2+ : Jack.txt →1 + Little Jack Horner →2 + Sat in the corner Delete : 2- : 0+ : Mary.txt 1→ - Mary had a little lamb 2→ - It's fleece was white as snow # Summary and line diff information is shown for all files in the diff. .EXAMPLE ## Get formatted summary information only for a diff ## PS C:\> $Powdrgit.Path = 'C:\PowdrgitExamples\Project2' # to ensure the repository paths are defined PS C:\> Set-GitBranch -Repo Project2 -BranchName main -SetLocation # move to the repository directory and checkout the main branch PS C:\PowdrgitExamples\Project2> $commitHash = Get-GitLog -NoMerges | Where-Object Subject -eq "Update Mary's bio" | Select-Object -ExpandProperty SHA1Hash # pick a commit from the log PS C:\PowdrgitExamples\Project2> $diff = Get-GitDiff -SHA1HashFrom $commitHash PS C:\PowdrgitExamples\Project2> $diff | Format-GitDiff -NoLines 2 files changed, 2 insertions(+), 2 deletions(-) Add : 0- : 2+ : Jack.txt Delete : 2- : 0+ : Mary.txt # Only summary information is shown for all files in the diff. .EXAMPLE ## Get formatted line diff information only for a diff ## PS C:\> $Powdrgit.Path = 'C:\PowdrgitExamples\Project2' # to ensure the repository paths are defined PS C:\> Set-GitBranch -Repo Project2 -BranchName main -SetLocation # move to the repository directory and checkout the main branch PS C:\PowdrgitExamples\Project2> $commitHash = Get-GitLog -NoMerges | Where-Object Subject -eq "Update Mary's bio" | Select-Object -ExpandProperty SHA1Hash # pick a commit from the log PS C:\PowdrgitExamples\Project2> $diff = Get-GitDiff -SHA1HashFrom $commitHash PS C:\PowdrgitExamples\Project2> $diff | Format-GitDiff -NoSummary Add : 0- : 2+ : Jack.txt →1 + Little Jack Horner →2 + Sat in the corner Delete : 2- : 0+ : Mary.txt 1→ - Mary had a little lamb 2→ - It's fleece was white as snow # Only line diff information is shown for all files in the diff. .EXAMPLE ## Write formatted summary and line diff information to a text file ## PS C:\> $Powdrgit.Path = 'C:\PowdrgitExamples\Project2' # to ensure the repository paths are defined PS C:\> Set-GitBranch -Repo Project2 -BranchName main -SetLocation # move to the repository directory and checkout the main branch PS C:\PowdrgitExamples\Project2> $commitHash = Get-GitLog -NoMerges | Where-Object Subject -eq "Update Mary's bio" | Select-Object -ExpandProperty SHA1Hash # pick a commit from the log PS C:\PowdrgitExamples\Project2> $diff = Get-GitDiff -SHA1HashFrom $commitHash PS C:\PowdrgitExamples\Project2> $file = New-TemporaryFile PS C:\PowdrgitExamples\Project2> $diff | Format-GitDiff -PassThru | Add-Content -Path $file PS C:\PowdrgitExamples\Project2> Get-Content -Path $file 2 files changed, 2 insertions(+), 2 deletions(-) Add : 0- : 2+ : Jack.txt Delete : 2- : 0+ : Mary.txt Add : 0- : 2+ : Jack.txt →1 + Little Jack Horner →2 + Sat in the corner Delete : 2- : 0+ : Mary.txt 1→ - Mary had a little lamb 2→ - It's fleece was white as snow .NOTES Author : nmbell .LINK Get-GitCommit .LINK Get-GitDiff .LINK Get-GitCommitFile .LINK Get-GitFileHistory .LINK Get-GitLog .LINK Get-GitRepo .LINK about_powdrgit .LINK https://github.com/nmbell/powdrgit/blob/main/help/about_powdrgit.md #> # Function alias [Alias('fgd')] # Use cmdlet binding [CmdletBinding( HelpURI = 'https://github.com/nmbell/powdrgit/blob/main/help/Format-GitDiff.md' )] # Declare output type [OutputType([System.String])] # Declare parameters Param ( [Parameter( Mandatory = $true , HelpMessage = 'xHelpMessagex' #displays this message in the prompt that appears when a mandatory parameter value is missing from a command , Position = 0 #0-based; the Position argument takes precedence over the value of the PositionalBinding argument for the parameters on which it's declared , ValueFromPipeline = $true #indicates that the parameter accepts input from a pipeline object. Specify this argument if the function accepts the entire object, not just a property of the object. , ValueFromPipelineByPropertyName = $true #indicates that the parameter accepts input from a property of a pipeline object. The object property must have the same name or alias as the parameter. )] [AllowEmptyCollection()] [AllowNull()] [GitCommitDiff[]] $InputObject , [Switch] $NoSummary , [Switch] $NoLines , [Switch] $PassThru ) BEGIN { # Common BEGIN: # Set-StrictMode -Version 3.0 $thisFunctionName = $MyInvocation.MyCommand $start = Get-Date $indent = ($Powdrgit.DebugIndentChar[0]+' ')*($PowdrgitCallDepth++) $PSDefaultParameterValues += @{ '*:Verbose' = $(If ($DebugPreference -notin 'Ignore','SilentlyContinue') { $DebugPreference } Else { $VerbosePreference }) } # turn on Verbose with Debug # $warn = $Powdrgit.ShowWarnings -and !($PSBoundParameters.ContainsKey('WarningAction') -and $PSBoundParameters.WarningAction -eq 'Ignore') # because -WarningAction:Ignore is not implemented correctly Write-Debug " $(ts)$indent[$thisFunctionName][$bk]Start: $($start.ToString('yyyy-MM-dd HH:mm:ss.fff'))" # Function BEGIN: $wgo = @{ OutputType = 'Body' OutputStream = If ($PassThru) { 'Pipe' } Else { $Powdrgit.DiffLineColorSummary } } } PROCESS { ForEach ($diff in $InputObject) { # For spacing line numbers evenly $oldMax = $diff.File.DiffLine.LineNumBefore | ForEach-Object { $_.Length } | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum $newMax = $diff.File.DiffLine.LineNumAfter | ForEach-Object { $_.Length } | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum # Output diff summary info If (!$NoSummary) { $wgo.OutputStream = If ($PassThru) { 'Pipe' } Else { $Powdrgit.DiffLineColorSummary } Write-GitOut @wgo $diff.Summary ForEach ($file in $diff.File) { $outLine = '{0,-8} : ' -f $file.Action $outLine += "{0,$oldMax}- : {1,$newMax}+ : " -f $file.Old,$file.New $outLine += $file.Path $outLine += If ($file.PathNew) { " → $($file.PathNew)" } Else { '' } Write-GitOut @wgo $outLine } Write-GitOut @wgo } # Output diff line info If (!$NoLines) { ForEach ($file in $diff.File) { $outLine = '{0,-8} : ' -f $file.Action $outLine += "{0,$oldMax}- : {1,$newMax}+ : " -f $file.Old,$file.New $outLine += $file.Path $outLine += If ($file.PathNew) { " → $($file.PathNew)" } Else { '' } $wgo.OutputStream = If ($PassThru) { 'Pipe' } Else { $Powdrgit.DiffLineColorSummary } Write-GitOut @wgo $outLine If ($file.Action -notin 'Copy','Rename') { ForEach ($line in $file.DiffLine) { If (!$PassThru) { $fgColor = If ($line.LineChange -eq '+') { $Powdrgit.DiffLineColorNew } ElseIf ($line.LineChange -eq '-') { $Powdrgit.DiffLineColorOld } Else { $Powdrgit.DiffLineColorSame } $wgo.OutputStream = $fgColor } Write-GitOut @wgo ("{0,$oldMax}→{1,$newMax} {2} {3}" -f $line.LineNumBefore,$line.LineNumAfter,$line.LineChange,$line.LineText) } } Write-GitOut @wgo } } } } END { # Function END: # Common END: $end = Get-Date $duration = New-TimeSpan -Start $start -End $end Write-Verbose "[$thisFunctionName]Stopped: $($end.ToString('yyyy-MM-dd HH:mm:ss.fff')) ($($duration.ToString('d\d\ hh\:mm\:ss\.fff')))" } } |