Video

Want to see the full-length video right now for free?

Notes

Git and Vim: two focused tools that work even better together. You're probably familiar with using Vim to compose commit messages, and maybe even a rebase sequence. But there are a few other ways we can combine Vim and Git for even greater productivity.

Fugitive

Fugitive.vim is a Vim plugin by the one and only tpope that brings Git into Vim, allowing us to explore and interact with our git repo from within Vim. In tpope's own words, Fugitive is:

a Git wrapper so awesome, it should be illegal

Fugitive is impressively complete in the features and commands it exposes, so we won't cover all of them, but as an introduction, we can touch on a few of the commands and workflows that we use every day here at thoughtbot.

Gblame

:Gblame is a wrapper around the existing git blame command. When run at the command line, git blame <file> will dump out a wall of text displaying the most recent modifier (commit author) for each line. Unfortunately, this is a bit hard to work with:

$ git blame Gemfile
fda9ecd3 (Joe Ferris          2014-11-05 13:33:52 -0700  1) source "https://rubygems.org"
ccc4d709 (Chad Pytel          2011-04-29 12:13:58 -0400  2)
875272fa (Arun Agrawal        2015-04-24 13:31:46 +0200  3) ruby "2.2.2"
72a6c828 (Trevor O'Reilly     2012-10-24 13:10:32 -0400  4)
48bbd7b6 (Arun Agrawal        2015-01-20 10:20:34 -0500  5) gem "active_model_serializers", "0.8.3"
48bbd7b6 (Arun Agrawal        2015-01-20 10:20:34 -0500  6) gem "acts_as_list", "~> 0.6.0"
fda9ecd3 (Joe Ferris          2014-11-05 13:33:52 -0700  7) gem "airbrake"
6562f830 (Dan Croak           2014-10-01 13:34:07 -0700  8) gem "analytics-ruby", "~> 2.0.0", require: "segment/analytics"

Instead, with :Gblame from Fugitive, a new split will open beside the current file that displays a highlighted version of the git blame output aligned with each of the source lines in our file.

Even better, rather than this being a static text dump, as with the default git blame output, :Gblame's output is interactive. Specifically, we can put our cursor over a commit hash in the blame window and hit <cr> to jump to that commit. From there we can read the commit message and see a complete diff of when this line was updated to its current form.

Gdiff

:Gdiff wraps the normal git diff command, but embraces Vim's very nice split diff display with syntax highlighting and all of the comforts of Vim. When working on a given file, we can start it with just :Gdiff and it will display the diff against the current commit, using Vim's split diff display.

In addition, we can use :Gdiff to diff against any revision. For instance, :Gdiff master will diff against master rather than the most recent commit. :Gdiff even supports tab completion of the relevant refs you might want to use as an argument.

One additional benefit of working within Vim's diff view is that we have available specific commands for working with the :diffget and :diffput commands. These work by pulling or pushing diff sections between the two versions of the file, allowing you to easily revert changes you've made.

Gread

:Gread will update our buffer to match what the file looked like in the HEAD commit. This essentially acts as a git checkout <file>, but while working within Vim.

Similar to the :Gdiff command, :Gread can take an argument of any revision or branch specifier to pull in the version of the current file as it is in that revision. For example, using :Gread master will pull in the version of of the file on master, essentially resetting our changes on a feature branch.

Gremove

:Gremove is a shortcut that performs three actions:

  1. Removes the buffer in Vim.
  2. Deletes the file from our working directory.
  3. Runs the associated git rm command to stage the removal of the file.

While the first two are straightforward, the git rm is something I always forget and have to follow up on later. :Gremove gives us a single command that takes care of all three.

Gmove

:Gmove also combines multiple actions into a single command, in this case the steps needed to rename a file. Specifically, :Gmove full/path/to/new-name.rb will simultaneously:

  1. Rename the buffer in Vim.
  2. Rename the file in our working directory.
  3. Run the relevant git mv command to inform Git of the rename.

Gstatus

:Gstatus is one of Fugitive's most powerful commands, providing a full Vim-based interface for viewing and interacting with our status. To start, just run :Gstatus and Fugitive will open a split below the current file that displays the current status. We can then interact with this buffer using a handful of custom key mappings specific to the status window.

Mapping Operation
D Opens the file in a split diff to review changes.
- git add for unstaged files, or git reset for stage files.
<C-n> Jumps to the next file in the status list
<C-p> Jumps to the previous file in the status list.
cc Composes a commit message to commit the staged changes.

These are the primary key mappings you'll want to use, but there are many more which you can review by running :h gstatus, or using the key mapping g? when in the status window.

Vimcasts Fugitive Series

The above commands will certainly get you started, and may be all you ever need with fugitive, but they are really just scratching the surface. If you want to dive deeper into all that Fugitive has to offer, be sure to check out the [five part Fugitive series on Vimcasts][].

[five part fugitive series on Vimcasts]: http://vimcasts.org/blog/2011/05/the-fugitive-series/

Conflicted

[Conflicted][] is a Vim plugin that wraps Fugitive's :Gdiff functionality, specifically optimizing for resolving merge and rebase conflicts. Conflicted provides multiple views for conflicted files, specifically showing:

  • A three-way diff of the two parents and the working version.
  • A diff of the changes on the upstream branch (typically master).
  • A diff of the changes on the local branch (typically our feature branch).

While the typical view of a single file with the conflict markers Git provides can often be very confusing, the multiple views provided by Conflicted provide as much detail as we need to fully understand and resolve conflicts.

In addition, while giving us the three-way diff view to resolve the conflict, Conflicted also provides mappings to perform a :diffget from either the upstream or local versions of the file and then displays them on the side:

Mapping Operation
dgu :diffget from our upstream (typically master)
dgl :diffget from our local (typically our feature branch)

Finally, Conflicted also provides a command, :GitNextConflict, which will write the file, perform a git add to stage the resolved version, and either move on to the next Conflicted file if there is one, or quit back to the terminal.

One additional enhancement shown in the video is the use of a shell command, conflicted, to open Vim with the Conflicted plugin active. This is provided by the following shell function:

conflicted() {
  vim +Conflicted
}

[Conflicted]: https://github.com/christoomey/vim-conflicted

Conclusion

Now you've seen a few of the ways to combine Vim and Git for even greater productivity. Both are focused Unix tools that do their one thing well, but provide a clean interface that makes combining them even better. If you're a Vim and Git user but have yet to combine them, there is no better time to give it a shot!