Commit 3b750a88 by Tim

Git blame buffer gives the wrong result in many cases where there are multiple local edits.

I added multiple failing tests cases to tests/libgit2/blame/buffer.c to cover the test cases.

blame.c has been updated to get these tests to pass. 
Fixes include:
    shift_hunks_by now no longer shifts hunks before the start line. 
    Adjusting the wedge line in the case where a new hunk deletes and adds lines.
    In buffer_line_cb for addions humks are now shifted after the current diff line.
    Fixing the logic for removing a line in buffer_line_cb to work with multi-line deletions.
 
parent f041a94e
...@@ -119,8 +119,11 @@ static void shift_hunks_by(git_vector *v, size_t start_line, int shift_by) ...@@ -119,8 +119,11 @@ static void shift_hunks_by(git_vector *v, size_t start_line, int shift_by)
size_t i; size_t i;
if (!git_vector_bsearch2(&i, v, hunk_byfinalline_search_cmp, &start_line)) { if (!git_vector_bsearch2(&i, v, hunk_byfinalline_search_cmp, &start_line)) {
for (; i < v->length; i++) { for (i = 0; i < v->length; i++) {
git_blame_hunk *hunk = (git_blame_hunk*)v->contents[i]; git_blame_hunk *hunk = (git_blame_hunk*)v->contents[i];
if(hunk->final_start_line_number < start_line){
continue;
}
hunk->final_start_line_number += shift_by; hunk->final_start_line_number += shift_by;
} }
} }
...@@ -444,21 +447,20 @@ static int buffer_hunk_cb( ...@@ -444,21 +447,20 @@ static int buffer_hunk_cb(
GIT_UNUSED(delta); GIT_UNUSED(delta);
wedge_line = (hunk->old_lines == 0) ? hunk->new_start : hunk->old_start; wedge_line = (hunk->new_start >= hunk->old_start || hunk->old_lines==0) ? hunk->new_start : hunk->old_start;
blame->current_diff_line = wedge_line; blame->current_diff_line = wedge_line;
blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byline(blame, wedge_line); blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byline(blame, wedge_line);
if (!blame->current_hunk) { if (!blame->current_hunk) {
/* Line added at the end of the file */ /* Line added at the end of the file */
blame->current_hunk = new_hunk(wedge_line, 0, wedge_line, blame->current_hunk = new_hunk(wedge_line, 0, wedge_line,
blame->path, blame); blame->path, blame);
blame->current_diff_line++;
GIT_ERROR_CHECK_ALLOC(blame->current_hunk); GIT_ERROR_CHECK_ALLOC(blame->current_hunk);
git_vector_insert(&blame->hunks, blame->current_hunk); git_vector_insert(&blame->hunks, blame->current_hunk);
} else if (!hunk_starts_at_or_after_line(blame->current_hunk, wedge_line)){ } else if (!hunk_starts_at_or_after_line(blame->current_hunk, wedge_line)){
/* If this hunk doesn't start between existing hunks, split a hunk up so it does */ /* If this hunk doesn't start between existing hunks, split a hunk up so it does */
blame->current_hunk = split_hunk_in_vector(&blame->hunks, blame->current_hunk, blame->current_hunk = split_hunk_in_vector(&blame->hunks, blame->current_hunk,
wedge_line - blame->current_hunk->orig_start_line_number, true, wedge_line - blame->current_hunk->final_start_line_number, true,
blame); blame);
GIT_ERROR_CHECK_ALLOC(blame->current_hunk); GIT_ERROR_CHECK_ALLOC(blame->current_hunk);
} }
...@@ -484,13 +486,12 @@ static int buffer_line_cb( ...@@ -484,13 +486,12 @@ static int buffer_line_cb(
hunk_ends_at_or_before_line(blame->current_hunk, blame->current_diff_line)) { hunk_ends_at_or_before_line(blame->current_hunk, blame->current_diff_line)) {
/* Append to the current buffer-blame hunk */ /* Append to the current buffer-blame hunk */
blame->current_hunk->lines_in_hunk++; blame->current_hunk->lines_in_hunk++;
shift_hunks_by(&blame->hunks, blame->current_diff_line+1, 1); shift_hunks_by(&blame->hunks, blame->current_diff_line, 1);
} else { } else {
/* Create a new buffer-blame hunk with this line */ /* Create a new buffer-blame hunk with this line */
shift_hunks_by(&blame->hunks, blame->current_diff_line, 1); shift_hunks_by(&blame->hunks, blame->current_diff_line, 1);
blame->current_hunk = new_hunk(blame->current_diff_line, 1, 0, blame->path, blame); blame->current_hunk = new_hunk(blame->current_diff_line, 1, 0, blame->path, blame);
GIT_ERROR_CHECK_ALLOC(blame->current_hunk); GIT_ERROR_CHECK_ALLOC(blame->current_hunk);
git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL); git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL);
} }
blame->current_diff_line++; blame->current_diff_line++;
...@@ -498,15 +499,16 @@ static int buffer_line_cb( ...@@ -498,15 +499,16 @@ static int buffer_line_cb(
if (line->origin == GIT_DIFF_LINE_DELETION) { if (line->origin == GIT_DIFF_LINE_DELETION) {
/* Trim the line from the current hunk; remove it if it's now empty */ /* Trim the line from the current hunk; remove it if it's now empty */
size_t shift_base = blame->current_diff_line + blame->current_hunk->lines_in_hunk+1; size_t shift_base = blame->current_diff_line + blame->current_hunk->lines_in_hunk;
if (--(blame->current_hunk->lines_in_hunk) == 0) { if (--(blame->current_hunk->lines_in_hunk) == 0) {
size_t i; size_t i;
shift_base--; size_t i_next;
if (!git_vector_search2(&i, &blame->hunks, ptrs_equal_cmp, blame->current_hunk)) { if (!git_vector_search2(&i, &blame->hunks, ptrs_equal_cmp, blame->current_hunk)) {
git_vector_remove(&blame->hunks, i); git_vector_remove(&blame->hunks, i);
free_hunk(blame->current_hunk); free_hunk(blame->current_hunk);
blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byindex(blame, (uint32_t)i); i_next = min( i , blame->hunks.length -1);
blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byindex(blame, (uint32_t)i_next);
} }
} }
shift_hunks_by(&blame->hunks, shift_base, -1); shift_hunks_by(&blame->hunks, shift_base, -1);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment