Skip to content

feat: add e worktree for additional gclient working directories#836

Open
MarshallOfSound wants to merge 1 commit intomainfrom
sattard/e-worktree
Open

feat: add e worktree for additional gclient working directories#836
MarshallOfSound wants to merge 1 commit intomainfrom
sattard/e-worktree

Conversation

@MarshallOfSound
Copy link
Copy Markdown
Member

Stacked on #834.

Summary

  • e worktree add <name> <dir> wraps gclient-new-workdir.py to create a second working tree that shares git objects with an existing checkout, writes a matching build config (cloned from --source, default current), and runs e sync so it's immediately buildable. --no-sync / -o <out> / -f available.
  • e worktree clean <name> --yes removes the worktree directory and its build config. Guarded: refuses unless .gclient and src/.git/objects are symlinks (so a primary checkout can never be deleted), refuses if the config is currently active, and requires --yes.
  • Hard-bails on Windows since gclient-new-workdir.py requires symlinks.

Pairs well with per-shell active configs:

export EVM_CURRENT_FILE="$(mktemp --tmpdir evm-current.XXXXXXXX.txt)"

Test plan

  • yarn build clean
  • yarn lint:ts clean
  • yarn test (63 passing)
  • e worktree add testing2 ~/src/electron2 --no-sync creates an 18G workdir with symlinked .git/{objects,refs,config}, writes evm.testing2.json, switches to it
  • e sync in the new workdir fetches gn/clang/node and e build --gen-only produces build.ninja
  • e worktree clean testing --yes refuses (primary checkout)
  • e worktree clean <active> --yes refuses (config in use)
  • e worktree clean testing2 --yes removes dir + config

@MarshallOfSound MarshallOfSound requested review from a team and ckerr as code owners April 6, 2026 05:41
@MarshallOfSound MarshallOfSound force-pushed the sattard/e-worktree branch 2 times, most recently from e59a2b8 to 4a55cd5 Compare April 6, 2026 09:26
Base automatically changed from sam/ts-migration to main April 6, 2026 21:16
Comment on lines +160 to +164
evmConfig.remove(name);
console.log(`Removed config ${color.config(name)}`);

console.log(`Deleting ${color.path(root)}...`);
deleteDir(root);
Copy link
Copy Markdown
Member

@codebytere codebytere Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If deleteDir fails we've already nuked the config, so now you've got an orphaned worktree directory with no config pointing at it. Can we flip the order here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, flipped — deleteDir runs first now and I hoisted the active-config check so we don't nuke the dir then fatal on that either.

Comment on lines +27 to +36
function isDerivedWorktree(root: string): boolean {
const gclient = path.join(root, '.gclient');
const gitObjects = path.join(root, 'src', '.git', 'objects');
return (
fs.existsSync(gclient) &&
fs.lstatSync(gclient).isSymbolicLink() &&
fs.existsSync(gitObjects) &&
fs.lstatSync(gitObjects).isSymbolicLink()
);
}
Copy link
Copy Markdown
Member

@codebytere codebytere Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If somebody runs git gc in the worktree, the .git/objects symlink can get replaced with a real directory, so this check would fail and they'd be unable to clean up via e worktree clean. Maybe add a --force escape hatch on clean?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think git gc actually replaces the symlink (it writes through it into pack/), but the wider point is fair. Rather than a --force that could take out a real checkout I've relaxed the check to just .gclient being a symlink — that's the one thing gclient-new-workdir.py always leaves and git never touches.

console.log(
`Running ${color.cmd('e sync')} to fetch toolchains and apply patches in the new worktree...`,
);
const e = path.resolve(__dirname, 'e');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import.meta.dirname?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, done — rebased on #838 and swapped this + the import extensions over.

Adds `e worktree add <name> <dir>` and `e worktree clean <name>`.

`add` wraps depot_tools' gclient-new-workdir.py to create a second
working tree that shares git object stores with an existing checkout,
clones the source build config with the new root/buildtools path, and
runs `e sync` so the worktree is immediately buildable.

`clean` removes the worktree directory and its config. It refuses to
touch a directory unless `.gclient` and `src/.git/objects` are
symlinks, so it can never delete a primary checkout.

Not supported on Windows (gclient-new-workdir.py requires symlinks).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants