Skip to content

Instantly share code, notes, and snippets.

@ychin
Last active January 16, 2022 17:36
Show Gist options
  • Save ychin/d9f83a8053ab4f2452b716123599eca3 to your computer and use it in GitHub Desktop.
Save ychin/d9f83a8053ab4f2452b716123599eca3 to your computer and use it in GitHub Desktop.
Powershell scripts to find the "parent" branch (aka the branch with the closest parent commit) to the current one.
@powershell -NoProfile -File %~dp0findParentBranch.ps1 %*
<#
.SYNOPSIS
Find the "parent" branch of the current branch.
.PARAMETER BranchName
If provided, will search for `BranchName` instead of the current branch in the repo.
.PARAMETER FindAll
Provide the list of all parent and found grandparent branches. Otherwise it will only find the immediate parent.
.DESCRIPTION
This script is not fool-proof as it's mostly a guess since Git doesn't really know the difference between parent and children branches. It uses "git show-branch", so it will use whatever short name the command decides to name a commit to compute which one is the parent branch.
#>
Param
(
[Parameter(Position=0)]
[string] $BranchName,
[switch] $FindAll
)
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "git"
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = "show-branch --current --topo-order --no-color"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$stdout = $p.StandardOutput
$currentBranchIndex = 0
$currentBranchName = ""
$currentBranchFound = $false
$allBranchesListed = $false
$foundBranch = ""
$foundBranches = @{}
while ( ($stdout -ne $null) -and (($line = $stdout.ReadLine()) -ne $null) )
{
if (-not $currentBranchFound)
{
if ($BranchName -eq "")
{
if ($line.Length -gt $currentBranchIndex -and $line[$currentBranchIndex] -eq '*')
{
$currentBranchFound = $true
# 1) Branch names can contain ']', but not spaces, and the string pattern here is "[<branch_name>] <description>", so search for "] " which will guarantee we find the delimiter rather than part of a branch name.
# 2) Use non-greedy (.*?) matching so that commit descriptions that contain "] " wouldn't mess up the search.
if ($line -match '\[(.*?)\] ')
{
$currentBranchName = $Matches[1]
}
else
{
Write-Host "Error: Couldn't parse current branch info from Git"
$stdout.Close()
if (-not $p.HasExited)
{
$p.Kill()
}
exit -1
}
}
else
{
$currentBranchIndex += 1
}
}
else
{
if ($line -match '\[(.*?)\] ' -and $Matches[1] -eq $BranchName)
{
$currentBranchName = $BranchName
$currentBranchFound = $true
}
else
{
$currentBranchIndex += 1
}
}
}
if ($line -match '^-*$')
{
if (-not $currentBranchFound)
{
Write-Host "Couldn't find the current branch."
$stdout.Close()
if (-not $p.HasExited)
{
$p.Kill()
}
exit -1
}
$allBranchesListed = $true
}
elseif ($allBranchesListed)
{
if ($line[$currentBranchIndex] -ne ' ')
{
if ($line -match '\[(.*?)(\] |~|\^)')
{
$lineBranch = $Matches[1]
if ($lineBranch -ne $currentBranchName)
{
$foundBranch = $lineBranch
if (-not $foundBranches[$foundBranch])
{
$foundBranches[$foundBranch] = $true
Write-Host $foundBranch
if (-not $FindAll)
{
break
}
}
}
}
}
}
}
$stdout.Close()
if (-not $p.HasExited)
{
$p.Kill()
}
if ($foundBranch -eq "")
{
Write-Host "Couldn't find parent branch."
exit -1
}
@johan-boule
Copy link

johan-boule commented Jan 15, 2022

Heh, let's check what a shell has to be then.
I totally agree that powershell is actually better described as a scripting language like perl, python, etc.
A shell is what the user is interacting with.
One of the things a user does is starting programs, binaries, in separate processes. On Unices, the user also wants to combine programs though pipes without having to become a programmer, so the shell has to make that easy to do.
Now, powershell ceases to be a shell as soon as you want to use programs that haven't been designed and written in or for powershell. It's a closed ecosystem, severed from the world.
Scripting git commands is a prime example of where powershell makes little sense. You'd better use bash or any well spread portable scripting language like python.

@ychin
Copy link
Author

ychin commented Jan 16, 2022

You'd better use bash or any well spread portable scripting language like python.

That's fair, except Python is not guaranteed to be installed on a Windows machine, but PowerShell is. So if you write a script that you want to be guaranteed to work on others' Windows boxes without needing extra dependencies (WSL is a dependency), PowerShell is the way to go. Same reason why people write complicated bash scripts for *nix systems instead of Python/etc when you are trying to maximize compatibility.

If you use PowerShell in Windows as an actual shell, scripts written in PowerShell also gets auto-complete for free, which a script written in other languages don't. It's all about knowing what audience you want for a particular tool and what environments they will be run in.

@ychin
Copy link
Author

ychin commented Jan 16, 2022

Scripting git commands is a prime example of where powershell makes little sense

Also, bash isn't that great with Git anyway IMO. You end up scripting complicated awk / sed / grep chains that would make the script much more complicated and unreadable than the PowerShell script here, which can just do regex builtin and loop through each line. My personal opinion is bash / zsh are designed primarily for interactive use, whereas their scripting ability and syntax are actually quite poor, and most people only write bash scripts since it's available everywhere. Python is probably a good choice for a script like this, but there are compatibility issues as I discussed if you are targeting Windows users (which I was at the time).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment