diff --git a/doc/tigrc.5.adoc b/doc/tigrc.5.adoc index 10f1f570b..0570836cf 100644 --- a/doc/tigrc.5.adoc +++ b/doc/tigrc.5.adoc @@ -225,6 +225,10 @@ The following variables can be set: Whether to show staged and unstaged changes in the main view. +'show-untracked' (bool):: + + Whether to show also untracked changes in the main view. + 'vertical-split' (mixed) [auto|]:: Whether to split the view horizontally or vertically. diff --git a/include/tig/diff.h b/include/tig/diff.h index ea725639e..f7cb97507 100644 --- a/include/tig/diff.h +++ b/include/tig/diff.h @@ -19,6 +19,7 @@ struct diff_state { bool after_commit_title; bool after_diff; + bool reading_diff_chunk; bool reading_diff_stat; bool combined_diff; bool adding_describe_ref; diff --git a/include/tig/main.h b/include/tig/main.h index 8c51663a9..c7737478a 100644 --- a/include/tig/main.h +++ b/include/tig/main.h @@ -39,6 +39,7 @@ struct main_state { bool first_parent; bool add_changes_staged; bool add_changes_unstaged; + bool add_changes_untracked; }; bool main_get_column_data(struct view *view, const struct line *line, struct view_column_data *column_data); diff --git a/include/tig/options.h b/include/tig/options.h index 324cb9591..c02291996 100644 --- a/include/tig/options.h +++ b/include/tig/options.h @@ -69,6 +69,7 @@ typedef struct view_column *view_settings; _(send_child_enter, bool, VIEW_NO_FLAGS) \ _(show_changes, bool, VIEW_NO_FLAGS) \ _(show_notes, bool, VIEW_NO_FLAGS) \ + _(show_untracked, bool, VIEW_NO_FLAGS) \ _(split_view_height, double, VIEW_RESET_DISPLAY) \ _(split_view_width, double, VIEW_RESET_DISPLAY) \ _(stage_view, view_settings, VIEW_NO_FLAGS) \ diff --git a/include/tig/watch.h b/include/tig/watch.h index 79ee97618..24b139344 100644 --- a/include/tig/watch.h +++ b/include/tig/watch.h @@ -30,13 +30,16 @@ enum watch_trigger { WATCH_INDEX_STAGED_NO = 1 << 1, WATCH_INDEX_UNSTAGED_YES = 1 << 2, WATCH_INDEX_UNSTAGED_NO = 1 << 3, - WATCH_HEAD = 1 << 4, - WATCH_STASH = 1 << 5, - WATCH_REFS = 1 << 6, + WATCH_INDEX_UNTRACKED_YES = 1 << 4, + WATCH_INDEX_UNTRACKED_NO = 1 << 5, + WATCH_HEAD = 1 << 6, + WATCH_STASH = 1 << 7, + WATCH_REFS = 1 << 8, WATCH_INDEX_STAGED = WATCH_INDEX_STAGED_YES | WATCH_INDEX_STAGED_NO, WATCH_INDEX_UNSTAGED = WATCH_INDEX_UNSTAGED_YES | WATCH_INDEX_UNSTAGED_NO, - WATCH_INDEX = WATCH_INDEX_STAGED | WATCH_INDEX_UNSTAGED, + WATCH_INDEX_UNTRACKED = WATCH_INDEX_UNTRACKED_YES | WATCH_INDEX_UNTRACKED_NO, + WATCH_INDEX = WATCH_INDEX_STAGED | WATCH_INDEX_UNSTAGED | WATCH_INDEX_UNTRACKED, }; struct watch { diff --git a/src/blame.c b/src/blame.c index 6df2a09e1..ad5e0b5bb 100644 --- a/src/blame.c +++ b/src/blame.c @@ -126,9 +126,23 @@ blame_open(struct view *view, enum open_flags flags) blame_update_file_name_visibility(view); - if (!view->env->file[0]) - return error("No file chosen, press %s to open tree view", - get_view_key(view, REQ_VIEW_TREE)); + if (!view->env->file[0]) { + struct stat statbuf; + + if (opt_file_args && !opt_file_args[1] && + stat(opt_file_args[0], &statbuf) == 0 && !S_ISDIR(statbuf.st_mode)) { + const char *ls_files_argv[] = { + "git", "ls-files", "-z", "--full-name", "--", opt_file_args[0], NULL + }; + char name[SIZEOF_STR] = ""; + + io_run_buf(ls_files_argv, name, sizeof(name), NULL, false); + string_ncopy(view->env->file, name, strlen(name)); + } else { + return error("No file chosen, press %s to open tree view", + get_view_key(view, REQ_VIEW_TREE)); + } + } if (!view->prev && *repo.prefix && !(flags & (OPEN_RELOAD | OPEN_REFRESH))) { string_copy(path, view->env->file); diff --git a/src/diff.c b/src/diff.c index 63877a286..0db42e48a 100644 --- a/src/diff.c +++ b/src/diff.c @@ -263,6 +263,7 @@ diff_common_read(struct view *view, const char *data, struct diff_state *state) if (!strncmp(data + len, "combined ", strlen("combined ")) || !strncmp(data + len, "cc ", strlen("cc "))) state->combined_diff = true; + state->reading_diff_chunk = false; } else if (type == LINE_DIFF_CHUNK) { const char *context = strstr(data + STRING_SIZE("@@"), "@@"); @@ -278,6 +279,7 @@ diff_common_read(struct view *view, const char *data, struct diff_state *state) box->cell[0].length = (context + 2) - data; box->cell[1].length = strlen(context + 2); box->cell[box->cells++].type = LINE_DIFF_STAT; + state->reading_diff_chunk = true; return true; } else if (state->highlight && strchr(data, 0x1b)) { @@ -291,6 +293,14 @@ diff_common_read(struct view *view, const char *data, struct diff_state *state) if (!state->combined_diff && (type == LINE_DIFF_ADD2 || type == LINE_DIFF_DEL2)) type = LINE_DEFAULT; + /* DEL_FILE, ADD_FILE and START are only valid outside diff chunks */ + if (state->reading_diff_chunk) { + if (type == LINE_DIFF_DEL_FILE || type == LINE_DIFF_START) + type = LINE_DIFF_DEL; + else if (type == LINE_DIFF_ADD_FILE) + type = LINE_DIFF_ADD; + } + return pager_common_read(view, data, type, NULL); } diff --git a/src/draw.c b/src/draw.c index a5efaaf8d..bec721089 100644 --- a/src/draw.c +++ b/src/draw.c @@ -593,7 +593,7 @@ draw_view_line_search_result(struct view *view, unsigned int lineno) regoff_t end = pmatch[0].rm_eo; if (start == -1 || end <= 0 || end <= start) - continue; + break; mvwchgat(view->win, lineno, utf8_width_of(buf, bufpos + start, -1), diff --git a/src/main.c b/src/main.c index e73aa854a..a7ec856ff 100644 --- a/src/main.c +++ b/src/main.c @@ -45,6 +45,8 @@ main_status_exists(struct view *view, enum line_type type) return true; if (type == LINE_STAT_UNSTAGED && state->add_changes_unstaged) return true; + if (type == LINE_STAT_UNTRACKED && state->add_changes_untracked) + return true; return false; } @@ -60,9 +62,9 @@ main_register_commit(struct view *view, struct commit *commit, const char *ids, string_copy_rev(commit->id, ids); /* FIXME: lazily check index state here instead of in main_open. */ - if ((state->add_changes_unstaged || state->add_changes_staged) && is_head_commit(commit->id)) { + if ((state->add_changes_untracked || state->add_changes_unstaged || state->add_changes_staged) && is_head_commit(commit->id)) { main_add_changes(view, state, ids); - state->add_changes_unstaged = state->add_changes_staged = false; + state->add_changes_untracked = state->add_changes_unstaged = state->add_changes_staged = false; } if (state->with_graph) @@ -147,9 +149,16 @@ main_check_index(struct view *view, struct main_state *state) { struct index_diff diff; - if (!index_diff(&diff, false, false)) + if (!index_diff(&diff, opt_show_untracked, false)) return false; + if (!diff.untracked) { + watch_apply(&view->watch, WATCH_INDEX_UNTRACKED_NO); + } else { + watch_apply(&view->watch, WATCH_INDEX_UNTRACKED_YES); + state->add_changes_untracked = true; + } + if (!diff.unstaged) { watch_apply(&view->watch, WATCH_INDEX_UNSTAGED_NO); } else { @@ -172,6 +181,7 @@ main_add_changes(struct view *view, struct main_state *state, const char *parent { const char *staged_parent = parent; const char *unstaged_parent = NULL_ID; + const char *untracked_parent = NULL_ID; if (!state->add_changes_staged) { staged_parent = NULL; @@ -180,9 +190,16 @@ main_add_changes(struct view *view, struct main_state *state, const char *parent if (!state->add_changes_unstaged) { unstaged_parent = NULL; + if (!state->add_changes_staged) + untracked_parent = parent; + } + + if (!state->add_changes_untracked) { + untracked_parent = NULL; } - return main_add_changes_commit(view, LINE_STAT_UNSTAGED, unstaged_parent, "Unstaged changes") + return main_add_changes_commit(view, LINE_STAT_UNTRACKED, untracked_parent, "Untracked changes") + && main_add_changes_commit(view, LINE_STAT_UNSTAGED, unstaged_parent, "Unstaged changes") && main_add_changes_commit(view, LINE_STAT_STAGED, staged_parent, "Staged changes"); } @@ -528,6 +545,8 @@ main_request(struct view *view, enum request request, struct line *line) if (line->type == LINE_STAT_UNSTAGED || line->type == LINE_STAT_STAGED) open_stage_view(view, NULL, line->type, flags); + else if (line->type == LINE_STAT_UNTRACKED) + open_status_view(view, flags); else open_diff_view(view, flags); break; @@ -558,7 +577,7 @@ main_select(struct view *view, struct line *line) { struct commit *commit = line->data; - if (line->type == LINE_STAT_STAGED || line->type == LINE_STAT_UNSTAGED) { + if (line->type == LINE_STAT_STAGED || line->type == LINE_STAT_UNSTAGED || line->type == LINE_STAT_UNTRACKED) { string_ncopy(view->ref, commit->title, strlen(commit->title)); status_stage_info(view->env->status, line->type, NULL); } else { diff --git a/src/pager.c b/src/pager.c index c41df281b..9f1455064 100644 --- a/src/pager.c +++ b/src/pager.c @@ -101,11 +101,39 @@ pager_wrap_line(struct view *view, const char *data, enum line_type type) bool pager_common_read(struct view *view, const char *data, enum line_type type, struct line **line_ptr) { + struct diff_state *state = view->private; struct line *line; if (!data) return true; + if (type == LINE_DIFF_HEADER) { + const int len = STRING_SIZE("diff --"); + + if (!strncmp(data + len, "combined ", strlen("combined ")) || + !strncmp(data + len, "cc ", strlen("cc "))) + state->combined_diff = true; + state->reading_diff_chunk = false; + + } else if (type == LINE_DIFF_CHUNK) { + state->reading_diff_chunk = true; + + } else if (type == LINE_PP_MERGE) { + state->combined_diff = true; + } + + /* ADD2 and DEL2 are only valid in combined diff hunks */ + if (!state->combined_diff && (type == LINE_DIFF_ADD2 || type == LINE_DIFF_DEL2)) + type = LINE_DEFAULT; + + /* DEL_FILE, ADD_FILE and START are only valid outside diff chunks */ + if (state->reading_diff_chunk) { + if (type == LINE_DIFF_DEL_FILE || type == LINE_DIFF_START) + type = LINE_DIFF_DEL; + else if (type == LINE_DIFF_ADD_FILE) + type = LINE_DIFF_ADD; + } + if (opt_wrap_lines) { line = pager_wrap_line(view, data, type); } else { @@ -188,7 +216,7 @@ static struct view_ops pager_ops = { "line", "", VIEW_OPEN_DIFF | VIEW_NO_REF | VIEW_NO_GIT_DIR, - 0, + sizeof(struct diff_state), pager_open, pager_read, view_column_draw, diff --git a/src/repo.c b/src/repo.c index 882592da5..bfa24baae 100644 --- a/src/repo.c +++ b/src/repo.c @@ -150,7 +150,7 @@ index_diff(struct index_diff *diff, bool untracked, bool count_all) /* Ignore staged but unmerged entries. */ else if (buf.data[0] != ' ' && buf.data[0] != 'U') diff->staged++; - if (buf.data[1] != ' ') + if (buf.data[1] != ' ' && buf.data[1] != '?') diff->unstaged++; if (!count_all && diff->staged && diff->unstaged && (!untracked || diff->untracked)) diff --git a/test/diff/editor-test b/test/diff/editor-test index 108b36107..34814a485 100755 --- a/test/diff/editor-test +++ b/test/diff/editor-test @@ -4,6 +4,7 @@ . libgit.sh tigrc < Max Power " EOF tigrc < + :save-display result.screen +' + +tigrc <