Local working changes are edits in your working tree that are not in a commit. They live in one of three places — modified but unstaged, staged in the index, or stashed — and every error message about them makes sense once you know which one git is talking about.
The phrase turns up in error messages far more often than in documentation, usually when git refuses to switch branches or pull. It is worth understanding structurally rather than memorising the commands, because the same three-way distinction explains a dozen different messages.
Table of contents
- Three places, one command to see them all
- Why git refuses to switch branches
- The stash, and how it loses work
- Discarding changes, and what is actually recoverable
- How this fits the rest of the stack
- FAQ
Three places, one command to see them all
Git has three areas your changes can occupy before they become history.
- The working tree. The files on disk as you edited them. Changes here are modified but not staged.
- The index, or staging area. A snapshot you have marked for the next commit. Changes here are staged.
- The stash. A stack of set-aside changes, off to one side, belonging to no branch.
git status reports the first two, and the short form is faster to read once you know it:
git status --short
# M src/app.js staged modification
# M src/util.js modified, not staged
# MM src/api.js staged changes AND further unstaged edits
# ?? notes.txt untracked -- git has never seen this file
The two columns are the point. The left is the index, the right is the working tree. MM means you staged a version and then kept editing, so a commit right now captures the earlier state and not what is on disk. That is the single most common cause of I committed but my change is not there.
See the actual content of each:
git diff # working tree vs index -- unstaged changes
git diff --staged # index vs last commit -- what would be committed
git diff HEAD # working tree vs last commit -- everything uncommitted
Untracked files are a separate category. Git will not lose them because it is not tracking them, but it also will not stash or stow them by default — which is why they survive operations that move everything else.
Why git refuses to switch branches
The message says your local changes would be overwritten by checkout. It is not being cautious for the sake of it: the file you edited differs between the two branches, so switching would have to overwrite your work, and git will not silently destroy something that exists nowhere else.
Note the asymmetry, because it explains the confusion. If the file is identical in both branches, git carries your uncommitted change across without complaint. So switching branches with uncommitted work usually just works, which makes the occasional refusal feel arbitrary. It is not — it means that specific file genuinely conflicts.
Four ways out, in the order worth trying:
# 1. Commit it. It is a branch; you can always amend or rebase later.
git add -A && git commit -m "WIP: partial refactor"
# 2. Stash it, switch, come back, restore.
git stash push -m "partial refactor"
git switch other-branch
# ...later...
git switch -
git stash pop
# 3. Take the changes with you deliberately (fails safely if it cannot)
git switch -m other-branch
# 4. Discard -- only when you are certain
git restore src/app.js
The first option is underrated. There is a widespread reluctance to commit unfinished work, and it causes more lost changes than any other habit. A commit on a local branch is not a promise; it is a save point. You can squash, amend or reset it later, and unlike a stash it has a message, a date, and a place in the reflog.
The stash, and how it loses work
git stash takes your uncommitted changes, saves them to a stack, and returns your working tree to a clean state.
git stash push -m "half-finished auth refactor"
git stash list
git stash show -p stash@{0} # what is actually in it
git stash pop # apply and remove from the stack
git stash apply stash@{1} # apply and keep it on the stack
Two defaults cause the trouble.
Untracked files are not stashed. New files you have created stay in the working tree, so a stash-then-switch leaves them behind on the wrong branch. Use -u to include them, or -a to include ignored files as well.
git stash push -u -m "refactor including new files"
A stash has no branch. It is a global stack. Stash on one branch, switch, pop on another, and git will happily apply changes that make no sense there — sometimes cleanly, which is worse than a conflict because nothing tells you.
Always use -m with a real message. stash@{3} tells you nothing, and a stack of four unnamed stashes from last month is functionally lost work — nobody will ever go through them, and they will sit there until someone cleans the repository.
The honest guidance: stash for minutes, not days. For anything longer, make a branch and commit. A stash is a place to put something while you check one thing, not a place to store work.
Discarding changes, and what is actually recoverable
The commands that throw work away, from narrow to total:
git restore src/app.js # discard unstaged changes to one file
git restore --staged src/app.js # unstage, keep the edits on disk
git restore . # discard ALL unstaged changes
git reset --hard # discard staged and unstaged
git clean -fd # delete untracked files and directories
git reset --hard and git clean -fd are the two genuinely dangerous ones, and they are dangerous in different ways.
reset --hard destroys uncommitted changes permanently. Anything committed is recoverable through the reflog — git reflog lists where HEAD has been for the last ninety days, and you can reset back to any of it. Anything never committed is simply gone.
clean -fd deletes untracked files, which frequently includes local .env files, credentials, or scratch work you meant to keep. Always dry-run it:
git clean -nd # -n = show what would be deleted, delete nothing
The principle worth internalising: git protects committed work extremely well and uncommitted work not at all. The reflog is a safety net that only catches things that were once commits. Almost every genuinely lost-work story starts with changes that had never been committed anywhere.
Which points back at the same habit. Commit early on a local branch, even messily. Rewriting history is easy; recovering something that was never in history is not.
How this fits the rest of the stack
Uncommitted changes are the only state in git that is not recoverable, and the discipline that avoids the problem is small: commit often locally, name your stashes, and dry-run anything that deletes.
The same argument scales up to deploys. A release you can name and return to is a release you can be relaxed about; a deploy that overwrote the previous state is one you have to get right first time. On RunxBuild, a service or static site builds from your GitHub repository and keeps deploy history, so rolling back is selecting the previous deploy rather than reconstructing it from memory. Build logs and runtime logs sit in one place, managed MySQL and Postgres run alongside on private networking with backups, and autoscaling moves between plan bounds you choose. To see what a service, a database and storage add up to, the RunxBuild hosting calculator lists them as separate line items.
Useful related references:
- Zapier Alternatives: What Changes When Automation Runs on Your Own Infrastructure
- Network Deployment: What Changes When an App Leaves Your Laptop
- Yarn Update Package: Why yarn upgrade Often Changes Nothing
- Services on RunxBuild
FAQ
What does local working changes mean in git?
Edits in your working tree that are not yet in a commit. They sit in one of three places: modified but unstaged, staged in the index, or set aside in the stash. git status --short shows the first two, with the left column representing the index and the right column the working tree.
Why will git not let me switch branches?
Because a file you have edited differs between the two branches, so switching would overwrite work that exists nowhere else. If the file were identical in both branches, git would carry your change across without complaint — which is why the refusal only happens sometimes and can feel arbitrary.
Does git stash include untracked files?
Not by default. New files you have created stay in the working tree, so stashing and switching branches leaves them behind. Use git stash push -u to include untracked files, or -a to include ignored ones as well. This is the most common way a stash appears to lose work.
Can I recover changes after git reset —hard?
Only if they were committed at some point. The reflog records where HEAD has been for around ninety days, so any commit can be recovered from it. Changes that were never committed are permanently gone — git protects history very well and uncommitted work not at all.
Should I commit unfinished work?
On a local branch, yes. A commit is a save point, not a promise: you can amend, squash or rebase it before anyone else sees it. The reluctance to commit work in progress causes more lost changes than any other habit, because uncommitted work is the only state git cannot recover.