548 lines
15 KiB
Markdown
548 lines
15 KiB
Markdown
|
## vim-gitgutter
|
|||
|
|
|||
|
A Vim plugin which shows a git diff in the 'gutter' (sign column). It shows which lines have been added, modified, or removed. You can also preview, stage, and undo individual hunks. The plugin also provides a hunk text object.
|
|||
|
|
|||
|
The signs are always up to date and the plugin never saves your buffer.
|
|||
|
|
|||
|
Features:
|
|||
|
|
|||
|
* Shows signs for added, modified, and removed lines.
|
|||
|
* Runs the diffs asynchronously where possible.
|
|||
|
* Ensures signs are always up to date.
|
|||
|
* Quick jumping between blocks of changed lines ("hunks").
|
|||
|
* Stage/undo/preview individual hunks.
|
|||
|
* Provides a hunk text object.
|
|||
|
* Diffs against index (default) or any commit.
|
|||
|
* Handles line endings correctly, even with repos that do CRLF conversion.
|
|||
|
* Optional line highlighting.
|
|||
|
* Fully customisable (signs, sign column, line highlights, mappings, extra git-diff arguments, etc).
|
|||
|
* Can be toggled on/off.
|
|||
|
* Preserves signs from other plugins.
|
|||
|
* Easy to integrate diff stats into status line; built-in integration with [vim-airline](https://github.com/bling/vim-airline/).
|
|||
|
* Works with fish shell (in addition to the usual shells).
|
|||
|
|
|||
|
Constraints:
|
|||
|
|
|||
|
* Supports git only. If you work with other version control systems, I recommend [vim-signify](https://github.com/mhinz/vim-signify).
|
|||
|
* Relies on the `FocusGained` event. If your terminal doesn't report focus events, either use something like [Terminus][] or set `let g:gitgutter_terminal_reports_focus=0`.
|
|||
|
|
|||
|
|
|||
|
### Screenshot
|
|||
|
|
|||
|
![screenshot](https://raw.github.com/airblade/vim-gitgutter/master/screenshot.png)
|
|||
|
|
|||
|
In the screenshot above you can see:
|
|||
|
|
|||
|
* Line 15 has been modified.
|
|||
|
* Lines 21-24 are new.
|
|||
|
* A line or lines were removed between lines 25 and 26.
|
|||
|
|
|||
|
|
|||
|
### Installation
|
|||
|
|
|||
|
Before installation, please check your Vim supports signs by running `:echo has('signs')`. `1` means you're all set; `0` means you need to install a Vim with signs support. If you're compiling Vim yourself you need the 'big' or 'huge' feature set. MacVim supports signs.
|
|||
|
|
|||
|
You install vim-gitgutter like any other vim plugin.
|
|||
|
|
|||
|
##### Pathogen
|
|||
|
|
|||
|
```
|
|||
|
cd ~/.vim/bundle
|
|||
|
git clone git://github.com/airblade/vim-gitgutter.git
|
|||
|
```
|
|||
|
|
|||
|
##### Voom
|
|||
|
|
|||
|
Edit your plugin manifest (`voom edit`) and add:
|
|||
|
|
|||
|
```
|
|||
|
airblade/vim-gitgutter
|
|||
|
```
|
|||
|
|
|||
|
##### VimPlug
|
|||
|
|
|||
|
Place this in your .vimrc:
|
|||
|
|
|||
|
```viml
|
|||
|
Plug 'airblade/vim-gitgutter'
|
|||
|
```
|
|||
|
|
|||
|
Then run the following in Vim:
|
|||
|
|
|||
|
```
|
|||
|
:source %
|
|||
|
:PlugInstall
|
|||
|
```
|
|||
|
|
|||
|
##### NeoBundle
|
|||
|
|
|||
|
Place this in your .vimrc:
|
|||
|
|
|||
|
```viml
|
|||
|
NeoBundle 'airblade/vim-gitgutter'
|
|||
|
```
|
|||
|
|
|||
|
Then run the following in Vim:
|
|||
|
|
|||
|
```
|
|||
|
:source %
|
|||
|
:NeoBundleInstall
|
|||
|
```
|
|||
|
|
|||
|
##### No plugin manager
|
|||
|
|
|||
|
Copy vim-gitgutter's subdirectories into your vim configuration directory:
|
|||
|
|
|||
|
```
|
|||
|
cd /tmp && git clone git://github.com/airblade/vim-gitgutter.git
|
|||
|
cp -r vim-gitgutter/* ~/.vim/
|
|||
|
```
|
|||
|
|
|||
|
See `:help add-global-plugin`.
|
|||
|
|
|||
|
|
|||
|
### Getting started
|
|||
|
|
|||
|
When you make a change to a file tracked by git, the diff markers should appear automatically. The delay is governed by vim's `updatetime` option; the default value is `4000`, i.e. 4 seconds, but I suggest reducing it to around 100ms (add `set updatetime=100` to your vimrc).
|
|||
|
|
|||
|
You can jump between hunks with `[c` and `]c`. You can preview, stage, and undo hunks with `<leader>hp`, `<leader>hs`, and `<leader>hu` respectively.
|
|||
|
|
|||
|
You cannot unstage a staged hunk.
|
|||
|
|
|||
|
|
|||
|
#### Activation
|
|||
|
|
|||
|
You can explicitly turn vim-gitgutter off and on (defaults to on):
|
|||
|
|
|||
|
* turn off with `:GitGutterDisable`
|
|||
|
* turn on with `:GitGutterEnable`
|
|||
|
* toggle with `:GitGutterToggle`.
|
|||
|
|
|||
|
You can turn the signs on and off (defaults to on):
|
|||
|
|
|||
|
* turn on with `:GitGutterSignsEnable`
|
|||
|
* turn off with `:GitGutterSignsDisable`
|
|||
|
* toggle with `:GitGutterSignsToggle`.
|
|||
|
|
|||
|
And you can turn line highlighting on and off (defaults to off):
|
|||
|
|
|||
|
* turn on with `:GitGutterLineHighlightsEnable`
|
|||
|
* turn off with `:GitGutterLineHighlightsDisable`
|
|||
|
* toggle with `:GitGutterLineHighlightsToggle`.
|
|||
|
|
|||
|
Note that if you have line highlighting on and signs off, you will have an empty sign column – more accurately, a sign column with invisible signs. This is because line highlighting requires signs and Vim always shows the sign column even if the signs are invisible.
|
|||
|
|
|||
|
If you switch off both line highlighting and signs, you won't see the sign column. That is unless you configure the sign column always to be there (see Sign Column section).
|
|||
|
|
|||
|
To keep your Vim snappy, vim-gitgutter will suppress the signs when a file has more than 500 changes. As soon as the number of changes falls below the limit vim-gitgutter will show the signs again. You can configure the threshold with:
|
|||
|
|
|||
|
```viml
|
|||
|
let g:gitgutter_max_signs = 500 " default value
|
|||
|
```
|
|||
|
|
|||
|
#### Hunks
|
|||
|
|
|||
|
You can jump between hunks:
|
|||
|
|
|||
|
* jump to next hunk (change): `]c`
|
|||
|
* jump to previous hunk (change): `[c`.
|
|||
|
|
|||
|
Both of those take a preceding count.
|
|||
|
|
|||
|
To set your own mappings for these, for example `]h` and `[h`:
|
|||
|
|
|||
|
```viml
|
|||
|
nmap ]h <Plug>GitGutterNextHunk
|
|||
|
nmap [h <Plug>GitGutterPrevHunk
|
|||
|
```
|
|||
|
|
|||
|
You can stage or undo an individual hunk when your cursor is in it:
|
|||
|
|
|||
|
* stage the hunk with `<Leader>hs` or
|
|||
|
* undo it with `<Leader>hu`.
|
|||
|
|
|||
|
See the FAQ if you want to unstage staged changes.
|
|||
|
|
|||
|
The `.` command will work with both these if you install [repeat.vim](https://github.com/tpope/vim-repeat).
|
|||
|
|
|||
|
To set your own mappings for these, for example if you prefer the mnemonics hunk-add and hunk-revert:
|
|||
|
|
|||
|
```viml
|
|||
|
nmap <Leader>ha <Plug>GitGutterStageHunk
|
|||
|
nmap <Leader>hr <Plug>GitGutterUndoHunk
|
|||
|
```
|
|||
|
|
|||
|
And you can preview a hunk's changes with `<Leader>hp`. You can of course change this mapping, e.g:
|
|||
|
|
|||
|
```viml
|
|||
|
nmap <Leader>hv <Plug>GitGutterPreviewHunk
|
|||
|
```
|
|||
|
|
|||
|
A hunk text object is provided which works in visual and operator-pending modes.
|
|||
|
|
|||
|
- `ic` operates on all lines in the current hunk.
|
|||
|
- `ac` operates on all lines in the current hunk and any trailing empty lines.
|
|||
|
|
|||
|
To re-map these, for example to `ih` and `ah`:
|
|||
|
|
|||
|
```viml
|
|||
|
omap ih <Plug>GitGutterTextObjectInnerPending
|
|||
|
omap ah <Plug>GitGutterTextObjectOuterPending
|
|||
|
xmap ih <Plug>GitGutterTextObjectInnerVisual
|
|||
|
xmap ah <Plug>GitGutterTextObjectOuterVisual
|
|||
|
```
|
|||
|
|
|||
|
If you don't want vim-gitgutter to set up any mappings at all, use this:
|
|||
|
|
|||
|
```viml
|
|||
|
let g:gitgutter_map_keys = 0
|
|||
|
```
|
|||
|
|
|||
|
Finally, you can force vim-gitgutter to update its signs across all visible buffers with `:GitGutterAll`.
|
|||
|
|
|||
|
See the customisation section below for how to change the defaults.
|
|||
|
|
|||
|
|
|||
|
### Customisation
|
|||
|
|
|||
|
You can customise:
|
|||
|
|
|||
|
* The sign column's colours
|
|||
|
* Whether or not the sign column is shown when there aren't any signs (defaults to no)
|
|||
|
* The signs' colours and symbols
|
|||
|
* Line highlights
|
|||
|
* The base of the diff
|
|||
|
* Extra arguments for `git` when running `git diff`
|
|||
|
* Extra arguments for `git diff`
|
|||
|
* Key mappings
|
|||
|
* Whether or not vim-gitgutter is on initially (defaults to on)
|
|||
|
* Whether or not signs are shown (defaults to yes)
|
|||
|
* Whether or not line highlighting is on initially (defaults to off)
|
|||
|
* Whether or not vim-gitgutter runs in "realtime" (defaults to yes)
|
|||
|
* Whether or not vim-gitgutter runs eagerly (defaults to yes)
|
|||
|
* Whether or not vim-gitgutter runs asynchronously (defaults to yes)
|
|||
|
|
|||
|
Please note that vim-gitgutter won't override any colours or highlights you've set in your colorscheme.
|
|||
|
|
|||
|
|
|||
|
#### Sign column
|
|||
|
|
|||
|
By default vim-gitgutter will make the sign column look like the line number column.
|
|||
|
|
|||
|
To customise your sign column's background color, first tell vim-gitgutter to leave it alone:
|
|||
|
|
|||
|
```viml
|
|||
|
let g:gitgutter_override_sign_column_highlight = 0
|
|||
|
```
|
|||
|
|
|||
|
And then either update your colorscheme's `SignColumn` highlight group or set it in your vimrc:
|
|||
|
|
|||
|
```viml
|
|||
|
highlight SignColumn ctermbg=whatever " terminal Vim
|
|||
|
highlight SignColumn guibg=whatever " gVim/MacVim
|
|||
|
```
|
|||
|
|
|||
|
By default the sign column will appear when there are signs to show and disappear when there aren't. To always have the sign column, add to your vimrc:
|
|||
|
|
|||
|
```viml
|
|||
|
if exists('&signcolumn') " Vim 7.4.2201
|
|||
|
set signcolumn=yes
|
|||
|
else
|
|||
|
let g:gitgutter_sign_column_always = 1
|
|||
|
endif
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
#### Signs' colours and symbols
|
|||
|
|
|||
|
To customise the colours, set up the following highlight groups in your colorscheme or `~/.vimrc`:
|
|||
|
|
|||
|
```viml
|
|||
|
GitGutterAdd " an added line
|
|||
|
GitGutterChange " a changed line
|
|||
|
GitGutterDelete " at least one removed line
|
|||
|
GitGutterChangeDelete " a changed line followed by at least one removed line
|
|||
|
```
|
|||
|
|
|||
|
You can either set these with `highlight GitGutterAdd {key}={arg}...` or link them to existing highlight groups with, say, `highlight link GitGutterAdd DiffAdd`.
|
|||
|
|
|||
|
To customise the symbols, add the following to your `~/.vimrc`:
|
|||
|
|
|||
|
```viml
|
|||
|
let g:gitgutter_sign_added = 'xx'
|
|||
|
let g:gitgutter_sign_modified = 'yy'
|
|||
|
let g:gitgutter_sign_removed = 'zz'
|
|||
|
let g:gitgutter_sign_removed_first_line = '^^'
|
|||
|
let g:gitgutter_sign_modified_removed = 'ww'
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
#### Line highlights
|
|||
|
|
|||
|
Similarly to the signs' colours, set up the following highlight groups in your colorscheme or `~/.vimrc`:
|
|||
|
|
|||
|
```viml
|
|||
|
GitGutterAddLine " default: links to DiffAdd
|
|||
|
GitGutterChangeLine " default: links to DiffChange
|
|||
|
GitGutterDeleteLine " default: links to DiffDelete
|
|||
|
GitGutterChangeDeleteLine " default: links to GitGutterChangeLineDefault, i.e. DiffChange
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
#### The base of the diff
|
|||
|
|
|||
|
By default buffers are diffed against the index. However you can diff against any commit by setting:
|
|||
|
|
|||
|
```viml
|
|||
|
let g:gitgutter_diff_base = '<commit SHA>'
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
#### Extra arguments for `git` when running `git diff`
|
|||
|
|
|||
|
If you want to pass extra arguments to `git` when running `git diff`, do so like this:
|
|||
|
|
|||
|
```viml
|
|||
|
let g:gitgutter_git_args = '--git-dir-""'
|
|||
|
```
|
|||
|
|
|||
|
#### Extra arguments for `git diff`
|
|||
|
|
|||
|
If you want to pass extra arguments to `git diff`, for example to ignore whitespace, do so like this:
|
|||
|
|
|||
|
```viml
|
|||
|
let g:gitgutter_diff_args = '-w'
|
|||
|
```
|
|||
|
|
|||
|
#### Key mappings
|
|||
|
|
|||
|
To disable all key mappings:
|
|||
|
|
|||
|
```viml
|
|||
|
let g:gitgutter_map_keys = 0
|
|||
|
```
|
|||
|
|
|||
|
See above for configuring maps for hunk-jumping and staging/undoing.
|
|||
|
|
|||
|
|
|||
|
#### Use a custom `grep` command
|
|||
|
|
|||
|
If you use an alternative to grep, you can tell vim-gitgutter to use it here.
|
|||
|
|
|||
|
```viml
|
|||
|
" Default:
|
|||
|
let g:gitgutter_grep = 'grep'
|
|||
|
```
|
|||
|
|
|||
|
#### To turn off vim-gitgutter by default
|
|||
|
|
|||
|
Add `let g:gitgutter_enabled = 0` to your `~/.vimrc`.
|
|||
|
|
|||
|
|
|||
|
#### To turn off signs by default
|
|||
|
|
|||
|
Add `let g:gitgutter_signs = 0` to your `~/.vimrc`.
|
|||
|
|
|||
|
|
|||
|
#### To turn on line highlighting by default
|
|||
|
|
|||
|
Add `let g:gitgutter_highlight_lines = 1` to your `~/.vimrc`.
|
|||
|
|
|||
|
|
|||
|
#### To turn off asynchronous updates
|
|||
|
|
|||
|
By default diffs are run asynchronously. To run diffs synchronously instead:
|
|||
|
|
|||
|
```viml
|
|||
|
let g:gitgutter_async = 0
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
### Extensions
|
|||
|
|
|||
|
#### Operate on every line in a hunk
|
|||
|
|
|||
|
You can map an operator to do whatever you want to every line in a hunk.
|
|||
|
|
|||
|
Let's say, for example, you want to remove trailing whitespace.
|
|||
|
|
|||
|
```viml
|
|||
|
function! CleanUp(...)
|
|||
|
if a:0 " opfunc
|
|||
|
let [first, last] = [line("'["), line("']")]
|
|||
|
else
|
|||
|
let [first, last] = [line("'<"), line("'>")]
|
|||
|
endif
|
|||
|
for lnum in range(first, last)
|
|||
|
let line = getline(lnum)
|
|||
|
|
|||
|
" clean up the text, e.g.:
|
|||
|
let line = substitute(line, '\s\+$', '', '')
|
|||
|
|
|||
|
call setline(lnum, line)
|
|||
|
endfor
|
|||
|
endfunction
|
|||
|
|
|||
|
nmap <silent> <Leader>x :set opfunc=CleanUp<CR>g@
|
|||
|
```
|
|||
|
|
|||
|
Then place your cursor in a hunk and type `\xic` (assuming a leader of `\`).
|
|||
|
|
|||
|
Alternatively you could place your cursor in a hunk, type `vic` to select it, then `:call CleanUp()`.
|
|||
|
|
|||
|
|
|||
|
#### Operate on every changed line in a file
|
|||
|
|
|||
|
You can write a command to do whatever you want to every changed line in a file.
|
|||
|
|
|||
|
```viml
|
|||
|
function! GlobalChangedLines(ex_cmd)
|
|||
|
for hunk in GitGutterGetHunks()
|
|||
|
for lnum in range(hunk[2], hunk[2]+hunk[3]-1)
|
|||
|
let cursor = getcurpos()
|
|||
|
silent! execute lnum.a:ex_cmd
|
|||
|
call setpos('.', cursor)
|
|||
|
endfor
|
|||
|
endfor
|
|||
|
endfunction
|
|||
|
|
|||
|
command -nargs=1 Glines call GlobalChangedLines(<q-args>)
|
|||
|
```
|
|||
|
|
|||
|
Let's say, for example, you want to remove trailing whitespace from all changed lines:
|
|||
|
|
|||
|
```viml
|
|||
|
:Glines s/\s\+$//
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
#### Cycle through hunks in all buffers
|
|||
|
|
|||
|
`]c` and `[c` jump from one hunk to the next in the current buffer. You can use this code to jump to the next hunk no matter which buffer it's in.
|
|||
|
|
|||
|
```viml
|
|||
|
function! NextHunkAllBuffers()
|
|||
|
let line = line('.')
|
|||
|
GitGutterNextHunk
|
|||
|
if line('.') != line
|
|||
|
return
|
|||
|
endif
|
|||
|
|
|||
|
let bufnr = bufnr('')
|
|||
|
while 1
|
|||
|
bnext
|
|||
|
if bufnr('') == bufnr
|
|||
|
return
|
|||
|
endif
|
|||
|
if !empty(GitGutterGetHunks())
|
|||
|
normal! 1G
|
|||
|
GitGutterNextHunk
|
|||
|
return
|
|||
|
endif
|
|||
|
endwhile
|
|||
|
endfunction
|
|||
|
|
|||
|
function! PrevHunkAllBuffers()
|
|||
|
let line = line('.')
|
|||
|
GitGutterPrevHunk
|
|||
|
if line('.') != line
|
|||
|
return
|
|||
|
endif
|
|||
|
|
|||
|
let bufnr = bufnr('')
|
|||
|
while 1
|
|||
|
bprevious
|
|||
|
if bufnr('') == bufnr
|
|||
|
return
|
|||
|
endif
|
|||
|
if !empty(GitGutterGetHunks())
|
|||
|
normal! G
|
|||
|
GitGutterPrevHunk
|
|||
|
return
|
|||
|
endif
|
|||
|
endwhile
|
|||
|
endfunction
|
|||
|
|
|||
|
nmap <silent> ]c :call NextHunkAllBuffers()<CR>
|
|||
|
nmap <silent> [c :call PrevHunkAllBuffers()<CR>
|
|||
|
```
|
|||
|
|
|||
|
|
|||
|
### FAQ
|
|||
|
|
|||
|
> How can I turn off realtime updates?
|
|||
|
|
|||
|
Add this to your vim configuration (in an `/after/plugin` directory):
|
|||
|
|
|||
|
```viml
|
|||
|
" .vim/after/plugin/gitgutter.vim
|
|||
|
autocmd! gitgutter CursorHold,CursorHoldI
|
|||
|
```
|
|||
|
|
|||
|
> I turned off realtime updates, how can I have signs updated when I save a file?
|
|||
|
|
|||
|
If you really want to update the signs when you save a file, add this to your vimrc:
|
|||
|
|
|||
|
```viml
|
|||
|
autocmd BufWritePost * GitGutter
|
|||
|
```
|
|||
|
|
|||
|
> Why can't I unstage staged changes?
|
|||
|
|
|||
|
This plugin is for showing changes between the working tree and the index (and staging/undoing those changes). Unstaging a staged hunk would require showing changes between the index and HEAD, which is out of scope.
|
|||
|
|
|||
|
> Why are the colours in the sign column weird?
|
|||
|
|
|||
|
Your colorscheme is configuring the `SignColumn` highlight group weirdly. Please see the section above on customising the sign column.
|
|||
|
|
|||
|
> What happens if I also use another plugin which uses signs (e.g. Syntastic)?
|
|||
|
|
|||
|
Vim only allows one sign per line. Before adding a sign to a line, vim-gitgutter checks whether a sign has already been added by somebody else. If so it doesn't do anything. In other words vim-gitgutter won't overwrite another plugin's signs. It also won't remove another plugin's signs.
|
|||
|
|
|||
|
|
|||
|
### Troubleshooting
|
|||
|
|
|||
|
#### When no signs are showing at all
|
|||
|
|
|||
|
Here are some things you can check:
|
|||
|
|
|||
|
* Try adding `let g:gitgutter_grep=''` to your vimrc. If it works, the problem is grep producing non-plain output; e.g. ANSI escape codes or colours.
|
|||
|
* Verify `:echo system("git --version")` succeeds.
|
|||
|
* Verify your git config is compatible with the version of git returned by the command above.
|
|||
|
* Verify your Vim supports signs (`:echo has('signs')` should give `1`).
|
|||
|
* Verify your file is being tracked by git and has unstaged changes.
|
|||
|
|
|||
|
#### When the whole file is marked as added
|
|||
|
|
|||
|
* If you use zsh, and you set `CDPATH`, make sure `CDPATH` doesn't include the current directory.
|
|||
|
|
|||
|
#### When signs take a few seconds to appear
|
|||
|
|
|||
|
* Try reducing `updatetime`, e.g. `set updatetime=100`.
|
|||
|
|
|||
|
#### When signs don't update after focusing Vim
|
|||
|
|
|||
|
* Your terminal probably isn't reporting focus events. Either try installing [Terminus][] or set `let g:gitgutter_terminal_reports_focus=0`.
|
|||
|
|
|||
|
|
|||
|
### Shameless Plug
|
|||
|
|
|||
|
If this plugin has helped you, or you'd like to learn more about Vim, why not check out this screencast I wrote for PeepCode:
|
|||
|
|
|||
|
* [Smash Into Vim][siv]
|
|||
|
|
|||
|
This was one of PeepCode's all-time top three bestsellers and is now available at Pluralsight.
|
|||
|
|
|||
|
You can read reviews on my [website][airblade].
|
|||
|
|
|||
|
|
|||
|
### Intellectual Property
|
|||
|
|
|||
|
Copyright Andrew Stewart, AirBlade Software Ltd. Released under the MIT licence.
|
|||
|
|
|||
|
|
|||
|
[pathogen]: https://github.com/tpope/vim-pathogen
|
|||
|
[siv]: http://pluralsight.com/training/Courses/TableOfContents/smash-into-vim
|
|||
|
[airblade]: http://airbladesoftware.com/peepcode-vim
|
|||
|
[terminus]: https://github.com/wincent/terminus
|