Problem
You have a file already tracked in your git repository, but you don’t want future modifications to it to be tracked.
A perfect example of this is the DB config file for Rails projects (config/database.yml). You’ll probably want to track this to keep the production and staging configuration stored and consistent. But it’s quite likely that individual development configurations will be different. Having a commit for each developer adding their own local configuration and thereby polluting it for others when they push is just silly.
Solution
1 | git update-index --assume-unchanged FILENAME |
The fun details
I had initially thought that git-ignore was the thing to use for this. That was wrong: git-ignore’s purpose is to allow ignoring of untracked files. In the case of the Rails DB conf example above, the file is already tracked. It has the instance information that needs to be shared and now we want to add local development details. Since it’s tracked, any modifications are going to be picked up by git-status. When you do a commit including changes to tracked files listed in .gitignore, the changes do indeed get pushed, which isn’t what we want.
When looking around for solutions to this, I saw “git rm –cached FILENAME” suggested as a way to stop tracking currently tracked files. And it does this, but it also deletes the file in the commit, which isn’t what we want either.
git update-index allows you to alter your staging area more manually than usual. With it you can perform a wide range of operations not otherwise possible through standard commands. Continuing with the Rails DB conf example, here’s what the workflow would look like:
- config/database.yml with permanent instance info is added, committed, and pushed
- Application code is pulled down by new developer
- Local modifications made to database.yml for new developer’s local DB setup
- git update-index –assume-unchanged config/database.yml
Thereafter, the changes to conf/database.yml won’t appear in status or get committed. You can continue making and committing other changes, going about business as usual. But any changes to config/database.yml won’t ever appear.
In case you decide you do want to start tracking changes on a file previously ignored, just run:
1 | git update-index --no-assume-unchanged FILENAME |
Thereafter changes to FILENAME will appear in git-status.
Gotcha
The annoying part about this solution is that it has to be run against every new checkout of the repo with the files whose modifications should not be tracked. The command sets a particular bit per checkout, and it’s not passed on to new checkouts.
Just as I was finishing up some fact checking for this write-up, I came across Jesper Rønn-Jensen’s post on this exact topic. If only it had come up in my initial searches! Here’s hoping the next searcher has better luck.
No related posts.
Related posts brought to you by Yet Another Related Posts Plugin.









Fantastic! Just what I was looking for. Just out of curiosity do you know of any side-effects when switching branches or pushing stashes with this tip?
You know, I could not tolerate this setting getting overwritten on every checkout. And it is just that I consistently have the same version A of the file in the repo, and the same version B locally. I now use this stupidly simple solution. I created a branch called ‘local’ with exactly one commit: A->B. Now, I just rebase this local branch to wherever I am, then ‘git checkout -b newbranch’. After my work on that branch is done, I prepare it (for git-svn) with a ‘git rebase –onto master local newbranch’. Having the ‘local’ branch reminds me of the local modification very visibly, and it doesn’t silently get lost as in –assume-unchanged.
Thanks for updating your response – I’m running into the same issues with a Django repository and this has been the first local branch and rebase solution I’ve seen.
So much simpler
!