microsoft/hve-core

Public

mirrored fromhttps://github.com/microsoft/hve-coreAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/fix-copilot-code-review

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

scripts/dev-tools/Generate-PrReference.ps1

439lines · modecode

1<#
2.SYNOPSIS
3Generates the Copilot PR reference XML using git history and diff data.
4
5.DESCRIPTION
6Creates .copilot-tracking/pr/pr-reference.xml relative to the repository root,
7mirroring the behaviour of scripts/pr-ref-gen.sh. Supports excluding markdown
8files from the diff and specifying an alternate base branch for comparisons.
9
10.PARAMETER BaseBranch
11Git branch used as the comparison base. Defaults to "main".
12
13.PARAMETER ExcludeMarkdownDiff
14When supplied, excludes markdown (*.md) files from the diff output.
15#>
16[CmdletBinding()]
17param(
18 [Parameter()]
19 [string]$BaseBranch = "main",
20
21 [Parameter()]
22 [switch]$ExcludeMarkdownDiff
23)
24
25$ErrorActionPreference = 'Stop'
26
27function Test-GitAvailability {
28<#
29.SYNOPSIS
30Verifies the git executable is available.
31.DESCRIPTION
32Throws a terminating error when git can't be resolved from PATH.
33#>
34 if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
35 throw "Git is required but was not found on PATH."
36 }
37}
38
39function Get-RepositoryRoot {
40<#
41.SYNOPSIS
42Gets the repository root path.
43.DESCRIPTION
44Runs git rev-parse --show-toplevel and throws when the command fails.
45.OUTPUTS
46System.String
47#>
48 $repoRoot = (& git rev-parse --show-toplevel).Trim()
49 if (-not $repoRoot) {
50 throw "Unable to determine repository root."
51 }
52
53 return $repoRoot
54}
55
56function New-PrDirectory {
57<#
58.SYNOPSIS
59Creates the PR tracking directory when missing.
60.DESCRIPTION
61Ensures .copilot-tracking/pr exists beneath the supplied repository root.
62.PARAMETER RepoRoot
63Absolute path to the git repository root.
64.OUTPUTS
65System.String
66#>
67 [CmdletBinding(SupportsShouldProcess = $true)]
68 param(
69 [Parameter(Mandatory = $true)]
70 [string]$RepoRoot
71 )
72
73 $prDirectory = Join-Path $RepoRoot '.copilot-tracking/pr'
74 if (-not (Test-Path $prDirectory)) {
75 if ($PSCmdlet.ShouldProcess($prDirectory, 'Create PR tracking directory')) {
76 $null = New-Item -ItemType Directory -Path $prDirectory -Force
77 }
78 }
79
80 return $prDirectory
81}
82
83function Resolve-ComparisonReference {
84<#
85.SYNOPSIS
86Resolves the git reference used for comparisons.
87.DESCRIPTION
88Prefers origin/<BaseBranch> when available and falls back to the provided branch.
89.PARAMETER BaseBranch
90Branch name supplied by the caller.
91.OUTPUTS
92PSCustomObject
93#>
94 param(
95 [Parameter(Mandatory = $true)]
96 [string]$BaseBranch
97 )
98
99 $candidates = @()
100 if ($BaseBranch -notlike 'origin/*' -and $BaseBranch -notlike 'refs/*') {
101 $candidates += "origin/$BaseBranch"
102 }
103 $candidates += $BaseBranch
104
105 foreach ($candidate in $candidates) {
106 & git rev-parse --verify $candidate *> $null
107 if ($LASTEXITCODE -eq 0) {
108 $label = if ($candidate -eq $BaseBranch) {
109 $BaseBranch
110 } else {
111 "$BaseBranch (via $candidate)"
112 }
113
114 return [PSCustomObject]@{
115 Ref = $candidate
116 Label = $label
117 }
118 }
119 }
120
121 throw "Branch '$BaseBranch' does not exist or is not accessible."
122}
123
124function Get-ShortCommitHash {
125<#
126.SYNOPSIS
127Retrieves the short commit hash for a ref.
128.DESCRIPTION
129Uses git rev-parse --short to resolve the supplied ref.
130.PARAMETER Ref
131Git reference to resolve.
132.OUTPUTS
133System.String
134#>
135 param(
136 [Parameter(Mandatory = $true)]
137 [string]$Ref
138 )
139
140 $commit = (& git rev-parse --short $Ref).Trim()
141 if ($LASTEXITCODE -ne 0) {
142 throw "Failed to resolve ref '$Ref'."
143 }
144
145 return $commit
146}
147
148function Get-CommitEntry {
149<#
150.SYNOPSIS
151Collects formatted commit metadata.
152.DESCRIPTION
153Runs git log to gather commit entries relative to the supplied comparison ref.
154.PARAMETER ComparisonRef
155Git reference that acts as the diff base.
156.OUTPUTS
157System.String[]
158#>
159 param(
160 [Parameter(Mandatory = $true)]
161 [string]$ComparisonRef
162 )
163
164 $logArgs = @(
165 '--no-pager',
166 'log',
167 '--pretty=format:<commit hash="%h" date="%cd"><message><subject><![CDATA[%s]]></subject><body><![CDATA[%b]]></body></message></commit>',
168 '--date=short',
169 "${ComparisonRef}..HEAD"
170 )
171
172 $entries = & git @logArgs
173 if ($LASTEXITCODE -ne 0) {
174 throw "Failed to retrieve commit history."
175 }
176
177 return $entries
178}
179
180function Get-CommitCount {
181<#
182.SYNOPSIS
183Counts commits between HEAD and the comparison ref.
184.DESCRIPTION
185Executes git rev-list --count to measure branch divergence.
186.PARAMETER ComparisonRef
187Git reference that acts as the diff base.
188.OUTPUTS
189System.Int32
190#>
191 param(
192 [Parameter(Mandatory = $true)]
193 [string]$ComparisonRef
194 )
195
196 $countText = (& git --no-pager rev-list --count "${ComparisonRef}..HEAD").Trim()
197 if ($LASTEXITCODE -ne 0) {
198 throw "Failed to count commits."
199 }
200
201 if (-not $countText) {
202 return 0
203 }
204
205 return [int]$countText
206}
207
208function Get-DiffOutput {
209<#
210.SYNOPSIS
211Builds the git diff output for the comparison ref.
212.DESCRIPTION
213Runs git diff against the comparison ref with optional markdown exclusion.
214.PARAMETER ComparisonRef
215Git reference that acts as the diff base.
216.PARAMETER ExcludeMarkdownDiff
217Switch to omit markdown files from the diff.
218.OUTPUTS
219System.String[]
220#>
221 param(
222 [Parameter(Mandatory = $true)]
223 [string]$ComparisonRef,
224
225 [Parameter()]
226 [switch]$ExcludeMarkdownDiff
227 )
228
229 $diffArgs = @('--no-pager', 'diff', $ComparisonRef)
230 if ($ExcludeMarkdownDiff) {
231 $diffArgs += @('--', ':!*.md')
232 }
233
234 $diffOutput = & git @diffArgs
235 if ($LASTEXITCODE -ne 0) {
236 throw "Failed to retrieve diff output."
237 }
238
239 return $diffOutput
240}
241
242function Get-DiffSummary {
243<#
244.SYNOPSIS
245Summarizes the diff for quick reporting.
246.DESCRIPTION
247Uses git diff --shortstat against the comparison ref.
248.PARAMETER ComparisonRef
249Git reference that acts as the diff base.
250.PARAMETER ExcludeMarkdownDiff
251Switch to omit markdown files from the summary.
252.OUTPUTS
253System.String
254#>
255 param(
256 [Parameter(Mandatory = $true)]
257 [string]$ComparisonRef,
258
259 [Parameter()]
260 [switch]$ExcludeMarkdownDiff
261 )
262
263 $diffStatArgs = @('--no-pager', 'diff', '--shortstat', $ComparisonRef)
264 if ($ExcludeMarkdownDiff) {
265 $diffStatArgs += @('--', ':!*.md')
266 }
267
268 $summary = & git @diffStatArgs
269 if ($LASTEXITCODE -ne 0) {
270 throw "Failed to summarize diff output."
271 }
272
273 if (-not $summary) {
274 return '0 files changed'
275 }
276
277 return $summary
278}
279
280function Get-PrXmlContent {
281<#
282.SYNOPSIS
283Constructs the PR reference XML document.
284.DESCRIPTION
285Creates XML containing the current branch, base branch, commits, and diff.
286.PARAMETER CurrentBranch
287Name of the active git branch.
288.PARAMETER BaseBranch
289Branch used as the base reference.
290.PARAMETER CommitEntries
291Formatted commit entries produced by Get-CommitEntry.
292.PARAMETER DiffOutput
293Diff lines produced by Get-DiffOutput.
294.OUTPUTS
295System.String
296#>
297 param(
298 [Parameter(Mandatory = $true)]
299 [string]$CurrentBranch,
300
301 [Parameter(Mandatory = $true)]
302 [string]$BaseBranch,
303
304 [Parameter()]
305 [string[]]$CommitEntries,
306
307 [Parameter()]
308 [string[]]$DiffOutput
309 )
310
311 $commitBlock = if ($CommitEntries) {
312 ($CommitEntries | ForEach-Object { " $_" }) -join [Environment]::NewLine
313 } else {
314 ""
315 }
316
317 $diffBlock = if ($DiffOutput) {
318 ($DiffOutput | ForEach-Object { " $_" }) -join [Environment]::NewLine
319 } else {
320 ""
321 }
322
323 return @"
324<commit_history>
325 <current_branch>
326 $CurrentBranch
327 </current_branch>
328
329 <base_branch>
330 $BaseBranch
331 </base_branch>
332
333 <commits>
334$commitBlock
335 </commits>
336
337 <full_diff>
338$diffBlock
339 </full_diff>
340</commit_history>
341"@
342}
343
344function Get-LineImpact {
345<#
346.SYNOPSIS
347Calculates total line impact from a diff summary.
348.DESCRIPTION
349Parses insertion and deletion counts from git diff --shortstat output.
350.PARAMETER DiffSummary
351Short diff summary text.
352.OUTPUTS
353System.Int32
354#>
355 param(
356 [Parameter(Mandatory = $true)]
357 [string]$DiffSummary
358 )
359
360 $lineImpact = 0
361 if ($DiffSummary -match '(\d+) insertions') {
362 $lineImpact += [int]$matches[1]
363 }
364 if ($DiffSummary -match '(\d+) deletions') {
365 $lineImpact += [int]$matches[1]
366 }
367
368 return $lineImpact
369}
370
371function Invoke-PrReferenceGeneration {
372<#
373.SYNOPSIS
374Generates the pr-reference.xml file.
375.DESCRIPTION
376Coordinates git queries, XML creation, and console reporting for Copilot usage.
377.PARAMETER BaseBranch
378Branch used as the comparison base.
379.PARAMETER ExcludeMarkdownDiff
380Switch to omit markdown files from the diff and summary.
381.OUTPUTS
382System.IO.FileInfo
383#>
384 param(
385 [Parameter(Mandatory = $true)]
386 [string]$BaseBranch,
387
388 [Parameter()]
389 [switch]$ExcludeMarkdownDiff
390 )
391
392 Test-GitAvailability
393
394 $repoRoot = Get-RepositoryRoot
395 $prDirectory = New-PrDirectory -RepoRoot $repoRoot
396 $prReferencePath = Join-Path $prDirectory 'pr-reference.xml'
397
398 $diffSummary = '0 files changed'
399 $commitCount = 0
400 $comparisonInfo = $null
401 $baseCommit = ''
402
403 Push-Location $repoRoot
404 try {
405 $currentBranch = (& git --no-pager branch --show-current).Trim()
406 $comparisonInfo = Resolve-ComparisonReference -BaseBranch $BaseBranch
407 $baseCommit = Get-ShortCommitHash -Ref $comparisonInfo.Ref
408 $commitEntries = Get-CommitEntry -ComparisonRef $comparisonInfo.Ref
409 $commitCount = Get-CommitCount -ComparisonRef $comparisonInfo.Ref
410 $diffOutput = Get-DiffOutput -ComparisonRef $comparisonInfo.Ref -ExcludeMarkdownDiff:$ExcludeMarkdownDiff
411 $diffSummary = Get-DiffSummary -ComparisonRef $comparisonInfo.Ref -ExcludeMarkdownDiff:$ExcludeMarkdownDiff
412
413 $xmlContent = Get-PrXmlContent -CurrentBranch $currentBranch -BaseBranch $BaseBranch -CommitEntries $commitEntries -DiffOutput $diffOutput
414 $xmlContent | Set-Content -LiteralPath $prReferencePath
415 }
416 finally {
417 Pop-Location
418 }
419
420 $lineCount = (Get-Content -LiteralPath $prReferencePath).Count
421 $lineImpact = Get-LineImpact -DiffSummary $diffSummary
422
423 Write-Host "Created $prReferencePath"
424 if ($ExcludeMarkdownDiff) {
425 Write-Host 'Note: Markdown files were excluded from diff output'
426 }
427 Write-Host "Lines: $lineCount"
428 Write-Host "Base branch: $($comparisonInfo.Label) (@ $baseCommit)"
429 Write-Host "Commits compared: $commitCount"
430 Write-Host "Diff summary: $diffSummary"
431
432 if ($lineImpact -gt 1000) {
433 Write-Host 'Large diff detected. Rebase onto the intended base branch or narrow your changes if this scope is unexpected.'
434 }
435
436 return Get-Item -LiteralPath $prReferencePath
437}
438
439Invoke-PrReferenceGeneration -BaseBranch $BaseBranch -ExcludeMarkdownDiff:$ExcludeMarkdownDiff | Out-Null
440