diff --git a/README.mkd b/README.mkd index cefb9bc..1668c49 100644 --- a/README.mkd +++ b/README.mkd @@ -187,6 +187,11 @@ You can stage or undo an individual hunk when your cursor is in it: * stage the hunk with `hs` or * undo it with `hu`. +You can stage part of an additions-only hunk by: + +* either visually selecting the part you want and staging with your mapping, e.g. `hs`; +* or using a range with the `GitGutterStageHunk` command, e.g. `:42,45GitGutterStageHunk`. + 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). diff --git a/autoload/gitgutter.vim b/autoload/gitgutter.vim index 0f89007..b413834 100644 --- a/autoload/gitgutter.vim +++ b/autoload/gitgutter.vim @@ -126,6 +126,9 @@ function! gitgutter#setup_maps() nmap ]c GitGutterNextHunk endif + if !hasmapto('GitGutterStageHunk') && maparg('hs', 'x') ==# '' + xmap hs GitGutterStageHunk + endif if !hasmapto('GitGutterStageHunk') && maparg('hs', 'n') ==# '' nmap hs GitGutterStageHunk endif diff --git a/autoload/gitgutter/hunk.vim b/autoload/gitgutter/hunk.vim index 041e4c7..ee71c49 100644 --- a/autoload/gitgutter/hunk.vim +++ b/autoload/gitgutter/hunk.vim @@ -169,8 +169,12 @@ function! gitgutter#hunk#text_object(inner) abort endfunction -function! gitgutter#hunk#stage() abort - call s:hunk_op(function('s:stage')) +function! gitgutter#hunk#stage(...) abort + if a:0 && (a:1 != 1 || a:2 != line('$')) + call s:hunk_op(function('s:stage'), a:1, a:2) + else + call s:hunk_op(function('s:stage')) + endif silent! call repeat#set("\GitGutterStageHunk", -1) endfunction @@ -185,7 +189,7 @@ function! gitgutter#hunk#preview() abort endfunction -function! s:hunk_op(op) +function! s:hunk_op(op, ...) let bufnr = bufnr('') if gitgutter#utility#is_active(bufnr) @@ -210,7 +214,14 @@ function! s:hunk_op(op) call gitgutter#utility#warn('did not recognise your choice') endif else - call a:op(gitgutter#diff#hunk_diff(bufnr, diff)) + let hunk_diff = gitgutter#diff#hunk_diff(bufnr, diff) + + if a:0 + let hunk_first_line = s:current_hunk()[2] + let hunk_diff = s:part_of_diff(hunk_diff, a:1-hunk_first_line, a:2-hunk_first_line) + endif + + call a:op(hunk_diff) endif endif endfunction @@ -271,6 +282,18 @@ function! s:preview(hunk_diff) endfunction +" Returns a new hunk diff using the specified lines from the given one. +" a:first, a:last - 0-based indexes into the body of the hunk. +function! s:part_of_diff(hunk_diff, first, last) + let diff_lines = split(a:hunk_diff, '\n', 1) + + " adjust line count in header + let diff_lines[4] = substitute(diff_lines[4], '\(+\d\+\)\(,\d\+\)\?', '\=submatch(1).",".(a:last-a:first+1)', '') + + return join(diff_lines[0:4] + diff_lines[5+a:first:5+a:last], "\n")."\n" +endfunction + + function! s:adjust_header(bufnr, hunk_diff) let filepath = gitgutter#utility#repo_path(a:bufnr, 0) return s:adjust_hunk_summary(s:fix_file_references(filepath, a:hunk_diff)) diff --git a/doc/gitgutter.txt b/doc/gitgutter.txt index b5ed29a..034104e 100644 --- a/doc/gitgutter.txt +++ b/doc/gitgutter.txt @@ -164,7 +164,9 @@ Commands for jumping between hunks:~ Commands for operating on a hunk:~ *gitgutter-:GitGutterStageHunk* -:GitGutterStageHunk Stage the hunk the cursor is in. +:GitGutterStageHunk Stage the hunk the cursor is in. Use a visual selection + to stage part of an (additions-only) hunk; or use a + range. *gitgutter-:GitGutterUndoHunk* :GitGutterUndoHunk Undo the hunk the cursor is in. diff --git a/plugin/gitgutter.vim b/plugin/gitgutter.vim index 591c4db..15305eb 100644 --- a/plugin/gitgutter.vim +++ b/plugin/gitgutter.vim @@ -136,7 +136,7 @@ command! -bar GitGutterSignsToggle call gitgutter#sign#toggle() command! -bar -count=1 GitGutterNextHunk call gitgutter#hunk#next_hunk() command! -bar -count=1 GitGutterPrevHunk call gitgutter#hunk#prev_hunk() -command! -bar GitGutterStageHunk call gitgutter#hunk#stage() +command! -bar -range=% GitGutterStageHunk call gitgutter#hunk#stage(,) command! -bar GitGutterUndoHunk call gitgutter#hunk#undo() command! -bar GitGutterPreviewHunk call gitgutter#hunk#preview() @@ -191,6 +191,7 @@ command! -bar GitGutterDebug call gitgutter#debug#debug() nnoremap GitGutterNextHunk &diff ? ']c' : ":\execute v:count1 . 'GitGutterNextHunk'\" nnoremap GitGutterPrevHunk &diff ? '[c' : ":\execute v:count1 . 'GitGutterPrevHunk'\" +xnoremap GitGutterStageHunk :GitGutterStageHunk nnoremap GitGutterStageHunk :GitGutterStageHunk nnoremap GitGutterUndoHunk :GitGutterUndoHunk nnoremap GitGutterPreviewHunk :GitGutterPreviewHunk diff --git a/test/test_gitgutter.vim b/test/test_gitgutter.vim index 6aea6f1..ca1ddcc 100644 --- a/test/test_gitgutter.vim +++ b/test/test_gitgutter.vim @@ -417,6 +417,75 @@ function Test_hunk_stage_nearby_hunk() endfunction +function Test_hunk_stage_partial_visual_added() + call append(5, ['A','B','C','D']) + execute "normal 7GVj:GitGutterStageHunk\" + + let expected = [ + \ 'line=6 id=3000 name=GitGutterLineAdded priority=10', + \ 'line=9 id=3001 name=GitGutterLineAdded priority=10', + \ ] + call assert_equal(expected, s:signs('fixture.txt')) + + let expected = [ + \ 'diff --git a/fixture.txt b/fixture.txt', + \ 'index 8a7026e..f5c6aff 100644', + \ '--- a/fixture.txt', + \ '+++ b/fixture.txt', + \ '@@ -6,2 +5,0 @@ e', + \ '-B', + \ '-C', + \ ] + call assert_equal(expected, s:git_diff()) + + let expected = [ + \ 'diff --git a/fixture.txt b/fixture.txt', + \ 'index f5c6aff..8a7026e 100644', + \ '--- a/fixture.txt', + \ '+++ b/fixture.txt', + \ '@@ -5,0 +6,2 @@ e', + \ '+B', + \ '+C', + \ ] + call assert_equal(expected, s:git_diff_staged()) +endfunction + + +function Test_hunk_stage_partial_cmd_added() + call append(5, ['A','B','C','D']) + normal 6G + 7,8GitGutterStageHunk + + let expected = [ + \ 'line=6 id=3000 name=GitGutterLineAdded priority=10', + \ 'line=9 id=3001 name=GitGutterLineAdded priority=10', + \ ] + call assert_equal(expected, s:signs('fixture.txt')) + + let expected = [ + \ 'diff --git a/fixture.txt b/fixture.txt', + \ 'index 8a7026e..f5c6aff 100644', + \ '--- a/fixture.txt', + \ '+++ b/fixture.txt', + \ '@@ -6,2 +5,0 @@ e', + \ '-B', + \ '-C', + \ ] + call assert_equal(expected, s:git_diff()) + + let expected = [ + \ 'diff --git a/fixture.txt b/fixture.txt', + \ 'index f5c6aff..8a7026e 100644', + \ '--- a/fixture.txt', + \ '+++ b/fixture.txt', + \ '@@ -5,0 +6,2 @@ e', + \ '+B', + \ '+C', + \ ] + call assert_equal(expected, s:git_diff_staged()) +endfunction + + function Test_hunk_undo() let _shell = &shell set shell=foo