Advanced
Stash, worktrees, hooks, reset vs restore, and disaster recovery via reflog.
Advanced Git
These tools solve problems that the core workflow doesn't cover: juggling multiple tasks, splitting a repository, enforcing policy at commit time, and recovering from catastrophic mistakes.
Analogy
Advanced git is the toolkit a contractor keeps on the truck for bad days. stash is the tarp you throw over a half-built wall so you can drive off to a different job without losing the loose bricks. Worktrees are parking a second trailer at a different address so you can frame one house and re-shingle another without packing up between trips. Submodules are embedding a pre-fabricated shed on the property with a plaque noting exactly which factory batch it came from. Hooks are the inspector who stands at the gate and refuses to let sloppy lumber leave the yard. And the reflog is the site's private CCTV that keeps rolling even when you demolish a wall — if you swing the sledgehammer at the wrong one, you can replay the tape and rebuild.
git stash
Stash saves your uncommitted changes to a temporary stack and restores a clean working tree. Use it when you need to switch context without committing half-done work.
git stash # save everything (tracked + staged)
git stash -u # include untracked files
git stash push -m "wip: auth refactor" # name the stash
git stash list # view all stashes
git stash pop # apply and remove the most recent stash
git stash apply stash@{2} # apply a specific stash, keep it in the list
git stash drop stash@{0} # delete a stash
git stash clear # delete all stashes
# Create a branch from a stash (useful for long-lived stashes)
git stash branch feat/auth-refactor stash@{0}
Stashes are local. They do not push to the remote. For anything you might need more than a day, push a WIP branch instead.
git worktrees
A worktree lets you check out two branches simultaneously in different directories, from the same repository clone. You do not need two separate clones.
git worktree add ../project-hotfix fix/payment-crash
# ../project-hotfix is now a directory with that branch checked out
git worktree list # list all worktrees
git worktree remove ../project-hotfix # remove when done
Common use: you're mid-refactor on a feature branch and a hotfix arrives. Open a new worktree for the hotfix, fix it, merge, and remove the worktree — without touching your feature branch at all.
Submodules
A submodule embeds another repository inside yours at a specific commit. The parent repo tracks only the submodule's commit SHA, not its full content.
git submodule add https://github.com/org/lib.git vendor/lib
git submodule update --init --recursive # clone all submodules after fresh clone
git submodule update --remote # update to the latest commit on the submodule's remote
Submodules are powerful but operationally heavy. Alternatives: package managers, git subtree, monorepo tools. Use submodules only when you need to pin to a specific commit of an external repo that you don't own.
Hooks
Hooks are scripts that git runs at specific points in the workflow. They live in .git/hooks/ and are not committed by default.
Common hooks:
| Hook | When it runs | Common use |
|---|---|---|
pre-commit |
Before git commit executes |
Run linter, type-checker, tests |
commit-msg |
After you write the commit message | Enforce conventional commit format |
pre-push |
Before git push sends to remote |
Run full test suite |
post-merge |
After a merge completes | Run pnpm install if lockfile changed |
A pre-commit hook:
#!/bin/sh
# .git/hooks/pre-commit
pnpm lint-staged
To share hooks with the team, store them in a directory (e.g., .hooks/) and configure git to use it:
git config core.hooksPath .hooks
Or use a tool like Husky which wires this up automatically.
reset vs restore vs checkout
These three commands overlap historically. Git has been tightening the semantics:
| Command | Affects | Use for |
|---|---|---|
git restore <file> |
Working tree | Discard unstaged changes to a file |
git restore --staged <file> |
Index (staging area) | Unstage a file |
git reset HEAD~1 |
HEAD + index | Undo last commit, keep changes staged |
git reset --hard HEAD~1 |
HEAD + index + working tree | Undo last commit and discard all changes |
git checkout <sha> -- <file> |
Working tree | Restore a file from a specific commit |
The old way was to use git checkout for all three. The new restore command is more explicit and harder to misuse. Prefer it.
git reset --hard is the most dangerous command in everyday git use. It discards working-tree changes permanently — they do not go to the trash. Before running it, verify you're resetting to the right commit.
Recovering from disaster via reflog
The reflog is your safety net. It records every position HEAD has occupied, including commits that were "lost" by reset, rebase, or branch deletion.
git reflog # find the SHA you want to recover
git checkout -b recovery/lost-work <sha> # branch from it
# or:
git cherry-pick <sha> # apply just that commit to current branch
Scenarios and recoveries:
| Disaster | Recovery |
|---|---|
git reset --hard to wrong commit |
git reflog → find old HEAD → git reset --hard <sha> |
| Deleted a branch | git reflog → find last commit on it → git checkout -b <name> <sha> |
| Rebased and lost commits | git reflog → find pre-rebase SHA → git reset --hard <sha> |
| Dropped a stash | git stash list (if still there), or git fsck --lost-found |
The reflog has a default expiry of 90 days. After that, unreachable commits become candidates for garbage collection.
The recovery playground
The playground below presents three disaster states — detached HEAD, dropped stash, force-pushed branch — and asks you to pick the right recovery command for each.