fix(scripts): prioritize .specify over git for repo root detection#1933
fix(scripts): prioritize .specify over git for repo root detection#1933mbachorik wants to merge 8 commits intogithub:mainfrom
Conversation
When spec-kit is initialized in a subdirectory that doesn't have its own .git, but a parent directory does, spec-kit was incorrectly using the parent's git repository root. This caused specs to be created in the wrong location. The fix changes repo root detection to prioritize .specify directory over git rev-parse, ensuring spec-kit respects its own initialization boundary rather than inheriting a parent git repo. Fixes github#1932 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes repo-root detection in the bash and PowerShell helper scripts by prioritizing the presence of a .specify directory over git rev-parse, preventing spec-kit from mistakenly using a parent Git repo as the project root in nested-directory setups.
Changes:
- Added upward-search helpers (
find_specify_root/Find-SpecifyRoot) to locate the nearest.specifydirectory. - Updated root resolution (
get_repo_root/Get-RepoRoot) to prefer.specify, then fall back to Git, then script location. - Updated Git-availability checks (
has_git/Test-HasGit) to be root-scoped instead of inheriting parent Git repos.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| scripts/powershell/common.ps1 | Adds .specify-first root discovery and updates Get-RepoRoot/Test-HasGit behavior. |
| scripts/powershell/create-new-feature.ps1 | Switches root detection + git detection to rely on shared common.ps1 functions. |
| scripts/bash/common.sh | Adds .specify-first root discovery and updates get_repo_root/has_git behavior. |
| scripts/bash/create-new-feature.sh | Switches root detection + git detection to rely on shared common.sh functions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Normalize paths in find_specify_root to prevent infinite loop with relative paths - Use -PathType Container in PowerShell to only match .specify directories - Improve has_git/Test-HasGit to check git command availability and validate work tree - Handle git worktrees/submodules where .git can be a file - Remove dead fallback code in create-new-feature scripts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Fixes edge case where project root is at filesystem root (common in containers). The loop now checks for .specify before checking the termination condition. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
scripts/powershell/common.ps1:111
Test-HasGitcorrectly scopes git detection to the spec-kit root, butGet-CurrentBranchstill runsgit rev-parse --abbrev-ref HEADfrom the current working directory. In a.specifyproject nested under a parent git repo, that git call can succeed (via the parent) even thoughTest-HasGitreturns false, which bypasses the non-git fallback (scanningspecs/) and yields the wrong branch name. Consider updatingGet-CurrentBranchto only call git whenTest-HasGitis true, and run it asgit -C $repoRoot ...usingGet-RepoRoot.
# Check if we have git available at the spec-kit root level
# Returns true only if git is installed and the repo root is inside a git work tree
# Handles both regular repos (.git directory) and worktrees/submodules (.git file)
function Test-HasGit {
$repoRoot = Get-RepoRoot
# First check if git command is available
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
return $false
}
# Check if .git exists (directory or file for worktrees/submodules)
if (-not (Test-Path (Join-Path $repoRoot ".git"))) {
return $false
}
# Verify it's actually a valid git work tree
try {
$null = git -C $repoRoot rev-parse --is-inside-work-tree 2>$null
return ($LASTEXITCODE -eq 0)
} catch {
return $false
}
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- get_current_branch now uses has_git check and runs git with -C to prevent using parent git repo branch names in .specify-only projects - Same fix applied to PowerShell Get-CurrentBranch - Removed unused find_repo_root() from create-new-feature.sh - Removed unused Find-RepositoryRoot from create-new-feature.ps1 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Prevents cd from interpreting directory names like -P or -L as options. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Avoids unnecessary work when git isn't installed since get_repo_root may internally call git rev-parse. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Use -LiteralPath in Find-SpecifyRoot to handle paths with wildcard characters ([, ], *, ?) - Check Get-Command git before calling Get-RepoRoot in Test-HasGit to avoid unnecessary work when git isn't installed Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
scripts/powershell/common.ps1:72
- In
Get-CurrentBranch,Test-Path $specsDircan interpret wildcard characters in$repoRoot/$specsDiras globs, which can break branch fallback logic for repos located in paths containing[,],*, or?(the same edge caseFind-SpecifyRootwas updated to handle). PreferTest-Path -LiteralPath $specsDir -PathType Container(and consider usingGet-ChildItem -LiteralPathtoo for consistency).
# For non-git repos, try to find the latest feature directory
$specsDir = Join-Path $repoRoot "specs"
if (Test-Path $specsDir) {
$latestFeature = ""
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Prevents Test-Path from treating wildcard characters in paths as globs. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
scripts/powershell/create-new-feature.ps1:126
Set-Location $repoRootuses the-Pathparameter positionally, which means PowerShell can interpret wildcard characters (e.g.[,],*,?) in$repoRoot. Since this PR explicitly adds-LiteralPathhandling for root detection, switching toSet-Location -LiteralPath $repoRoot(and similarly using-LiteralPathfor subsequent filesystem ops rooted at$repoRoot) would prevent misnavigation when the repo lives under a path containing wildcard characters.
Set-Location $repoRoot
$specsDir = Join-Path $repoRoot 'specs'
New-Item -ItemType Directory -Path $specsDir -Force | Out-Null
scripts/powershell/common.ps1:76
- In
Get-CurrentBranch,$specsDircan contain wildcard characters if the repo path contains[,],*, or?. UsingTest-Path $specsDirandGet-ChildItem -Path $specsDirwill treat those as globs and may fail to detect an existingspecsdirectory. Consider usingTest-Path -LiteralPath $specsDirandGet-ChildItem -LiteralPath $specsDirto be consistent with the new literal-path handling inFind-SpecifyRoot/Test-HasGit.
$specsDir = Join-Path $repoRoot "specs"
if (Test-Path $specsDir) {
$latestFeature = ""
$highest = 0
Get-ChildItem -Path $specsDir -Directory | ForEach-Object {
if ($_.Name -match '^(\d{3})-') {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
A small fix of annoying edge case. |
Summary
.specifydirectory as rootfind_specify_root()/Find-SpecifyRootfunctions to search upward for.specifydirectoryget_repo_root()/Get-RepoRootto check.specifyfirst, then fall back to githas_git()/Test-HasGitto verify git exists at the spec-kit root level (not parent)Problem
When spec-kit is initialized in a subdirectory that doesn't have its own
.git, but a parent directory does,git rev-parse --show-toplevelfinds the parent's git repo and uses that as root. This causes specs to be created in the wrong location.Example:
/work/has.git/work/my-project/has.specify(spec-kit initialized here)/work/specs/(wrong)/work/my-project/specs/(correct)Test plan
bash -n.specify, parent with.git→ returns child.specifyand.git→ returns project, has_git=true.git, no.specify→ falls back to git correctly.specify→ found correctly.specifyin parent, working in child → finds parent.specify.specifyas file (not directory) → correctly ignoredFixes #1932
🤖 Generated with Claude Code