Today, we are going to tame the complex beast that git add is. And since an image is worth a thousand words, you should get the picture with the various illustrations!
source: https://pixabay.com/en/nature-sky-water-landscape-sun-3058859/ [CC0 licence]

In the following figures, orange boxes represent files. Colors (green and red) are the one used by git status. A red box is something that was changed but not added to the index (either an addition, a new file, or a deletion), and a green box is something that was added to the index.

Initial state:
starting point

As you can see, A.txt and C.txt were edited. D.txt was added. B.txt wasn’t touched. No git commands were run after the last commit, and therefore nothing was added yet in the index.

Any next command starts from this state.


git add -u add all patches to the files known by git. Since D.txt is new, it will not be added in the index.

git add -u


git add -p will give you a prompt to select all the patches you want. In this example, we selected the patches A1 and C3.

git add -p


git add $FILE_OR_DIRECTORY will add those files or directories to the index.

For example, git add D.txt will add the new file D.txt. After this operation, this file is known by git.

git add $NEW_FILE_OR_DIRECTORY

git add A.txt In this example, we added A.txt, a file already known by git. All of it current content was just added to the index.

git add $EXISTING_FILE_OR_DIRECTORY


git add --all add all files (including the new ones) to the index. Be careful to not add unwanted files in the process. If you want to use this option, you must (and anyway you should) have a .gitignore properly configured.

git add –all


From now on you should have a better understanding on how to populate your index. You can obviously run multiple git add ... commands in a row. You may want to add multiple files, or you forgot a patch, or you want to add an untracked file after a git add -p, …

Now, let’s do a quick Q/A!

What is the role of the index?

The index is the place where you prepare your commit. When your commit is ready to be created, you just have to run git commit (with the -m "message" option if you want), and a new commit will be created with exactly the same content as your index.

That’s nice, but someone told me to run git commit -am "message" and that’s it!

git commit -am "message" will do exactly the same as git add -u && git commit -m "message". As you saw in the diagram, it’s maybe not what you want. Taking the time to manually add all files, or even better using git add -p will give you an opportunity to do a quick self-code-review.

I’m a bit lost, I did a bunch of git add, and I don’t know what is the state of my index.

git status will give you a good idea of what you did. The color (red and green) used in the schema are the same as git uses to show what is in the index and what is only in your working directory.

If you want to see the modifications left in your working directory, run git diff, and if you want to see the modifications in your index (the draft of your commit under construction) it’s git diff --cached.

I added a file (or even just a part of it) that I did not want to add to my index.

git reset -p is your friend. To re-add them, use git add as usual.

And I want to undo a modification from my working directory. For example, I don’t need all those printf() anymore.

It’s git checkout -p, but be careful, it cannot be undone. And if you already added them to your index, you will need to run git reset -p first.

I think I understand how it’s working, but why do I need an index?

That’s a good question. Typically, it’s when you add some comments for yourself (like a bunch of to-do’s) or when you added some functions to debug (like some printf()), and you both don’t want to commit them, while keeping them in your working directory. It’s also extremely useful if you just worked on two different things (like when you start implementing a feature and you fix a bug in the middle). Ideally, you want to create a commit for each of those modifications and the index makes it easy.

My typical workflow

Firstly I do some edits on my files (obviously!). Then I use git status to have a global idea (in conjunction with git diff). Then I run git add -p to add all the changes I want (and doing a quick self-review). I double check with git diff --cached. And finally I commit with git commit -m "message" (without the -a option). This may looks tedious, but in practice it’s quite natural to do, and I spend much less time in pair-reviews or in maintenance.

If I just (unintentionally) worked on two unrelated changes, I will create two commits, by using git add -p && git commit two times.

Sum-up

  • git add -p add patches to your index
  • git add $FILE add a file to your index (needed if $FILE is a new file)
  • git commit commit the content of your index
  • git reset -p undo modifications from your index (but not the working directory)
  • git checkout -p undo modifications from your working directory (cannot be undone)

And don’t use git commit -a! You may forget your new files while pushing unwanted comments.

Creative Commons License