From db4a6b4285cc826bc78fc06a46a983cd6d8567cb Mon Sep 17 00:00:00 2001 From: Thomas Koutcher Date: Thu, 26 May 2022 13:08:35 +0200 Subject: [PATCH] Change the blame view to use porcelain Fixes #978 Fixes #1189 --- NEWS.adoc | 1 + include/tig/git.h | 3 +- include/tig/options.h | 2 +- include/tig/parse.h | 2 +- src/blame.c | 117 +++++++++++------------------------------- src/diff.c | 2 +- src/parse.c | 12 ++--- 7 files changed, 42 insertions(+), 97 deletions(-) diff --git a/NEWS.adoc b/NEWS.adoc index d52555d1f..6b6b1e065 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -15,6 +15,7 @@ Bug fixes: - Fix cursor behaviour during staging. (#842, #1028) - Fix navigation in split tree view. - Enable textconv in the stage view. + - Change the blame view to use porcelain. (#978, #1189) tig-2.5.5 --------- diff --git a/include/tig/git.h b/include/tig/git.h index 23b7adb40..e60e8d467 100644 --- a/include/tig/git.h +++ b/include/tig/git.h @@ -43,7 +43,8 @@ "git", "diff-files", (output_arg), "%(cmdlineargs)", NULL #define GIT_DIFF_BLAME(encoding_arg, context_arg, space_arg, new_name) \ - GIT_DIFF_UNSTAGED(encoding_arg, context_arg, space_arg, "", new_name) + "git", "diff-files", (encoding_arg), "--root", "--textconv", "--patch-with-stat", "-C", "-M", \ + (context_arg), (space_arg), "--", (new_name), NULL #define GIT_DIFF_BLAME_NO_PARENT(encoding_arg, context_arg, space_arg, new_name) \ GIT_DIFF_INITIAL(encoding_arg, "", context_arg, space_arg, "/dev/null", new_name) diff --git a/include/tig/options.h b/include/tig/options.h index 5c526e178..650c2d50f 100644 --- a/include/tig/options.h +++ b/include/tig/options.h @@ -114,7 +114,7 @@ OPTION_INFO(DEFINE_OPTION_EXTERNS) _(width, int, VIEW_NO_FLAGS) \ #define FILE_NAME_COLUMN_OPTIONS(_) \ - _(display, enum filename, VIEW_GREP_LIKE) \ + _(display, enum filename, VIEW_BLAME_LIKE | VIEW_GREP_LIKE) \ _(width, int, VIEW_NO_FLAGS) \ _(maxwidth, int, VIEW_NO_FLAGS) \ diff --git a/include/tig/parse.h b/include/tig/parse.h index 4c90b19ac..fcd698da3 100644 --- a/include/tig/parse.h +++ b/include/tig/parse.h @@ -47,7 +47,7 @@ struct blame_header { size_t group; }; -bool parse_blame_header(struct blame_header *header, const char *text, size_t max_lineno); +bool parse_blame_header(struct blame_header *header, const char *text); bool parse_blame_info(struct blame_commit *commit, char author[SIZEOF_STR], char *line); /* Parse author lines where the name may be empty: diff --git a/src/blame.c b/src/blame.c index 12cd6be4f..a8022d6b2 100644 --- a/src/blame.c +++ b/src/blame.c @@ -49,9 +49,8 @@ struct blame { struct blame_state { struct blame_commit *commit; + struct blame_header header; char author[SIZEOF_STR]; - int blamed; - bool done_reading; bool auto_filename_display; const char *filename; /* The history state for the current view is cached in the view @@ -78,7 +77,11 @@ static enum status_code blame_open(struct view *view, enum open_flags flags) { struct blame_state *state = view->private; - const char *file_argv[] = { repo.exec_dir, view->env->file , NULL }; + const char *blame_argv[] = { + "git", "blame", encoding_arg, "%(blameargs)", "-p", + *view->env->ref ? view->env->ref : "", "--", view->env->file, NULL + }; + enum status_code code; size_t i; if (!(repo.is_inside_work_tree || *repo.worktree)) @@ -145,15 +148,9 @@ blame_open(struct view *view, enum open_flags flags) return error("No file chosen, press %s to open tree view", get_view_key(view, REQ_VIEW_TREE)); - if (*view->env->ref || begin_update(view, repo.exec_dir, file_argv, flags) != SUCCESS) { - const char *blame_cat_file_argv[] = { - "git", "cat-file", "blob", "%(ref):%(file)", NULL - }; - enum status_code code = begin_update(view, repo.exec_dir, blame_cat_file_argv, flags); - - if (code != SUCCESS) - return code; - } + code = begin_update(view, repo.exec_dir, blame_argv, flags); + if (code != SUCCESS) + return code; /* First pass: remove multiple references to the same commit. */ for (i = 0; i < view->lines; i++) { @@ -210,77 +207,27 @@ get_blame_commit(struct view *view, const char *id) static struct blame_commit * read_blame_commit(struct view *view, const char *text, struct blame_state *state) { - struct blame_header header; struct blame_commit *commit; - struct blame *blame; - if (!parse_blame_header(&header, text, view->lines)) + if (!parse_blame_header(&state->header, text)) return NULL; commit = get_blame_commit(view, text); if (!commit) return NULL; - state->blamed += header.group; - while (header.group--) { - struct line *line = &view->line[header.lineno + header.group - 1]; - - blame = line->data; - blame->commit = commit; - blame->lineno = header.orig_lineno + header.group - 1; - line->dirty = 1; - } - return commit; } -static bool -blame_read_file(struct view *view, struct buffer *buf, struct blame_state *state) -{ - if (!buf) { - const char *blame_argv[] = { - "git", "blame", encoding_arg, "%(blameargs)", "--incremental", - *view->env->ref ? view->env->ref : "--incremental", "--", view->env->file, NULL - }; - - if (failed_to_load_initial_view(view)) - die("No blame exist for %s", view->vid); - - if (view->lines == 0 || begin_update(view, repo.exec_dir, blame_argv, OPEN_EXTRA) != SUCCESS) { - report("Failed to load blame data"); - return true; - } - - if (view->env->goto_lineno > 0) { - select_view_line(view, view->env->goto_lineno); - view->env->goto_lineno = 0; - } - - state->done_reading = true; - return false; - - } else { - struct blame *blame; - - if (!add_line_alloc(view, &blame, LINE_DEFAULT, buf->size, false)) - return false; - - blame->commit = NULL; - strncpy(blame->text, buf->data, buf->size); - blame->text[buf->size] = 0; - return true; - } -} - static bool blame_read(struct view *view, struct buffer *buf, bool force_stop) { struct blame_state *state = view->private; - if (!state->done_reading) - return blame_read_file(view, buf, state); - if (!buf) { + if (failed_to_load_initial_view(view)) + die("No blame exist for %s", view->vid); + string_format(view->ref, "%s", view->vid); if (view_is_displayed(view)) { update_view_title(view); @@ -291,13 +238,24 @@ blame_read(struct view *view, struct buffer *buf, bool force_stop) if (!state->commit) { state->commit = read_blame_commit(view, buf->data, state); - string_format(view->ref, "%s %2zu%%", view->vid, - view->lines ? 5 * (size_t) (state->blamed * 20 / view->lines) : 0); - } else if (parse_blame_info(state->commit, state->author, buf->data)) { - bool update_view_columns = true; - int i; + } else if (*buf->data == '\t') { + struct blame *blame; + struct line *line = add_line_alloc(view, &blame, LINE_DEFAULT, buf->size - 1, false); + if (!line) + return false; + + blame->commit = state->commit; + blame->lineno = state->header.orig_lineno; + strncpy(blame->text, buf->data + 1, buf->size - 1); + blame->text[buf->size - 1] = 0; + + view_column_info_update(view, line); + + state->commit = NULL; + + } else if (parse_blame_info(state->commit, state->author, buf->data)) { if (!state->commit->filename) return false; @@ -309,19 +267,6 @@ blame_read(struct view *view, struct buffer *buf, bool force_stop) blame_update_file_name_visibility(view); } - for (i = 0; i < view->lines; i++) { - struct line *line = &view->line[i]; - struct blame *blame = line->data; - - if (blame && blame->commit == state->commit) { - line->dirty = 1; - if (update_view_columns) - view_column_info_update(view, line); - update_view_columns = false; - } - } - - state->commit = NULL; } return true; @@ -363,7 +308,7 @@ setup_blame_parent_line(struct view *view, struct blame *blame) char from[SIZEOF_REF + SIZEOF_STR]; char to[SIZEOF_REF + SIZEOF_STR]; const char *diff_tree_argv[] = { - "git", "diff", encoding_arg, "--no-textconv", "--no-ext-diff", + "git", "diff", encoding_arg, "--no-ext-diff", "--no-color", "-U0", from, to, "--", NULL }; struct io io; @@ -539,7 +484,7 @@ blame_select(struct view *view, struct line *line) static struct view_ops blame_ops = { "line", argv_env.commit, - VIEW_SEND_CHILD_ENTER | VIEW_BLAME_LIKE, + VIEW_SEND_CHILD_ENTER | VIEW_BLAME_LIKE | VIEW_REFRESH, sizeof(struct blame_state), blame_open, blame_read, diff --git a/src/diff.c b/src/diff.c index aa2106b0a..e1e1e8683 100644 --- a/src/diff.c +++ b/src/diff.c @@ -584,7 +584,7 @@ diff_blame_line(const char *ref, const char *file, unsigned long lineno, while (io_get(&io, &buf, '\n', true)) { if (header) { - if (!parse_blame_header(header, buf.data, 9999999)) + if (!parse_blame_header(header, buf.data)) break; header = NULL; diff --git a/src/parse.c b/src/parse.c index 969a5a802..e60e836ff 100644 --- a/src/parse.c +++ b/src/parse.c @@ -92,7 +92,7 @@ parse_author_line(char *ident, const struct ident **author, struct time *time) */ static bool -parse_number(const char **posref, size_t *number, size_t min, size_t max) +parse_number(const char **posref, size_t *number) { const char *pos = *posref; @@ -101,15 +101,13 @@ parse_number(const char **posref, size_t *number, size_t min, size_t max) if (!pos || !isdigit(pos[1])) return false; *number = atoi(pos + 1); - if (*number < min || *number > max) - return false; *posref = pos; return true; } bool -parse_blame_header(struct blame_header *header, const char *text, size_t max_lineno) +parse_blame_header(struct blame_header *header, const char *text) { const char *pos = text + SIZEOF_REV - 2; @@ -118,10 +116,10 @@ parse_blame_header(struct blame_header *header, const char *text, size_t max_lin string_ncopy(header->id, text, SIZEOF_REV); - if (!parse_number(&pos, &header->orig_lineno, 1, 9999999) || - !parse_number(&pos, &header->lineno, 1, max_lineno) || - !parse_number(&pos, &header->group, 1, max_lineno - header->lineno + 1)) + if (!parse_number(&pos, &header->orig_lineno) || + !parse_number(&pos, &header->lineno)) return false; + parse_number(&pos, &header->group); return true; }