Git.psm1
function Add-GitTag { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Version ) $tagName = "v$Version" $sha = Start-NativeExecution git rev-parse HEAD $shortSha = $shortSha = $sha.Substring(0, 8) $commitMessage = "Added tag $tagName for changeset $shortSha" Log info $commitMessage Start-NativeExecution git tag -a $tagName -m $commitMessage } function Clear-GitBranches { [CmdletBinding()] param( [switch] $IncludeGoneRemoteBranches ) $currentBranch = Start-NativeExecution git rev-parse --abbrev-ref HEAD if ($LASTEXITCODE -ne 0) { Log warning 'Error on getting the current branch. Are you outside of a Git repository?' Log trace "$currentBranch" return } Log info "Searching for branches which already merged into '$currentBranch'..." $branches = @{ } # 1. Try the default way (no remote rebased branches can be detected) $mergedBranches = Get-GitMergedBranches $mergedBranches | ForEach-Object { Log trace "Branch found with Git default logic: '$_'" } if ($mergedBranches) { $branches.Add('default logic', $mergedBranches) > $null } # 2. Try the custom way (check each hash, also remote rebased branches can be detected) $mergedOrRebasedBranches = Get-GitMergedOrRebasedBranches $mergedOrRebasedBranches | ForEach-Object { Log trace "Branch found with Git deeper logic: '$_'" } if ($mergedOrRebasedBranches) { $branches.Add('deep logic', $mergedOrRebasedBranches) > $null } if ($IncludeGoneRemoteBranches) { # 3. Get also remote gone branches $remoteGoneBranches = Get-GitRemoteGoneBranches $remoteGoneBranches | ForEach-Object { Log trace "Branch found with Git remote gone logic: '$_'" } if ($remoteGoneBranches) { $branches.Add('gone logic', $remoteGoneBranches) > $null } } # Filter out duplicates $uniqueMergedBranches = Get-Distincted @($mergedBranches, $mergedOrRebasedBranches, $remoteGoneBranches) | Sort-Object if (-not $uniqueMergedBranches) { Log info 'No merged branches were found' return } $sb = [System.Text.StringBuilder]::new() $prefix = 'Merged branch:' $logicColumn = ($uniqueMergedBranches | Sort-Object Length -Descending | Select-Object -First 1).Length + 2 foreach ($branch in $uniqueMergedBranches) { $logics = Get-Logics -groupedBranches $branches -branchName $branch $sb.Clear() > $null $sb.Append(("$prefix {0,-$logicColumn} {1}" -f "'$($branch)'", "[detected by: $($logics -join ', ')]")) > $null Log info $sb.ToString() } $title = 'Delete merged branches' $message = 'Do you want to delete the already-merged local branches displayed above?' $yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'Delete the remote branches listed.' $no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Leave the branches alone.' $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no) $result = $Host.UI.PromptForChoice($title, $message, $options, 1) if ($result -eq 1) { return } $uniqueMergedBranches | ForEach-Object { Start-NativeExecution git branch -D $_ | Log info } } function Get-GitMergedBranches() { $mergedBranches = [System.Collections.ArrayList]@() $branches = Start-NativeExecution git branch --merged if ($LASTEXITCODE -ne 0) { Log trace "Error on getting merged branches: $branches" return } $filteredBranches = $branches | ForEach-Object { $_.Trim() } | Where-Object { $_ -notmatch '^\* (master|main)$' } | Where-Object { $_ -notmatch '^(master|main)$' } | Where-Object { $_ -notmatch '^\* .*$' } foreach ($branch in $filteredBranches) { Log trace "Branch already merged '$branch'" $mergedBranches.Add($branch) > $null } return $mergedBranches } function Get-GitMergedOrRebasedBranches() { $currentBranch = Start-NativeExecution git rev-parse --abbrev-ref HEAD if ($LASTEXITCODE -ne 0) { Log trace "Error on getting merged or rebased branches: $currentBranch" return } Log trace "currentBranch: '$currentBranch'" $currentLocalBranch = Start-NativeExecution git symbolic-ref -q HEAD if ($LASTEXITCODE -ne 0) { Log trace "Error on getting merged or rebased branches: $currentLocalBranch" return } Log trace "currentLocalBranch: '$currentLocalBranch'" $currentRemoteBranch = Start-NativeExecution git for-each-ref '--format="%(upstream:short)"' $currentLocalBranch if ($LASTEXITCODE -ne 0) { Log trace "Error on getting merged or rebased branches: $currentRemoteBranch" return } Log trace "currentRemoteBranch: '$currentRemoteBranch'" $mergedbranches = [System.Collections.ArrayList]@() if (-not $currentRemoteBranch) { return $mergedbranches } $localBranches = Start-NativeExecution git for-each-ref refs/heads '--format="%(refname:short)"' | Where-Object { $_ -notmatch '(master|main)' } | Where-Object { $_ -notmatch $currentBranch } if ($LASTEXITCODE -ne 0) { Log trace "Error on getting merged or rebased branches: $localBranches" return } foreach ($localBranch in $localBranches) { $notMergedHashFound = $false Log trace "Check local branch '$localBranch'..." $results = Start-NativeExecution git cherry $currentRemoteBranch $localBranch if ($LASTEXITCODE -ne 0) { Log trace "Get merged hashes failed: '$results'" break } foreach ($result in $results) { if ($result -match '^\-') { Log trace "Hash merged '$result'" continue } if ($result -match '^\+') { Log trace "Hash was not merged '$result'" } else { Log trace "No hash '$result'" } $notMergedHashFound = $true break } if (-not $notMergedHashFound) { Log trace "Branch already merged '$localBranch'" $mergedbranches.Add($localBranch) > $null } } return $mergedbranches } function Get-GitRemoteGoneBranches() { $goneBranches = [System.Collections.ArrayList]@() $branches = Start-NativeExecution git branch -v if ($LASTEXITCODE -ne 0) { Log trace "Error on getting remote gone branches: $branches" return } $filteredBranches = $branches | ForEach-Object { $_.Trim() } | Where-Object { $_ -notmatch '^\* (master|main)' } | Where-Object { $_ -notmatch '^(master|main)' } | Where-Object { $_ -notmatch '^\* .*' } | Where-Object { $_ -match '^.*\[gone\].*' } if (-not $filteredBranches) { Log trace 'No remote gone branches found' return } $filteredBranchNames = $filteredBranches | ForEach-Object { $_.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)[0] } foreach ($branchName in $filteredBranchNames) { Log trace "Branch is removed on remote '$branchName'" $goneBranches.Add($branchName) > $null } return $goneBranches } function Get-Distincted { param( [Parameter()] [object[][]] $Items ) $hashSet = [System.Collections.Generic.HashSet[System.Object]]::new() foreach ($item in $Items) { foreach ($entry in $item) { $hashSet.Add($entry) > $null } } $distinctedList = [System.Collections.ArrayList]@() foreach ($value in $hashSet) { $distinctedList.Add($value) > $null } return $distinctedList } function Get-Logics([hashtable] $groupedBranches, [string] $branchName) { $logics = [System.Collections.ArrayList]@() foreach ($logic in $groupedBranches.GetEnumerator()) { [object[]] $values = $logic.Value if ($values.Contains($branchName)) { $logics.Add($logic.Name) > $null } } return $logics } function Invoke-GitkAll() { Invoke-Gitk -All @args } function Invoke-Gitk() { param( [switch] $All ) $command = 'gitk' $arguments = [System.Collections.ArrayList]@() if ($All) { $arguments.Add('--all') > $null } $arguments += $args Start-NativeExecution $command @arguments } function Merge-GitAllRemoteBranches($RemoteRefs = $null, $Strategie = 'recursive', [switch] $UseTheirs = $false, [switch] $UseOurs = $false) { if ($null -eq $RemoteRefs) { $RemoteRefs = 'origin/' } $currentBranch = (git rev-parse --abbrev-ref HEAD).Trim() git fetch --all git branch -r | ForEach-Object { $localBranch = $_ $localBranch = $localBranch.Trim().Replace($RemoteRefs, '').Trim() $localBranch = $localBranch.Trim().Replace('*', '').Trim() $remoteBranch = $_.Trim().Replace('*', '').Trim() if ($remoteBranch.StartsWith($RemoteRefs) -and -not $remoteBranch.StartsWith($RemoteRefs + 'HEAD')) { Log info "Processing branch '${localBranch}'..." git checkout $localBranch if ($UseTheirs) { git merge $remoteBranch -s $Strategie -X theirs } elseif ($UseOurs) { git merge $remoteBranch -s $Strategie -X ours } else { git merge $remoteBranch } } } git checkout $currentBranch } function Merge-GitBranchUseTheirs($SourceBranchName, $DestinationBranchName) { if (($null -eq $SourceBranchName) -or ($null -eq $DestinationBranchName)) { return } $currentBranch = (git rev-parse --abbrev-ref HEAD).Trim() $tempHash = Get-Checksum -Value ([System.Guid]::NewGuid()) -Algorithm SHA1 $tempBranchName = "merge-use-theirs-${tempHash}" $commitMessage = "Merge branch '${SourceBranchName}' into '${DestinationBranchName}'" git checkout -b $tempBranchName $SourceBranchName git merge --strategy=ours $DestinationBranchName git checkout $DestinationBranchName git merge --no-ff $tempBranchName -m $commitMessage git branch -D $tempBranchName git checkout $currentBranch } function Push-GitAllTrackedBranches($RemoteRefs) { if ($null -eq $RemoteRefs) { $RemoteRefs = 'origin/' } $currentBranch = (git rev-parse --abbrev-ref HEAD).Trim() git branch | ForEach-Object { $localBranch = $_ $localBranch = $localBranch.Trim().Replace('*', '').Trim() $trackingRefs = git config branch.$localBranch.merge if ($trackingRefs -and ($trackingRefs.Trim().StartsWith('refs/heads/' + $RemoteRefs))) { Log info "Processing branch '${localBranch}'..." git checkout $localBranch git push } } git checkout $currentBranch } function Remove-GitAllBranches($RemoteRefs, [switch] $ForceAll = $false) { if ($null -eq $RemoteRefs) { $RemoteRefs = 'origin/' } $currentBranch = (git rev-parse --abbrev-ref HEAD).Trim() $tempHash = Get-Checksum -Value ([System.Guid]::NewGuid()) -Algorithm SHA1 $tempBranchName = "remove-all-${tempHash}" git checkout -f -b $tempBranchName git branch | ForEach-Object { $localBranch = $_ $localBranch = $localBranch.Trim().Replace('*', '').Trim() $trackingRefs = git config branch.$localBranch.merge if ($trackingRefs -and ($localBranch -ne $tempBranchName) -and (($ForceAll) -or ($null -ne $trackingRefs))) { if (($ForceAll) -or ($trackingRefs.Trim().StartsWith('refs/remotes/' + $RemoteRefs)) -or ($trackingRefs.Trim().StartsWith('refs/heads/' + $RemoteRefs))) { Log info "Delete local branch '${localBranch}'..." git branch -D $localBranch } } } if ($ForceAll) { git checkout master } else { git checkout $currentBranch } git branch -D $tempBranchName } function Reset-GitAllBranches($RemoteRefs) { Remove-GitAllBranches $RemoteRefs Set-GitTrackAllRemoteBranches $RemoteRefs Merge-GitAllRemoteBranches $RemoteRefs } function Set-GitTrackAllRemoteBranches($RemoteRefs) { if ($null -eq $RemoteRefs) { $RemoteRefs = 'origin/' } $localBranches = git branch | ForEach-Object { $_.Trim().Replace('*', '').Trim() } $notTrackedLocalBranches = @() git for-each-ref --format='%(refname:short) <- %(upstream:short)' 'refs/heads' | ForEach-Object { $row = $_ -split '<-' $localBranch = $row[0].Trim() $remoteBranch = $row[1].Trim() $query = "branch.${localBranch}.remote" $configRemote = git config --get $query if (-not $remoteBranch -and -not $configRemote) { $notTrackedLocalBranches += $localBranch } } git branch -r | ForEach-Object { $localBranch = $_ $localBranch = $localBranch.Trim().Replace($remoteRefs, '').Trim() $localBranch = $localBranch.Trim().Replace('*', '').Trim() $remoteBranch = $_.Trim().Replace('*', '').Trim() if ($remoteBranch.StartsWith($remoteRefs + 'HEAD') -or -not ($remoteBranch.StartsWith($remoteRefs))) { return } if (-not ($notTrackedLocalBranches -contains $localBranch) -and -not ($localBranches -contains $localBranch)) { git branch -t $localBranch $remoteBranch } elseif ($notTrackedLocalBranches -contains $localBranch) { git branch -u $remoteBranch $localBranch } } } function Set-GitTrackMatchedRemoteBranches($RemoteRefs) { if ($null -eq $RemoteRefs) { $RemoteRefs = 'origin/' } $notTrackedLocalBranches = @() git for-each-ref --format='%(refname:short) <- %(upstream:short)' 'refs/heads' | ForEach-Object { $row = $_ -split '<-' $localBranch = $row[0].Trim() $remoteBranch = $row[1].Trim() $query = "branch.${localBranch}.remote" $configRemote = git config --get $query if (-not $remoteBranch -and -not $configRemote) { $notTrackedLocalBranches += $localBranch } } $remotes = git branch -r | ForEach-Object { $_.Trim().Replace('*', '').Trim() } $notTrackedLocalBranches | ForEach-Object { $localBranch = $_ $remotes | ForEach-Object { $remoteBranch = $_ if ($remoteBranch -eq ($RemoteRefs + $localBranch)) { git branch -u $remoteBranch $localBranch return } } } } function Update-GitAllBranches() { $currentBranch = git rev-parse --abbrev-ref HEAD git fetch --all $branchPairs = git for-each-ref --format='%(refname:short) <- %(upstream:short)' 'refs/heads' $branchPairs | ForEach-Object { $pair = $_ -split ' <- ' $localBranch = $pair[0] $remoteBranch = $pair[1] if ($localBranch -and $remoteBranch) { Log info "Processing branch '${localBranch}'..." git checkout $localBranch git rebase $remoteBranch } } git checkout $currentBranch } function Update-GitBranch() { git fetch --all $localBranch = git rev-parse --abbrev-ref HEAD $remoteBranch = git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' if (-not $remoteBranch) { Print-Warning "No upstream configured for branch '${localBranch}'" return } $remoteBranches = git for-each-ref --format='%(upstream:short)' 'refs/heads' foreach ($branch in $remoteBranches) { if ( $branch -eq $remoteBranch) { Log info "Rebase branch '${remoteBranch}' into '${localBranch}'..." git rebase $remoteBranch break } } } function Add-AssumedUnchanged { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $Path ) $files = Get-Item $Path | Select-Object -ExpandProperty FullName $files | Log info $title = 'Add assume-unchanged flag' $message = 'Do you want to add the assume-unchanged flag for the files displayed above?' $yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'Add the assume-unchanged flag for each file at the Git index.' $no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Does not make any changes.' $options = [System.Management.Automation.Host.ChoiceDescription[]] @($yes, $no) $choice = $Host.UI.PromptForChoice($title, $message, $options, 1) switch ($choice) { 0 { foreach ($file in $files) { git update-index --assume-unchanged -- $file } Log info 'Add assumed-unchanged.' } 1 { Log info 'Nothing changed.' } } } function Remove-AssumedUnchanged { [CmdletBinding()] param( [Parameter(Mandatory)] [string] $Path ) $files = Get-Item $Path | Select-Object -ExpandProperty FullName $files | Log info $title = 'Remove assume-unchanged flag' $message = 'Do you want to remove the assume-unchanged flag for the files displayed above?' $yes = New-Object System.Management.Automation.Host.ChoiceDescription '&Yes', 'Remove the assume-unchanged flag for each file at the Git index.' $no = New-Object System.Management.Automation.Host.ChoiceDescription '&No', 'Does not make any changes.' $options = [System.Management.Automation.Host.ChoiceDescription[]] @($yes, $no) $choice = $Host.UI.PromptForChoice($title, $message, $options, 1) switch ($choice) { 0 { foreach ($file in $files) { git update-index --no-assume-unchanged -- $file } Log info 'Removed assumed-unchanged.' } 1 { Log info 'Nothing changed.' } } } New-Alias agt Add-GitTag New-Alias cgb Clear-GitBranches New-Alias gk Invoke-Gitk New-Alias gka Invoke-GitkAll New-Alias pgst Push-GitAllTrackedBranches New-Alias ugab Update-GitAllBranches New-Alias ugb Update-GitBranch # SIG # Begin signature block # MIIoFAYJKoZIhvcNAQcCoIIoBTCCKAECAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCg4d1DNB+t++h0 # 9Xw6FG6q96jKuBaqEH1FEN5EJdIJOqCCIRcwggWNMIIEdaADAgECAhAOmxiO+dAt # 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa # Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E # MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy # unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF # xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1 # 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB # MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR # WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6 # nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB # YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S # UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x # q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB # NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP # TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC # AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0 # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB # LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc # Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov # Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy # oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW # juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF # mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z # twGpn1eqXijiuZQwggauMIIElqADAgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqG # SIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMx # GTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRy # dXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMx # CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMy # RGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcg # Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXH # JQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMf # UBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w # 1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRk # tFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYb # qMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKlSNbwsDETqVcplicu9Yemj052FVUm # cJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP6 # 5x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzK # QtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo # 80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjB # Jgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXche # MBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB # /wIBADAdBgNVHQ4EFgQUuhbZbU2FL3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU # 7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoG # CCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29j # c3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDig # NqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v # dEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZI # hvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd # 4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiC # qBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl # /Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeC # RK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYT # gAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/ # a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37 # xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmL # NriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0 # YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJ # RyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIG # sDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0BAQwFADBiMQsw # CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu # ZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQw # HhcNMjEwNDI5MDAwMDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYDVQQGEwJVUzEX # MBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0 # ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMIICIjAN # BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5WRuxiEL1M4zr # PYGXcMW7xIUmMJ+kjmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJPDqFX/IiZwZHM # gQM+TXAkZLON4gh9NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXzENOLsvsI8Irg # nQnAZaf6mIBJNYc9URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bqHPNlaJGiTUyC # EUhSaN4QvRRXXegYE2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTCfMjqGzLmysL0 # p6MDDnSlrzm2q2AS4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaDG7dqZy3SvUQa # khCBj7A7CdfHmzJawv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urOkfW+0/tvk2E0 # XLyTRSiDNipmKF+wc86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7ADK5GyNnm+960I # HnWmZcy740hQ83eRGv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4R+Z1MI3sMJN2 # FKZbS110YU0/EpF23r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlNWdt4z4FKPkBH # X8mBUHOFECMhWWCKZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0IU0F8WD1Hs/q2 # 7IwyCQLMbDwMVhECAwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYD # VR0OBBYEFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaAFOzX44LScV1k # TN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcD # AzB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj # ZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t # L0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0 # cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmww # HAYDVR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcNAQEMBQADggIB # ADojRD2NCHbuj7w6mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcTEp6QRJ9L/Z6j # fCbVN7w6XUhtldU/SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WTauPrINHVUHmI # moqKwba9oUgYftzYgBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9ntSZz0rdKOtf # JqGVWEjVGv7XJz/9kNF2ht0csGBc8w2o7uCJob054ThO2m67Np375SFTWsPK6Wrx # oj7bQ7gzyE84FJKZ9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0HKKlS43Nb3Y3 # LIU/Gs4m6Ri+kAewQ3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL6TEa/y4ZXDlx # 4b6cpwoG1iZnt5LmTl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+16oh7cGvmoLr9 # Oj9FpsToFpFSi0HASIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8M4+uKIw8y4+I # Cw2/O/TOHnuO77Xry7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrFhsP2JjMMB0ug # 0wcCampAMEhLNKhRILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy1lKQ/a+FSCH5 # Vzu0nAPthkX0tGFuv2jiJmCG6sivqf6UHedjGzqGVnhOMIIGwjCCBKqgAwIBAgIQ # BUSv85SdCDmmv9s/X+VhFjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEX # MBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0 # ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIzMDcxNDAw # MDAwMFoXDTM0MTAxMzIzNTk1OVowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRp # Z2lDZXJ0LCBJbmMuMSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMzCC # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKNTRYcdg45brD5UsyPgz5/X # 5dLnXaEOCdwvSKOXejsqnGfcYhVYwamTEafNqrJq3RApih5iY2nTWJw1cb86l+uU # UI8cIOrHmjsvlmbjaedp/lvD1isgHMGXlLSlUIHyz8sHpjBoyoNC2vx/CSSUpIIa # 2mq62DvKXd4ZGIX7ReoNYWyd/nFexAaaPPDFLnkPG2ZS48jWPl/aQ9OE9dDH9kgt # XkV1lnX+3RChG4PBuOZSlbVH13gpOWvgeFmX40QrStWVzu8IF+qCZE3/I+PKhu60 # pCFkcOvV5aDaY7Mu6QXuqvYk9R28mxyyt1/f8O52fTGZZUdVnUokL6wrl76f5P17 # cz4y7lI0+9S769SgLDSb495uZBkHNwGRDxy1Uc2qTGaDiGhiu7xBG3gZbeTZD+BY # QfvYsSzhUa+0rRUGFOpiCBPTaR58ZE2dD9/O0V6MqqtQFcmzyrzXxDtoRKOlO0L9 # c33u3Qr/eTQQfqZcClhMAD6FaXXHg2TWdc2PEnZWpST618RrIbroHzSYLzrqawGw # 9/sqhux7UjipmAmhcbJsca8+uG+W1eEQE/5hRwqM/vC2x9XH3mwk8L9CgsqgcT2c # kpMEtGlwJw1Pt7U20clfCKRwo+wK8REuZODLIivK8SgTIUlRfgZm0zu++uuRONhR # B8qUt+JQofM604qDy0B7AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYD # VR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgG # BmeBDAEEAjALBglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxq # II+eyG8wHQYDVR0OBBYEFKW27xPn783QZKHVVqllMaPe1eNJMFoGA1UdHwRTMFEw # T6BNoEuGSWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRH # NFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGD # MIGAMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYB # BQUHMAKGTGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0 # ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQEL # BQADggIBAIEa1t6gqbWYF7xwjU+KPGic2CX/yyzkzepdIpLsjCICqbjPgKjZ5+PF # 7SaCinEvGN1Ott5s1+FgnCvt7T1IjrhrunxdvcJhN2hJd6PrkKoS1yeF844ektrC # QDifXcigLiV4JZ0qBXqEKZi2V3mP2yZWK7Dzp703DNiYdk9WuVLCtp04qYHnbUFc # jGnRuSvExnvPnPp44pMadqJpddNQ5EQSviANnqlE0PjlSXcIWiHFtM+YlRpUurm8 # wWkZus8W8oM3NG6wQSbd3lqXTzON1I13fXVFoaVYJmoDRd7ZULVQjK9WvUzF4UbF # KNOt50MAcN7MmJ4ZiQPq1JE3701S88lgIcRWR+3aEUuMMsOI5ljitts++V+wQtaP # 4xeR0arAVeOGv6wnLEHQmjNKqDbUuXKWfpd5OEhfysLcPTLfddY2Z1qJ+Panx+VP # NTwAvb6cKmx5AdzaROY63jg7B145WPR8czFVoIARyxQMfq68/qTreWWqaNYiyjvr # moI1VygWy2nyMpqy0tg6uLFGhmu6F/3Ed2wVbK6rr3M66ElGt9V/zLY4wNjsHPW2 # obhDLN9OTH0eaHDAdwrUAuBcYLso/zjlUlrWrBciI0707NMX+1Br/wd3H3GXREHJ # uEbTbDJ8WC9nR2XlG3O2mflrLAZG70Ee8PBf4NvZrZCARK+AEEGKMIIHVjCCBT6g # AwIBAgIQDBXwscbz14x9ek55doBnRzANBgkqhkiG9w0BAQ0FADBpMQswCQYDVQQG # EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0 # IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0Ex # MB4XDTI0MDMxMjAwMDAwMFoXDTI1MDMxMjIzNTk1OVowXjELMAkGA1UEBhMCREUx # HzAdBgNVBAcTFkdhcm1pc2NoLVBhcnRlbmtpcmNoZW4xFjAUBgNVBAoTDU1hbnVl # bCBUYW56ZXIxFjAUBgNVBAMTDU1hbnVlbCBUYW56ZXIwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCTsOVLH5jtnViI9CzKdwLIOODRJZAZlTZ6g0fCkQmJ # R44iMkFBK8/O46dckAy/OLWKIGPXuatA/FpylNeSKLeqDusFTELqVgOaFPr7qr6m # F6Y1b1NTv9139cNd2tBEkAbwWxJdzsoVGHjneejsC/u7VazQDiOHRYFJj//yrX02 # T5jFIO38sme77dAgGTfxl+E8yEq5Xzza38sw206dYA3OpX7MQsLl8TjXHkuWC/PK # nc1FRMCfenDYq8KEtUT2b4R4EluRbs9T1ZePZ5kl1pCMsr94CicDfMOuF5QWsqAh # xdEfzpfecVTq0u/NTBd6CzTtyFE8qHSu1yckU6qlcCJXjqSoAcLKZS1J/Qil2eUb # /cbwtupGlx8RCj1OzA0A9rZYmrYk4AeYQEAXOr2dPDVCN0zKPcOkKlNPFDbe8mqX # 6y+h/9Czg1EDwPFP23HKD0bGC8L5G5NHxqPezcdukGKoqtSeQy81lR3GYdiNV5wt # YyPDcSjxgkII6ZHedg6GMGIVCL/0NHh6EXE8SJgXX+JIvjChetUZEsF18ro2VZKj # 4hW6FzK+0HUgyeFsdU6CoWQB097UC5uPaeWyXTIkskG3m+8KvUZp88t6a+K9JI4/ # 26/o77m6WTsnVoXIOGfUlhXmu/vPc5DKwxBTMA5IcGPPJrd4pM27m8niGnzkVq/i # jwIDAQABo4ICAzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIw # HQYDVR0OBBYEFA630pXEHsvRNF7iqqyeERyJpcd0MD4GA1UdIAQ3MDUwMwYGZ4EM # AQQBMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAO # BgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCB # qjBToFGgT4ZNaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3Rl # ZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0 # dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWdu # aW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMIGUBggrBgEFBQcBAQSBhzCBhDAk # BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAC # hlBodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRD # b2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0G # CSqGSIb3DQEBDQUAA4ICAQCrfVGhAbhMkdZAwsrSd4rsmwJWGlNbwnimYvs0xcB4 # A5QiwyPJ6cPibEEdU7soXow25Dk7DHltqlNpJP8eO/IGbmSBtqQPo9oxcNtrs/Hm # FbJh8wKyJ2OtwRi2y7pXxWZZekIvecJ/afSzbs9BEWo2DcmjknYCZkRZmO9CWdjU # 8sVIzBq3zUHsF8gXtm8Tp7kp6Ceo5lKoW8JbOM6RnA0ySj2iGagnQo++3qiH3u/C # GtOlxlz5TR2J3FKXa6Kq3adGm7tSoajN121uEmmo2AoU0gLoQwAxEb4o0czrgNya # /P34G8Tep8Wgq+wzE0KEWLWfJ4Q1u5pMPfAyKUiY6HU2tIpQEN2BKF0tSeRyGdQz # 0p6b4k0+R5dCU3QlbuM2TbU87Nu4ZxS6IiN19qZkxEZN5LPDhS8d6BNQy34uuugg # I/M/iKQCcBtreWEkXZ+DSMCE2D7ElfsqPqkEYemxuIN/MA3RWdlZeFzafNJErQo6 # O/MWmmrvCYunX0i+evzTO4/L7XT+Lldg1Q1psAZfowqGM691aWR6mtd+OX76O1cB # 79IWvHl8ObNQpR5P9U++sn6r20uaWfvK7tliH0LSPLYPfDBsD09VUSx+xHy9h2W2 # KWnX6nd5VU0ig36EaT153pqX54I49b8vj+b+5JcNHeqEV8B6+6xfr1CIux1GgYnL # 1jGCBlMwggZPAgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0 # LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmlu # ZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENBMQIQDBXwscbz14x9ek55doBnRzANBglg # hkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3 # DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEV # MC8GCSqGSIb3DQEJBDEiBCDb8+HnolEsJ/IkIhDh2Hp45kAJ/ESwVewmedqck8bO # ZDANBgkqhkiG9w0BAQEFAASCAgCSw+J4pVIOPvO72KfayPNC1eHdnV6Dgnor4l8Z # yasftz24HR2qPw5P3R2RLyU7YP2EGn+wOsCj1CsYphsWT5tK7OQhvIAhwUyO5kWi # UNxJUZIGl5hUWGGLTMuv4oxy/F4CPHPu8m2DFZkCMWKx/IXX1PfvwA/qjg7iHyyS # NXIaSZjOIsD04y4a5TrCSzoGaE9Lew8yrsxdpUpm0fz75Ddgs17mMQhcGGV6aY86 # Eid48uilzYqcLxG8oRMBxhUZD3fcrMJHGhNST0XOzrIu+ofNWhaPLnMNzG7WCDUv # WlkeILSaK/ijOddzhqpatV1aUYGUZLQa46vt7HvG+SmXuxY7+2D1yFxWilWFrCgh # kuStOmiIiJQ6rPPzHAlQOxon/pLZN7zxuYqS1mve1hZLAMHQFAp2nk2+9byavzNY # SPcJANYqI908nAzGwGZTn8iPW9F/y9zr64nstRbyARjXm0Go4Q7L9N7Z0kuiffrv # kPopegXN/eJRnoME2lbhTWWCmv7FxBeE5s+sbbMUChOG0Y6JbnFcOAorVZ2MEKMw # bH1cPhJiPt2EyzlW7m9xFilO7Rsc8ttn6P9ZDo5MESan99qD+u6AeQyqeMFVobC1 # XSDQf1PF06KpJjPmE+4v2grmeRRpZ9r6xFlkchJ4YOkzkr5hApuzUzRQsbUHInrH # Vy2QEKGCAyAwggMcBgkqhkiG9w0BCQYxggMNMIIDCQIBATB3MGMxCzAJBgNVBAYT # AlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQg # VHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0ECEAVEr/OU # nQg5pr/bP1/lYRYwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZI # hvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yNDA1MTYyMjM1NTZaMC8GCSqGSIb3DQEJ # BDEiBCBHku3UbvuNcbKuKZ36Kdq7DlKPLujoLns1uu6VWB8m7zANBgkqhkiG9w0B # AQEFAASCAgAyGtJUfRcxu7z7ONipPNujglHc+ua6gO9wmskDMt/tMqsrLydMXkmd # c52l1K/o+sdog26n7ZuC0ND/olNGZg7lSQ9v85sMEVQvwdInyL+Ge3oi447z65Rp # zMBhrt+8QPxf9gFo9K/eZoC0U6Bk0ZKLh+hSve77F4zjXPhFIzMCyOcLfSyF2tNw # 8OwxiNhgajHAyPuzY3sltdybFC7WVqNTHSwLd7SwO5snl+n5zA17x5TXcUckOE6c # 9TbQFtNuy1SXsHNAJHMTbSSZfqX/K8kUChFm25oV2X9Eg+3yC7t5d20fKYybDwJZ # vYyd99kyPXQBevASlNOmrA1XM8CYDpBiKZnkDQLKjqkQ/J6nKk8Eyy2RExwoaTxK # DxNe2s/RstD3wra8j8ZoyLeYOxyD9ZZiZkLXAo9GNCkg1IEGvAxCS94eTObUI2Yi # D9i6RrFV199e5dUgB65zKZBwagY48xDdVkjnpFfq3MBQUF7Z1rk09OKaY/AyVUzI # /DLIZK1zXyTj/ndntXbEcnx7pvd2pidYjemdTmGFr0CPXy4deVBRSb/Rv8OWt06x # hq4D/OdPiLCIxNQAQTzL0ly4gWtzVw9ZiO5CW7wObnwi3Tr/KpryjkalHEPudv7t # t17/k0h8yF3UJeHlVRlxjEzhHapTZ/Gpng0XSSS65uMZddZ3B+2AUA== # SIG # End signature block |