Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion extensions/git/scripts/bash/create-new-feature-branch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ get_highest_from_specs() {

# Function to get highest number from git branches
get_highest_from_branches() {
git branch -a 2>/dev/null | sed 's/^[* ]*//; s|^remotes/[^/]*/||' | _extract_highest_number
git branch -a 2>/dev/null | sed -E 's/^[+*][[:space:]]+//; s/^[[:space:]]+//; s|^remotes/[^/]*/||' | _extract_highest_number
}

# Extract the highest sequential feature number from a list of ref names (one per line).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ function Get-HighestNumberFromBranches {
$branches = git branch -a 2>$null
if ($LASTEXITCODE -eq 0 -and $branches) {
$cleanNames = $branches | ForEach-Object {
$_.Trim() -replace '^\*?\s+', '' -replace '^remotes/[^/]+/', ''
$_.Trim() -replace '^[+*]?\s+', '' -replace '^remotes/[^/]+/', ''
}
return Get-HighestNumberFromNames -Names $cleanNames
}
Expand Down
60 changes: 60 additions & 0 deletions tests/extensions/git/test_git_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ def _write_config(project: Path, content: str) -> Path:
return config_path


def _add_sibling_worktree(project: Path, path: Path, branch: str) -> None:
"""Add a sibling worktree so `git branch -a` marks it with `+`."""
subprocess.run(
["git", "worktree", "add", "-q", "-b", branch, str(path), "HEAD"],
cwd=project,
check=True,
capture_output=True,
text=True,
)


# Git identity env vars for CI runners without global git config
_GIT_ENV = {
"GIT_AUTHOR_NAME": "Test User",
Expand Down Expand Up @@ -312,6 +323,40 @@ def test_increments_from_existing_specs(self, tmp_path: Path):
data = json.loads(result.stdout)
assert data["FEATURE_NUM"] == "003"

def test_dry_run_counts_branches_checked_out_in_worktrees(self, tmp_path: Path):
"""Branches checked out in sibling worktrees still reserve their prefix."""
project = _setup_project(tmp_path / "project")
_add_sibling_worktree(project, tmp_path / "sibling-worktree", "007-worktree-feature")

result = _run_bash(
"create-new-feature-branch.sh", project,
"--json", "--dry-run", "--short-name", "next", "Next feature",
)

assert result.returncode == 0, result.stderr
data = json.loads(result.stdout)
assert data["BRANCH_NAME"] == "008-next"
assert data["FEATURE_NUM"] == "008"

def test_dry_run_preserves_literal_plus_branch_prefix(self, tmp_path: Path):
"""A literal leading plus in a branch name is not a git worktree marker."""
project = _setup_project(tmp_path)
subprocess.run(
["git", "branch", "+007-plus-prefix"],
cwd=project,
check=True,
)

result = _run_bash(
"create-new-feature-branch.sh", project,
"--json", "--dry-run", "--short-name", "next", "Next feature",
)

assert result.returncode == 0, result.stderr
data = json.loads(result.stdout)
assert data["BRANCH_NAME"] == "001-next"
assert data["FEATURE_NUM"] == "001"

def test_no_git_graceful_degradation(self, tmp_path: Path):
"""create-new-feature-branch.sh works without git (outputs branch name, skips branch creation)."""
project = _setup_project(tmp_path, git=False)
Expand Down Expand Up @@ -351,6 +396,21 @@ def test_creates_branch_sequential(self, tmp_path: Path):
data = json.loads(result.stdout)
assert data["BRANCH_NAME"] == "001-user-auth"

def test_dry_run_counts_branches_checked_out_in_worktrees(self, tmp_path: Path):
"""Branches checked out in sibling worktrees still reserve their prefix."""
project = _setup_project(tmp_path / "project")
_add_sibling_worktree(project, tmp_path / "sibling-worktree", "007-worktree-feature")

result = _run_pwsh(
"create-new-feature-branch.ps1", project,
"-Json", "-DryRun", "-ShortName", "next", "Next feature",
)

assert result.returncode == 0, result.stderr
data = json.loads(result.stdout)
assert data["BRANCH_NAME"] == "008-next"
assert data["FEATURE_NUM"] == "008"

def test_creates_branch_timestamp(self, tmp_path: Path):
"""Extension create-new-feature-branch.ps1 creates timestamp branch."""
project = _setup_project(tmp_path)
Expand Down