3½ Git Tips

Due to a tragic lack of imagination, here is an attempt to be practical instead.

It's my top 3½ git tips of the week!

The Three Ways to Ignore

Quite often, your development machine will clutter up a working directory with extra files that don't need to be shared, and therefore shouldn't be committed. Ideally while not committing them, you'd like to keep them out of the git status output, avoid adding them accidentally, and generally keep things “tidy”.

There are three ways to do this. Which is the right one depends on what kind of file you're ignoring.

.gitignore

This is the right place for exclusions that are relevant to everyone working on the project — for example:
  • build products (/target, /dist etc.)
  • managed dependencies (/node_modules, /lib/vendor etc.)
  • (easily re-)generated code

In any directory under Git's control, you can create a .gitignore file with a list of file paths to ignore within that directory. This .gitignore file can be committed as normal and shared with everyone.

.git/info/exclude

If you are doing something in your working directory which no one else in their right mind will do, and want to locally ignore some files while doing it, this is the right place.

Every Git working directory contains a .git folder containing historical file versions and all the various metadata to go with them. One metadata file in this folder is info/exclude. You can list paths here in the same style as .gitignore, and the files will be ignored.

~/.gitignore

You can also have a file similar to .git/info/exclude, which applies to all repositories on your machine. This is very useful. I keep mine at ~/.gitignore. To set this up, globally, do the following:

$ git config --global core.excludesfile ~/.gitignore
$ vim ~/.gitignore
This is ideal for system-wide things related to your machine or software choices, that aren't anything to do with any particular project and don't necessarily affect anyone else:
  • OS clutter (.DS_Store from OS X, Thumbs.db or desktop.ini from Windows, etc.)
  • IDE clutter (.settings, .project, .idea etc.)

It's nice to be able to ignore these things globally — that way, if you switch IDE you have only one file to edit, and the .gitignore files in each project stay clean and sensical.

Harmless Pulls

When using automation (such as myrepos, which I used when I first wrote this article) to update a bunch of repos at once, you have to be quite careful what pull-style command you use. A standard pull automatically merges. Fair enough. I prefer rebases most of the time, unless things get too complicated. Whatever.

The problem is, either of those can fail with conflicts, and it's quite inconvenient to have directories left in an inconsistent state full of equals signs and <<<<<. So, now I have a custom git update command, defined by a tiny shell script at ~/bin/git-update:

git fetch --prune && (git rebase || (git rebase --abort && false))

This basically tries a rebase and immediately aborts it (still returning an error code) if it screws up.

Admittedly this can result in interdependent repos having non-matching versions, but at least it doesn't leave mess lying around — and git status -sb will still show when the local copy is out of sync with origin!

Tig

Just fetched? Failed at a catastrophic merge and want to know what's going on? Generally curious about your project history? Try tig. It's a command-line based history and diff browser with a built in graph visualization of branches and merges. Or if that's too confusing, there's always gitk — Git's built-in viewer graph viewer that, while slow and ugly, is always effective!

(I also like to alias tig --all to tiga and gitk --all & to gka, to more easily see my branches compared to origin.)

Autosquash

Committed something, a few commits ago, that was hilariously broken?

You can fix it easily!

git commit --fixup 56ba62a3a3fcac941c303f56bc2a3eef113fb2a1

<or>

git commit --fixup ':/Start of message of commit to be fixed'

(Then, if you later run git rebase -i --autosquash, it will automatically reorder the fixing commit to be squashed in with the one it's supposed to fix.)

That's All For Now

I hope this has been somehow helpful and/or interesting. Bye!