Finer granularity for staging/reverting hunks.

This commit is contained in:
Andy Stewart
2015-03-05 10:17:38 +01:00
parent 4e22ad501f
commit c6ed14c662
3 changed files with 69 additions and 24 deletions

View File

@@ -19,7 +19,7 @@ function! gitgutter#process_buffer(bufnr, realtime)
endif endif
try try
if !a:realtime || gitgutter#utility#has_fresh_changes() if !a:realtime || gitgutter#utility#has_fresh_changes()
let diff = gitgutter#diff#run_diff(a:realtime || gitgutter#utility#has_unsaved_changes(), 1, 0) let diff = gitgutter#diff#run_diff(a:realtime || gitgutter#utility#has_unsaved_changes(), 1)
call gitgutter#hunk#set_hunks(gitgutter#diff#parse_diff(diff)) call gitgutter#hunk#set_hunks(gitgutter#diff#parse_diff(diff))
let modified_lines = gitgutter#diff#process_hunks(gitgutter#hunk#hunks()) let modified_lines = gitgutter#diff#process_hunks(gitgutter#hunk#hunks())
@@ -156,11 +156,8 @@ function! gitgutter#stage_hunk()
" It doesn't make sense to stage a hunk otherwise. " It doesn't make sense to stage a hunk otherwise.
silent write silent write
" construct a diff let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk('stage')
let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(1, 1) call gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file('git apply --cached --unidiff-zero - '), diff_for_hunk)
" apply the diff
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 " refresh gitgutter's view of buffer
silent execute "GitGutter" silent execute "GitGutter"
@@ -175,11 +172,8 @@ function! gitgutter#revert_hunk()
" It doesn't make sense to stage a hunk otherwise. " It doesn't make sense to stage a hunk otherwise.
silent write silent write
" construct a diff let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk('revert')
let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(1, 1) call gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file('git apply --reverse --unidiff-zero - '), diff_for_hunk)
" apply the diff
call gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file('git apply --reverse - '), diff_for_hunk)
" reload file " reload file
silent edit silent edit
@@ -192,10 +186,8 @@ function! gitgutter#preview_hunk()
if gitgutter#utility#is_active() if gitgutter#utility#is_active()
silent write silent write
" construct a diff let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk('preview')
let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(0, 0)
" preview the diff
silent! wincmd P silent! wincmd P
if !&previewwindow if !&previewwindow
execute 'bo ' . &previewheight . ' new' execute 'bo ' . &previewheight . ' new'

View File

@@ -10,7 +10,7 @@ endif
let s:hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@' let s:hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@'
function! gitgutter#diff#run_diff(realtime, use_external_grep, lines_of_context) function! gitgutter#diff#run_diff(realtime, use_external_grep)
" Wrap compound commands in parentheses to make Windows happy. " Wrap compound commands in parentheses to make Windows happy.
let cmd = '(' let cmd = '('
@@ -33,7 +33,7 @@ function! gitgutter#diff#run_diff(realtime, use_external_grep, lines_of_context)
execute 'silent write' buff_file execute 'silent write' buff_file
endif endif
let cmd .= 'git diff --no-ext-diff --no-color -U'.a:lines_of_context.' '.g:gitgutter_diff_args.' -- ' let cmd .= 'git diff --no-ext-diff --no-color -U0 '.g:gitgutter_diff_args.' -- '
if a:realtime if a:realtime
let cmd .= blob_file.' '.buff_file let cmd .= blob_file.' '.buff_file
else else
@@ -214,17 +214,26 @@ function! gitgutter#diff#process_modified_and_removed(modifications, from_count,
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! gitgutter#diff#generate_diff_for_hunk(keep_header, lines_of_context) " Generates a zero-context diff for the current hunk.
let diff = gitgutter#diff#run_diff(0, 0, a:lines_of_context) "
let diff_for_hunk = gitgutter#diff#discard_hunks(diff, a:keep_header) " type - stage | revert | preview
if !a:keep_header function! gitgutter#diff#generate_diff_for_hunk(type)
" Discard summary line " Although (we assume) diff is up to date, we don't store it anywhere so we
let diff_for_hunk = join(split(diff_for_hunk, '\n')[1:-1], "\n") " have to regenerate it now...
let diff = gitgutter#diff#run_diff(0, 0)
let diff_for_hunk = gitgutter#diff#discard_hunks(diff, a:type == 'stage' || a:type == 'revert')
if a:type == 'stage' || a:type == 'revert'
let diff_for_hunk = gitgutter#diff#adjust_hunk_summary(diff_for_hunk, a:type == 'stage')
endif endif
return diff_for_hunk return diff_for_hunk
endfunction endfunction
" diff - with non-zero lines of context " Returns the diff with all hunks discarded except the current.
"
" diff - the diff to process
" keep_header - truthy to keep the diff header and hunk summary, falsy to discard it
function! gitgutter#diff#discard_hunks(diff, keep_header) function! gitgutter#diff#discard_hunks(diff, keep_header)
let modified_diff = [] let modified_diff = []
let keep_line = a:keep_header let keep_line = a:keep_header
@@ -237,5 +246,36 @@ function! gitgutter#diff#discard_hunks(diff, keep_header)
call add(modified_diff, line) call add(modified_diff, line)
endif endif
endfor endfor
if a:keep_header
return join(modified_diff, "\n") . "\n" return join(modified_diff, "\n") . "\n"
else
" Discard hunk summary too.
return join(modified_diff[1:], "\n") . "\n"
endif
endfunction endfunction
" Adjust hunk summary (from's / to's line number) to ignore changes above/before this one.
"
" diff_for_hunk - a diff containing only the hunk of interest
" staging - truthy if the hunk is to be staged, falsy if it is to be reverted
"
" TODO: push this down to #discard_hunks?
function! gitgutter#diff#adjust_hunk_summary(diff_for_hunk, staging)
let line_adjustment = gitgutter#hunk#line_adjustment_for_current_hunk()
let adj_diff = []
for line in split(a:diff_for_hunk, '\n')
if match(line, s:hunk_re) != -1
if a:staging
" increment 'to' line number
let line = substitute(line, '+\@<=\(\d\+\)', '\=submatch(1)+line_adjustment', '')
else
" decrement 'from' line number
let line = substitute(line, '-\@<=\(\d\+\)', '\=submatch(1)-line_adjustment', '')
endif
endif
call add(adj_diff, line)
endfor
return join(adj_diff, "\n") . "\n"
endfunction

View File

@@ -94,3 +94,16 @@ function! gitgutter#hunk#cursor_in_hunk(hunk)
return 0 return 0
endfunction endfunction
" 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.
function! gitgutter#hunk#line_adjustment_for_current_hunk()
let adj = 0
for hunk in s:hunks
if gitgutter#hunk#cursor_in_hunk(hunk)
break
else
let adj += hunk[1] - hunk[3]
endif
endfor
return adj
endfunction