|
1 | 1 | #!/usr/bin/env pwsh |
2 | 2 |
|
3 | 3 | Param( |
4 | | - [Parameter(Mandatory)][String] $sourceBranch, |
5 | | - [Parameter(Mandatory)][String] $target, |
| 4 | + [Parameter(Mandatory)][Alias('sourceBranch')][String] $source, |
| 5 | + [Parameter(Mandatory)][Alias('targetBranch')][String] $target, |
6 | 6 | [Parameter()][Alias('message')][Alias('m')][string] $comment, |
7 | | - [Parameter()][String[]] $preserve, |
8 | | - [switch] $dryRun, |
9 | | - [switch] $cleanupOnly |
| 7 | + [Parameter()][String[]] $preserve = @(), |
| 8 | + [switch] $cleanupOnly, |
| 9 | + [switch] $force, |
| 10 | + [switch] $noFetch, |
| 11 | + [switch] $quiet, |
| 12 | + [switch] $dryRun |
10 | 13 | ) |
11 | 14 |
|
12 | | -. $PSScriptRoot/config/core/coalesce.ps1 |
13 | | -. $PSScriptRoot/config/core/ArrayToHash.ps1 |
14 | 15 | Import-Module -Scope Local "$PSScriptRoot/utils/query-state.psm1" |
15 | | -Import-Module -Scope Local "$PSScriptRoot/config/git/Get-GitFileNames.psm1" |
16 | | -Import-Module -Scope Local "$PSScriptRoot/config/git/Set-MultipleUpstreamBranches.psm1" |
| 16 | +Import-Module -Scope Local "$PSScriptRoot/utils/framework.psm1" |
| 17 | +Import-Module -Scope Local "$PSScriptRoot/utils/actions.psm1" |
17 | 18 |
|
| 19 | +$diagnostics = New-Diagnostics |
18 | 20 | $config = Get-Configuration |
| 21 | +if (-not $noFetch) { |
| 22 | + Update-GitRemote -quiet:$quiet |
| 23 | +} |
19 | 24 |
|
20 | | -Update-GitRemote |
| 25 | +$commonParams = @{ |
| 26 | + diagnostics = $diagnostics |
| 27 | +} |
21 | 28 |
|
22 | | -if ($cleanupOnly) { |
23 | | - # Verify that $target already has all of $sourceBranch |
24 | | - $count = git rev-list ($config.remote -eq $nil ? $sourceBranch : "$($config.remote)/$($sourceBranch)") "^$($config.remote -eq $nil ? $target : "$($config.remote)/$($target)")" --count |
25 | | - if ($count -ne 0) { |
26 | | - throw "Found $count commits in $sourceBranch that were not included in $target" |
27 | | - } |
28 | | -} else { |
29 | | - $count = git rev-list ($config.remote -eq $nil ? $target : "$($config.remote)/$($target)") "^$($config.remote -eq $nil ? $sourceBranch : "$($config.remote)/$($sourceBranch)")" --count |
30 | | - if ($count -ne 0) { |
31 | | - throw "Could not fast-forward $target to $sourceBranch; $count commits in $target that were not included in $sourceBranch" |
| 29 | +# Assert up-to-date |
| 30 | +# a) if $cleanupOnly, ensure no commits are in source that are not in target |
| 31 | +# b) otherwise, ensure no commits are in target that are not in source |
| 32 | +if (-not $force) { |
| 33 | + Invoke-LocalAction @commonParams @{ |
| 34 | + type = 'assert-updated' |
| 35 | + parameters = $cleanupOnly ` |
| 36 | + ? @{ downstream = $target; upstream = $source } |
| 37 | + : @{ downstream = $source; upstream = $target } |
32 | 38 | } |
| 39 | + Assert-Diagnostics $diagnostics |
33 | 40 | } |
34 | 41 |
|
35 | | -$allPreserve = [String[]](@($target, $preserve) | ForEach-Object { $_ } | Select-Object -uniq) |
36 | | - |
37 | | -$allUpstream = Select-UpstreamBranches $sourceBranch -recurse |
| 42 | +# $toRemove = (git show-upstream $source -recurse) without ($target, git show-upstream $target -recurse, $preserve) |
| 43 | +$sourceUpstream = Invoke-LocalAction @commonParams @{ |
| 44 | + type = 'get-upstream' |
| 45 | + parameters = @{ target = $source; recurse = $true } |
| 46 | +} |
| 47 | +Assert-Diagnostics $diagnostics |
38 | 48 |
|
39 | | -$upstreamCache = @($allUpstream, $allPreserve) | ForEach-Object { $_ } | ArrayToHash -getValue { Select-UpstreamBranches $_ -recurse } |
| 49 | +$targetUpstream = Invoke-LocalAction @commonParams @{ |
| 50 | + type = 'get-upstream' |
| 51 | + parameters = @{ target = $target; recurse = $true } |
| 52 | +} |
| 53 | +Assert-Diagnostics $diagnostics |
| 54 | + |
| 55 | +[string[]]$keep = @($target) + $targetUpstream + $preserve |
| 56 | +[string[]]$toRemove = (@($source) + $sourceUpstream) | Where-Object { $_ -notin $keep } |
| 57 | + |
| 58 | +# Assert all branches removed are up-to-date, unless $force is set |
| 59 | +if (-not $force) { |
| 60 | + foreach ($branch in $toRemove) { |
| 61 | + if ($branch -eq $source) { continue } |
| 62 | + Invoke-LocalAction @commonParams @{ |
| 63 | + type = 'assert-updated' |
| 64 | + parameters = @{ downstream = $cleanupOnly ? $target : $source; upstream = $branch } |
| 65 | + } |
| 66 | + } |
| 67 | + Assert-Diagnostics $diagnostics |
| 68 | +} |
40 | 69 |
|
41 | | -$preservedUpstream = [String[]]($allPreserve |
42 | | - | ForEach-Object { $_; $upstreamCache[$_] } |
43 | | - | ForEach-Object { $_ } |
44 | | - | Select-Object -uniq) |
| 70 | +# For all branches: |
| 71 | +# 1. Replace $toRemove branches with $target |
| 72 | +# 2. Simplify (new) |
45 | 73 |
|
46 | | -$toRemove = @($allUpstream, $sourceBranch) | ForEach-Object { $_ } | Select-Object -uniq | Where-Object { $preservedUpstream -notcontains $_ } |
| 74 | +$originalUpstreams = Invoke-LocalAction @commonParams @{ |
| 75 | + type = 'get-all-upstreams' |
| 76 | + parameters= @{} |
| 77 | +} |
| 78 | +Assert-Diagnostics $diagnostics |
47 | 79 |
|
48 | | -function Invoke-RemoveBranches($branch) { |
49 | | - if ($toRemove -contains $branch) { |
50 | | - return $upstreamCache[$branch] | ForEach-Object { Invoke-RemoveBranches $_ } | Foreach-Object { $_ } | Select-Object -uniq |
| 80 | +$resultUpstreams = @{} |
| 81 | +foreach ($branch in $originalUpstreams.Keys) { |
| 82 | + if ($branch -in $toRemove) { |
| 83 | + $resultUpstreams[$branch] = $null |
| 84 | + continue |
| 85 | + } |
| 86 | + |
| 87 | + if ($originalUpstreams[$branch] | Where-Object { $_ -in $toRemove }) { |
| 88 | + $resultUpstreams[$branch] = Invoke-LocalAction @commonParams @{ |
| 89 | + type = 'filter-branches' |
| 90 | + parameters = @{ |
| 91 | + include = @($target) + $originalUpstreams[$branch] |
| 92 | + exclude = $toRemove |
| 93 | + } |
| 94 | + } |
| 95 | + Assert-Diagnostics $diagnostics |
51 | 96 | } |
52 | | - return $branch |
53 | 97 | } |
54 | 98 |
|
55 | | -$updates = Get-GitFileNames -branchName $config.upstreamBranch -remote $config.remote | ForEach-Object { |
56 | | - if ($toRemove -contains $_) { return $nil } |
57 | | - $upstream = Select-UpstreamBranches $_ |
58 | | - if (($upstream | Where-Object { $toRemove -contains $_ })) { |
59 | | - # Needs to change |
60 | | - return @{ |
61 | | - branch = $_ |
62 | | - newUpstream = [string[]]($upstream | ForEach-Object { Invoke-RemoveBranches $_ } | ForEach-Object { $_ } | Select-Object -uniq) |
| 99 | +$keys = @() + $resultUpstreams.Keys |
| 100 | +foreach ($branch in $keys) { |
| 101 | + if (-not $resultUpstreams[$branch]) { continue } |
| 102 | + $resultUpstreams[$branch] = Invoke-LocalAction @commonParams @{ |
| 103 | + type = 'simplify-upstream' |
| 104 | + parameters = @{ |
| 105 | + upstreamBranches = $resultUpstreams[$branch] |
| 106 | + overrideUpstreams = $resultUpstreams |
| 107 | + branchName = $branch |
63 | 108 | } |
64 | 109 | } |
65 | | - return $nil |
66 | | -} | Where-Object { $_ -ne $nil } |
| 110 | + Assert-Diagnostics $diagnostics |
| 111 | +} |
67 | 112 |
|
68 | | -if ($dryRun) { |
69 | | - if (-not $cleanupOnly) { |
70 | | - Write-Host "Would push $sourceBranch to $target" |
71 | | - } |
72 | | - Write-Host "Would remove $toRemove" |
73 | | - Write-Host "Would perform updates: $(ConvertTo-Json $updates)" |
74 | | -} else { |
75 | | - $commitMessage = "Release $sourceBranch to $target$($comment -eq $nil -or $comment -eq '' ? '' : "`n`n$comment")" |
76 | | - $upstreamContents = $updates | ArrayToHash -getKey { $_.branch } -getValue { $_.newUpstream } |
77 | | - $upstreamContents[$sourceBranch] = $nil |
78 | | - $toRemove | ForEach-Object { |
79 | | - $upstreamContents[$_] = $nil |
| 113 | +$upstreamHash = Invoke-LocalAction @commonParams @{ |
| 114 | + type = 'set-upstream' |
| 115 | + parameters = @{ |
| 116 | + upstreamBranches = $resultUpstreams |
| 117 | + message = "Release $($source) to $($target)$($comment -eq '' ? '' : " for $($params.comment)")" |
80 | 118 | } |
81 | | - $commitish = Set-MultipleUpstreamBranches $upstreamContents -m $commitMessage |
| 119 | +} |
| 120 | +Assert-Diagnostics $diagnostics |
82 | 121 |
|
83 | | - if ($config.remote -ne $nil) { |
84 | | - $gitDeletions = [String[]]($toRemove | ForEach-Object { ":$_" }) |
| 122 | +$sourceHash = Get-BranchCommit (Get-RemoteBranchRef $source) |
85 | 123 |
|
86 | | - $atomicPart = $config.atomicPushEnabled ? @("--atomic") : @() |
87 | | - $releasePart = $cleanupOnly ? @() : @("$($config.remote)/$($sourceBranch):$target") |
| 124 | +# Finalize: |
| 125 | +# 1. Push the following: |
| 126 | +# - Update _upstream |
| 127 | +# - Delete $toRemove branches |
| 128 | +# - If not $cleanupOnly, push $source commitish to $target |
88 | 129 |
|
89 | | - git push @atomicPart $config.remote @releasePart @gitDeletions "$($commitish):refs/heads/$($config.upstreamBranch)" |
90 | | - } else { |
91 | | - git branch -f $config.upstreamBranch $commitish |
92 | | - if (-not $cleanupOnly) { |
93 | | - git branch -f $sourceBranch $target |
94 | | - } |
95 | | - $toRemove | ForEach-Object { |
96 | | - git branch -D $_ |
97 | | - } |
| 130 | +$commonParams = @{ |
| 131 | + diagnostics = $diagnostics |
| 132 | + dryRun = $dryRun |
| 133 | +} |
| 134 | + |
| 135 | +$resultBranches = @{ |
| 136 | + "$($config.upstreamBranch)" = $upstreamHash.commit |
| 137 | +} |
| 138 | +foreach ($branch in $toRemove) { |
| 139 | + $resultBranches[$branch] = $null |
| 140 | +} |
| 141 | +if (-not $cleanupOnly) { |
| 142 | + $resultBranches[$target] = $sourceHash |
| 143 | +} |
| 144 | + |
| 145 | +Invoke-FinalizeAction @commonParams @{ |
| 146 | + type = 'set-branches' |
| 147 | + parameters = @{ |
| 148 | + branches = $resultBranches |
98 | 149 | } |
99 | 150 | } |
| 151 | +Assert-Diagnostics $diagnostics |
0 commit comments