Git/Git.ps1

function Get-DiffBetweenGitBranches
{
    Param(
    [Parameter(Mandatory=$true)]
    [string]$Branch1,
    [Parameter(Mandatory=$true)]
    [string]$Branch2
    )

    $DiffObjects = @()

    $Diffs = git diff --name-status $Branch1..$Branch2 --no-renames
    foreach ($Diff in $Diffs)
    {
        $DiffObject = New-Object System.Object
        $DiffObject | Add-Member -MemberType NoteProperty -Name 'Status' -Value $Diff.Split("`t")[0]
        $DiffObject | Add-Member -MemberType NoteProperty -Name 'Path' -Value $Diff.Split("`t")[1]
        $DiffObjects += $DiffObject
    }

    $DiffObjects
}

function Get-DiffForCommit
{
    Param(
    [Parameter(Mandatory=$true)]
    [string]$Commit
    )

    $DiffObjects = @()

    $Diffs = git show --name-status --no-commit-id $Commit
    [boolean]$CommitLineReached = $false

    foreach ($Diff in $Diffs)
    {
        if ($Diff.Length -gt 6)
        {
            if ($Diff.Substring(0,6) -eq 'commit')
            {
                $CommitLineReached  = $true
            }
        }

        if (!$CommitLineReached)
        {
            $DiffObject = New-Object System.Object
            $DiffObject | Add-Member -MemberType NoteProperty -Name 'Status' -Value $Diff.Split("`t")[0]
            $DiffObject | Add-Member -MemberType NoteProperty -Name 'Path' -Value $Diff.Split("`t")[1]
            $DiffObjects += $DiffObject
        }
    }

    $DiffObjects
}

function Get-CurrentGitBranch
{
    [string]$CurrentBranch = Get-GitBranches | where{$_.Contains("*")}
    $CurrentBranch.Substring(2)
}

function Get-GitBranches
{
    git branch -a
}

function Select-GitBranch
{
    $Branch = Get-GitBranches | Out-GridView -OutputMode Single -Title 'Please select a branch'
    if ($Branch -eq $null) {
        return
    }

    $Branch.Substring(2)
}

function Checkout-GitBranch
{
    Param(
    [Parameter(Mandatory=$false)]
    [string]$BranchName,
    [Parameter(Mandatory=$false)]
    [string]$ServiceTier
    )

    if ($BranchName -eq '')
    {
        $BranchName = Select-GitBranch
    }

    if ($BranchName -eq '' -or $BranchName -eq $null)
    {
        return
    }

    if ($ServiceTier -eq '')
    {
        $ServiceTier = Select-ServiceTier
    }

    if ($ServiceTier -eq '')
    {
        return
    }

    $CurrentBranch = Get-CurrentGitBranch

    if ($CurrentBranch -eq $BranchName)
    {
        return
    }

    #if a remote branch was selected, checkout it out with --track to create a branch of the same name
    if ($BranchName.IndexOf('origin') -gt 0) {
        git checkout $BranchName --track
        git checkout $CurrentBranch
        $BranchName = $BranchName.Substring('remotes/origin/'.Length)    
    }

    $Diffs = Get-DiffBetweenGitBranches -Branch1 (Get-CurrentGitBranch) -Branch2 $BranchName
    git checkout $BranchName

    #import the checkout content, compile, synchronise
    Write-Host -ForegroundColor Green "Calculating diffs between $CurrentBranch and $BranchName"

    if ($Diffs -ne $null)
    {
        Apply-DiffsToServiceTier $Diffs $ServiceTier
    }

    Write-Host -ForegroundColor Green "$ServiceTier synchronised with branch $BranchName"
    $Diffs
}

function Apply-CommitsToServiceTier
{
    Param(    
    [Parameter(Mandatory=$false)]
    [string]$ServiceTier,
    [Parameter(Mandatory=$false)]
    [int]$Top = 0,
    [Parameter(Mandatory=$false)]
    [string]$Since = ''
    )

    if ($ServiceTier -eq '')
    {
        $ServiceTier = Select-ServiceTier
    }

    if ($ServiceTier -eq '')
    {
        return
    }

    if ($Since -ne '') {
        $Commits = Display-Commits -Since $Since
    }
    elseif ($Top -gt 0) {
        $Commits = Display-Commits | Select-Object -First $Top
    }
    else {
        $Commits = Select-Commits
    }

    [int]$CommitNo = 0
    foreach($Commit in $Commits)
    {
        $CommitNo += 1
        Write-Progress -Activity 'Applying commits' -PercentComplete (($CommitNo / $Commits.Count) * 100)
        $Diffs = Get-DiffForCommit $Commit.Hash
        Apply-DiffsToServiceTier $Diffs $ServiceTier    
    }
    Write-Progress -Activity 'Applying commits' -Completed
}

function Apply-CommitToServiceTier
{
    Param(
    [Parameter(Mandatory=$false)]
    [string]$Commit,
    [Parameter(Mandatory=$false)]
    [string]$ServiceTier
    )

    if ($Commit -eq '')
    {
        $Commit = Select-Commit
    }

    if ($Commit -eq '')
    {
        return
    }
    
    if ($ServiceTier -eq '')
    {
        $ServiceTier = Select-ServiceTier
    }

    if ($ServiceTier -eq '')
    {
        return
    }

    $Diffs = Get-DiffForCommit $Commit
    if ($Diffs -ne $null)
    {
        Apply-DiffsToServiceTier $Diffs $ServiceTier
    }
}

function Apply-DiffsToServiceTier
{
    Param(
    [Parameter(Mandatory=$true)]
    $Diffs,
    [Parameter(Mandatory=$true)]
    [string]$ServiceTier
    )

    if (($Diffs -eq $null) -or ($Diffs.Count -eq 0))
    {
        return
    }

    Delete-ObjectsFromDiffs -Diffs $Diffs -ServiceTier $ServiceTier

    $CheckoutContent = Create-CheckoutContentFromDiffs ($Diffs | where Status -ne D)

    if ($CheckoutContent -ne '')
    {
        $CheckoutFilePath = Join-Path (Create-TempDirectory) 'Checkout.txt'
        $LogDirectory = Create-TempDirectory        
        Add-Content -Path $CheckoutFilePath -Value $CheckoutContent                
        Import-NAVApplicationObject2 -Path $CheckoutFilePath -ServerInstance $ServiceTier -SynchronizeSchemaChanges No -ImportAction Overwrite -LogPath (Join-Path $LogDirectory 'Import.txt')        
        Compile-NAVApplicationObject2 -ServerInstance $ServiceTier -LogPath (Join-Path $LogDirectory 'Compile.txt') -Recompile:$false -SynchronizeSchemaChanges Force
    }
}

function Delete-ObjectsFromDiffs
{
    Param(
    [Parameter(Mandatory=$true)]
    $Diffs,
    [Parameter(Mandatory=$true)]
    $ServiceTier
    )

    $Deletions = $Diffs | Where{$_.Status -eq 'D'}
    $LogPath = Join-Path (Create-TempDirectory) 'Log.txt'
    foreach ($Deletion in $Deletions)
    {
        $ObjectType = (Split-Path $Deletion.Path -Leaf).Substring(0,3)
        $ObjectID = (Split-Path $Deletion.Path -Leaf).Substring(3,(Split-Path $Deletion.Path -Leaf).Length - 7)
        Delete-NAVApplicationObject2 -ServerInstance $ServiceTier -Filter "Type=$ObjectType;ID=$ObjectID" -SynchronizeSchemaChanges No -LogPath $LogPath -Confirm:$false
    }
}

function Create-CheckoutContentFromDiffs
{
    Param(
    [Parameter(Mandatory=$true)]
    $Diffs
    )

    [string]$CheckoutContent = ''

    foreach($Diff in $Diffs)
    {
        $ObjectPath = Join-Path (Get-Location) $Diff.Path
        if (Test-Path $ObjectPath)
        {
            $CheckoutContent += Get-Content $ObjectPath -Raw
        }
    }

    $CheckoutContent
}

function Export-ModifiedObjectsToWorkingTree {
    Param(
    [Parameter(Mandatory=$false)]
    [string]$ServiceTier
    )

    if ($ServiceTier -eq '') {
        $ServiceTier = Select-ServiceTier
    }

    if ($ServiceTier -eq '') {
        return
    }
 
    $TempPath = Create-TempDirectory
    Export-NAVApplicationObject2 -ServerInstance $ServiceTier -Path (Join-Path $TempPath 'Changes.txt') -Filter "Modified=Yes" -LogPath $TempPath
    $ChangesPath = (Join-Path $TempPath 'Changes')
    Create-EmptyDirectory $ChangesPath
    Split-NAVApplicationObjectFile (Join-Path $TempPath 'Changes.txt') $ChangesPath
    $Objects = Get-ChildItem $ChangesPath
    foreach ($Object in $Objects) {
        Set-NAVApplicationObjectProperty -TargetPath $Object.FullName -ModifiedProperty No
        Set-NAVApplicationObjectProperty -TargetPath $Object.FullName -DateTimeProperty ([DateTime]::new([DateTime]::Now.Year,1,1,12,0,0).ToString()) 
        if (!(Test-Path (Join-Path (Get-Location) ($Object.Name)))) {
            if (Test-Path (Join-Path (Join-Path (Get-Location) 'Tests') ($Object.Name))) {
                Copy-Item $Object.FullName (Join-Path (Join-Path (Get-Location) 'Tests') ($Object.Name))
            }
            else {
                Copy-Item $Object.FullName (Join-Path (Get-Location) ($Object.Name))
            }
        }
        else {
            Copy-Item $Object.FullName (Join-Path (Get-Location) ($Object.Name))
        }
    }
}

function Export-FilteredObjectsToWorkingTree {
    Param(
    [Parameter(Mandatory=$false)]
    [string]$ServiceTier = (Select-ServiceTier),
    [Parameter(Mandatory=$true)]
    [string]$Filter
    )

    $TempPath = Create-TempDirectory
    Export-NAVApplicationObject2 -ServerInstance $ServiceTier -Path (Join-Path $TempPath 'Changes.txt') -Filter "Version List=$Filter" -LogPath $TempPath
    $ChangesPath = (Join-Path $TempPath 'Changes')
    Create-EmptyDirectory $ChangesPath
    Split-NAVApplicationObjectFile (Join-Path $TempPath 'Changes.txt') $ChangesPath
    $Objects = Get-ChildItem $ChangesPath
    foreach ($Object in $Objects) {
        Set-NAVApplicationObjectProperty -TargetPath $Object.FullName -ModifiedProperty No
        Set-NAVApplicationObjectProperty -TargetPath $Object.FullName -DateTimeProperty ([DateTime]::new([DateTime]::Now.Year,1,1,12,0,0).ToString()) 
        if (!(Test-Path (Join-Path (Get-Location) ($Object.Name)))) {
            if (Test-Path (Join-Path (Join-Path (Get-Location) 'Tests') ($Object.Name))) {
                Copy-Item $Object.FullName (Join-Path (Join-Path (Get-Location) 'Tests') ($Object.Name))
            }
            else {
                Copy-Item $Object.FullName (Join-Path (Get-Location) ($Object.Name))
            }
        }
        else {
            Copy-Item $Object.FullName (Join-Path (Get-Location) ($Object.Name))
        }
    }
}

function Select-ServiceTier
{
    #if there is a service tier with the same name as the git repo folder then assume that's the one that we want to use
    if ((Get-NavServerInstance (Split-Path (Get-Location) -Leaf)).State -eq 'Running')
    {
        Split-Path (Get-Location) -Leaf
    }
    else
    {
        $ServerInstance = (Get-NavServerInstance | Where{$_.State -eq 'Running'} | Out-GridView -OutputMode Single -Title 'Please select a service tier').ServerInstance
        if ($ServerInstance -ne '')
        {
            $ServerInstance.Substring($ServerInstance.IndexOf('$') + 1)
        }
    }
}

function Select-Commit
{    
    $Commit = Display-Commits | Out-GridView -Title 'Please select a commit' -OutputMode Single
    $Commit.Hash
}

function Select-Commits
{
    $Commits = Display-Commits | Out-GridView -Title 'Please select commit(s)' -OutputMode Multiple
    $Commits
}

function Display-Commits
{
    Param(
        [Parameter(Mandatory=$false)]
        [string]$Since = ''
    )

    $CommitObjects = @()

    if ($Since -ne '') {
        $Commits = git log --oneline ("$Since..")
    }
    else {
        $Commits = git log --oneline
    }

    foreach($Commit in $Commits)
    {        
        $CommitObject = New-Object System.Object
        $CommitObject | Add-Member -MemberType NoteProperty -Name 'Hash' -Value $Commit.Substring(0,$Commit.IndexOf(' '))
        $CommitObject | Add-Member -MemberType NoteProperty -Name 'Comment' -Value $Commit.Substring($Commit.IndexOf(' ') + 1)
        $Diffs = @()
        $Diffs += Get-DiffForCommit $Commit.Substring(0,$Commit.IndexOf(' '))
        $CommitObject | Add-Member -MemberType NoteProperty -Name 'Objects' -Value ($Diffs.Path -join ", ")
        $CommitObjects += $CommitObject
    }

    $CommitObjects
}

function Find-DuplicateProcIdsInObject
{
    Param(
        [string]$FileName
    )

    $Procedures = @()
    $Duplicates = @()

    $Matches = [Regex]::Matches((Get-Content $FileName),'PROCEDURE\s\w*@\d*')
    foreach ($Match in $Matches)
    {
        $Procedure = New-Object System.Object
        $Procedure | Add-Member -MemberType NoteProperty -Name Name -Value $Match.Value.Substring(0,$Match.Value.IndexOf('@'))
        $Procedure | Add-Member -MemberType NoteProperty -Name ID -Value ([Int]::Parse($Match.Value.Substring($Match.Value.IndexOf('@') + 1)))

        if (($Procedures | Where-Object -Property ID -eq $Procedure.ID).Count -gt 0)
        {
            $Duplicates += ($Procedures | Where-Object -Property ID -eq $Procedure.ID)
            $Duplicates += $Procedure
        }

        $Procedures += $Procedure    
    }    

    if ($Duplicates.Count -gt 0)
    {
        Write-Host -ForegroundColor Yellow ("{0} duplicates in $FileName" -f ($Duplicates.Count / 2))
        Write-Host -ForegroundColor Yellow ('Last ID is {0}' -f ($Procedures | Sort-Object -Property ID -Descending).Item(0).ID)
        
        $Duplicates | Sort-Object -Property ID | Out-Host
    }
}

function Find-DuplicateProcIdsInRepo
{
    Get-ChildItem (Get-Location) -Filter '*.TXT' | Foreach-Object {Find-DuplicateProcIdsInObject $_.FullName}    
}

function Get-Commits {
    Param(
        [Parameter(Mandatory=$false)]
        [string]$Filter = ''
    )

    $CommitObjects = @()
    $Commits = git log $Filter --oneline --no-abbrev-commit
    foreach($Commit in $Commits)
    {        
        $CommitObject = New-Object System.Object
        $CommitObject | Add-Member -MemberType NoteProperty -Name 'Hash' -Value $Commit.Substring(0,$Commit.IndexOf(' '))
        $CommitObject | Add-Member -MemberType NoteProperty -Name 'Comment' -Value $Commit.Substring($Commit.IndexOf(' ') + 1)
        $CommitObjects += $CommitObject
    }

    $CommitObjects
}

function Get-IsGitRepo {
    Param(
        [string]$Path
    )

    return Test-Path (Join-Path $Path '.git')
}

function Open-GitExtensions {
    start 'C:\Program Files (x86)\GitExtensions\GitExtensions.exe'
}

function Get-GitRepoFetchUrl {
    $FetchUrl = (git remote -v).Item(0)
    $FetchUrl = $FetchUrl.Substring($FetchUrl.IndexOf('http'))
    $FetchUrl = $FetchUrl.Substring(0,$FetchUrl.IndexOf('(fetch)') - 1)
    $FetchUrl
}

Export-ModuleMember -Function *