Functions/Compare-GitTree.ps1

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

function Compare-GitTree
{
    <#
    .SYNOPSIS
    Gets a diff of the file tree changes in a repository between two commits.
 
    .DESCRIPTION
    The `Compare-GitTree` function returns a `LibGit2Sharp.TreeChanges` object representing the file tree changes in a repository between two commits. The tree changes are the names of the files that have been added, removed, modified, and renamed in a git repository.
 
    Pass the name of commits to diff, such as commit hash, branch name, or tag name, to the `ReferenceCommit` and `DifferenceCommit` parameters.
 
    You must specify a commit reference name for the `ReferenceCommit` parameter. The `DifferenceCommit` parameter is optional and defaults to `HEAD`.
 
    This function implements the `git diff --name-only` command.
 
    .EXAMPLE
    Compare-GitTree -ReferenceCommit 'HEAD^'
 
    Demonstrates how to get the diff between the default `HEAD` commit and its parent commit referenced by `HEAD^`.
 
    .EXAMPLE
    Compare-GitTree -RepoRoot 'C:\build\repo' -ReferenceCommit 'tags/1.0' -DifferenceCommit 'tags/2.0'
 
    Demonstrates how to get the diff between the commit tagged with `2.0` and the older commit tagged with `1.0` in the repository located at `C:\build\repo`.
    #>

    [CmdletBinding(DefaultParameterSetName='RepositoryRoot')]
    [OutputType([LibGit2Sharp.TreeChanges])]
    param(
        [Parameter(ParameterSetName='RepositoryRoot')]
        [Alias('RepoRoot')]
        [string]
        # The root path to the repository. Defaults to the current directory.
        $RepositoryRoot = (Get-Location).ProviderPath,

        [Parameter(Mandatory=$true, ParameterSetName='RepositoryObject')]
        [LibGit2Sharp.Repository]
        $RepositoryObject,

        [Parameter(Mandatory=$true)]
        [string]
        # A commit to compare `DifferenceCommit` against, e.g. commit hash, branch name, tag name.
        $ReferenceCommit,

        [string]
        # A commit to compare `ReferenceCommit` against, e.g. commit hash, branch name, tag name. Defaults to `HEAD`.
        $DifferenceCommit = 'HEAD'
    )

    Set-StrictMode -Version 'Latest'
    Use-CallerPreference -Cmdlet $PSCmdlet -Session $ExecutionContext.SessionState

    if ($RepositoryObject)
    {
        $repo = $RepositoryObject
    }
    else
    {
        $repo = Find-GitRepository -Path $RepositoryRoot -Verify
        if (-not $repo)
        {
            Write-Error -Message ('Unable to get diff between ''{0}'' and ''{1}''. See previous errors for more details.' -f $ReferenceCommit, $DifferenceCommit)
            return
        }
    }

    try
    {
        $oldCommit = $repo.Lookup($ReferenceCommit)
        $newCommit = $repo.Lookup($DifferenceCommit)

        if (-not $oldCommit)
        {
            Write-Error -Message ('Commit ''{0}'' not found in repository ''{1}''.' -f $ReferenceCommit, $repo.Info.WorkingDirectory)
            return
        }
        elseif (-not $newCommit)
        {
            Write-Error -Message ('Commit ''{0}'' not found in repository ''{1}''.' -f $DifferenceCommit, $repo.Info.WorkingDirectory)
            return
        }

        return , [Git.Automation.Diff]::GetTreeChanges($repo, $oldCommit, $newCommit)
    }
    finally
    {
        if (-not $RepositoryObject)
        {
            Invoke-Command -NoNewScope -ScriptBlock {
                $repo.Dispose()
            }
        }
    }
}