Stage part of any hunk.

See #279.
This commit is contained in:
Andy Stewart
2019-08-13 15:57:22 +01:00
parent a5ee9f2f11
commit b036fcf527
4 changed files with 147 additions and 7 deletions

View File

@@ -12,6 +12,7 @@ Features:
* Never saves the buffer. * Never saves the buffer.
* Quick jumping between blocks of changed lines ("hunks"). * Quick jumping between blocks of changed lines ("hunks").
* Stage/undo/preview individual hunks. * Stage/undo/preview individual hunks.
* Stage partial hunks.
* Provides a hunk text object. * Provides a hunk text object.
* Diffs against index (default) or any commit. * Diffs against index (default) or any commit.
* Allows folding all unchanged text. * Allows folding all unchanged text.
@@ -187,11 +188,18 @@ You can stage or undo an individual hunk when your cursor is in it:
* stage the hunk with `<Leader>hs` or * stage the hunk with `<Leader>hs` or
* undo it with `<Leader>hu`. * undo it with `<Leader>hu`.
You can stage part of an additions-only hunk by: To stage part of an additions-only hunk by:
* either visually selecting the part you want and staging with your mapping, e.g. `<Leader>hs`; * either visually selecting the part you want and staging with your mapping, e.g. `<Leader>hs`;
* or using a range with the `GitGutterStageHunk` command, e.g. `:42,45GitGutterStageHunk`. * or using a range with the `GitGutterStageHunk` command, e.g. `:42,45GitGutterStageHunk`.
To stage part of any hunk:
* preview the hunk, e.g. `<Leader>hp`;
* move to the preview window, e.g. `:wincmd P`;
* delete the lines you do not want to stage;
* stage the remaining lines, e.g. `<Leader>hs` or `:GitGutterStageHunk`.
See the FAQ if you want to unstage staged changes. 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). The `.` command will work with both these if you install [repeat.vim](https://github.com/tpope/vim-repeat).

View File

@@ -192,6 +192,36 @@ endfunction
function! s:hunk_op(op, ...) function! s:hunk_op(op, ...)
let bufnr = bufnr('') let bufnr = bufnr('')
if &previewwindow
if string(a:op) =~ '_stage'
" combine hunk-body in preview window with updated hunk-header
let hunk_body = getline(1, '$')
let [removed, added] = [0, 0]
for line in hunk_body
if line[0] == '-'
let removed += 1
elseif line[0] == '+'
let added += 1
endif
endfor
let hunk_header = b:hunk_header
" from count
let hunk_header[4] = substitute(hunk_header[4], '\(-\d\+\)\(,\d\+\)\?', '\=submatch(1).",".removed', '')
" to count
let hunk_header[4] = substitute(hunk_header[4], '\(+\d\+\)\(,\d\+\)\?', '\=submatch(1).",".added', '')
let hunk_diff = join(hunk_header + hunk_body, "\n")."\n"
wincmd p
pclose
call s:stage(hunk_diff)
endif
return
endif
if gitgutter#utility#is_active(bufnr) if gitgutter#utility#is_active(bufnr)
" Get a (synchronous) diff. " Get a (synchronous) diff.
let [async, g:gitgutter_async] = [g:gitgutter_async, 0] let [async, g:gitgutter_async] = [g:gitgutter_async, 0]
@@ -278,11 +308,12 @@ function! s:preview(hunk_diff)
execute 'resize' previewheight execute 'resize' previewheight
endif endif
let b:hunk_header = header
setlocal noreadonly modifiable filetype=diff buftype=nofile bufhidden=delete noswapfile setlocal noreadonly modifiable filetype=diff buftype=nofile bufhidden=delete noswapfile
execute "%delete_" execute "%delete_"
call setline(1, body) call setline(1, body)
normal! gg normal! gg
setlocal readonly nomodifiable
noautocmd wincmd p noautocmd wincmd p
endfunction endfunction
@@ -340,11 +371,6 @@ function! s:adjust_hunk_summary(hunk_diff) abort
endfunction endfunction
function! s:discard_header(hunk_diff)
return join(split(a:hunk_diff, '\n', 1)[5:], "\n")
endfunction
" Returns the number of lines the current hunk is offset from where it would " Returns the number of lines the current hunk is offset from where it would
" be if any changes above it in the file didn't exist. " be if any changes above it in the file didn't exist.
function! s:line_adjustment_for_current_hunk() abort function! s:line_adjustment_for_current_hunk() abort

View File

@@ -168,6 +168,10 @@ Commands for operating on a hunk:~
to stage part of an (additions-only) hunk; or use a to stage part of an (additions-only) hunk; or use a
range. range.
To stage part of any hunk, first |GitGutterPreviewHunk|
it, then move to the preview window, delete the lines
you do not want to stage, and |GitGutterStageHunk|.
*gitgutter-:GitGutterUndoHunk* *gitgutter-:GitGutterUndoHunk*
:GitGutterUndoHunk Undo the hunk the cursor is in. :GitGutterUndoHunk Undo the hunk the cursor is in.
@@ -176,6 +180,10 @@ Commands for operating on a hunk:~
Use |:pclose| or |CTRL-W_CTRL-Z| to close the preview Use |:pclose| or |CTRL-W_CTRL-Z| to close the preview
window. window.
To stage part of the hunk, move to the preview window,
delete any lines you do not want to stage, and
|GitGutterStageHunk|.
Commands for folds:~ Commands for folds:~
*gitgutter-:GitGutterFold* *gitgutter-:GitGutterFold*

View File

@@ -486,6 +486,104 @@ function Test_hunk_stage_partial_cmd_added()
endfunction endfunction
function Test_hunk_stage_partial_preview_added()
call append(5, ['A','B','C','D'])
normal 6G
GitGutterPreviewHunk
wincmd P
" remove C and A so we stage B and D
3delete
1delete
GitGutterStageHunk
write
let expected = [
\ 'line=6 id=3000 name=GitGutterLineAdded priority=10',
\ 'line=8 id=3002 name=GitGutterLineAdded priority=10',
\ ]
call assert_equal(expected, s:signs('fixture.txt'))
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index 975852f..3dd23a3 100644',
\ '--- a/fixture.txt',
\ '+++ b/fixture.txt',
\ '@@ -5,0 +6 @@ e',
\ '+A',
\ '@@ -6,0 +8 @@ B',
\ '+C',
\ ]
call assert_equal(expected, s:git_diff())
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index f5c6aff..975852f 100644',
\ '--- a/fixture.txt',
\ '+++ b/fixture.txt',
\ '@@ -5,0 +6,2 @@ e',
\ '+B',
\ '+D',
\ ]
call assert_equal(expected, s:git_diff_staged())
endfunction
function Test_hunk_stage_partial_preview_added_removed()
4,5delete
call append(3, ['A','B','C','D'])
4
GitGutterPreviewHunk
wincmd P
" -d
" -e
" +A
" +B
" +C
" +D
" remove D and d so they do not get staged
6delete
1delete
GitGutterStageHunk
write
let expected = [
\ 'line=3 id=3004 name=GitGutterLineRemoved priority=10',
\ 'line=7 id=3003 name=GitGutterLineAdded priority=10',
\ ]
call assert_equal(expected, s:signs('fixture.txt'))
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index 9a19589..e63fb0a 100644',
\ '--- a/fixture.txt',
\ '+++ b/fixture.txt',
\ '@@ -4 +3,0 @@ c',
\ '-d',
\ '@@ -7,0 +7 @@ C',
\ '+D',
\ ]
call assert_equal(expected, s:git_diff())
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index f5c6aff..9a19589 100644',
\ '--- a/fixture.txt',
\ '+++ b/fixture.txt',
\ '@@ -5 +5,3 @@ d',
\ '-e',
\ '+A',
\ '+B',
\ '+C',
\ ]
call assert_equal(expected, s:git_diff_staged())
endfunction
function Test_hunk_undo() function Test_hunk_undo()
let _shell = &shell let _shell = &shell
set shell=foo set shell=foo