Don’t crib configuration files from others - at least not without understanding every line and making a conscious decision to include it. You can crib from mine, because you are reading this page and therefore have an explanation of every line.
Note: You can find the history of my .gitconfig
file in this public
Git repository.
[user]
name = Raymond E. Pasco
email = ray@ameretat.dev
My name and email address, which are necessary to make Git commits. I got this domain specifically for open source development; however, I do not ever want Github to send automated emails to it, and Github’s email settings may use other email addresses for me against my wishes on the rare occasions I use the web service.
It is also possible to have, instead of name and email settings, the
user.useConfigOnly
setting set to true
, in which case you will be
required to set local name and email settings for each repository.
Depending on your usage patterns, this may be more desirable.
If you want to set a per-repository name and email for only certain
repositories (e.g., work email for work repositories), you can do that
with git config --local
, and others will use your main configuration.
user.useConfigOnly
is for when you want to force yourself to
explicitly set it for each repository.
Recommendation: Necessary to make commits, so you should definitely set it.
[core]
editor = vi
Not vim
, although vi
is vim
on many systems. The main thing this
setting does is ensure that Git opens a sufficiently vi
-like editor.
Recommendation: Set this to your preferred terminal editor. A
non-terminal editor will work, but will awkwardly throw you out of the
terminal into your IDE or whatever; if you want to use a more heavy-duty
or IDE-like editor for merges specifically, look into git mergetool
.
[add "interactive"]
usebuiltin = true
Use the in-development C version of add-interactive rather than the
default Perl version. This is necessary to be able to patch-add new
files in Git 2.28 or later, because diffs from intent-to-add entries
(git add -N
) now (correctly) appear as new file diffs, but the Perl
version does not allow these hunks to be edited. (See the mailing
list
for more information.)
Recommendation: If you’re not impacted, you could wait for this to become the default.
[alias]
empty = commit --allow-empty
I find myself making toy commit graphs in Git for educational or testing purposes fairly regularly. When doing this, I mostly care about the graph structure, and make many empty commits. This alias makes this a bit quicker to type for me. Note that this option merely permits an empty commit - it does not require one. There could be changes staged in the index.
Recommendation: This is probably not useful to most people.
ff = merge --ff-only
Allows me to use git ff
to explicitly fast-forward a branch, when this
is actually the behavior I want. In other words, I have git ff
as the
command to do a fast-forward, and git merge
(in combination with
merge.ff = false
, described below) as the command to do a merge.
Recommendation: As with any alias, this is a matter of personal
taste. I recommend being aware of when git merge
will actually merge
and when it will fast-forward, at the very least. I discuss this more
below.
l = log --oneline --graph
This is my preferred log format, so I have given it the easy git l
alias. --graph
implies --topo-order
, so I don’t need to include that
option. Usually, I use gitk
to view histories - this is just a minor
convenience.
Recommendation: Whatever your thoughts on log formats and aliasing
them, you should make sure gitk
is working on your system, and use it.
If you already have another tool that does what gitk
does (show the
graphical history of a repository), that’s fine, but gitk
is the one
that comes with Git.
reroll = rebase --interactive --keep-base
Usually, a non-interactive rebase is about replaying commits on top of a
new base (hence the name ‘rebase’). An interactive rebase, however, is
about editing a branch, and I don’t want it to move to a new base - I
want to edit the branch as it is. Therefore, reroll
is a handy alias
to use instead of git rebase -i
.
Recommendation: People are usually fairly haphazard with where they base branches - but you can include more useful information (and sometimes ease workflows) by basing them in meaningful places, e.g., a bugfix on top of the commit which introduced the bug. Like all aliases, this alias is a matter of personal taste, but it points at a useful principle.
[init]
defaultBranch = master
The default branch name, formerly hardcoded “master”, is now
configurable via this setting. If it is not set, a warning is displayed
on every git init
.
Recommendation: Set this to your preferred default branch name to silence the warning.
[merge]
autoStash = true
This setting stashes worktree changes prior to running a merge, and unstashes them when the merge is complete, avoiding the loss of uncommitted changes in the worktree.
Recommendation: I recommend this just as a caution. Of course, you should probably not merge on a dirty worktree, especially since merging is a maintainer’s operation, but this will at least preserve accidentally dirty worktree content.
branchdesc = true
Descriptions can be set for branches with git branch --edit-description
. With this setting enabled, git merge --log
will
include these branch descriptions in the branch logs. I would like to
submit a patch to Git in the near future to have description-only logs
in merge commits as well, without including any commit names.
Recommendation: If you think this feature would be useful for your merges, feel free to start using it.
conflictStyle = diff3
Ordinarily, merge conflict annotations in files show only two sides: the “left” side, your current (integration) branch, and the “right” side, the branch you are merging into it. This is, of course, not enough information to resolve a conflict. A merge is a three-way operation: it involves finding the merge base, which is the common ancestor of the commits on either side of the merge, and applying the diff between the merge base and the left side, and the diff between the merge base and the right side, simultaneously. When the diffs cannot be applied simultaneously, a conflict arises.
It is absolutely vital to intelligently resolving conflicts that you be
able to see the version as of the merge base as well as the left and
right versions. This setting will add the merge base’s version of the
conflicted portion of the file to the conflict markers, in the middle
(with |||||||
above it and =======
below).
Recommendation: Turn on this setting immediately.
ff = false
git merge
may do one of two things - it may actually do a merge, or,
if the current HEAD is a direct ancestor of the branch to be merged, it
may attempt a fast-forward. In a fast-forward, rather than actually
creating a merge, the HEAD branch is simply adjusted to point at the tip
of the branch to be merged (“fast-foward” is another of Git’s many tape
deck metaphors).
This setting disables fast-forwarding, because I prefer git merge
to
always do a merge. I have the git ff
alias discussed above for when I
want to perform a fast-forward.
Recommendation: If you do any integration operations, you should be aware of the difference between a merge and a fast-forward. Doing each explicitly, rather than letting Git decide on its own, is preferable to me.
[pull]
ff = only
This setting allows the lazy man’s git pull
(typing git pull
on its
own to retrieve remote changes into one’s own repository clone) to be
possible. git pull
is not an analogous operation to cvs update
or
similar. Rather, git pull
is a command meant for Linus Torvalds to use
to merge a branch from someone else’s repository. git pull
is actually
a remote fetch followed by a merge.
As discussed above, git merge
tries to decide on its own whether to
actually do a merge, or whether to do a fast-forward instead. Most uses
of the lazy man’s git pull
trigger a fast-forward, permitting the
illusion that this command is analogous to something like cvs update
.
However, when one has made local changes, an explicit merge is
necessary, creating the useless information-free “Merge remote-tracking
branch origin/master into master” commits that plague many histories.
Some people set pull.rebase
instead, which rebases any local changes
on top of fetched changes, to try to preserve the cvs update
illusion
even with local commits to the branch. The actual correct response to a
failure to fast-forward, however, is not to blindly rebase, but to stop
and explicitly do whatever the correct operation is. On master
, this
is often to take the commit you lazily made on master
and give it its
own branch name, then reset master to the remote master - because
master
is an integration branch that belongs to maintainers.
On something like a topic branch with multiple people working closely,
where the topic branch history will be cleaned up at the end of work,
using git pull --ff
to get the default behavior is fine - the history
will be a temporary mess, but this can be fixed at the end.
Recommendation: Don’t do anything but explicit integration on integration branches. I do recommend this setting to avoid fallout from mistakes, especially if you do integration work and your errors have a chance to be permanent.
[push]
default = nothing
This setting prevents the lazy man’s git push
(i.e., typing just git push
to push the current branch to a default remote). The syntax for
git push
is more or less git push [remote] [local-ref]:[remote-ref]
.
If you leave any of these out, Git will try to use sensible defaults,
which will probably work out alright. However, if you do integration
work, anything you push is permanent by social contract - so it pays to
be explicit.
Setting push.default
to nothing
requires you to provide a remote and
refspec. Note that this is not perfect - if you have a local branch set
up to track a remote branch with a different name, e.g. local master
tracking origin/integration
, git push origin master
will try to push
to origin/master
because the refspec master
means master:master
. I
always type the full refspec explicitly (e.g. master:master
), avoiding
this, but if you are used to the default it might be surprising.
Recommendation: The default “simple” mode, which pushes to the remote-tracking branch but fails if it has a different name, should work for most people. You can always be more explicit in your commands. If you do public integration work, though, you should be careful about your pushes.
[rebase]
abbreviateCommands = true
This setting changes the automatically generated command script created
by git rebase -i
to use single-letter commands (e.g., p
instead of
pick
) rather than full word commands. Interactive rebase will always
accept either form, this setting only affects the script it generates
itself. I set this simply because I use vi, and the r
command to
replace a single letter is faster than replacing a word.
Recommendation: Pure personal preference. Learning at least the basics of the interactive rebase command language is an important Git skill, however.
autoSquash = true
This setting has nothing to do with the popular “worst practice” of aggressively oversquashing entire branches which the word “squash” has come to be associated with.
rebase.autoSquash
changes the behavior of interactive rebase as
follows: a commit with a message like squash! some commit message
or
fixup! some commit message
will be automatically placed directly after
the commit with the message some commit message
, and its initial
command will be changed from pick
to squash
or fixup
.
These commit messages can be generated for you with git commit --{squash,fixup}=hash
.
Recommendation: This is a useful feature which saves you some
editing time on the interactive rebase script. It is basically safe to
enable unless you, for some reason, name commits which are not intended
to be combined things starting with squash!
or fixup!
, which seems
unlikely - therefore, I recommend this setting.
autoStash = true
This setting stashes worktree changes prior to running a rebase, and unstashes them when the rebase is complete, avoiding the loss of uncommitted changes in the worktree.
Recommendation: This was almost exactly the same description as the
one above for merge.autoStash
, but this setting is more important for
rebases, which are more likely to be run with a dirty worktree. I
recommend turning this on.
[rerere]
enabled = true
Git has the capability to remember merge conflict resolutions and
automatically apply them if the same conflict is encountered again. This
is disabled by default; once it is enabled, Git will start recording
resolutions and applying them. Incorrect resolutions can be removed with
git rerere forget
and related commands. The cache of remembered
resolutions is local, but you can teach rerere a conflict resolution
from an existing merge commit by locally replaying the merge. The
rerere-train.sh
script included in Git’s contrib/
directory may help
with this.
Automatically resolved conflicts are not added to the index by default.
If you would prefer them to be, there is the rerere.autoUpdate
option.
I prefer taking the step explicitly.
Recommendation: The reason this is disabled by default is because
reaching a conflict, opening the file, and finding no conflict in the
file (because a recorded resolution was applied) is surprising behavior
to someone who does not know about rerere
. You have now read about it
and know what it does, and can therefore save yourself a lot of time by
enabling it now.
[sendemail]
annotate = true
This setting lets me stop to annotate commits being sent by git send-email
without explicitly specifying the --annotate
option every
time.
Recommendation: This is a good idea to set; most of the time you will at least want to look over the emails to be sent, even if you don’t end up annotating them.
confirm = always
This setting forces git send-email
to always stop for confirmation
before sending emails. It nearly always stops for confirmation anyway,
due to adding the sender to the CC list, but if you manually formatted
patches it might not.
Recommendation: I tend to prefer explicit confirmation before irrevocable actions like sending mail to a mailing list. I recommend this for that reason, but maybe you prefer living fast and dangerous.
# SMTP settings, if applicable, below
If your system is not set up to send mail via the system mailer daemon, you will need to specify your SMTP server settings here.
Recommendation: This is probably how most people will set up their
email. I prefer having a working system mailer where I can, but even I
use this much of the time. You may use forges like Github most of the
time, but I do recommend becoming familiar with git send-email
and its
counterpart, git am
(apply from email), which are the standard way to
share patches with Git.
[status]
showUntrackedFiles = all
Ordinarily, when you have a new directory in your worktree that is not
yet git add
ed, git status
shows only the directory name. This
setting makes git status
display each file individually, in the same
way it would if the directory were already tracked.
Recommendation: I recommend this setting. The only reason I can
think of not to include it is if you would regularly get huge git status
output from it - but in that case, you would get the same huge
output after git add
, anyway.