How Roo Code Handles Git Worktree Detection and Inclusion

Roo Code detects git worktrees by executing native git CLI commands and parsing porcelain output, while inclusion of untracked assets relies on a .worktreeinclude file that intersects with .gitignore patterns to selectively copy files using platform-native tools.

Roo Code’s worktree service provides platform-agnostic git worktree detection and inclusion capabilities without depending on VS Code APIs. Located in the RooCodeInc/Roo-Code repository, this pure Node.js implementation uses child_process to execute git commands directly, enabling reliable repository root discovery, worktree enumeration, and intelligent copying of untracked files into newly created worktrees.

Detecting Git Worktrees

The worktree detection layer identifies repository boundaries, current worktree context, and all registered worktrees through a series of standardized git commands.

Repository Root and Current Worktree Discovery

The service locates the repository root by executing git rev-parse --show-toplevel. In packages/core/src/worktree/worktree-service.ts, the WorktreeService.getGitRootPath method (lines 49‑55) runs this command and returns the absolute path or null if the directory is not within a git repository.

Similarly, WorktreeService.getCurrentWorktreePath (lines 61‑67) uses the identical command to determine the active worktree path. To identify the current branch, the service runs git rev-parse --abbrev-ref HEAD (lines 73‑80), normalizing a detached HEAD state to null for consistent handling.

Enumerating All Worktrees with Porcelain Parsing

To list all worktrees, Roo Code invokes git worktree list --porcelain (lines 86‑94). This machine-readable output is processed by parseWorktreeOutput (lines 48‑95), which extracts each worktree’s path, branch, commitHash, and status flags (bare, detached, locked). The parser identifies the current worktree by comparing normalized paths against the active directory.

Path Normalization for Cross-Platform Reliability

Before comparison, all paths undergo normalization via normalizePath (lines 100‑110). This utility removes trailing slashes and resolves relative segments (. and ..), ensuring reliable path matching across Windows and POSIX systems regardless of how the git CLI returns directory separators.

Example: Listing Worktrees and Identifying the Current Context

import { worktreeService } from "packages/core/src/worktree/worktree-service";

async function showWorktrees(cwd: string) {
  const worktrees = await worktreeService.listWorktrees(cwd);
  const current = await worktreeService.getCurrentWorktreePath(cwd);

  console.log("Repository root:", await worktreeService.getGitRootPath(cwd));
  console.log("Current worktree:", current);
  console.log("Detected worktrees:");
  worktrees.forEach(wt => {
    const marker = wt.isCurrent ? "← current" : "";
    console.log(`- ${wt.path} (branch: ${wt.branch || "detached"}) ${marker}`);
  });
}

// Call from any directory inside the repo
showWorktrees(process.cwd());

Including Untracked Files with .worktreeinclude

When creating new worktrees, developers often need to bring over untracked assets like compiled binaries or cached dependencies. Roo Code implements this through the WorktreeIncludeService, which respects both .worktreeinclude and .gitignore to prevent copying tracked files.

Detecting the Include File Presence

The service checks for inclusion directives at two levels. The hasWorktreeInclude function (lines 43‑49) tests the filesystem for a .worktreeinclude file in the source directory. For branches that have not been checked out, branchHasWorktreeInclude (lines 57‑66) uses git cat-file -e <branch>:.worktreeinclude to verify existence without switching branches.

The Intersection Logic: Filtering by Both Rules

Copying operates on an intersection principle: a file must be listed in both .worktreeinclude and .gitignore to qualify for copying. This ensures only intentionally ignored untracked assets are migrated, never repository source files.

The copyWorktreeIncludeFiles function (lines 17‑30) orchestrates the process:

  1. Validation – Aborts if either .worktreeinclude or .gitignore is missing.
  2. Pattern Parsing – parseIgnoreFile splits each file into arrays of patterns, stripping comments and blank lines.
  3. Matcher Construction – Uses the ignore npm package to create matchers for both files.
  4. Intersection Detection – findMatchingItems (lines 91‑118) iterates over the source directory’s top-level entries, skips the .git folder, and retains only paths that both matchers ignore.
  5. Copy Execution – For each match, the service uses fs.copyFile for files or copyDirectoryWithProgress (lines 97‑123) for directories.

Platform-Specific Copy Implementation

copyDirectoryWithProgress implements platform-optimized transfer mechanics. On Windows, it executes robocopy; on POSIX systems, it uses cp -r. The function polls the target directory size to provide real-time progress callbacks reporting cumulative bytes copied and the current item name.

Example: Creating a Worktree with Untracked Assets

import {
  worktreeService,
  worktreeIncludeService,
} from "packages/core/src/worktree";

async function createWorktreeWithInclude(
  cwd: string,
  path: string,
  baseBranch = "main"
) {
  // 1. Detect current repository state
  const repoRoot = await worktreeService.getGitRootPath(cwd);
  if (!repoRoot) throw new Error("Not a git repository");

  // 2. Create the worktree (new branch `featureX`)
  const result = await worktreeService.createWorktree(repoRoot, {
    path,
    branch: "featureX",
    baseBranch,
    createNewBranch: true,
  });
  if (!result.success) throw new Error(result.message);

  // 3. Copy untracked files if .worktreeinclude exists on the base branch
  const hasInclude = await worktreeIncludeService.branchHasWorktreeInclude(
    repoRoot,
    baseBranch
  );
  if (hasInclude) {
    await worktreeIncludeService.copyWorktreeIncludeFiles(
      repoRoot,
      path,
      ({ bytesCopied, itemName }) => {
        console.log(`Copying ${itemName} … (${bytesCopied} bytes)`);
      }
    );
  }

  console.log("Worktree ready at", path);
}

// Execute from within the repository
createWorktreeWithInclude(process.cwd(), "./temp/featureX");

Key Source Files

File Responsibility Location
worktree-service.ts Repository root detection, worktree enumeration, branch resolution, and worktree creation/deletion packages/core/src/worktree/worktree-service.ts
worktree-include.ts .worktreeinclude detection, existence checking on branches, and untracked file copying with progress packages/core/src/worktree/worktree-include.ts
worktree.ts TypeScript type definitions for Worktree, BranchInfo, and related interfaces packages/types/src/worktree.ts
Test suites Platform verification of detection and inclusion logic packages/core/src/worktree/__tests__/worktree-service.spec.ts and worktree-include.spec.ts

Summary

Roo Code’s approach to git worktree detection and inclusion follows a strict two-layer architecture:

  • Detection Layer – Executes git rev-parse and git worktree list --porcelain to identify repository roots, current worktrees, and all registered worktrees, with rigorous path normalization for cross-platform compatibility.
  • Inclusion Layer – Validates .worktreeinclude against .gitignore to copy only files present in both lists, using native platform copy commands (robocopy or cp) with progress reporting.

This Node.js implementation remains independent of editor-specific APIs, enabling robust worktree management in any environment where Git is available.

Frequently Asked Questions

How does Roo Code detect the current git worktree?

Roo Code runs git rev-parse --show-toplevel via WorktreeService.getCurrentWorktreePath in worktree-service.ts (lines 61‑67) to determine the active worktree path. It compares this result against the list generated by git worktree list --porcelain to mark the matching entry as current using normalized path comparison.

What is the purpose of the .worktreeinclude file in Roo Code?

The .worktreeinclude file directs Roo Code to copy specific untracked files—such as build artifacts or dependencies—into newly created worktrees. Unlike .gitignore which excludes files from version control, .worktreeinclude acts as a positive filter for the copying process, working in conjunction with .gitignore to identify which untracked assets should migrate.

How does Roo Code determine which untracked files to copy to a new worktree?

Roo Code applies intersection logic: a file must be ignored by both .worktreeinclude and .gitignore to qualify. The findMatchingItems function in worktree-include.ts (lines 91‑118) creates ignore matchers from both files, then selects only directory entries that pass both filters, ensuring no tracked source files are ever copied.

Why does Roo Code use platform-specific commands like robocopy instead of Node.js fs.copyFile for directories?

While fs.copyFile handles individual files, copyDirectoryWithProgress in worktree-include.ts (lines 97‑123) uses robocopy on Windows and cp -r on POSIX systems to leverage native optimized copy mechanisms. These tools provide better performance for large directory trees and enable size-polling for accurate progress reporting during the copy operation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →