Working with vim and git

May 5, 2020

Follow @learnvim for more Vim tips and tricks!

An efficient vim-git workflow can boost productivity. In this article, I will discuss different ways to integrate vim with git.

Here are topics I will cover:

  • Vanilla vim and git
  • Vim-fugitive
  • Conclusion
  • List of git plugins

Vanilla vim and git

Vim and git are two equally great tools for two different things. However, getting them to integrate with each other without plugins can be difficult, but not impossible.

If git is installed in your system, you can use vim's bang (:!) to execute any shell command.

:!git
:!git stash
:!git diff
:!git log

In vim, % means current file. With this, you can do:

:!git add %
:!git checkout %
:!git rm %

Vim has a built-in terminal, :term. You can run your git commands from terminal inside Vim. Personally, I find using a dedicated tmux window/pane for git far more useful than :term.

Although vim does not have a internal git integration, fortunately for us, Tim Pope created vim-fugitive.

Vim-fugitive

Some of fugitive's feature makes some git activities even easier in Vim than doing it from terminal! You can use any plugin managers or use built-in pack to install it.

I will cover some fugitive features below.

:Git

When you run :Git without any argument, fugitive displays a summary window similar to :Gstatus.

Compare running :Gstatus with :!git status. The latter will display an ugly "Press ENTER or type command to continue" outside vim window. Fugitive is a clear winner.

vim git status with bang

fugitive Gstatus

Some things you can do in :Gstatus window:

<Ctrl-p>/<Ctrl-n>     " to go between sections
<return>              " to open the file
-                     " to add/ reset file
s                     " Stage the file under cursor
u                     " Unstage file under cursor
-                     " toggle staging/unstaging
U                     " Unstage everything

To learn more, check out :h fugitive-staging-maps.

Fugitive's :Git wraps the git command. You can pass it any option that works with git command:

  • :Git status
  • :Git add "your-file.txt"
  • :Git commit -m "your message"

:Git blame

:Git blame creates a git blame window inside vim. You use this to find the last person responsible for that one buggy the code so you can yell at them (just kidding).

Fugitive git blame

Some shortcuts while you are on blame window:

A        " resize to show author
C        " resize to show commit column
D        " resize to show date/time column
gq       " close window

You can use Vim's :q or <Ctrl-w> + c to close the blame window.

:Gclog

Fugitive's :Gclog is another useful feature. It launches vim quickfix (:h quickfix) window to display all your commit logs.

Fugitive Gclog

Fugitive shows tree, parent, and changed files in one convenient window. Pressing enter on the tree or parent SHA will take you to that window. You can't do this easily with original git log without workaround with git ls-tree and git show.

Btw, here's a really cool trick with :Gclog. In the bottom of top window you see a SHA. That's the current file name. You can yank that with y + <Ctrl-g>, then paste it with p or P.

You can pass options too, like:

:Gclog -5
:Gclog --since=yesterday

Since :Gclog uses quickfix, normal quickfix navigation works:

:cclose    " close quickfix window
:copen     " open quickfix window
:cnext     " next item on quickfix window list
:cprev     " prev item
:cfirst    " first item
:clast     " last item

:Gread and :Gwrite

:Gwrite does :Git add % and :Gread does :Git checkout %.

Imagine that you are editing hello.c, then decided you want to revert this file back to its state in previous commit. You can run :Gread and all yourchanges will be replaced with previous commit. Vim considers this as a change, so you can undo (u) it if you change your mind.

If you're happy with your changes, run :Gwrite to stage it.

:Gdiffsplit

Fugitive performs a vimdiff (:h vimdiff) between a given file and a commit (default last commit) with :Gdiffsplit.

Fugitive Gdiffsplit

You can perform a diff against any commits. Fugitive is smart enough to compare it with current file from that commit. Just pass the SHA you want to compare it with:

:Gdiffsplit SHA_FROM_LONG_TIME_AGO

Since it uses Vim's diff window, you can use vimdiff's ]c and [c to jump to next / previous change.

:Gedit

You can view any file from commit SHA or branch with :Gedit.

:Gedit SHA
:Gedit SHA:path/to/file
:Gedit branch
:Gedit branch:path/tofile

You can get the SHA from git log or :Gclog. Keep in mind you can't edit the file because Vim is in Read-Only mode.

Fugitive Object

Vim-fugitive comes with its own fugitive-object. For a complete fugitive object list, check out :h fugitive-object.

Let's do an example with one of the objects to learn how to use it. A fugitive object for the commit referenced by head is @. If I want to :Gedit that, I can run :Gedit @.

Conclusion

I think this is a good place to stop. Vim and git are two wonderful tools for two different things. You can integrate vim and git without plugins with Vim's :term or bang (:!).

For better integration, a plugin is required.

Vim-fugitive is a very popular git wrapper and it is easy to see why. To learn more, vim-fugitive doc is very good.

Also, Drew Neil made a fugitive series almost 10 years ago, but most of contents are still relevant today. I highly recommend it.

Vim-fugitive is not the only git plugin that is available for vim. Below is a list of some git plugins.

List of git plugins

Here are some git-related plugins: