mirror of
https://github.com/airblade/vim-gitgutter.git
synced 2025-11-09 12:03:48 -05:00
Compare commits
20 Commits
872-rename
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
488c0555e4 | ||
|
|
85ca3a0872 | ||
|
|
a5ae0a5a18 | ||
|
|
6620e5fbbe | ||
|
|
33cb7744c3 | ||
|
|
d3a9986fe8 | ||
|
|
7b0b5098e3 | ||
|
|
bed580ab8b | ||
|
|
e801371917 | ||
|
|
67ef116100 | ||
|
|
84bc2d68c0 | ||
|
|
4b49965897 | ||
|
|
fe0e8a2630 | ||
|
|
3b5acc05a1 | ||
|
|
1e7be38a3c | ||
|
|
b9e9ad2ae2 | ||
|
|
6efb835aa2 | ||
|
|
61f80c80ba | ||
|
|
6a95f1b57c | ||
|
|
c3b99e52f5 |
@@ -18,6 +18,7 @@ Features:
|
||||
* Stage partial hunks.
|
||||
* Provides a hunk text object.
|
||||
* Diffs against index (default) or any commit.
|
||||
* Handles file moves / renames.
|
||||
* Heeds git's "assume unchanged" bit.
|
||||
* Allows folding all unchanged text.
|
||||
* Provides fold text showing whether folded lines have been changed.
|
||||
@@ -29,6 +30,7 @@ Features:
|
||||
* Fully customisable (signs, sign column, line (number) highlights, mappings, extra git-diff arguments, etc).
|
||||
* Can be toggled on/off, globally or per buffer.
|
||||
* Preserves signs from other plugins.
|
||||
* Does the right thing when viewing revisions with [fugitive](https://github.com/tpope/vim-fugitive)'s `:0Gclog`.
|
||||
* 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).
|
||||
|
||||
@@ -79,7 +81,7 @@ Second, ensure your `updatetime` and `signcolumn` options are set appropriately.
|
||||
|
||||
When you make a change to a file tracked by git, the diff markers should appear automatically after a short delay. 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). Note `updatetime` also controls the delay before vim writes its swap file (see `:help updatetime`).
|
||||
|
||||
The `signcolumn` option can have any value except `'off'`.
|
||||
The `signcolumn` option can have any value except `'no'`.
|
||||
|
||||
|
||||
### Windows
|
||||
@@ -531,6 +533,8 @@ let g:gitgutter_async = 0
|
||||
|
||||
Add `let g:gitgutter_preview_win_floating = 1` to your `~/.vimrc`. Note that on Vim this prevents you staging (partial) hunks via the preview window.
|
||||
|
||||
On Neovim, the preview hunk command will move the cursor into the floating window if it is already open.
|
||||
|
||||
|
||||
#### The appearance of a floating/popup window for hunk previews
|
||||
|
||||
|
||||
@@ -118,11 +118,16 @@ endfunction
|
||||
" }}}
|
||||
|
||||
|
||||
function! gitgutter#git()
|
||||
" Optional argument is buffer number
|
||||
function! gitgutter#git(...)
|
||||
let git = g:gitgutter_git_executable
|
||||
if a:0
|
||||
let git .= ' -C '.gitgutter#utility#dir(a:1)
|
||||
endif
|
||||
if empty(g:gitgutter_git_args)
|
||||
return g:gitgutter_git_executable
|
||||
return git
|
||||
else
|
||||
return g:gitgutter_git_executable.' '.g:gitgutter_git_args
|
||||
return git.' '.g:gitgutter_git_args
|
||||
endif
|
||||
endfunction
|
||||
|
||||
@@ -222,9 +227,11 @@ function! gitgutter#quickfix(current_file)
|
||||
let lnum = 0
|
||||
for line in diff
|
||||
if line =~ '^diff --git [^"]'
|
||||
" No quotation mark therefore no spaces in filenames
|
||||
let [fnamel, fnamer] = split(line)[2:3]
|
||||
let fname = fnamel ==# fnamer ? fnamer : fnamer[2:]
|
||||
elseif line =~ '^diff --git "'
|
||||
" Quotation mark therefore do not split on space
|
||||
let [_, fnamel, _, fnamer] = split(line, '"')
|
||||
let fname = fnamel ==# fnamer ? fnamer : fnamer[2:]
|
||||
elseif line =~ '^diff --cc [^"]'
|
||||
@@ -256,9 +263,7 @@ function! gitgutter#difforig()
|
||||
|
||||
if g:gitgutter_diff_relative_to ==# 'index'
|
||||
let index_name = gitgutter#utility#get_diff_base(bufnr).':'.gitgutter#utility#base_path(bufnr)
|
||||
let cmd = gitgutter#utility#cd_cmd(bufnr,
|
||||
\ gitgutter#git().' --no-pager show '.index_name
|
||||
\ )
|
||||
let cmd = gitgutter#git(bufnr).' --no-pager show '.index_name
|
||||
" NOTE: this uses &shell to execute cmd. Perhaps we should use instead
|
||||
" gitgutter#utility's use_known_shell() / restore_shell() functions.
|
||||
silent! execute "read ++edit !" cmd
|
||||
|
||||
@@ -46,7 +46,7 @@ function! s:build_command(cmd)
|
||||
endif
|
||||
|
||||
if has('win32')
|
||||
return has('nvim') ? ['cmd.exe', '/c', a:cmd] : 'cmd.exe /c '.a:cmd
|
||||
return has('nvim') ? a:cmd : 'cmd.exe /c '.a:cmd
|
||||
endif
|
||||
|
||||
throw 'unknown os'
|
||||
|
||||
@@ -4,14 +4,6 @@ let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ?
|
||||
|
||||
let s:hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@'
|
||||
|
||||
" True for git v1.7.2+.
|
||||
function! s:git_supports_command_line_config_override() abort
|
||||
let [_, error_code] = gitgutter#utility#system(gitgutter#git().' -c foo.bar=baz --version')
|
||||
return !error_code
|
||||
endfunction
|
||||
|
||||
let s:c_flag = s:git_supports_command_line_config_override()
|
||||
|
||||
let s:temp_from = tempname()
|
||||
let s:temp_buffer = tempname()
|
||||
let s:counter = 0
|
||||
@@ -124,20 +116,22 @@ function! gitgutter#diff#run_diff(bufnr, from, preserve_full_diff) abort
|
||||
|
||||
" Write file from index to temporary file.
|
||||
let index_name = gitgutter#utility#get_diff_base(a:bufnr).':'.gitgutter#utility#base_path(a:bufnr)
|
||||
let cmd .= gitgutter#git().' --no-pager show --textconv '.index_name.' > '.from_file.' || exit 0) && ('
|
||||
let cmd .= gitgutter#git(a:bufnr).' --no-pager show --textconv '.index_name
|
||||
let cmd .= ' > '.gitgutter#utility#shellescape(from_file).' || exit 0) && ('
|
||||
|
||||
elseif a:from ==# 'working_tree'
|
||||
let from_file = gitgutter#utility#repo_path(a:bufnr, 1)
|
||||
endif
|
||||
|
||||
" Call git-diff.
|
||||
let cmd .= gitgutter#git().' --no-pager'
|
||||
if s:c_flag
|
||||
let cmd .= gitgutter#git(a:bufnr).' --no-pager'
|
||||
if gitgutter#utility#git_supports_command_line_config_override()
|
||||
let cmd .= ' -c "diff.autorefreshindex=0"'
|
||||
let cmd .= ' -c "diff.noprefix=false"'
|
||||
let cmd .= ' -c "core.safecrlf=false"'
|
||||
endif
|
||||
let cmd .= ' diff --no-ext-diff --no-color -U0 '.g:gitgutter_diff_args.' -- '.from_file.' '.buff_file
|
||||
let cmd .= ' diff --no-ext-diff --no-color -U0 '.g:gitgutter_diff_args
|
||||
let cmd .= ' -- '.gitgutter#utility#shellescape(from_file).' '.gitgutter#utility#shellescape(buff_file)
|
||||
|
||||
" Pipe git-diff output into grep.
|
||||
if !a:preserve_full_diff && !empty(g:gitgutter_grep)
|
||||
@@ -152,8 +146,6 @@ function! gitgutter#diff#run_diff(bufnr, from, preserve_full_diff) abort
|
||||
|
||||
let cmd .= ')'
|
||||
|
||||
let cmd = gitgutter#utility#cd_cmd(a:bufnr, cmd)
|
||||
|
||||
if g:gitgutter_async && gitgutter#async#available()
|
||||
call gitgutter#async#execute(cmd, a:bufnr, {
|
||||
\ 'out': function('gitgutter#diff#handler'),
|
||||
|
||||
@@ -60,7 +60,8 @@ function! gitgutter#hunk#next_hunk(count) abort
|
||||
if hunk[2] > current_line
|
||||
let hunk_count += 1
|
||||
if hunk_count == a:count
|
||||
execute 'normal!' hunk[2] . 'Gzv'
|
||||
let keys = &foldopen =~# '\<block\>' ? 'zv' : ''
|
||||
execute 'normal!' hunk[2] . 'G' . keys
|
||||
if g:gitgutter_show_msg_on_hunk_jumping
|
||||
redraw | echo printf('Hunk %d of %d', index(hunks, hunk) + 1, len(hunks))
|
||||
endif
|
||||
@@ -90,8 +91,9 @@ function! gitgutter#hunk#prev_hunk(count) abort
|
||||
if hunk[2] < current_line
|
||||
let hunk_count += 1
|
||||
if hunk_count == a:count
|
||||
let keys = &foldopen =~# '\<block\>' ? 'zv' : ''
|
||||
let target = hunk[2] == 0 ? 1 : hunk[2]
|
||||
execute 'normal!' target . 'Gzv'
|
||||
execute 'normal!' target . 'G' . keys
|
||||
if g:gitgutter_show_msg_on_hunk_jumping
|
||||
redraw | echo printf('Hunk %d of %d', index(hunks, hunk) + 1, len(hunks))
|
||||
endif
|
||||
@@ -306,9 +308,8 @@ function! s:stage(hunk_diff)
|
||||
write
|
||||
let path = gitgutter#utility#repo_path(bufnr, 1)
|
||||
" Add file to index.
|
||||
let cmd = gitgutter#utility#cd_cmd(bufnr,
|
||||
\ gitgutter#git().' add '.
|
||||
\ gitgutter#utility#shellescape(gitgutter#utility#filename(bufnr)))
|
||||
let cmd = gitgutter#git(bufnr).' add '.
|
||||
\ gitgutter#utility#shellescape(gitgutter#utility#filename(bufnr))
|
||||
let [_, error_code] = gitgutter#utility#system(cmd)
|
||||
else
|
||||
return
|
||||
@@ -318,7 +319,7 @@ function! s:stage(hunk_diff)
|
||||
let diff = s:adjust_header(bufnr, a:hunk_diff)
|
||||
" Apply patch to index.
|
||||
let [_, error_code] = gitgutter#utility#system(
|
||||
\ gitgutter#utility#cd_cmd(bufnr, gitgutter#git().' apply --cached --unidiff-zero - '),
|
||||
\ gitgutter#git(bufnr).' apply --cached --unidiff-zero - ',
|
||||
\ diff)
|
||||
endif
|
||||
|
||||
@@ -358,6 +359,11 @@ endfunction
|
||||
|
||||
|
||||
function! s:preview(hunk_diff)
|
||||
if g:gitgutter_preview_win_floating && exists('*nvim_set_current_win') && s:winid != 0
|
||||
call nvim_set_current_win(s:winid)
|
||||
return
|
||||
endif
|
||||
|
||||
let lines = split(a:hunk_diff, '\r\?\n')
|
||||
let header = lines[0:4]
|
||||
let body = lines[5:]
|
||||
|
||||
@@ -6,6 +6,15 @@ function! gitgutter#utility#supports_overscore_sign()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" True for git v1.7.2+.
|
||||
function! gitgutter#utility#git_supports_command_line_config_override() abort
|
||||
if !exists('s:c_flag')
|
||||
let [_, error_code] = gitgutter#utility#system(gitgutter#git().' -c foo.bar=baz --version')
|
||||
let s:c_flag = !error_code
|
||||
endif
|
||||
return s:c_flag
|
||||
endfunction
|
||||
|
||||
function! gitgutter#utility#setbufvar(buffer, varname, val)
|
||||
let buffer = +a:buffer
|
||||
" Default value for getbufvar() was introduced in Vim 7.3.831.
|
||||
@@ -57,7 +66,7 @@ function! gitgutter#utility#is_active(bufnr) abort
|
||||
endfunction
|
||||
|
||||
function! s:not_git_dir(bufnr) abort
|
||||
return s:dir(a:bufnr) !~ '[/\\]\.git\($\|[/\\]\)'
|
||||
return gitgutter#utility#dir(a:bufnr) !~ '[/\\]\.git\($\|[/\\]\)'
|
||||
endfunction
|
||||
|
||||
function! s:is_file_buffer(bufnr) abort
|
||||
@@ -153,9 +162,8 @@ function! gitgutter#utility#set_repo_path(bufnr, continuation) abort
|
||||
" * -3 - assume unchanged
|
||||
|
||||
call gitgutter#utility#setbufvar(a:bufnr, 'path', -1)
|
||||
let cmd = gitgutter#utility#cd_cmd(a:bufnr,
|
||||
\ gitgutter#git().' ls-files -v --error-unmatch --full-name -z -- '.
|
||||
\ gitgutter#utility#shellescape(gitgutter#utility#filename(a:bufnr)))
|
||||
let cmd = gitgutter#git(a:bufnr).' ls-files -v --error-unmatch --full-name -z -- '.
|
||||
\ gitgutter#utility#shellescape(gitgutter#utility#filename(a:bufnr))
|
||||
|
||||
if g:gitgutter_async && gitgutter#async#available() && !has('vim_starting')
|
||||
let handler = copy(s:set_path_handler)
|
||||
@@ -184,9 +192,8 @@ endfunction
|
||||
function! gitgutter#utility#clean_smudge_filter_applies(bufnr)
|
||||
let filtered = gitgutter#utility#getbufvar(a:bufnr, 'filter', -1)
|
||||
if filtered == -1
|
||||
let cmd = gitgutter#utility#cd_cmd(a:bufnr,
|
||||
\ gitgutter#git().' check-attr filter -- '.
|
||||
\ gitgutter#utility#shellescape(gitgutter#utility#filename(a:bufnr)))
|
||||
let cmd = gitgutter#git(a:bufnr).' check-attr filter -- '.
|
||||
\ gitgutter#utility#shellescape(gitgutter#utility#filename(a:bufnr))
|
||||
let [out, _] = gitgutter#utility#system(cmd)
|
||||
let filtered = out !~ 'unspecified'
|
||||
call gitgutter#utility#setbufvar(a:bufnr, 'filter', filtered)
|
||||
@@ -195,19 +202,6 @@ function! gitgutter#utility#clean_smudge_filter_applies(bufnr)
|
||||
endfunction
|
||||
|
||||
|
||||
function! gitgutter#utility#cd_cmd(bufnr, cmd) abort
|
||||
let cd = s:unc_path(a:bufnr) ? 'pushd' : (gitgutter#utility#windows() && s:dos_shell() ? 'cd /d' : 'cd')
|
||||
return cd.' '.s:dir(a:bufnr).' && '.a:cmd
|
||||
endfunction
|
||||
|
||||
function! s:unc_path(bufnr)
|
||||
return s:abs_path(a:bufnr, 0) =~ '^\\\\'
|
||||
endfunction
|
||||
|
||||
function! s:dos_shell()
|
||||
return &shell == 'cmd.exe' || &shell == 'command.com'
|
||||
endfunction
|
||||
|
||||
function! s:use_known_shell() abort
|
||||
if has('unix') && &shell !=# 'sh'
|
||||
let [s:shell, s:shellcmdflag, s:shellredir, s:shellpipe, s:shellquote, s:shellxquote] = [&shell, &shellcmdflag, &shellredir, &shellpipe, &shellquote, &shellxquote]
|
||||
@@ -243,7 +237,14 @@ function! gitgutter#utility#base_path(bufnr)
|
||||
" If we already know the original path at this diff base, return it.
|
||||
let basepath = gitgutter#utility#getbufvar(a:bufnr, 'basepath', '')
|
||||
if !empty(basepath)
|
||||
let [base, bpath] = split(basepath, ':', 1)
|
||||
" basepath is diffbase:path
|
||||
" Note that path can also contain colons.
|
||||
" List destructuring / unpacking where the remaining items are assigned
|
||||
" to a single variable (:help let-unpack) is only available in v8.2.0540.
|
||||
let parts = split(basepath, ':', 1)
|
||||
let base = parts[0]
|
||||
let bpath = join(parts[1:], ':')
|
||||
|
||||
if base == diffbase
|
||||
return gitgutter#utility#shellescape(bpath)
|
||||
endif
|
||||
@@ -287,15 +288,24 @@ endfunction
|
||||
" Returns a dict of current path to original path at the given base.
|
||||
function! s:obtain_file_renames(bufnr, base)
|
||||
let renames = {}
|
||||
let cmd = gitgutter#git().' diff --diff-filter=R --name-status '.a:base
|
||||
let [out, error_code] = gitgutter#utility#system(gitgutter#utility#cd_cmd(a:bufnr, cmd))
|
||||
let cmd = gitgutter#git(a:bufnr)
|
||||
if gitgutter#utility#git_supports_command_line_config_override()
|
||||
let cmd .= ' -c "core.safecrlf=false"'
|
||||
endif
|
||||
let cmd .= ' diff --diff-filter=R --name-status '.a:base
|
||||
let [out, error_code] = gitgutter#utility#system(cmd)
|
||||
if error_code
|
||||
" Assume the problem is the diff base.
|
||||
call gitgutter#utility#warn('g:gitgutter_diff_base ('.a:base.') is invalid')
|
||||
return {}
|
||||
endif
|
||||
for line in split(out, '\n')
|
||||
let [original, current] = split(line)[1:]
|
||||
let fields = split(line)
|
||||
if len(fields) != 3
|
||||
call gitgutter#utility#warn('gitgutter: unable to list renamed files: '.line)
|
||||
return {}
|
||||
endif
|
||||
let [original, current] = fields[1:]
|
||||
let renames[current] = original
|
||||
endfor
|
||||
return renames
|
||||
@@ -310,7 +320,8 @@ function! s:abs_path(bufnr, shellesc)
|
||||
return a:shellesc ? gitgutter#utility#shellescape(p) : p
|
||||
endfunction
|
||||
|
||||
function! s:dir(bufnr) abort
|
||||
" Shellescaped
|
||||
function! gitgutter#utility#dir(bufnr) abort
|
||||
return gitgutter#utility#shellescape(fnamemodify(s:abs_path(a:bufnr, 0), ':h'))
|
||||
endfunction
|
||||
|
||||
|
||||
@@ -201,11 +201,13 @@ Commands for operating on a hunk:~
|
||||
:GitGutterUndoHunk Undo the hunk the cursor is in.
|
||||
|
||||
*gitgutter-:GitGutterPreviewHunk*
|
||||
:GitGutterPreviewHunk Preview the hunk the cursor is in.
|
||||
:GitGutterPreviewHunk Preview the hunk the cursor is in or, if you are using
|
||||
floating preview windows in Neovim and the window is
|
||||
already open, move the cursor into the window.
|
||||
|
||||
To stage part of the hunk, move to the preview window,
|
||||
delete any lines you do not want to stage, and
|
||||
|GitGutterStageHunk|.
|
||||
delete any lines you do not want to stage, and |write|
|
||||
or |GitGutterStageHunk|.
|
||||
|
||||
To close a non-floating preview window use |:pclose|
|
||||
or |CTRL-W_z| or |CTRL-W_CTRL-Z|; or normal window-
|
||||
|
||||
@@ -341,8 +341,18 @@ augroup gitgutter
|
||||
autocmd BufFilePre * call s:on_buffilepre(expand('<abuf>'))
|
||||
autocmd BufFilePost * call s:on_buffilepost(expand('<abuf>'))
|
||||
|
||||
autocmd QuickFixCmdPre *vimgrep* let b:gitgutter_was_enabled = gitgutter#utility#getbufvar(expand('<abuf>'), 'enabled') | GitGutterBufferDisable
|
||||
autocmd QuickFixCmdPost *vimgrep* if b:gitgutter_was_enabled | GitGutterBufferEnable | endif | unlet b:gitgutter_was_enabled
|
||||
autocmd QuickFixCmdPre *vimgrep*
|
||||
\ if gitgutter#utility#getbufvar(expand('<abuf>'), 'enabled') |
|
||||
\ let s:gitgutter_was_enabled = expand('<abuf>') |
|
||||
\ else |
|
||||
\ let s:gitgutter_was_enabled = 0 |
|
||||
\ endif |
|
||||
\ GitGutterBufferDisable
|
||||
autocmd QuickFixCmdPost *vimgrep*
|
||||
\ if s:gitgutter_was_enabled |
|
||||
\ call gitgutter#buffer_enable(s:gitgutter_was_enabled) |
|
||||
\ endif |
|
||||
\ unlet s:gitgutter_was_enabled
|
||||
augroup END
|
||||
|
||||
" }}}
|
||||
|
||||
@@ -52,6 +52,7 @@ endfunction
|
||||
"
|
||||
|
||||
function SetUp()
|
||||
let g:gitgutter_diff_base = ''
|
||||
call system("git init ".s:test_repo.
|
||||
\ " && cd ".s:test_repo.
|
||||
\ " && cp ../.gitconfig .".
|
||||
@@ -195,6 +196,20 @@ function Test_filename_with_equals()
|
||||
endfunction
|
||||
|
||||
|
||||
function Test_filename_with_colon()
|
||||
call system('touch fix:ture.txt && git add fix:ture.txt')
|
||||
edit fix:ture.txt
|
||||
normal ggo*
|
||||
call s:trigger_gitgutter()
|
||||
|
||||
let expected = [
|
||||
\ {'lnum': 1, 'name': 'GitGutterLineAdded'},
|
||||
\ {'lnum': 2, 'name': 'GitGutterLineAdded'}
|
||||
\ ]
|
||||
call s:assert_signs(expected, 'fix:ture.txt')
|
||||
endfunction
|
||||
|
||||
|
||||
function Test_filename_with_square_brackets()
|
||||
call system('touch fix[tu]re.txt && git add fix[tu]re.txt')
|
||||
edit fix[tu]re.txt
|
||||
@@ -280,6 +295,29 @@ function Test_saveas()
|
||||
endfunction
|
||||
|
||||
|
||||
function Test_file_mv()
|
||||
call system('git mv fixture.txt fixture_moved.txt')
|
||||
edit fixture_moved.txt
|
||||
normal ggo*
|
||||
call s:trigger_gitgutter()
|
||||
let expected = [{'lnum': 2, 'name': 'GitGutterLineAdded'}]
|
||||
call s:assert_signs(expected, 'fixture_moved.txt')
|
||||
|
||||
write
|
||||
call system('git add fixture_moved.txt && git commit -m "moved and edited"')
|
||||
GitGutterDisable
|
||||
GitGutterEnable
|
||||
let expected = []
|
||||
call s:assert_signs(expected, 'fixture_moved.txt')
|
||||
|
||||
GitGutterDisable
|
||||
let g:gitgutter_diff_base = 'HEAD^'
|
||||
GitGutterEnable
|
||||
let expected = [{'lnum': 2, 'name': 'GitGutterLineAdded'}]
|
||||
call s:assert_signs(expected, 'fixture_moved.txt')
|
||||
endfunction
|
||||
|
||||
|
||||
" FIXME: this test fails when it is the first (or only) test to be run
|
||||
function Test_follow_symlink()
|
||||
let tmp = 'symlink'
|
||||
|
||||
Reference in New Issue
Block a user