Hiding local file changes from git

How to override files in your dev environment without causing headaches.

Photo by Alex Knight on Unsplash

I’m currently working on refactoring some Angular components and updating their styling for our booking confirmation screen. My colleague Matt showed me a handy way to manually stick a big object in the relevant model file so that I can directly go to the booking confirmation screen without having to click through the whole search/price/book process. However, now every time I do git status, I get this:

This is annoying. That “modified” file is not actually modified in the sense of version control — I don’t plan to commit my changes later and push them to the upstream repo. I don’t want to see it when I do git status, and I especially don’t want to add the changes when I do git add.

So I’ve found a neat way to work around this.

  1. git update-index --skip-worktree src/app/price/confirmation/confirmation-model.js will “hide” the file from git; it will no longer show up as a file that I’ve changed. If the file gets changed remotely and I do git pull, git will give me a conflict message and explain what needs to be done to resolve it. And git reset --hard will not reset the file, allowing the changes to persist for a long time (e.g. across branches).
  2. To view a list of all files I’ve hidden in this way, I can just do git ls-files -v . | grep ^S. All the files listed with status code S (for skip-worktree) will be displayed.
  3. To “unhide” a file, I can simply do git update-index --no-skip-worktree src/app/price/confirmation/confirmation-model.js.

How is this different to .gitignore?

The most common way to “hide” files is to add them to .gitignore. This won’t work for me: the file I’m editing is already tracked in git, which means changes will still show up in git status.

.gitignore is intended for things like build artifacts, which shouldn’t be committed to the repo at all.

How is this different to assume-unchanged?

Now we’re getting somewhere! git update-index --assume-unchanged is fairly similar in function to --skip-worktree. The difference is that --assume-unchanged seems to be designed for performance uses rather than for local overwriting of configs and the like. It’s meant to be used for cases where checking for file changes is very expensive. Critically, operations like git pull are very likely to reset this flag and discard all your changes with no option to recover them.

Tips

If you plan on using these, I recommend you add some aliases to your .zshrc/.bashrc file:

Then you can do ghide path/to/file to hide, gunhide path/to/file to unhide, and ghidden to list all hidden files.

I hope you find this useful! Do remember, if you are going to use this, just do ghidden every so often to check whether you haven’t forgotten about a hidden file. (I’m still looking for a clever way to show this in git status…)

Update (2017-09-14)

I don’t know why I didn’t think of this before! Even better than making these commands shell aliases, you can put them directly in your git config (~/.gitconfig). This “scopes” the commands to git, allowing git to autocomplete the commands for example, and making the commands resemble standard git commands. It also means the commands will be available to you across multiple shells.

The commands then become git hide path/to/file, git unhide path/to/file, and git hidden.


Originally published at henrebotha.github.io on August 30, 2017.