From e5eb9e6ecfb1a8b5403bd3b0e7d28c8b4e9b0a65 Mon Sep 17 00:00:00 2001 From: Andy Stewart Date: Thu, 16 Oct 2014 11:50:20 +0200 Subject: [PATCH] Fix staging of hunks coming after deleted lines. Previously vim-gitgutter generated context-free patches and applied those to the index. However when staging a hunk situated after any deleted lines, the line numbers on the patch were out by the number of lines deleted, and without any context git would apply the patch to the wrong part of the file in the index. This commit ensure patches are generated with 1 line of context, allowing git to adjust the line numbers appropriately and apply the patch to the right location. More lines of context would help git more to adjust line numbers; but the more context we have the more we group together hunks we would like to treat separately. --- autoload/gitgutter.vim | 30 ++++++------------------------ autoload/gitgutter/diff.vim | 20 +++++++++++--------- autoload/gitgutter/hunk.vim | 22 +++++++++++++++------- 3 files changed, 32 insertions(+), 40 deletions(-) diff --git a/autoload/gitgutter.vim b/autoload/gitgutter.vim index 1ea2888..b9f48f6 100644 --- a/autoload/gitgutter.vim +++ b/autoload/gitgutter.vim @@ -19,7 +19,7 @@ function! gitgutter#process_buffer(file, realtime) endif try if !a:realtime || gitgutter#utility#has_fresh_changes(a:file) - let diff = gitgutter#diff#run_diff(a:realtime || gitgutter#utility#has_unsaved_changes(a:file), 1) + let diff = gitgutter#diff#run_diff(a:realtime || gitgutter#utility#has_unsaved_changes(a:file), 1, 0) call gitgutter#hunk#set_hunks(gitgutter#diff#parse_diff(diff)) let modified_lines = gitgutter#diff#process_hunks(gitgutter#hunk#hunks()) @@ -156,17 +156,11 @@ function! gitgutter#stage_hunk() " It doesn't make sense to stage a hunk otherwise. silent write - " find current hunk - let current_hunk = gitgutter#hunk#current_hunk() - if empty(current_hunk) - return - endif - " construct a diff - let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(current_hunk, 1) + let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(1, 1) " apply the diff - call gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file('git apply --cached --unidiff-zero - '), diff_for_hunk) + call gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file('git apply --cached --recount --allow-overlap - '), diff_for_hunk) " refresh gitgutter's view of buffer silent execute "GitGutter" @@ -179,17 +173,11 @@ function! gitgutter#revert_hunk() " It doesn't make sense to stage a hunk otherwise. silent write - " find current hunk - let current_hunk = gitgutter#hunk#current_hunk() - if empty(current_hunk) - return - endif - " construct a diff - let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(current_hunk, 1) + let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(1, 1) " apply the diff - call gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file('git apply --reverse --unidiff-zero - '), diff_for_hunk) + call gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file('git apply --reverse - '), diff_for_hunk) " reload file silent edit @@ -200,14 +188,8 @@ function! gitgutter#preview_hunk() if gitgutter#utility#is_active() silent write - " find current hunk - let current_hunk = gitgutter#hunk#current_hunk() - if empty(current_hunk) - return - endif - " construct a diff - let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(current_hunk, 0) + let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(0, 0) " preview the diff silent! wincmd P diff --git a/autoload/gitgutter/diff.vim b/autoload/gitgutter/diff.vim index c7eb200..2fddf47 100644 --- a/autoload/gitgutter/diff.vim +++ b/autoload/gitgutter/diff.vim @@ -3,7 +3,7 @@ let s:grep_command = ' | ' . (g:gitgutter_escape_grep ? '\grep' : 'grep') . ' -e let s:hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@' -function! gitgutter#diff#run_diff(realtime, use_external_grep) +function! gitgutter#diff#run_diff(realtime, use_external_grep, lines_of_context) " Wrap compound command in parentheses to make Windows happy. let cmd = '(git ls-files --error-unmatch ' . gitgutter#utility#shellescape(gitgutter#utility#filename()) . ' && (' @@ -11,9 +11,9 @@ function! gitgutter#diff#run_diff(realtime, use_external_grep) let blob_name = ':' . gitgutter#utility#shellescape(gitgutter#utility#file_relative_to_repo_root()) let blob_file = tempname() let cmd .= 'git show ' . blob_name . ' > ' . blob_file . - \ ' && diff -U0 ' . g:gitgutter_diff_args . ' ' . blob_file . ' - ' + \ ' && diff -U'.a:lines_of_context.' ' . g:gitgutter_diff_args . ' ' . blob_file . ' - ' else - let cmd .= 'git diff --no-ext-diff --no-color -U0 ' . g:gitgutter_diff_args . ' ' . gitgutter#utility#shellescape(gitgutter#utility#filename()) + let cmd .= 'git diff --no-ext-diff --no-color -U'.a:lines_of_context.' ' . g:gitgutter_diff_args . ' ' . gitgutter#utility#shellescape(gitgutter#utility#filename()) endif if a:use_external_grep && s:grep_available @@ -182,22 +182,24 @@ function! gitgutter#diff#process_modified_and_removed(modifications, from_count, let a:modifications[-1] = [a:to_line + offset - 1, 'modified_removed'] endfunction -function! gitgutter#diff#generate_diff_for_hunk(hunk, keep_header) - let diff = gitgutter#diff#discard_hunks(gitgutter#diff#run_diff(0, 0), a:hunk, a:keep_header) +function! gitgutter#diff#generate_diff_for_hunk(keep_header, lines_of_context) + let diff = gitgutter#diff#run_diff(0, 0, a:lines_of_context) + let diff_for_hunk = gitgutter#diff#discard_hunks(diff, a:keep_header) if !a:keep_header " Discard summary line - let diff = join(split(diff, '\n')[1:-1], "\n") + let diff_for_hunk = join(split(diff_for_hunk, '\n')[1:-1], "\n") endif - return diff + return diff_for_hunk endfunction -function! gitgutter#diff#discard_hunks(diff, hunk_to_keep, keep_header) +" diff - with non-zero lines of context +function! gitgutter#diff#discard_hunks(diff, keep_header) let modified_diff = [] let keep_line = a:keep_header for line in split(a:diff, '\n') let hunk_info = gitgutter#diff#parse_hunk(line) if len(hunk_info) == 4 " start of new hunk - let keep_line = (hunk_info == a:hunk_to_keep) + let keep_line = gitgutter#hunk#cursor_in_hunk(hunk_info) endif if keep_line call add(modified_diff, line) diff --git a/autoload/gitgutter/hunk.vim b/autoload/gitgutter/hunk.vim index 0f49691..7ee0ae6 100644 --- a/autoload/gitgutter/hunk.vim +++ b/autoload/gitgutter/hunk.vim @@ -67,15 +67,9 @@ endfunction " hunk. function! gitgutter#hunk#current_hunk() let current_hunk = [] - let current_line = line('.') for hunk in s:hunks - if current_line == 1 && hunk[2] == 0 - let current_hunk = hunk - break - endif - - if current_line >= hunk[2] && current_line < hunk[2] + (hunk[3] == 0 ? 1 : hunk[3]) + if gitgutter#hunk#cursor_in_hunk(hunk) let current_hunk = hunk break endif @@ -86,3 +80,17 @@ function! gitgutter#hunk#current_hunk() endif endfunction +function! gitgutter#hunk#cursor_in_hunk(hunk) + let current_line = line('.') + + if current_line == 1 && a:hunk[2] == 0 + return 1 + endif + + if current_line >= a:hunk[2] && current_line < a:hunk[2] + (a:hunk[3] == 0 ? 1 : a:hunk[3]) + return 1 + endif + + return 0 +endfunction +