Branch workspaces
On this page
- Overview
- How do I make a branch workspace?
- What address does it get?
- Which database does it use?
- What comes across from the site?
- Do Git hooks run?
- Can a coding assistant hand the work to Short Leash's model?
- How do I merge a branch workspace back?
- How do I remove a branch workspace?
- Can I do this from the terminal?
A branch workspace is a second checkout of a site's repository, on another branch, served as a site of its own. The site keeps running on its branch while you, or a coding agent, work on the other one. Short Leash makes it with git worktree, copies in the files Git ignores so it boots (.env, vendor, node_modules, a SQLite file), gives it its own address, and gives it a database that's shared with the site, a copy of the site's, or empty.
Branch workspaces are part of Short Leash Pro.
Branch workspaces are for sites Short Leash serves from their folder, in a Git repository. A site on a port Short Leash didn't start, a service's address, and another branch workspace can't have one.
How do I make a branch workspace?
Open the site and choose Branch workspaces in its pages, then New branch workspace. Name the branch and choose its database. The branch can be:
- a local branch the repository has,
- a remote branch Git already knows about, such as
origin/fix/login: Short Leash makes a local branch that tracks it. It never fetches to find one, so fetch first in a terminal if it's new, - a new branch, which starts from the site's current branch unless you choose another base.
A branch already checked out, in the site or in another worktree, can't have one. The steps show as they run: Check out, Copy files Git ignores with a file count, the database, and Set up with the new address. When they're done, it says "Branch workspace ready at fix-login-shop.test".
Stop ends it at any step, Git's checkout, the copy of the files Git ignores and a database copy included. Copying the files Git ignores stops by itself after an hour, and a database dump or restore after 6 hours, so a disk or server that stopped answering can't hold it; the message says how far it got and what wasn't copied. If a step fails or you stop it, Short Leash undoes what it made, in reverse: it drops a database it made, moves the new folder to the Trash (the Recycle Bin on Windows), and deletes a branch it made, as long as that branch has no commits of its own. The steps' log says what was undone.
What address does it get?
The branch's name, then the site's: a branch workspace for fix/login of shop answers at fix-login-shop.test, with HTTPS when the site has it, and its subdomains (api.fix-login-shop.test) work as any site's do. A long branch name is cut so the whole name fits an address, ending in a short code when there's room so two long branches never share one. A name another site, alias or Short Leash service already has is refused: fix/login and fix-login can't both have a branch workspace of shop, and adding the second says "fix/login's branch workspace already uses fix-login-shop.test. Pick another branch name." A branch or base whose name holds characters that hide text (bidi overrides, zero-width or tag characters) is refused too, since its name could read as another's.
Its folder sits beside the repository, in a hidden folder named after it: ~/Sites/.shop-workspaces/fix-login. Being hidden, it never turns into a parked site, and being outside the site's folder, your editor doesn't index it twice. A folder that's already there is refused, so Short Leash never takes over one it didn't make. For a site in a subfolder of its repository, the branch workspace serves the same subfolder.
Which database does it use?
Short Leash reads the site's database from its .env, the way leash db does: a database URL (DB_URL or DATABASE_URL) first, then DB_CONNECTION, DB_HOST, DB_PORT and DB_DATABASE, matched to one of Short Leash's services by its port.
| Shared | Copy | Empty | |
|---|---|---|---|
| MySQL | The site's database | A new database, fix_login_shop, with a copy of the site's (mysqldump --single-transaction) |
A new, empty fix_login_shop |
| MariaDB | The site's database | A new database with a copy (mariadb-dump --single-transaction) |
A new, empty database |
| PostgreSQL | The site's database | A new database with a copy (pg_dump and pg_restore) |
A new, empty database |
| SQLite | The site's file | A copy of the file, and its -wal, at the same place in the branch workspace |
An empty file at the same place |
A service that's off is turned on first. When the service already has a database with the new name, the branch workspace is refused rather than taking it over, since removing the branch workspace later would drop it.
Only Shared is offered, with the reason beside it, when Short Leash can't tell which database the site uses, when it's on another computer or a service Short Leash doesn't run, or when its URL holds a password. A SQLite file outside the site's folder can be shared or replaced by an empty one, not copied.
The site's .env is never rewritten. The branch workspace's own settings get DB_DATABASE (and the database URL's key, when the site's database came from one) pointing at its database, and every value in .env that names the site's address, such as APP_URL or SESSION_DOMAIN, pointing at its own. These reach its PHP, its server and processes, Short Leash's terminals, scratchpads and leash php. Values with a user name, a password or anything that looks like a secret are never carried over: a database URL with a user name and no password, such as the one leash db url prints (mysql://[email protected]:3306/shop), changes only in the branch workspace's own .env (below).
With Copy or Empty, Short Leash also writes the same database and address lines into the branch workspace's own .env, the copy it made from the site's, so a command you run outside Short Leash, such as a PHP from Homebrew or php artisan migrate:fresh in your editor's terminal, uses the branch workspace's database and not the site's. Only those lines change: every other line, comment and the order stay as they were, and a line the file didn't have, such as DB_DATABASE, is added at the end. The add's last step says Its .env names its own database and address. For a database URL with a user name in it, that .env is the only place its database changes, so when Short Leash can't rewrite it (see below), the add stops, undoes what it made, and says to choose Shared or take the user name out of the URL.
With Shared, both checkouts write to one database, so a migration in the branch workspace changes the site's schema too. Copy and Empty keep them apart. Copying a SQLite file while the site writes to it can catch it half-written; MySQL, MariaDB and PostgreSQL copies are consistent. While a MySQL, MariaDB or PostgreSQL copy runs, Backups shows it as Copying shop and starts no backup or restore of that service until it's done, and a copy asked for while one runs is refused.
What comes across from the site?
The files Git ignores. Everything git ls-files --others --ignored --exclude-standard lists in the site's repository is copied in, never over a file the branch already has. On macOS each copy is an APFS clone: near-instant, and it takes no extra space until a file changes. On Windows the files are copied one by one, so a large node_modules takes a while.
Some ignored files are left out, because they'd make the branch workspace use the site's database or address whatever its settings say, or because they belong to one running copy:
- Laravel's cached config, routes, events, packages and services in
bootstrap/cache, its framework cache, sessions and compiled views, andstorage/logs - Symfony's
var/cache - Vite's
public/hot,node_modules/.cache,node_modules/.viteand.next/cache .DS_Store, files ending in.log, and SQLite's-shmfiles
Links are copied as links. One that points into the site's folder by its full path, such as Laravel's public/storage, is pointed at the same place in the branch workspace instead. Where the branch itself has a link on the way to a copied file, that file is left out and the log says so, so nothing is ever written through it. On Windows, a link Windows won't let Short Leash make is left out and counted.
Short Leash's settings for the site, copied once when the branch workspace is made: its PHP version, env, processes and the rest, without its aliases. Later changes to the site don't carry over. The site's project files (leash.toml) come with the branch's checkout, and act only once its folder is trusted.
Processes, stopped. Every process comes across set to start When you start it, never With Short Leash or On first visit, so queue workers and dev servers don't run twice and visiting the branch workspace starts nothing. Start the ones you want on the branch workspace's Processes page. Processes from a trusted leash.toml wait too, through Short Leash restarting and the first visit, until you start them.
Do Git hooks run?
Only when the repository is trusted. Making a branch workspace runs Git's post-checkout and reference-transaction hooks in a trusted repository, and merging one back runs the merge's hooks (see below). In one that isn't, Short Leash makes it without them, and the log says hooks were skipped; a setup a hook does, such as installing dependencies, then has to be run by hand. A repository whose own Git settings run programs or change where Git connects isn't changed at all until it's trusted: Short Leash asks first, as Changes does, and Trust repository trusts it.
When the site's folder is trusted, the branch workspace's folders are trusted too, as long as the branch's project files run the same things. A branch whose leash.toml runs something else asks like any new folder. The grant covers the hooks and Git settings the site's did: when the branch brings a hook of its own or a Git setting that needs trust that the site didn't have, the branch workspace's Git actions ask first.
A branch workspace can't be renamed or unlinked on its own: remove it instead. A site that has branch workspaces can't be unlinked, nor its parked folder forgotten, until they're removed. Renaming the site keeps its branch workspaces with it, at their own addresses.
Can a coding assistant hand the work to Short Leash's model?
Yes. With delegate_to_local, a coding assistant connected through MCP gives Short Leash's local model a task and the files it may change in a branch workspace. Short Leash writes the model's files there, never in the site's own folder, and nothing is committed. The assistant then reviews the change on the branch workspace before merging it. While the model writes, the branch workspace can't be merged or removed.
How do I merge a branch workspace back?
Choose Merge into main (the branch workspace's base) on the site's Branch workspaces page. Short Leash merges the branch into its base in the site's own checkout, with a merge commit (git merge --no-ff --no-edit), so the branch's history stays as it was. When it's done, it says "Merged into main". You can also ask it to:
- Push
mainafter merging, with Changes' push: to the base's upstream only, never forced and never with tags. The base needs an upstream: when it has none, the merge doesn't start, and the message says to push it once from the site's Changes page, which offers to set one. Short Leash never asks for a password: when the remote needs you to sign in, it says to sign in once in a terminal, and the merge stays. - Remove the branch workspace after a clean merge, as Remove branch workspace does, with the same checks. When one of them refuses, the merge stays and the message says why. The branch stays either way.
The merge doesn't start, and nothing changes, when:
| What's wrong | What to do |
|---|---|
| The branch workspace has uncommitted changes | Commit or discard them on its Changes page, then merge again. |
| The site is on another branch than the base | Switch it back to the base, then merge again. |
| The site has uncommitted changes | Commit or discard them on its Changes page, then merge again. |
The site is in the middle of a merge, cherry-pick, revert, rebase or git am |
Finish or abort it there, then merge again. Short Leash never concludes or aborts it for you. |
| The branch has no commits the base doesn't have | There's nothing to merge. |
| The branch or the base isn't a branch in the repository any more | Nothing to merge: remove the branch workspace. |
| Git can't read the branch workspace's changes | Check its folder, then merge again. |
| The site is gone, or its folder is in another repository now | Merge in a terminal, or remove the branch workspace. |
When the branch and the base change the same lines, Short Leash stops the merge (git merge --abort), names the files that conflict, and leaves the base as it was. To merge it, run git merge main in a terminal in the branch workspace's folder, resolve the conflicts in your editor, commit them on its Changes page, then merge again. A hook that stops the merge leaves the base as it was too. As in a terminal, Git replaces a file the site ignores when the branch has a tracked file at that path, and aborting after a conflict takes that file away.
Stopping a merge stops it before it begins, or after it: before or during the push, or before the removal. A merge that began always finishes, or is stopped and leaves the base as it was. If Short Leash itself quits in the middle of a merge, the site's checkout can be left mid-merge: the next merge says so, and git merge --abort in a terminal puts it back.
Before pushing, Short Leash checks again that the site is still on the base and that its trust hasn't changed. If either changed after the merge, nothing is pushed, the merge stays, and the message says to push the base from the site's Changes page.
Hooks follow Changes' rules. In a trusted repository, the merge runs its pre-merge-commit, prepare-commit-msg, commit-msg, post-merge and reference-transaction hooks, and the push its pre-push. In one that isn't trusted but has hooks of its own, Short Leash asks first: Trust and run hooks trusts the repository and merges with them, Merge without hooks merges without any, or cancel. A repository whose own Git settings run programs or change where Git connects isn't merged until it's trusted: Trust repository trusts it. Only you can trust a repository, in the app or at a terminal; a coding agent can't.
How do I remove a branch workspace?
Choose Remove branch workspace on the site's Branch workspaces page. Before anything goes, Short Leash shows what will: the folder, the files in it with uncommitted changes and how many untracked files it has, and the database it will drop. When Git can't read its changes, because its repository is gone for example, it says so, and removing it needs you to confirm. When it's done, it says "Branch workspace removed".
What goes:
- What runs for it: its server, processes, terminals, scratchpads, calls and share are stopped.
- The folder moves to the Trash (the Recycle Bin on Windows), uncommitted changes and all, so it can be put back. Nothing is deleted: when the bin can't take the folder, the removal stops there with nothing removed. On Windows, a folder too big for the Recycle Bin makes Windows ask whether to delete it permanently; choose No to keep it, and the removal stops.
- A database Short Leash made for it, with Copy or Empty, is backed up first, filed under Before remove in Backups, then dropped. When the backup fails, the database stays and the removal stops. A SQLite file goes with the folder.
- Git's record of the worktree (
git worktree prune, without hooks), and the hidden workspaces folder once it holds no other branch workspace. - Its settings, tasks, review comments, pin and its folders' trust.
What stays:
- The branch, with its commits. Delete it yourself in a terminal if you don't need it.
- The site's database, when the branch workspace shares it. Short Leash only ever drops the database it made for the branch workspace, by its own name (
fix_login_shop), and never one another site's.envnames.
Short Leash checks first that the address is still served by this branch workspace, and that the folder is the one it made: inside the repository's workspaces folder, not a link, and the worktree Git made for this branch workspace, with no other site served from inside it. When the database is to be dropped, its service must be running and not backing up or restoring. If one of these checks fails, the removal doesn't start: nothing is stopped, moved or dropped, and the message says which check failed and what to do. The folder is checked again right before it moves, and the database right before it's dropped; if one fails then, the removal stops there.
If a step fails or you stop the removal, the steps already done stay done. Remove the branch workspace again to finish: it picks up where it stopped. A folder that's no longer there counts as moved.
A branch workspace whose site is gone, because its folder was deleted, keeps serving and can still be removed. When its repository is gone too, there's no worktree record left to tidy, and that step is skipped.
Can I do this from the terminal?
Yes, with leash workspace. Each command that makes a change follows its job, printing each step as it finishes, and Ctrl-C stops it between steps.
leash workspace add fix/login --database copy # in the site's folder, or with --site shop
leash workspace list
leash workspace merge fix-login-shop --push
leash workspace remove fix-login-shop
addtakes--basefor the branch a new branch starts from, and--database shared,copyorempty(Shared when left out). When the repository's Git settings need trust, it asks to trust the repository first.listshows each branch workspace's address, branch, and how many commits it has that its base doesn't.mergeandremovework on the branch workspace you're in when you don't name one. Both say what they'll do and ask first;--yesanswers for a script, and without a terminal to ask at, they refuse unless it's given.merge --removeremoves the branch workspace after merging, and the question lists what the removal takes.merge --hooks: in a repository that isn't trusted and has hooks,autoasks,trustshows what trusting the repository covers (its folder, hooks, processes, services and env, with the fingerprint) and asksTrust ~/Sites/shop and run its hooks? [y/N], andskipmerges without them. Trusting always needs you at a terminal: without one,autoandtrustrefuse and say to run the command in a terminal or trust the repository in Short Leash's dialog.- What leashd and the repository say, such as a branch name or Git's output, shows hidden characters (bidi overrides, control characters) as codes like
\u{202E}, so it can't reorder or drive the terminal.list --jsonwrites them as JSON escapes (\u202e), so the JSON reads back the same. removelists what goes before it asks. When Short Leash can't read the branch workspace's changes, it refuses unless you add--force, and the question then says why.
Unlinking a site that has branch workspaces names the command that removes them. Every option is in the CLI reference.