PSSvnBranch.psm1

class BranchStatus {
    [string]    $Url
    [bool]      $Merged
    [int[]]     $Revisions
}

class BranchDetails {
    [string]                $Url
    [bool]                  $Merged
    [PoshSvn.SvnLogOutput]  $Created
    [int[]]                 $Revisions
}

class BranchAction {
    [string]    $BranchName
    [int]       $MergeRevision
    [string]    $MergeLogMessage
    [bool]      $FullMerge
    [int[]]     $UnmergedRevisions
}

function Get-SvnBranchSummary {
    [CmdletBinding()]
    [OutputType([BranchStatus])]
    param (
        [string][Parameter(Mandatory = $true)]
        $RootUrl,

        [string][Parameter(Mandatory = $true)]
        $BranchName
    )

    process {
        $url = "$RootUrl/branches/$BranchName"
        $trunk = "$RootUrl/trunk"

        $eligible_revs = svn-mergeinfo -Source $url -Target $trunk -ShowRevisions Eligible -ProgressAction SilentlyContinue

        if ($null -eq $eligible_revs) {
            $obj = [BranchStatus]@{
                Url    = $url
                Merged = $true
            }
            Write-Output $obj
        }
        else {
            $obj = [BranchStatus]@{
                Url       = $url
                Merged    = $false
                Revisions = ($eligible_revs | ForEach-Object { $_.Revision })
            }
            Write-Output $obj
        }
    }
}

function Get-SvnProjectStatus {
    [CmdletBinding()]
    [OutputType([BranchStatus])]
    param (
        [string][Parameter(Mandatory = $true)]
        $RootUrl,

        [string][Parameter(Mandatory = $false)]
        $Trunk = "trunk"
    )

    process {
        $branchesUrl = "$RootUrl/branches"

        Write-Progress -activity "Collecting branches..." -status $branchesUrl -PercentComplete 0

        $branches = svn-list $branchesUrl -ProgressAction SilentlyContinue
        $i = 0

        $branches | ForEach-Object {
            $url = $_.Uri.ToString()
            $i++

            $obj = Get-SvnBranchSummary -RootUrl $RootUrl -BranchName $_.Name
            Write-Output $obj

            Write-Progress -activity "Retrieving branch information..." -status $url -PercentComplete ($i * 100 / $branches.Count)
        }
    }
}

function Get-SvnBranchDetails {
    [CmdletBinding()]
    [OutputType([BranchDetails])]
    param (
        [string][Parameter(Mandatory = $true)]
        $RootUrl,

        [string][Parameter(Mandatory = $true)]
        $BranchName
    )

    process {
        $url = "$RootUrl/branches/$BranchName"
        $trunk = "$RootUrl/trunk"

        $mergeinfo = svn-mergeinfo -Source $url -Target $trunk -ProgressAction SilentlyContinue
        $created = svn-log -Target $mergeinfo.BaseUrl.ToString() -Revision $mergeinfo.BaseRevision -ProgressAction SilentlyContinue
        $summary = Get-SvnBranchSummary -RootUrl $RootUrl -BranchName $BranchName

        $obj = [BranchDetails]@{
            Url       = $url
            Merged    = $summary.Merged
            Created   = $created
            Revisions = $summary.Revisions
        }
        Write-Output $obj
    }
}

function Search-SvnBranchHistory {
    [CmdletBinding()]
    [OutputType([BranchAction])]
    param (
        [string][Parameter(Mandatory = $true)]
        $RootUrl,
        [int][Parameter(Mandatory = $false)]
        $Limit = -1,
        [PoshSvn.SvnRevision][Parameter(Mandatory = $false)]
        $StartRevision = "HEAD"
    )

    process {
        $trunk = "$RootUrl/trunk"
        $branchesUrl = "$RootUrl/branches"

        $branches = svn-list $branchesUrl -ProgressAction SilentlyContinue
        $rev = New-Object PoshSvn.SvnRevisionRange $StartRevision, 0
        $log = svn-log $trunk -Revision $rev -Limit $Limit -ProgressAction SilentlyContinue

        $i = 0
        $log | ForEach-Object {
            Write-Progress -activity "Scanning history" -status "Processing revision: " -PercentComplete ($i * 100 / $log.Count)
            $diff = svn-diff $trunk -Depth Empty -Change $_.Revision -ProgressAction SilentlyContinue
            $rev = $_.Revision
            $message = $_.Message

            if ($diff) {
                $branches | ForEach-Object {
                    $branchName = $_.Name

                    if ($diff | Select-String -SimpleMatch "Merged $($_.BasePath)/${branchName}:") {
                        $summary = Get-SvnBranchSummary -RootUrl $RootUrl -BranchName $branchName

                        $obj = [BranchAction]@{
                            BranchName        = $branchName
                            MergeRevision     = $rev
                            MergeLogMessage   = $message
                            FullMerge         = $summary.Merged
                            UnmergedRevisions = $summary.Revisions
                        }
                        Write-Output $obj
                    }
                }
            }
        }
    }
}