How to unstage files from index
As you probably already know, Git uses the concept of an index, or a “staging” area, to allow you to decide what code to include in a commit. You can add files or changes to files to staging using the command `git add` - the next time you make a commit, they’ll be included.
However, let’s say you accidentally add something to staging that you don’t want to keep. And maybe you don’t want to throw away the changes completely - you’re just not quite ready to push them to the repository yet. What to do? You want to unstage these changes, i.e. remove them from staging.
If you’re in a hurry, here’s the magic word to unstage all of your changes, without deleting anything:
Now you can go back and `git add` the correct files, leaving your index tidied up and ready for committing. But wait, there’s more! If you want to learn how this works or some alternate approaches, read on.
What exactly is the “index”?
Before we dive into fine-tuning the `git reset` command, it’s worth quickly reviewing what the index actually is in Git, why it’s necessary, and how it works. There’s a great article on the Git workflow at git scm, but I’ll summarize it here.
There are three steps: first, you create and/or modify files in your working directory, the files as they exist on your hard drive. Then, you need to specify which changes you want batch together. Finally, you take that set of changes and name and describe them, creating a commit which is then stored in Git for future reference.
As I mentioned briefly at the beginning of this article, the index plays a crucial role in this process: helping you batch those changes together. As you’re working, you might make a number of different updates before reaching a good breaking point, and you don’t necessarily want to store them all together. So you need a way to pick, of the changes to your files since the last commit, which ones belong together.
The index is actually nothing more than a single binary file stored in .git/index (if you want to get really in-depth, read this article on the Git index). After you create a commit, it’s reset to an empty state. And as you `git add` changes, they’re added to this file.
With this understanding in hand, it’s easy to imagine not only how `git add`, `git reset`, and `git rm` work, but also some preventative measures to avoid having to manually unstage changes. We’ll cover some of those later, too.
How to reset a specific file
If you need to unstage a specific file, you can simply pass the path to that file to the `git reset` command. This works with both files and directories:
This command will leave the rest of your changes in the index untouched. It’s handy for when you’re ready to commit a series of changes, and then realize you want to extract some of them (whether because they’re cleanup, or out of scope, or just messy up your diff) to another commit, without having to go back and re-add everything else. It’s all about efficiency!
Removing a new file from the index
We’ve talked about the Git remove command `git rm` before in the context of removing untracked files. Applied with a lighter touch, it can be used to unstage files as well:
As with the targeted `git reset` command, this command will also leave the rest of your files untouched as well. This command is particularly useful for when you’ve just created and staged a file, and less so for when you’ve only staged changes to an existing and committed file (because in those cases that file will still exist in Git history).
Using .gitignore to prevent adding files to index
As you may know, `git add .` is the most common way of staging all of the changes since the last commit. The period means “add everything in this current directory”, so if you’re operating at the top level of your working directory, that means `git add` will recursively go through every directory add every single change in every single file to index.
Obviously, this isn’t always ideal, since you often have files in your repository that you don’t want to ever commit (think build log files, secret files, node_modules, etc.).
In these cases, you can create a .gitignore file. The `git add` command will always check this file when attempting to stage changes. If the file Git is attempting to stage is referenced in .gitignore, it will - surprise! - completely ignore that file. This means that, with a properly configured ignore file, you can run `git addsafely whenever you want, without having to manually unstage the same files over and over again.
Unstaging files just added to .gitignore
So let’s say you’ve added a new logging tool or build process and introduced some junk to your working directory, or maybe you decided to engage in a little spring cleaning. Let’s further suppose you’ve already accidentally committed or staged these unwanted files to the repository.
You can and should do two things at this point: add those files to your .gitignore file, and unstage the changes that have already been added. Luckily, there’s a quick and easy way to combine those two steps into one.
You can read up on how to do it in detail here. The basic process is to make sure all your changes are committed, including your .gitignore file, delete all the files in your index, and then re-add everything.
Since `git add` only adds things that aren’t in your .gitignore files, all of the unwanted work won’t make it into your next commit. And since you made sure you committed everything before kicking off the process, you don’t have to be afraid of accidentally discarding changes.