diff --git a/extensions/git/scripts/bash/create-new-feature.sh b/extensions/git/scripts/bash/create-new-feature.sh index dfae29df73..c83e8c613f 100755 --- a/extensions/git/scripts/bash/create-new-feature.sh +++ b/extensions/git/scripts/bash/create-new-feature.sh @@ -366,8 +366,11 @@ if [ "$DRY_RUN" != true ]; then if [ "$ALLOW_EXISTING" = true ]; then if [ "$current_branch" = "$BRANCH_NAME" ]; then : - elif ! git checkout "$BRANCH_NAME" 2>/dev/null; then + elif ! switch_branch_error=$(git checkout -q "$BRANCH_NAME" 2>&1); then >&2 echo "Error: Failed to switch to existing branch '$BRANCH_NAME'. Please resolve any local changes or conflicts and try again." + if [ -n "$switch_branch_error" ]; then + >&2 printf '%s\n' "$switch_branch_error" + fi exit 1 fi elif [ "$USE_TIMESTAMP" = true ]; then diff --git a/extensions/git/scripts/powershell/create-new-feature.ps1 b/extensions/git/scripts/powershell/create-new-feature.ps1 index 75a4e69814..ded6eaa72f 100644 --- a/extensions/git/scripts/powershell/create-new-feature.ps1 +++ b/extensions/git/scripts/powershell/create-new-feature.ps1 @@ -327,9 +327,13 @@ if (-not $DryRun) { if ($currentBranch -eq $branchName) { # Already on the target branch } else { - git checkout -q $branchName 2>$null | Out-Null + $switchBranchError = git checkout -q $branchName 2>&1 | Out-String if ($LASTEXITCODE -ne 0) { - Write-Error "Error: Branch '$branchName' exists but could not be checked out. Resolve any uncommitted changes or conflicts and try again." + if ($switchBranchError) { + Write-Error "Error: Branch '$branchName' exists but could not be checked out.`n$($switchBranchError.Trim())" + } else { + Write-Error "Error: Branch '$branchName' exists but could not be checked out. Resolve any uncommitted changes or conflicts and try again." + } exit 1 } } diff --git a/scripts/bash/create-new-feature.sh b/scripts/bash/create-new-feature.sh index f9ba9545df..1879647026 100644 --- a/scripts/bash/create-new-feature.sh +++ b/scripts/bash/create-new-feature.sh @@ -337,8 +337,11 @@ if [ "$DRY_RUN" != true ]; then if [ "$current_branch" = "$BRANCH_NAME" ]; then : # Otherwise switch to the existing branch instead of failing. - elif ! git checkout "$BRANCH_NAME" 2>/dev/null; then + elif ! switch_branch_error=$(git checkout -q "$BRANCH_NAME" 2>&1); then >&2 echo "Error: Failed to switch to existing branch '$BRANCH_NAME'. Please resolve any local changes or conflicts and try again." + if [ -n "$switch_branch_error" ]; then + >&2 printf '%s\n' "$switch_branch_error" + fi exit 1 fi elif [ "$USE_TIMESTAMP" = true ]; then diff --git a/scripts/powershell/create-new-feature.ps1 b/scripts/powershell/create-new-feature.ps1 index 3e7e525b86..2f23283fc4 100644 --- a/scripts/powershell/create-new-feature.ps1 +++ b/scripts/powershell/create-new-feature.ps1 @@ -315,9 +315,13 @@ if (-not $DryRun) { # Already on the target branch — nothing to do } else { # Otherwise switch to the existing branch instead of failing. - git checkout -q $branchName 2>$null | Out-Null + $switchBranchError = git checkout -q $branchName 2>&1 | Out-String if ($LASTEXITCODE -ne 0) { - Write-Error "Error: Branch '$branchName' exists but could not be checked out. Resolve any uncommitted changes or conflicts and try again." + if ($switchBranchError) { + Write-Error "Error: Branch '$branchName' exists but could not be checked out.`n$($switchBranchError.Trim())" + } else { + Write-Error "Error: Branch '$branchName' exists but could not be checked out. Resolve any uncommitted changes or conflicts and try again." + } exit 1 } } diff --git a/tests/test_timestamp_branches.py b/tests/test_timestamp_branches.py index 2c13853119..605ae48965 100644 --- a/tests/test_timestamp_branches.py +++ b/tests/test_timestamp_branches.py @@ -15,6 +15,12 @@ PROJECT_ROOT = Path(__file__).resolve().parent.parent CREATE_FEATURE = PROJECT_ROOT / "scripts" / "bash" / "create-new-feature.sh" CREATE_FEATURE_PS = PROJECT_ROOT / "scripts" / "powershell" / "create-new-feature.ps1" +EXT_CREATE_FEATURE = ( + PROJECT_ROOT / "extensions" / "git" / "scripts" / "bash" / "create-new-feature.sh" +) +EXT_CREATE_FEATURE_PS = ( + PROJECT_ROOT / "extensions" / "git" / "scripts" / "powershell" / "create-new-feature.ps1" +) COMMON_SH = PROJECT_ROOT / "scripts" / "bash" / "common.sh" @@ -428,6 +434,43 @@ def test_allow_existing_no_git(self, no_git_dir: Path): ) assert result.returncode == 0, result.stderr + def test_allow_existing_surfaces_checkout_error(self, git_repo: Path): + """Checkout failures on an existing branch should include Git's stderr.""" + shared_file = git_repo / "shared.txt" + shared_file.write_text("base\n") + subprocess.run( + ["git", "add", "shared.txt"], + cwd=git_repo, check=True, capture_output=True, + ) + subprocess.run( + ["git", "commit", "-m", "add shared file", "-q"], + cwd=git_repo, check=True, capture_output=True, + ) + subprocess.run( + ["git", "checkout", "-b", "010-checkout-failure"], + cwd=git_repo, check=True, capture_output=True, + ) + shared_file.write_text("branch version\n") + subprocess.run( + ["git", "commit", "-am", "branch change", "-q"], + cwd=git_repo, check=True, capture_output=True, + ) + subprocess.run( + ["git", "checkout", "-"], + cwd=git_repo, check=True, capture_output=True, + ) + shared_file.write_text("uncommitted main change\n") + + result = run_script( + git_repo, "--allow-existing-branch", "--short-name", "checkout-failure", + "--number", "10", "Checkout failure", + ) + + assert result.returncode != 0, "checkout should fail with conflicting local changes" + assert "Failed to switch to existing branch '010-checkout-failure'" in result.stderr + assert "would be overwritten by checkout" in result.stderr + assert "shared.txt" in result.stderr + class TestAllowExistingBranchPowerShell: def test_powershell_supports_allow_existing_branch_flag(self): @@ -437,6 +480,26 @@ def test_powershell_supports_allow_existing_branch_flag(self): # Ensure the flag is referenced in script logic, not just declared assert "AllowExistingBranch" in contents.replace("-AllowExistingBranch", "") + def test_powershell_surfaces_checkout_errors(self): + """Static guard: PS script preserves checkout stderr on existing-branch failures.""" + contents = CREATE_FEATURE_PS.read_text(encoding="utf-8") + assert "$switchBranchError = git checkout -q $branchName 2>&1 | Out-String" in contents + assert "exists but could not be checked out.`n$($switchBranchError.Trim())" in contents + + +class TestGitExtensionParity: + def test_bash_extension_surfaces_checkout_errors(self): + """Static guard: git extension bash script preserves checkout stderr.""" + contents = EXT_CREATE_FEATURE.read_text(encoding="utf-8") + assert 'switch_branch_error=$(git checkout -q "$BRANCH_NAME" 2>&1)' in contents + assert "Failed to switch to existing branch '$BRANCH_NAME'" in contents + + def test_powershell_extension_surfaces_checkout_errors(self): + """Static guard: git extension PowerShell script preserves checkout stderr.""" + contents = EXT_CREATE_FEATURE_PS.read_text(encoding="utf-8") + assert "$switchBranchError = git checkout -q $branchName 2>&1 | Out-String" in contents + assert "exists but could not be checked out.`n$($switchBranchError.Trim())" in contents + # ── Dry-Run Tests ────────────────────────────────────────────────────────────