mirror of
https://github.com/airblade/vim-gitgutter.git
synced 2025-11-09 03:53:47 -05:00
Stage and revert individual hunks.
This commit is contained in:
14
README.mkd
14
README.mkd
@@ -1,6 +1,6 @@
|
|||||||
## Vim Git Gutter
|
## Vim Git Gutter
|
||||||
|
|
||||||
A Vim plugin which shows a git diff in the 'gutter' (sign column). It shows whether each line has been added, modified, and where lines have been removed.
|
A Vim plugin which shows a git diff in the 'gutter' (sign column). It shows whether each line has been added, modified, and where lines have been removed. You can also stage and revert individual hunks.
|
||||||
|
|
||||||
|
|
||||||
### Screenshot
|
### Screenshot
|
||||||
@@ -65,6 +65,18 @@ nmap gh <Plug>GitGutterNextHunk
|
|||||||
nmap gH <Plug>GitGutterPrevHunk
|
nmap gH <Plug>GitGutterPrevHunk
|
||||||
```
|
```
|
||||||
|
|
||||||
|
When your cursor is in a hunk, you can:
|
||||||
|
|
||||||
|
* stage the hunk with `<Leader>hs` or
|
||||||
|
* revert it with `<Leader>hr`.
|
||||||
|
|
||||||
|
To set your own mappings for these, for example if you prefer the mnemonics hunk-add and hunk-undo:
|
||||||
|
|
||||||
|
```viml
|
||||||
|
nmap <Leader>ha <Plug>GitGutterStageHunk
|
||||||
|
nmap <Leader>hu <Plug>GitGutterRevertHunk
|
||||||
|
```
|
||||||
|
|
||||||
Finally, you can force vim-gitgutter to update its signs across all visible buffers with `:GitGutterAll`.
|
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.
|
See the customisation section below for how to change the defaults.
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
let s:grep_available = executable('grep')
|
let s:grep_available = executable('grep')
|
||||||
let s:grep_command = ' | ' . (g:gitgutter_escape_grep ? '\grep' : 'grep') . ' -e "^@@ "'
|
let s:grep_command = ' | ' . (g:gitgutter_escape_grep ? '\grep' : 'grep') . ' -e "^@@ "'
|
||||||
|
let s:hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@'
|
||||||
|
|
||||||
|
|
||||||
function! diff#run_diff(realtime)
|
function! diff#run_diff(realtime, use_external_grep)
|
||||||
if a:realtime
|
if a:realtime
|
||||||
let blob_name = ':./' . fnamemodify(utility#file(),':t')
|
let blob_name = ':./' . fnamemodify(utility#file(),':t')
|
||||||
let blob_file = tempname()
|
let blob_file = tempname()
|
||||||
@@ -10,7 +11,7 @@ function! diff#run_diff(realtime)
|
|||||||
else
|
else
|
||||||
let cmd = 'git diff --no-ext-diff --no-color -U0 ' . g:gitgutter_diff_args . ' ' . shellescape(utility#file())
|
let cmd = 'git diff --no-ext-diff --no-color -U0 ' . g:gitgutter_diff_args . ' ' . shellescape(utility#file())
|
||||||
endif
|
endif
|
||||||
if s:grep_available
|
if a:use_external_grep && s:grep_available
|
||||||
let cmd .= s:grep_command
|
let cmd .= s:grep_command
|
||||||
endif
|
endif
|
||||||
let cmd = utility#escape(cmd)
|
let cmd = utility#escape(cmd)
|
||||||
@@ -31,21 +32,29 @@ function! diff#run_diff(realtime)
|
|||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! diff#parse_diff(diff)
|
function! diff#parse_diff(diff)
|
||||||
let hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@'
|
|
||||||
let hunks = []
|
let hunks = []
|
||||||
for line in split(a:diff, '\n')
|
for line in split(a:diff, '\n')
|
||||||
let matches = matchlist(line, hunk_re)
|
let hunk_info = diff#parse_hunk(line)
|
||||||
if len(matches) > 0
|
if len(hunk_info) == 4
|
||||||
let from_line = str2nr(matches[1])
|
call add(hunks, hunk_info)
|
||||||
let from_count = (matches[2] == '') ? 1 : str2nr(matches[2])
|
|
||||||
let to_line = str2nr(matches[3])
|
|
||||||
let to_count = (matches[4] == '') ? 1 : str2nr(matches[4])
|
|
||||||
call add(hunks, [from_line, from_count, to_line, to_count])
|
|
||||||
endif
|
endif
|
||||||
endfor
|
endfor
|
||||||
return hunks
|
return hunks
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! diff#parse_hunk(line)
|
||||||
|
let matches = matchlist(a:line, s:hunk_re)
|
||||||
|
if len(matches) > 0
|
||||||
|
let from_line = str2nr(matches[1])
|
||||||
|
let from_count = (matches[2] == '') ? 1 : str2nr(matches[2])
|
||||||
|
let to_line = str2nr(matches[3])
|
||||||
|
let to_count = (matches[4] == '') ? 1 : str2nr(matches[4])
|
||||||
|
return [from_line, from_count, to_line, to_count]
|
||||||
|
else
|
||||||
|
return []
|
||||||
|
end
|
||||||
|
endfunction
|
||||||
|
|
||||||
function! diff#process_hunks(hunks)
|
function! diff#process_hunks(hunks)
|
||||||
call hunk#reset()
|
call hunk#reset()
|
||||||
let modified_lines = []
|
let modified_lines = []
|
||||||
@@ -153,3 +162,23 @@ function! diff#process_modified_and_removed(modifications, from_count, to_count,
|
|||||||
endwhile
|
endwhile
|
||||||
let a:modifications[-1] = [a:to_line + offset - 1, 'modified_removed']
|
let a:modifications[-1] = [a:to_line + offset - 1, 'modified_removed']
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! diff#generate_diff_for_hunk(hunk)
|
||||||
|
return diff#discard_hunks(diff#run_diff(0, 0), a:hunk)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! diff#discard_hunks(diff, hunk_to_keep)
|
||||||
|
let modified_diff = []
|
||||||
|
let keep_line = 1 " start by keeping header
|
||||||
|
for line in split(a:diff, '\n')
|
||||||
|
let hunk_info = diff#parse_hunk(line)
|
||||||
|
if len(hunk_info) == 4 " start of new hunk
|
||||||
|
let keep_line = (hunk_info == a:hunk_to_keep)
|
||||||
|
endif
|
||||||
|
if keep_line
|
||||||
|
call add(modified_diff, line)
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
" call append('$', modified_diff)
|
||||||
|
return join(modified_diff, "\n") . "\n"
|
||||||
|
endfunction
|
||||||
|
|||||||
@@ -86,6 +86,14 @@ Commands for jumping between marked hunks:
|
|||||||
:GitGutterPrevHunk *:GitGutterPrevHunk*
|
:GitGutterPrevHunk *:GitGutterPrevHunk*
|
||||||
Jump to the previous marked hunk. Takes a count.
|
Jump to the previous marked hunk. Takes a count.
|
||||||
|
|
||||||
|
Commands for staging or reverting individual hunks:
|
||||||
|
|
||||||
|
:GitGutterStageHunk *:GitGutterStageHunk*
|
||||||
|
Stage the hunk the cursor is in.
|
||||||
|
|
||||||
|
:GitGutterRevertHunk *:GitGutterRevertHunk*
|
||||||
|
Revert the hunk the cursor is in.
|
||||||
|
|
||||||
===============================================================================
|
===============================================================================
|
||||||
5. CUSTOMISATION *GitGutterCustomisation*
|
5. CUSTOMISATION *GitGutterCustomisation*
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ function! GitGutter(file, realtime)
|
|||||||
call utility#set_file(a:file)
|
call utility#set_file(a:file)
|
||||||
if utility#is_active()
|
if utility#is_active()
|
||||||
if !a:realtime || utility#has_fresh_changes(a:file)
|
if !a:realtime || utility#has_fresh_changes(a:file)
|
||||||
let diff = diff#run_diff(a:realtime || utility#has_unsaved_changes(a:file))
|
let diff = diff#run_diff(a:realtime || utility#has_unsaved_changes(a:file), 1)
|
||||||
let s:hunks = diff#parse_diff(diff)
|
let s:hunks = diff#parse_diff(diff)
|
||||||
let modified_lines = diff#process_hunks(s:hunks)
|
let modified_lines = diff#process_hunks(s:hunks)
|
||||||
if g:gitgutter_sign_column_always
|
if g:gitgutter_sign_column_always
|
||||||
@@ -154,6 +154,68 @@ function! GitGutterPrevHunk(count)
|
|||||||
endfunction
|
endfunction
|
||||||
command -count=1 GitGutterPrevHunk call GitGutterPrevHunk(<count>)
|
command -count=1 GitGutterPrevHunk call GitGutterPrevHunk(<count>)
|
||||||
|
|
||||||
|
function! GitGutterStageHunk()
|
||||||
|
if utility#is_active()
|
||||||
|
" Ensure the working copy of the file is up to date.
|
||||||
|
" It doesn't make sense to stage a hunk otherwise.
|
||||||
|
silent write
|
||||||
|
|
||||||
|
" find current hunk (i.e. which one the cursor is in)
|
||||||
|
let current_hunk = []
|
||||||
|
let current_line = line('.')
|
||||||
|
for hunk in s:hunks
|
||||||
|
if current_line >= hunk[2] && current_line < hunk[2] + (hunk[3] == 0 ? 1 : hunk[3])
|
||||||
|
let current_hunk = hunk
|
||||||
|
break
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
if len(current_hunk) != 4
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" construct a diff
|
||||||
|
let diff_for_hunk = diff#generate_diff_for_hunk(current_hunk)
|
||||||
|
|
||||||
|
" apply the diff
|
||||||
|
call system(utility#command_in_directory_of_file('git apply --cached --unidiff-zero - '), diff_for_hunk)
|
||||||
|
|
||||||
|
" refresh gitgutter's view of buffer
|
||||||
|
silent execute "GitGutter"
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
command GitGutterStageHunk call GitGutterStageHunk()
|
||||||
|
|
||||||
|
function! GitGutterRevertHunk()
|
||||||
|
if utility#is_active()
|
||||||
|
" Ensure the working copy of the file is up to date.
|
||||||
|
" It doesn't make sense to stage a hunk otherwise.
|
||||||
|
write
|
||||||
|
|
||||||
|
" find current hunk (i.e. which one the cursor is in)
|
||||||
|
let current_hunk = []
|
||||||
|
let current_line = line('.')
|
||||||
|
for hunk in s:hunks
|
||||||
|
if current_line >= hunk[2] && current_line < hunk[2] + (hunk[3] == 0 ? 1 : hunk[3])
|
||||||
|
let current_hunk = hunk
|
||||||
|
break
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
if len(current_hunk) != 4
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" construct a diff
|
||||||
|
let diff_for_hunk = diff#generate_diff_for_hunk(current_hunk)
|
||||||
|
|
||||||
|
" apply the diff
|
||||||
|
call system(utility#command_in_directory_of_file('git apply --reverse --unidiff-zero - '), diff_for_hunk)
|
||||||
|
|
||||||
|
" reload file
|
||||||
|
silent edit
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
command GitGutterRevertHunk call GitGutterRevertHunk()
|
||||||
|
|
||||||
" Returns the git-diff hunks for the file or an empty list if there
|
" Returns the git-diff hunks for the file or an empty list if there
|
||||||
" aren't any hunks.
|
" aren't any hunks.
|
||||||
"
|
"
|
||||||
@@ -182,6 +244,7 @@ function! GitGutterGetHunkSummary()
|
|||||||
return hunk#summary()
|
return hunk#summary()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
nnoremap <silent> <Plug>GitGutterNextHunk :<C-U>execute v:count1 . "GitGutterNextHunk"<CR>
|
nnoremap <silent> <Plug>GitGutterNextHunk :<C-U>execute v:count1 . "GitGutterNextHunk"<CR>
|
||||||
nnoremap <silent> <Plug>GitGutterPrevHunk :<C-U>execute v:count1 . "GitGutterPrevHunk"<CR>
|
nnoremap <silent> <Plug>GitGutterPrevHunk :<C-U>execute v:count1 . "GitGutterPrevHunk"<CR>
|
||||||
|
|
||||||
@@ -190,6 +253,18 @@ if !hasmapto('<Plug>GitGutterNextHunk') && maparg(']h', 'n') ==# ''
|
|||||||
nmap [h <Plug>GitGutterPrevHunk
|
nmap [h <Plug>GitGutterPrevHunk
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
nnoremap <silent> <Plug>GitGutterStageHunk :GitGutterStageHunk<CR>
|
||||||
|
nnoremap <silent> <Plug>GitGutterRevertHunk :GitGutterRevertHunk<CR>
|
||||||
|
|
||||||
|
if !hasmapto('<Plug>GitGutterStageHunk') && maparg('<Leader>ha', 'n') ==# ''
|
||||||
|
nmap <Leader>ha <Plug>GitGutterStageHunk
|
||||||
|
endif
|
||||||
|
if !hasmapto('<Plug>GitGutterRevertHunk') && maparg('<Leader>hr', 'n') ==# ''
|
||||||
|
nmap <Leader>hr <Plug>GitGutterRevertHunk
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
augroup gitgutter
|
augroup gitgutter
|
||||||
autocmd!
|
autocmd!
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user