How to back up a git worktree before you delete it
git worktree remove --force destroys uncommitted and untracked files. Snapshot them into a ref with a temporary index first, then restore them later.
Short answer: before git worktree remove --force, save the worktree’s uncommitted and untracked files as a commit under a ref of your own, using a temporary index so nothing in the worktree changes. To restore the files, create a new worktree at the snapshot’s parent and run git read-tree -u --reset <ref>. Ignored files such as .env are not captured, so copy those separately.
Why this isn’t just git stash
Removing a worktree leaves its branch and commits in the repository. It deletes the files in that checkout, including uncommitted edits and untracked files. Save them before you force-remove the worktree.
git stash looks like the obvious fix, with two catches:
- Plain
git stashskips untracked files. You needgit stash push -u. - All worktrees of a repository share one stash list. Entries are named after the branch they came from, but they pile up next to every other worktree’s, and
git stash dropor a latergit stash clearon any worktree can remove them.
The method below stores the snapshot as a commit under its own ref instead, so it never touches the stash list, the branch, or the files in the worktree.
Snapshot with a temporary index
Run this inside the worktree you are about to delete. Replace backup/feat-search with a name you will recognise later.
(
set -e
index_dir=$(mktemp -d)
trap 'rm -r -- "$index_dir"' EXIT
export GIT_INDEX_FILE="$index_dir/index"
git read-tree HEAD
git add -A
tree=$(git write-tree)
commit=$(git commit-tree "$tree" -p HEAD -m "backup: feat/search")
git update-ref refs/backup/feat-search "$commit" ""
)
Because GIT_INDEX_FILE points somewhere else, git add -A never touches the worktree’s real index. Run git status --short afterwards and you will see exactly what you saw before. Before removing the worktree, check that the snapshot succeeded and contains the files you need. The last command fails if the backup ref already exists, so use a new name for each backup.
git show --stat refs/backup/feat-search
Also write down which commit the branch was on (git rev-parse HEAD), or keep the branch. The snapshot commit’s parent is that commit, so git log refs/backup/feat-search shows it too.
Ignored files are not in the snapshot
git add -A respects .gitignore, so .env, keys and local config stay out. That is usually what you want in a commit, and exactly what you don’t want when deleting a directory. List them and copy what matters:
git ls-files --others --ignored --exclude-standard
Copy the files you need to a private directory outside the worktree, keeping their relative paths. Verify those copies before deleting the worktree.
Build output like node_modules will show up in that list too. Skip it; you can regenerate it.
Then remove the worktree
git -C /path/to/app worktree remove --force ../app-search
Run this from the main checkout (here /path/to/app) after verifying both the snapshot and any separate file copies. For the full routine, including how to confirm the branch has landed in main, see how to clean up git worktrees without losing work. Errors from remove are covered in how to remove a git worktree.
Restore it
From the main checkout, create a recovery branch at the snapshot’s parent. This keeps the original branch intact even if it has moved since the backup.
(
set -e
git worktree add -b recover/feat-search ../app-search 'refs/backup/feat-search^'
cd ../app-search
git read-tree -u --reset refs/backup/feat-search
git reset -q
)
Run git status --short. The snapshot restores file contents. Changes to tracked files are unstaged, and files absent from the parent commit are untracked. In a test round trip with a modified file, an untracked file and an ignored .env, the first two came back and the .env did not, which is why it needs its own copy. Copy your backed-up files back in last.
Delete the snapshot when you are sure
git update-ref -d refs/backup/feat-search
Git’s garbage collection removes unreachable objects later. Until then the commit stays recoverable, but don’t rely on that. Keep the ref for as long as you might want the changes.
Doing it for every cleanup
Typing this for one worktree is fine. Remembering to do it before each of twenty cleanups is where work gets lost. Orchard takes the same kind of snapshot automatically before it removes a worktree: uncommitted changes, untracked files and the branch tip. It also backs up ignored files with names like .env, *.pem, and *.key separately. Snapshots are kept for 7 days and can be restored from the app. It refuses to clean up a worktree that has a running agent or an interrupted rebase.
FAQ
Does git stash include untracked files?
Not by default. git stash only saves changes to tracked files. Use git stash push -u to include untracked files, and --all to include ignored files too. Stashes are shared by all worktrees of a repository.
Can I recover files after git worktree remove --force?
A copy or snapshot is the reliable way to recover those files. The reflog records where refs pointed, so it can help you find old commits. It does not record unstaged edits or untracked files.
Why use a temporary index instead of git add -A and git commit?
A normal commit would change the branch and the worktree’s index. With GIT_INDEX_FILE pointing at a temporary file, the snapshot is built on the side, the branch does not move, and the worktree looks the same afterwards.