MCPcopy Index your code
hub / github.com/codeaashu/claude-code / copyWorktreeIncludeFiles

Function copyWorktreeIncludeFiles

src/utils/worktree.ts:391–504  ·  view source on GitHub ↗
(
  repoRoot: string,
  worktreePath: string,
)

Source from the content-addressed store, hash-verified

389 * that directory is expanded with a second scoped `ls-files` call.
390 */
391export async function copyWorktreeIncludeFiles(
392 repoRoot: string,
393 worktreePath: string,
394): Promise<string[]> {
395 let includeContent: string
396 try {
397 includeContent = await readFile(join(repoRoot, '.worktreeinclude'), 'utf-8')
398 } catch {
399 return []
400 }
401
402 const patterns = includeContent
403 .split(/\r?\n/)
404 .map(line => line.trim())
405 .filter(line => line.length > 0 && !line.startsWith('#'))
406 if (patterns.length === 0) {
407 return []
408 }
409
410 // Single pass with --directory: collapses fully-gitignored dirs (node_modules/,
411 // .turbo/, etc.) into single entries instead of listing every file inside.
412 // In a large repo this cuts ~500k entries/~7s down to ~hundreds of entries/~100ms.
413 const gitignored = await execFileNoThrowWithCwd(
414 gitExe(),
415 ['ls-files', '--others', '--ignored', '--exclude-standard', '--directory'],
416 { cwd: repoRoot },
417 )
418 if (gitignored.code !== 0 || !gitignored.stdout.trim()) {
419 return []
420 }
421
422 const entries = gitignored.stdout.trim().split('\n').filter(Boolean)
423 const matcher = ignore().add(includeContent)
424
425 // --directory emits collapsed dirs with a trailing slash; everything else is
426 // an individual file.
427 const collapsedDirs = entries.filter(e => e.endsWith('/'))
428 const files = entries.filter(e => !e.endsWith('/') && matcher.ignores(e))
429
430 // Edge case: a .worktreeinclude pattern targets a path inside a collapsed dir
431 // (e.g. pattern `config/secrets/api.key` when all of `config/secrets/` is
432 // gitignored with no tracked siblings). Expand only dirs where a pattern has
433 // that dir as its explicit path prefix (stripping redundant leading `/`), the
434 // dir falls under an anchored glob's literal prefix (e.g. `config/**/*.key`
435 // expands `config/secrets/`), or the dir itself matches a pattern. We don't
436 // expand for `**/` or anchorless patterns -- those match files in tracked dirs
437 // (already listed individually) and expanding every collapsed dir for them
438 // would defeat the perf win.
439 const dirsToExpand = collapsedDirs.filter(dir => {
440 if (
441 patterns.some(p => {
442 const normalized = p.startsWith('/') ? p.slice(1) : p
443 // Literal prefix match: pattern starts with the collapsed dir path
444 if (normalized.startsWith(dir)) return true
445 // Anchored glob: dir falls under the pattern's literal (non-glob) prefix
446 // e.g. `config/**/*.key` has literal prefix `config/` → expand `config/secrets/`
447 const globIdx = normalized.search(/[*?[]/)
448 if (globIdx > 0) {

Callers 1

performPostCreationSetupFunction · 0.85

Calls 7

readFileFunction · 0.85
execFileNoThrowWithCwdFunction · 0.85
mkdirFunction · 0.85
logForDebuggingFunction · 0.85
searchMethod · 0.65
addMethod · 0.45
pushMethod · 0.45

Tested by

no test coverage detected