From 33df8976ca714a6c3e772c4c6814756ade33ec19 Mon Sep 17 00:00:00 2001 From: Joseph Mearman Date: Sun, 23 Nov 2025 16:01:56 +0000 Subject: [PATCH] fix: use global maximum for branch numbering to prevent collisions The check_existing_branches (bash) and Get-NextBranchNumber (PowerShell) functions were only looking for branches/specs matching the SAME short name when determining the next feature number. This caused collisions where multiple features could be assigned the same number if they had different short names. For example, if feature 023-ci-optimization existed, creating a new feature with a different short name would incorrectly use 001 instead of 024. This fix changes both functions to: 1. Use get_highest_from_branches() / Get-HighestNumberFromBranches to find the highest number across ALL branches globally 2. Use get_highest_from_specs() / Get-HighestNumberFromSpecs to find the highest number across ALL spec directories globally 3. Return the maximum of both + 1 The helper functions already existed but were not being used. This fix properly utilizes them to ensure features are numbered sequentially regardless of their short names. Issue: Branch number collisions when creating features with different names Impact: Prevents multiple features from sharing the same number prefix --- scripts/bash/create-new-feature.sh | 34 +++++------- scripts/powershell/create-new-feature.ps1 | 65 ++++------------------- 2 files changed, 24 insertions(+), 75 deletions(-) diff --git a/scripts/bash/create-new-feature.sh b/scripts/bash/create-new-feature.sh index 592dab2e..9e57cc6e 100644 --- a/scripts/bash/create-new-feature.sh +++ b/scripts/bash/create-new-feature.sh @@ -130,30 +130,22 @@ get_highest_from_branches() { check_existing_branches() { local short_name="$1" local specs_dir="$2" - + # Fetch all remotes to get latest branch info (suppress errors if no remotes) git fetch --all --prune 2>/dev/null || true - - # Find all branches matching the pattern using git ls-remote (more reliable) - local remote_branches=$(git ls-remote --heads origin 2>/dev/null | grep -E "refs/heads/[0-9]+-${short_name}$" | sed 's/.*\/\([0-9]*\)-.*/\1/' | sort -n) - - # Also check local branches - local local_branches=$(git branch 2>/dev/null | grep -E "^[* ]*[0-9]+-${short_name}$" | sed 's/^[* ]*//' | sed 's/-.*//' | sort -n) - - # Check specs directory as well - local spec_dirs="" - if [ -d "$specs_dir" ]; then - spec_dirs=$(find "$specs_dir" -maxdepth 1 -type d -name "[0-9]*-${short_name}" 2>/dev/null | xargs -n1 basename 2>/dev/null | sed 's/-.*//' | sort -n) + + # Get highest number from ALL branches (not just matching short name) + local highest_branch=$(get_highest_from_branches) + + # Get highest number from ALL specs (not just matching short name) + local highest_spec=$(get_highest_from_specs "$specs_dir") + + # Take the maximum of both + local max_num=$highest_branch + if [ "$highest_spec" -gt "$max_num" ]; then + max_num=$highest_spec fi - - # Combine all sources and get the highest number - local max_num=0 - for num in $remote_branches $local_branches $spec_dirs; do - if [ "$num" -gt "$max_num" ]; then - max_num=$num - fi - done - + # Return next number echo $((max_num + 1)) } diff --git a/scripts/powershell/create-new-feature.ps1 b/scripts/powershell/create-new-feature.ps1 index 351f4e9e..80e989c3 100644 --- a/scripts/powershell/create-new-feature.ps1 +++ b/scripts/powershell/create-new-feature.ps1 @@ -104,66 +104,23 @@ function Get-NextBranchNumber { [string]$ShortName, [string]$SpecsDir ) - + # Fetch all remotes to get latest branch info (suppress errors if no remotes) try { git fetch --all --prune 2>$null | Out-Null } catch { # Ignore fetch errors } - - # Find remote branches matching the pattern using git ls-remote - $remoteBranches = @() - try { - $remoteRefs = git ls-remote --heads origin 2>$null - if ($remoteRefs) { - $remoteBranches = $remoteRefs | Where-Object { $_ -match "refs/heads/(\d+)-$([regex]::Escape($ShortName))$" } | ForEach-Object { - if ($_ -match "refs/heads/(\d+)-") { - [int]$matches[1] - } - } - } - } catch { - # Ignore errors - } - - # Check local branches - $localBranches = @() - try { - $allBranches = git branch 2>$null - if ($allBranches) { - $localBranches = $allBranches | Where-Object { $_ -match "^\*?\s*(\d+)-$([regex]::Escape($ShortName))$" } | ForEach-Object { - if ($_ -match "(\d+)-") { - [int]$matches[1] - } - } - } - } catch { - # Ignore errors - } - - # Check specs directory - $specDirs = @() - if (Test-Path $SpecsDir) { - try { - $specDirs = Get-ChildItem -Path $SpecsDir -Directory | Where-Object { $_.Name -match "^(\d+)-$([regex]::Escape($ShortName))$" } | ForEach-Object { - if ($_.Name -match "^(\d+)-") { - [int]$matches[1] - } - } - } catch { - # Ignore errors - } - } - - # Combine all sources and get the highest number - $maxNum = 0 - foreach ($num in ($remoteBranches + $localBranches + $specDirs)) { - if ($num -gt $maxNum) { - $maxNum = $num - } - } - + + # Get highest number from ALL branches (not just matching short name) + $highestBranch = Get-HighestNumberFromBranches + + # Get highest number from ALL specs (not just matching short name) + $highestSpec = Get-HighestNumberFromSpecs -SpecsDir $SpecsDir + + # Take the maximum of both + $maxNum = [Math]::Max($highestBranch, $highestSpec) + # Return next number return $maxNum + 1 }