Git has become almost unavoidable when you work as a dev today.
Git is a pretty awesome tool in my opinion, that has never let me down during the past 10 years. (Really. The thing. Just. Works. Kudos to the developers and maintainers because not all our devtools achieve this over 10 years).
Git, however, has one big drawback in everyday use (some people find a lot of other, more philosophical drawbacks): the User Interface sucks.
I’ve been spending the last 10 years trying to explain to junior developers or even non-developers staff, the subtile differences between checkout
and reset
and when to use one or the other. How rebase
works. Don’t get me started on submodule
.
Basic everyday operations like unstaging or discarding changes, used to require remembering commands like git reset HEAD
or git checkout -- <file>
, both used for totally different operation like changing the working branch or undoing commit…
There are two things that have really helped me “grok” and become more fluent in Git:
1/ reading Pro Git by Scott Chacon and Ben Straub
(available for free, but it’s so good I bought 2 paper versions). It really explains the working models of Git perfectly, and how to build your own mental model to use it, and to understand what you’re doing.
The book uses nice, well-thought-out pictures and diagrams to explain each concept. Still a reference after all those years, and one of the best reading investments I’ve done in my carrier.
2/ customizing Git with my own configuration,
Basically building my own User Interface, with my daily used commands perfectly setup for my current workflow.
What started as a way to improve my Git experience ended up in exploring the config of other Git users to learn more tricks with my favorite tool.
You just need to edit you Git config file (usually ~/.gitconfig
) to start improving your Git experience.
Config options
The first way to customize Git is just to configure some of its many options.
Some are quite basics, like changing the colors for the different status of files in git status
output:
[color "status"]
added = green
changed = red
untracked = cyan
unmerged = magenta
Other options allow you to define the tools you want to use with Git, like which merge tool to use to resolve conflicts:
[merge]
tool = kdiff3
Finally, the more interesting options can change the default behaviour of some of the most used Git command.
For example, you can configure rebase
to auto-stash your local modifications before starting the rebasing process, and auto-pop them once you’re done, allowing you to easily rebase your working area on main without worry.
[rebase]
autostash = true
Git aliases
The other way to improve your Git user experience is to use aliases to define your own new Git commands.
This can be used to shorten commands that you’ll type hundreds of time every day.
[alias]
s = status
Or to enable some flags by default. Let’s say you want your diff to ignore all space modifications by default:
d = diff --ignore-all-space
In those simple cases, the command you want to execute with your alias is another Git command so you just need to write the command and its flags without any particular syntax.
my-alias = <git cmd> [...flags]
Then you’ll be able to invoke you alias as you would any other Git command.
git my-alias
And Git will automatically substitute your alias with the command specified.
Invoking shell commands
You can, in fact, execute any command with a Git alias, including non-git commands. You’ll however need to prefix those commands with !
to denote external commands, and usually you’ll also have to wrap them in double quotes.
hello = "!echo 'hello'"
Typing git hello
will simply print “hello” in your shell.
This really unlocks the power of the Git aliases, allowing you to do some really convenient stuff.
Like invoking external tools:
ignore = "!gi() { curl -L -s <https://www.gitignore.io/api/$@> ;}; gi"
Now by typing git ignore node
you’ll get a default .gitignore
file for Node projects.
(Note the use of an inline shell function to include in the URL, the arguments you pass to your Git alias).
You can also chain Git commands.
addrm = "!git rm $(git ls-files --deleted)"
This alias will stage all the deleted files in your working area.
Finally, this syntax can be used to invoke other Git aliases. You can normally not define an alias using another alias directly, but by executing another Git process to launch the alias, you can achieve the same result.
di = diff --color-words
diff-staged = "!git di --cached"
git diff-staged
will show you the diff between HEAD and your staging area (aka index), what would be committed by git commit
.
Building your own configuration
My own advice would be to build your own configuration incrementally.
Avoid adding a lot of aliases or options at once, since you’ll then struggle to become familiar with them all. Start small and add just a few aliases for your most used commands in your everyday flow.
You can also dedicate a section of your config to the cools tricks you found along the way, that you won’t use every day.
When you want to see your aliases you can use
alias = "!git config -l | grep alias | cut -c 7-"
Conclusion
Using the Git configuration file you can totally customize your Git experience.
Many developers have been building their own configuration files over the years, and publish them online on sites like Github. Reading those files is probably the best way to learn new tricks and to discover the rich configuration options available.
I should also mention that Git user interface is gradually and steadily improving with each new version. This started with adding useful command reminders in the output of git status
command and the like. And today new commands are added like git restore
to avoid using unrelated commands like checkout
to unstage or discard your modifications.
While old users like me will probably keep using their time-honed aliases in their daily flow, hats off to the Git developers and maintainers that keep working to improve the experience of new or non-technical users.
Now go forth and pimp your Git.