Git worktrees setup

How to run your project in parallel

Recap: why worktrees?

How else can we run things in parallel?

idea 1: clone the repo multiple times

Advantages:

  • Easy, just clone it as many times as needed

 

Disadvantages:

  • Doesn't guarantee isolated environments (same DB, same rails/vite servers, etc.)
  • Need to setup env vars and other required-but-gitignored configurations manually
  • Duplicates entire project, including .git (bye bye storage)

idea 2: Using docker containers

Advantages:

  • Guaranteed isolated environments
  • Easy to setup: one Dockerfile/image, multiple containers

 

Disadvantages:

  • Doesn't isolate codebase, only environment
  • Image is big, consumes lots of memory
  • Need to rebuild image every time a change is made. Slow process

idea 3: Using worktrees

Advantages:

  • Guaranteed codebase environment, can work in multiple branches simultaneously
  • Easy to setup (one command)
  • Consumes less memory than the other options

 

Disadvantages:

  • Need to setup env vars and other required-but-gitignored configurations manually
  • Doesn't isolate environment

Solving worktree problems

Because that's what AIs are for, amiright?

Problem 1: gitignored config

The problem

.gitignored files are not moved over to the worktree once created. That includes some important ones:

  • .env files
  • master.key
  • node_modules

The solution: Symlinks

Symlink them all during setup, edit them (if needed) in the main session

Problem 2: envrionment isolation

The problem

Changes made in one worktree will affect the environment shared by all. Rails, Vite, AnyCable... that can cause conflicting behaviors and/or raise errors

The solution: ports

For each worktree branch, define a set of ports to be used so servers are isolated. Ports are defined based on JIRA story numbers, and conflicts are resolved automatically by checking if port is already in use by other worktrees.

Problem 2: envrionment isolation (part 2 😱)

The problem

Some conflicts are a bit trickier to solve than simply assigning ports:

  • Shared DB affected any time a new migration is added (good luck resolving that)
  • Shared node_modules affected when a new package is installed
  • Shared bundler vendor affected when a new gem is added

The solution: optional isolation

Those are only issues when specific updates to the codebase are needed. We can create new DBs and new node_modules/vendors instead of symlinking/sharing them

Problem 3: actually setting it up

The problem

We need to:

  • Create the worktree
  • Set ports and configure Procfile to use those instead of defaults
  • Symlink files
  • (optionally) isolate DB
  • Test the environment to ensure things are up, running and not conflicting
  • not go crazy and fix it if something goes wrong

All that before starting the actual work

The solution...

bin/setup-worktree <branch-name>

[--isolate-db] [--isolate-gems] [--isolate-npm] [--isolate-all]

bin/setup-worktree 

What it does

  • Evaluates current scenario (current branch, unstaged changes, etc.)
  • Create worktree based off the current branch where the script was called
  • Define ports based on JIRA number set in branch name
    • Saves manifest with ports so other worktrees can refer to it when needed
    • Resolves port conflicts, so you can set the same JIRA number for multiple ones
  • Symlink what needs to be symlinked
  • Isolates what needs to be isolated (new DB, separate node modules, etc.)
  • Install dependencies (bundle install / npm install)
  • Spins up dev server to ensure it's running
  • Report back to user

Bonus: /worktree-setup skill

What it does

  • Guides user into correct usage of bin/setup-worktree
  • Capture exit code and act accordingly, trying to auto-fix known potential issues:
    • Pending DB migrations
    • DB migration creation failure (in case of isolation)
    • Stale vite/packs cache
  • Reports back to user both unhandled failures and/or successfully created worktrees

How to clean things up afterwards?

🤔

PR #999

Made with Slides.com