Skip to content

Commit

Permalink
Blame uncommitted deletions starting from HEAD
Browse files Browse the repository at this point in the history
When starting a blame in the stage view on a deleted line, it seems
appropriate to find the commit that added the line.  To this end, instead
of starting the blame view from the working-tree-state of a file, use
the version at HEAD, which still has the deleted line (unless the line
was only just staged).  Compute the position of the deleted line to
start the blame view there.

Closes jonas#1008
  • Loading branch information
krobelus committed May 1, 2020
1 parent afe8e82 commit afc152f
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 1 deletion.
4 changes: 4 additions & 0 deletions NEWS.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ Bug fixes:

- Fix wrapping of lines with multibyte characters. (#988)

Improvements:

- Start blame of uncommitted deleted lines from HEAD so the line's origin can be traced. (#1008)

tig-2.5.1
---------
Expand Down
70 changes: 69 additions & 1 deletion src/stage.c
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,70 @@ stage_chunk_is_wrapped(struct view *view, struct line *line)
return false;
}

unsigned long
find_deleted_line_in_head(struct view *view, struct line *line) {
if (line->type != LINE_DIFF_DEL)
return 0;

// Check if the file exists in HEAD - "AM" files do not.
const char *ls_tree_argv[] = {
"git", "ls-tree", "-z", "HEAD", view->env->file, NULL
};
char buf[SIZEOF_STR] = "";
io_run_buf(ls_tree_argv, buf, sizeof(buf), NULL, false);
if (!buf[0])
return 0;

// Compute the line number in HEAD. The current view is a diff
// of what changed between HEAD, so get the old line number.
unsigned int line_number_in_head = diff_get_lineno(view, line, true);
assert(line_number_in_head);

if (stage_line_type == LINE_STAT_STAGED)
return line_number_in_head;

// If we are in an unstaged diff, we also need to take into
// account the staged changes to this file, since they happened
// between HEAD and our diff.
const char *diff_index_argv[] = {
"git", "diff-index", "--root", "--patch", "--cached", "HEAD",
"--", view->env->file, NULL
};
struct io io;
if (!io_run(&io, IO_RD, repo.exec_dir, NULL, diff_index_argv) || io.status)
return 0;
struct buffer buffer;
long bias_by_staged_changes = 0;
unsigned long line_number = 0;
// line_number_in_head is still the line number in the staged
// version of the file. Go through the staged changes and
// up to our line number and count the additions and deletions
// on the way, to compute the line number before the staged changes.
while (line_number < line_number_in_head && io_get(&io, &buffer, '\n', true)) {
enum line_type type = get_line_type(buffer.data);
if (type == LINE_DIFF_CHUNK) {
struct chunk_header header;
if (!parse_chunk_header(&header, buffer.data))
return 0;
line_number = header.new.position;
continue;
}
if (!line_number) {
continue;
}
if (type == LINE_DIFF_DEL) {
bias_by_staged_changes--;
continue;
}
assert(type == LINE_DIFF_ADD || type == LINE_DEFAULT);
if (type == LINE_DIFF_ADD)
bias_by_staged_changes++;
line_number++;
}

return line_number_in_head - bias_by_staged_changes;
}

static enum request
stage_request(struct view *view, enum request request, struct line *line)
{
Expand Down Expand Up @@ -445,7 +509,11 @@ stage_request(struct view *view, enum request request, struct line *line)
}

view->env->ref[0] = 0;
view->env->goto_lineno = diff_get_lineno(view, line, false);
if ((view->env->goto_lineno = find_deleted_line_in_head(view, line))) {
string_copy(view->env->ref, "HEAD");
} else {
view->env->goto_lineno = diff_get_lineno(view, line, false);
}
if (view->env->goto_lineno > 0)
view->env->goto_lineno--;
return request;
Expand Down

0 comments on commit afc152f

Please sign in to comment.