Git: all about adding files to the staging area

Tricks and tips on adding files to staging area in Git: using globs and limiting the scope of add etc.

Adding files in git, so simple, yet sometimes really hard and weird. The following quote is grabbed from the Git documentation, and I think it sheds light to the business of adding really well:

Git has three main states that your files can reside in: committed, modified, and staged. Committed means that the data is safely stored in your local database. Modified means that you have changed the file but have not committed it to your database yet. Staged means that you have marked a modified file in its current version to go into your next commit snapshot.

The staging area is pretty cool thing, and unique only to Git. The act of adding, means adding to the staging area.

The basics of adding files to the staging area

$ git add <file>           # Add a single file
$ git add path/to/a/<file> # It can be a path also

Add all the changed files from all sub directories:

$ git add .

If you’re in the project root, the previous command will add everything from all the directories. If you’re in, say, projectroot/js it’ll only affect the files in the js directory. This comes really handy when there are tons of new files to add in a given directory.

Adding deleted files

Now, if $ git add . is issued, deleted files won’t be added to the commit (or the deletion of the file is not added to the commit). Enter the -u flag, short for --update.

From (emphasis mine):

[…] That means that it will never stage new files, but that it will stage modified new contents of tracked files and that it will remove files from the index if the corresponding files in the working tree have been removed.

This’ll add all deleted files:

$ git add -u

The previous command affects the whole working tree, all files in all folders are added, despite the current working directory.

The scope of this command can be limited by cding into the wanted dir, and issue the following command, note the dot:

$ git add -u .

How about add . and add -u at the same time? You might do:

$ git add -u . && git add -u

But there’s a built in flag for it: -A short for --all.

$ git add -A

Interesting thread on this.

Using Glob

Globbing is possible here. Globs are a bit like regex, but easier and more limited.

The next adds all files ending with .js in the javascript directory:

$ git add javascript/*.js

Or add all possible files in plugins dir:

$ git add plugins/*

You get the point.

Testing Globs with ls

Globs are handy to test with ls (list) bash command, in the end globs are just bash, not Git. Here’s a miniscule script that makes a test directory structure.

mkdir -p globtest/{javascript,dist} 
&& touch globtest/file-{1..10}.html 
&& touch globtest/javascript/file-{1..10}.js 
&& touch globtest/dist/test-{1..10}.min.js 

Paste that in a terminal and hit enter, it’ll create you the following structure to play with:

├── dist
│   ├── test-1.min.js
│   ├── test-10.min.js
│   ├── test-2.min.js
│   ├── test-3.min.js
│   ├── test-4.min.js
│   ├── test-5.min.js
│   ├── test-6.min.js
│   ├── test-7.min.js
│   ├── test-8.min.js
│   └── test-9.min.js
├── javascript
│   ├── file-1.js
│   ├── file-10.js
│   ├── file-2.js
│   ├── file-3.js
│   ├── file-4.js
│   ├── file-5.js
│   ├── file-6.js
│   ├── file-7.js
│   ├── file-8.js
│   └── file-9.js
├── file-1.html
├── file-10.html
├── file-2.html
├── file-3.html
├── file-4.html
├── file-5.html
├── file-6.html
├── file-7.html
├── file-8.html
└── file-9.html

Now you can easily see how the globs work. The following…

$ ls globtest/*

…returns all files in globtest:

globtest/file-1.html  globtest/file-2.html  globtest/file-4.html  globtest/file-6.html  globtest/file-8.html
globtest/file-10.html globtest/file-3.html  globtest/file-5.html  globtest/file-7.html  globtest/file-9.html

test-1.min.js  test-10.min.js test-2.min.js  test-3.min.js  test-4.min.js  test-5.min.js  test-6.min.js  test-7.min.js  test-8.min.js  test-9.min.js

file-1.js  file-10.js file-2.js  file-3.js  file-4.js  file-5.js  file-6.js  file-7.js  file-8.js  file-9.js


$ ls globtest/*.js

Returns nothing cause there’s no .js files in the root. Then again the following returns all .js files in all subdirs, but not from the root:

$ ls globtest/**/*.js
globtest/dist/test-1.min.js    globtest/dist/test-7.min.js    globtest/javascript/file-4.js
globtest/dist/test-10.min.js   globtest/dist/test-8.min.js    globtest/javascript/file-5.js
globtest/dist/test-2.min.js    globtest/dist/test-9.min.js    globtest/javascript/file-6.js
globtest/dist/test-3.min.js    globtest/javascript/file-1.js  globtest/javascript/file-7.js
globtest/dist/test-4.min.js    globtest/javascript/file-10.js globtest/javascript/file-8.js
globtest/dist/test-5.min.js    globtest/javascript/file-2.js  globtest/javascript/file-9.js
globtest/dist/test-6.min.js    globtest/javascript/file-3.js

The next gets all files with 4 in them, from sub directories, but again, not from the root.

$ ls globtest/**/*4*
globtest/dist/test-4.min.js   globtest/javascript/file-4.js

Here’s above with root included to the scope:

$ ls globtest/{,*/}**4*
globtest/dist/test-4.min.js   globtest/file-4.html          globtest/javascript/file-4.js


Removing files from the staging area

When talking about adding, we have to talk about removing also. Sometimes it comes handy to remove files from the staging area, i.e. to unstage files.

$ git reset HEAD <file> # Single file
$ git reset HEAD .      # All files, same rules apply here

Ignoring committed files

Tangentially related topic: dealing with files added to the .gitignore file afterwards.

Say, push to a repo, look at it in GitHub, notice a stray .DS_Store, if that .DS_Store file is now added to the .gitignore, it’ll persist in the repo forever. It needs to be removed:

$ git rm --cached .DS_Store

Or if a whole directory, like node_modules has creeped it’s way to the repo, use the -r (recursive) flag:

$ git rm -r --cached node_modules/


See the article on adding, it’s pretty comprohencive.

Club-Mate, the beverage →