@@ -56,6 +56,20 @@ function Test-GitRefExists {
5656 return ($LASTEXITCODE -eq 0 )
5757}
5858
59+ function Convert-ToPowerShellBranchCompletionText {
60+ param (
61+ [Parameter (Mandatory = $true )]
62+ [string ]$BranchName
63+ )
64+
65+ if ($BranchName.StartsWith (' #' )) {
66+ $escaped = $BranchName -replace " '" , " ''"
67+ return " '$escaped '"
68+ }
69+
70+ return $BranchName
71+ }
72+
5973
6074# --- Custom Git Command Functions ---
6175function UpMerge {
@@ -156,6 +170,81 @@ function ghash {
156170 }
157171}
158172
173+ function gfp {
174+ [CmdletBinding ()]
175+ param (
176+ [Parameter (Position = 0 )]
177+ [string ]$TargetBranch ,
178+ [Parameter (Position = 1 )]
179+ [string ]$OutputFile = ' series.mbox'
180+ )
181+
182+ if (-not (Test-InGitRepo )) {
183+ throw " Not a git repository."
184+ }
185+
186+ $defaultRemote = git config -- get checkout.defaultRemote 2> $null
187+ if (-not $defaultRemote ) { $defaultRemote = ' origin' }
188+
189+ if (-not $TargetBranch ) {
190+ if (Test-GitRefExists " refs/remotes/$defaultRemote /main" ) {
191+ $TargetBranch = ' main'
192+ } elseif (Test-GitRefExists " refs/remotes/$defaultRemote /master" ) {
193+ $TargetBranch = ' master'
194+ } else {
195+ $remoteHead = git symbolic- ref -- quiet " refs/remotes/$defaultRemote /HEAD" 2> $null
196+ if ($LASTEXITCODE -eq 0 -and $remoteHead ) {
197+ $prefix = " refs/remotes/$defaultRemote /"
198+ if ($remoteHead.StartsWith ($prefix )) {
199+ $TargetBranch = $remoteHead.Substring ($prefix.Length ).Trim()
200+ }
201+ }
202+ }
203+
204+ if (-not $TargetBranch ) { $TargetBranch = ' main' }
205+ }
206+
207+ $range = " $defaultRemote /$TargetBranch ..HEAD"
208+ $resolvedOutput = if ([IO.Path ]::IsPathRooted($OutputFile )) {
209+ $OutputFile
210+ } else {
211+ Join-Path (Get-Location ) $OutputFile
212+ }
213+
214+ $outputDir = Split-Path - Parent $resolvedOutput
215+ if ($outputDir -and -not (Test-Path $outputDir )) {
216+ New-Item - ItemType Directory - Path $outputDir - Force | Out-Null
217+ }
218+
219+ $stderrFile = [IO.Path ]::GetTempFileName()
220+ try {
221+ $process = Start-Process - FilePath ' git' `
222+ - ArgumentList @ (' format-patch' , ' --cover-letter' , ' --stat' , ' --stdout' , $range ) `
223+ - NoNewWindow `
224+ - PassThru `
225+ - Wait `
226+ - RedirectStandardOutput $resolvedOutput `
227+ - RedirectStandardError $stderrFile
228+
229+ $stderrText = Get-Content - LiteralPath $stderrFile - Raw - ErrorAction SilentlyContinue
230+ if ($stderrText ) {
231+ $stderrText = $stderrText.Trim ()
232+ if ($stderrText ) {
233+ Write-Host $stderrText
234+ }
235+ }
236+
237+ if ($process.ExitCode -ne 0 ) {
238+ Remove-Item - LiteralPath $resolvedOutput - Force - ErrorAction SilentlyContinue
239+ throw " git format-patch failed for '$range ' (exit code: $ ( $process.ExitCode ) )."
240+ }
241+
242+ return $resolvedOutput
243+ } finally {
244+ Remove-Item - LiteralPath $stderrFile - Force - ErrorAction SilentlyContinue
245+ }
246+ }
247+
159248function gsw {
160249 [CmdletBinding ()]
161250 param (
@@ -224,8 +313,12 @@ function Register-GitAliasCompletion {
224313 }
225314 }
226315
227- # Add your custom aliases from this module
228- Get-Command - Module GitAliases.Extras - CommandType Function | ForEach-Object {
316+ # Add custom aliases from this module without module-name lookups
317+ # to avoid self-import recursion during module initialization.
318+ $moduleName = $ExecutionContext.SessionState.Module.Name
319+ Get-Command - CommandType Function |
320+ Where-Object { $_.ModuleName -eq $moduleName } |
321+ ForEach-Object {
229322 $func = $_
230323 $definition = $func.ScriptBlock.ToString ()
231324 if ($definition -match $aliasRegex ) {
@@ -257,11 +350,32 @@ function Register-GitAliasCompletion {
257350 $gitLine = $line -replace " ^$commandName " , " git $subCommand "
258351 $offset = (" git $subCommand " ).Length - $commandName.Length
259352 $gitCursorPosition = $cursorPosition + $offset
353+ if ($gitCursorPosition -lt 0 ) { $gitCursorPosition = 0 }
354+ if ($gitCursorPosition -gt $gitLine.Length ) { $gitCursorPosition = $gitLine.Length }
260355
261356 # Use posh-git's official completion function
262357 if (Get-Command GitTabExpansion - ErrorAction SilentlyContinue) {
263358 try {
264- return GitTabExpansion $gitLine $gitCursorPosition
359+ $results = GitTabExpansion $gitLine $gitCursorPosition
360+ if ($subCommand -in @ (' checkout' , ' switch' , ' merge' , ' rebase' , ' branch' , ' reset' , ' revert' )) {
361+ return $results | ForEach-Object {
362+ if ($null -eq $_ ) { return }
363+ $completionText = $_.CompletionText
364+ if ($completionText -is [string ] -and $completionText.StartsWith (' #' )) {
365+ $safeText = Convert-ToPowerShellBranchCompletionText - BranchName $completionText
366+ return [System.Management.Automation.CompletionResult ]::new(
367+ $safeText ,
368+ $_.ListItemText ,
369+ $_.ResultType ,
370+ $_.ToolTip
371+ )
372+ }
373+
374+ return $_
375+ }
376+ }
377+
378+ return $results
265379 } catch {
266380 Write-Warning " posh-git's GitTabExpansion failed for alias '$commandName '."
267381 }
@@ -276,10 +390,14 @@ function Register-GitAliasCompletion {
276390 Where-Object { $_ -like " $wordToComplete *" }
277391 if ($branches ) {
278392 return $branches | ForEach-Object {
279- [System.Management.Automation.CompletionResult ]::new($_ , $_ , ' ParameterValue' , $_ )
393+ $branchName = $_
394+ $safeText = Convert-ToPowerShellBranchCompletionText - BranchName $branchName
395+ [System.Management.Automation.CompletionResult ]::new($safeText , $branchName , ' ParameterValue' , $branchName )
280396 }
281397 }
282- } catch {}
398+ } catch {
399+ return @ ()
400+ }
283401 }
284402
285403 # Return nothing if no completions are found
0 commit comments