Skip to content

Commit 8d3e48b

Browse files
committed
Fix git ignore for building file tree
1 parent ddb9e4e commit 8d3e48b

File tree

1 file changed

+56
-39
lines changed

1 file changed

+56
-39
lines changed

common/src/project-file-tree.ts

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,54 @@ export function getProjectFileTree(
9898
return root.children
9999
}
100100

101+
function rebaseGitignorePattern(
102+
rawPattern: string,
103+
relativeDirPath: string,
104+
): string {
105+
// Preserve negation and directory-only flags
106+
const isNegated = rawPattern.startsWith('!')
107+
let pattern = isNegated ? rawPattern.slice(1) : rawPattern
108+
109+
const dirOnly = pattern.endsWith('/')
110+
// Strip the trailing slash for slash-detection only
111+
const core = dirOnly ? pattern.slice(0, -1) : pattern
112+
113+
const anchored = core.startsWith('/') // anchored to .gitignore dir
114+
// Detect if the "meaningful" part (minus optional leading '/' and trailing '/')
115+
// contains a slash. If not, git treats it as recursive.
116+
const coreNoLead = anchored ? core.slice(1) : core
117+
const hasSlash = coreNoLead.includes('/')
118+
119+
// Build the base (where this .gitignore lives relative to projectRoot)
120+
const base = relativeDirPath.replace(/\\/g, '/') // normalize
121+
122+
let rebased: string
123+
if (anchored) {
124+
// "/foo" from evals/.gitignore -> "evals/foo"
125+
rebased = base ? `${base}/${coreNoLead}` : coreNoLead
126+
} else if (!hasSlash) {
127+
// "logs" or "logs/" should recurse from evals/: "evals/**/logs[/]"
128+
if (base) {
129+
rebased = `${base}/**/${coreNoLead}`
130+
} else {
131+
// At project root already; "logs" stays "logs" to keep recursive semantics
132+
rebased = coreNoLead
133+
}
134+
} else {
135+
// "foo/bar" relative to evals/: "evals/foo/bar"
136+
rebased = base ? `${base}/${coreNoLead}` : coreNoLead
137+
}
138+
139+
if (dirOnly && !rebased.endsWith('/')) {
140+
rebased += '/'
141+
}
142+
143+
// Normalize to forward slashes
144+
rebased = rebased.replace(/\\/g, '/')
145+
146+
return isNegated ? `!${rebased}` : rebased
147+
}
148+
101149
export function parseGitignore(
102150
fullDirPath: string,
103151
projectRoot: string,
@@ -111,48 +159,17 @@ export function parseGitignore(
111159
]
112160

113161
for (const ignoreFilePath of ignoreFiles) {
114-
if (fs.existsSync(ignoreFilePath)) {
115-
const ignoreContent = fs.readFileSync(ignoreFilePath, 'utf8')
116-
const lines = ignoreContent.split('\n')
117-
for (let line of lines) {
118-
line = line.trim()
119-
if (line === '' || line.startsWith('#')) {
120-
continue
121-
}
162+
if (!fs.existsSync(ignoreFilePath)) continue
122163

123-
let isNegated = false
124-
let pattern = line
125-
if (pattern.startsWith('!')) {
126-
isNegated = true
127-
pattern = pattern.slice(1)
128-
}
164+
const ignoreContent = fs.readFileSync(ignoreFilePath, 'utf8')
165+
const lines = ignoreContent.split('\n')
166+
for (let line of lines) {
167+
line = line.trim()
168+
if (line === '' || line.startsWith('#')) continue
129169

130-
let finalPattern = pattern
131-
// All patterns added to the ignore instance should be relative to the projectRoot.
132-
if (pattern.startsWith('/')) {
133-
// A pattern starting with '/' is relative to the .gitignore file's directory.
134-
// Remove the leading '/' and prepend the relativeDirPath to make it relative to projectRoot.
135-
finalPattern = pattern.slice(1)
136-
if (relativeDirPath !== '') {
137-
finalPattern = path.join(relativeDirPath, finalPattern)
138-
}
139-
} else {
140-
// A pattern not starting with '/' is also relative to the .gitignore file's directory.
141-
// Prepend relativeDirPath (if it exists) to make it relative to projectRoot.
142-
// If relativeDirPath is empty, the pattern is already relative to projectRoot.
143-
if (relativeDirPath !== '') {
144-
finalPattern = path.join(relativeDirPath, pattern)
145-
}
146-
// else: pattern is already relative to projectRoot, so finalPattern remains pattern
147-
}
148-
finalPattern = finalPattern.replace(/\\/g, '/')
170+
const finalPattern = rebaseGitignorePattern(line, relativeDirPath)
149171

150-
if (isNegated) {
151-
ig.add(`!${finalPattern}`)
152-
} else {
153-
ig.add(finalPattern)
154-
}
155-
}
172+
ig.add(finalPattern)
156173
}
157174
}
158175

0 commit comments

Comments
 (0)