Git and Vim
Posted on
A lot has been written about plugins like Fugitive.vim that help you use Git from within Vim. Here I'm going to be looking at it from the other side; how can Vim help with common Git tasks? I'll start with how to set Vim as the default editor for Git, move on to Git's built-in support for viewing diffs and resolving merge conflicts using Vim and GVim, and then finally look at how Git's built in tools can be configured to use MacVim or any other tool you like.
The basics: Setting Git's default editor
The most basic level of integration is using Vim for Git commands that launch
an editor, commands like commit
and rebase -i
.
Git determines which editor to use by checking its own configuration first
(the $GIT_EDITOR
environment variable, and then the
core.editor
configuration variable), but if they're not set it
falls back to the standard UNIX environment variables ($VISUAL
and then $EDITOR
), before finally defaulting to vi
.
My $VISUAL
editor is set to MacVim, but I prefer to use plain
old Vim for Git commands so I don't have to leave the terminal I'm working
in. Git prefers $VISUAL
to $EDITOR
, so I override
the default behaviour by setting Git's core.editor
configuration
variable. Set it using the git config
command:
$ git config --global core.editor `which vim`
The --global flag indicates that this change should apply to all repositories, if you omit this flag and issue the command from a directory that is a Git repository then the setting will only apply to that one repository.
Diff and merge with Vim
Vim's diff mode
One of the things I was delighted to discover when I switched from TextMate to Vim was Vim's powerful diff mode.
You can launch Vim in diff mode with vim -d file1
file2 or vimdiff file1
file2 (gvimdiff
and mvimdiff
also
work, if you're using GVim or MacVim respectively). When you do, you are
presented with a Vim session split vertically into multiple windows, each
showing one of the files you specified at the command line with the
differences highlighted.
As you scroll around and make changes in any one of the windows, the other
windows scroll and update in real time, so you're always seeing accurate and
up-to-date diff information right next to the line you're editing.
This seemed like the perfect tool for viewing Git diffs, and resolving
merge conflicts so I did a little research and came across two Git
commands I hadn't previously encountered: difftool
and
mergetool
.
Git difftool
and mergetool
git difftool
is very similar to git diff
and
supports the same parameters, the difference is that the results aren't
printed to the standard output, instead they're sent to a specialised diff
visualisation tool.
git mergetool
is used to resolve merge conflicts by sending
each conflicted file in turn to a specialised conflict resolution tool.
Both of these commands support a variety of tools, including
opendiff
(which isn't directly relevant to a discussion of
using Git with Vim, but on OS X will launch the excellent
FileMerge
utility) and of course vimdiff
and gvimdiff
.
You can specify which tool to use with the -t parameter, for
example:
$ git difftool -t vimdiff
If you don't want to specify the tool to use every time you can set Git's
diff.tool
and merge.tool
configuration variables:
$ git config --global merge.tool vimdiff $ git config --global diff.tool vimdiff
While you're there, you might also want to suppress the Hit return to launch 'vimdiff' prompt:
$ git config --global mergetool.prompt false $ git config --global difftool.prompt false
Additional configuration
There are some additional options for both tools that I find useful. First
of all, git difftool
allows you to specify a second, GUI based
tool using the diff.guitool
variable. You can use the GUI
tool by invoking git difftool
with the -g option.
I use vimdiff
as my diff.tool
and MacVim as my
diff.guitool
(there's more on how to use MacVim later).
Secondly, by default git mergetool
will create a backup copy
of whichever files you're merging. You can remove these manually when you're
finished with either git clean
or plain old rm
, but
if you want git mergetool
to clean up after itself you can set
the mergetool.keepBackup
option to false
.
Using MacVim
While git mergetool
and git difftool
both have
baked in support for Vim and GVim, they don't support MacVim without a little
extra configuration.
The quick fix
The easiest way to use MacVim is just to edit Git's gvimdiff
path so it points to the mvimdiff
binary instead:
$ git config --global difftool.gvimdiff.path `which mvimdiff` $ git config --global mergetool.gvimdiff.path `which mvimdiff`
Now you can set the merge.tool
and diff.tool
or
diff.guitool
variables to gvimdiff
, or use the
-t gvimdiff argument, and Git will launch MacVim instead of GVim.
A custom mergetool
If you want to customise things further, and not just modify the executable
that git mergetool
calls, but also change the parameters, you
will need to define a custom mergetool.
You can do this from the command line using git config
, but
I think it's easier to do this kind of complex configuration by editing your
Git config file directly. For global settings you can edit
~/.gitconfig
, or for a specific project edit
.git/config
from the repository's root directory.
Add the following to your Git config file:
[mergetool "toolname"] cmd = command
You can then use your custom toolname with the -t
option or in the merge.tool
variable.
The following variables are available to use in your command, each is the path to a particular version of the file that is being merged:
$MERGED
-
This is the version of the file that you should edit; it is the version of
the file in your working copy of the repository, so it's what you would be
editing anyway if you were resolving a merge conflict without the help
of
git mergetool
. $LOCAL
- This is the file as it was on your local branch before you started the merge.
$REMOTE
- This is the file as it is on the remote branch, that is the branch you are merging into your local branch.
$BASE
- This is the base of the merge, that is the file as it was before the two branches introduced incompatible changes.
For example, my MacVim configuration looks like this:
[mergetool "mvimdiff"] cmd = /usr/local/bin/mvimdiff -f "$LOCAL" "$MERGED" "$REMOTE"
Note the -f flag; this tells MacVim to run in the foreground.
When you close your chosen tool git mergetool
will stage the
file for you if you've made changes, or will prompt you to stage it with the
question filename seems unchanged. Was the merge
successful? In either case, you can still make further
changes before you commit, mergetool
only stages your changes,
it doesn't commit them. Without the -f flag you can't take
advantage of this behaviour.
A custom difftool
The configuration for a custom difftool is very similar. As with a custom mergetool, you add the following to your Git config file:
[difftool "toolname"] cmd = command
You can use your custom toolname in either the -t
option, or the diff.tool
and diff.guitool
configuration variables.
The command can contain all of the same variables as a mergetool command,
although in this case $BASE
and $MERGED
will both
contain the path of the current working version.
Copy and paste
Here are some edited highlights from my ~/.gitconfig
file
which capture most of the configuration options discussed above. You may
need to change some paths for this to work on your system.
[core] editor = /usr/bin/vim [mergetool "mvimdiff"] cmd = /usr/local/bin/mvimdiff -f "$LOCAL" "$MERGED" "$REMOTE" [merge] tool = mvimdiff [mergetool] prompt = false keepBackup = false [difftool "gvimdiff"] path = /usr/local/bin/mvimdiff [diff] tool = vimdiff guitool = gvimdiff [difftool] prompt = false
1 Comment
Luba commented on :
Sorry for being off topic, but I just discovered http://javascriptweeklyfeed.heroku.com/ and subscribed to both JS and Ruby Weekly. Thank you! I wonder why there is such a disdain for web feeds. Using email for broadcast is wrong. Bulk emails always end up feeling like spam.