Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/tig/repo.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ struct index_diff {
int untracked;
};

bool index_diff(struct index_diff *diff, bool untracked, bool count_all);
bool index_diff(struct index_diff *diff, bool untracked, bool count_all, const char **files);
bool update_index(void);

#endif
Expand Down
30 changes: 26 additions & 4 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,20 @@ 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_untracked || state->add_changes_unstaged || state->add_changes_staged) && is_head_commit(commit->id)) {
main_add_changes(view, state, ids);
state->add_changes_untracked = state->add_changes_unstaged = state->add_changes_staged = false;
if (state->add_changes_untracked || state->add_changes_unstaged || state->add_changes_staged) {
/* When filtering by file, history simplification may prune
* the HEAD commit from the log. Anchor the changes commits
* to the first commit of the filtered log instead, so they
* stay at the top of the view. The changes commits have a
* null id and must not trigger this themselves. */
bool anchor = is_head_commit(commit->id) ||
(opt_file_args && opt_file_filter &&
strcmp(commit->id, NULL_ID));

if (anchor) {
main_add_changes(view, state, ids);
state->add_changes_untracked = state->add_changes_unstaged = state->add_changes_staged = false;
}
}

if (state->with_graph)
Expand Down Expand Up @@ -160,7 +171,8 @@ main_check_index(struct view *view, struct main_state *state)
{
struct index_diff diff;

if (!index_diff(&diff, opt_show_untracked, false))
if (!index_diff(&diff, opt_show_untracked, false,
opt_file_filter ? opt_file_args : NULL))
return false;

if (!diff.untracked) {
Expand Down Expand Up @@ -427,6 +439,16 @@ main_read(struct view *view, struct buffer *buf, bool force_stop)
if (!buf) {
main_flush_commit(view, commit);

/* The filtered log may be empty, e.g. when the paths have no
* commit history; add the changes commits on their own. */
if (opt_file_args && opt_file_filter &&
(state->add_changes_untracked || state->add_changes_unstaged ||
state->add_changes_staged)) {
main_add_changes(view, state, repo.head_id);
state->add_changes_untracked = state->add_changes_unstaged =
state->add_changes_staged = false;
}

if (!force_stop && failed_to_load_initial_view(view))
die("No revisions match the given arguments.");
if (view->lines > 0) {
Expand Down
19 changes: 16 additions & 3 deletions src/repo.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/

#include "tig/tig.h"
#include "tig/argv.h"
#include "tig/repo.h"
#include "tig/io.h"
#include "tig/refdb.h"
Expand Down Expand Up @@ -146,22 +147,34 @@ update_index(void)
}

bool
index_diff(struct index_diff *diff, bool untracked, bool count_all)
index_diff(struct index_diff *diff, bool untracked, bool count_all, const char **files)
{
const char *untracked_arg = !untracked ? "--untracked-files=no" :
count_all ? "--untracked-files=all" :
"--untracked-files=normal";
const char *status_argv[] = {
"git", "status", "--porcelain", "-z", untracked_arg, NULL
"git", "status", "--porcelain", "-z", untracked_arg, "--", NULL
};
const char **argv = NULL;
struct io io;
struct buffer buf;
bool ok = true;

memset(diff, 0, sizeof(*diff));

if (!io_run(&io, IO_RD, repo.exec_dir, NULL, status_argv))
/* Run in the current directory so relative pathspecs in the file
* arguments resolve like they do for the views, which do not use
* repo.exec_dir either (e.g. the main view). */
if (!argv_append_array(&argv, status_argv) ||
(files && !argv_append_array(&argv, files)) ||
!io_run(&io, IO_RD, NULL, NULL, argv)) {
argv_free(argv);
free(argv);
return false;
}

argv_free(argv);
free(argv);

while (io_get(&io, &buf, 0, true) && (ok = buf.size > 3)) {
if (buf.data[0] == '?')
Expand Down
2 changes: 1 addition & 1 deletion src/watch.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ watch_index_handler(struct watch_handler *handler, enum watch_event event, enum
if (event == WATCH_EVENT_SWITCH_VIEW)
return WATCH_NONE;

if (!index_diff(&diff, opt_show_untracked, opt_status_show_untracked_files))
if (!index_diff(&diff, opt_show_untracked, opt_status_show_untracked_files, NULL))
return check_file_mtime(&handler->last_modified, "%s/index", repo.git_dir)
? check : WATCH_NONE;

Expand Down
160 changes: 160 additions & 0 deletions test/main/filtered-changes-test
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#!/bin/sh
#
# Test that the main view shows index changes commits when filtering by
# file, also when history simplification prunes the HEAD commit from the
# filtered log.

. libtest.sh
. libgit.sh
. "$source_dir/util.sh"

export LINES=10

tigrc <<EOF
set vertical-split = no
set line-graphics = ascii
EOF

commit()
{
GIT_AUTHOR_NAME="A. U. Thor" \
GIT_AUTHOR_EMAIL="a.u.thor@example.net" \
GIT_AUTHOR_DATE="$1" \
GIT_COMMITTER_NAME="A. U. Thor" \
GIT_COMMITTER_EMAIL="a.u.thor@example.net" \
GIT_COMMITTER_DATE="$1" \
git commit -q -m "$2"
}

test_setup_work_dir()
{
git init -q .
git_config

echo "a 1" > a
echo "b 1" > b
echo "root foo" > foo
mkdir sub
echo "sub foo" > sub/foo
git add .
commit "2009-02-13 23:31 +0000" "change a and b"

echo "b 2" >> b
git add b
commit "2009-02-22 11:53 +0000" "change b"

# Staged changes to a, c and sub/foo; the root foo stays clean.
echo "a 2" >> a
git add a
echo "c 1" > c
git add c
echo "sub foo 2" >> sub/foo
git add sub/foo
}

test_tig_script() {
name="$1"; shift

tig_script "$name" "
:view-main
:save-display $name.screen
"

cat > "$name.expected"

test_tig "$@"

assert_equals "$name.screen" < "$name.expected"
}

# HEAD ("change b") is pruned from the filtered log; the changes commit
# is expected at the top of the view, anchored to the first commit.
test_tig_script 'filtered-with-staged' -- a <<EOF
$YYY_MM_DD_HH_MM +0000 Not Committed Yet * Staged changes
2009-02-13 23:31 +0000 A. U. Thor I change a and b






[main] Staged changes 100%
EOF

# The changes do not touch the filtered path; no changes commit is shown.
test_tig_script 'filtered-without-changes' -- b <<EOF
2009-02-22 11:53 +0000 A. U. Thor * [master] change b
2009-02-13 23:31 +0000 A. U. Thor I change a and b






[main] 97114c6c2d9599d316e26ed8f6bd2e922e5ba0f5 - commit 1 of 2 100%
EOF

# The filtered path has no history; only the changes commit is shown.
test_tig_script 'filtered-without-log' -- c <<EOF
$YYY_MM_DD_HH_MM +0000 Not Committed Yet * Staged changes







[main] Staged changes 100%
EOF

# Disabling the file filter must show changes outside the former filter.
tig_script 'filtered-toggle' "
:view-main
:save-display filtered-toggle-before.screen
:set file-filter = no
:view-main
:save-display filtered-toggle-after.screen
"

test_tig -- b

assert_equals 'filtered-toggle-before.screen' <<EOF
2009-02-22 11:53 +0000 A. U. Thor * [master] change b
2009-02-13 23:31 +0000 A. U. Thor I change a and b






[main] 97114c6c2d9599d316e26ed8f6bd2e922e5ba0f5 - commit 1 of 2 100%
EOF

assert_equals 'filtered-toggle-after.screen' <<EOF
$YYY_MM_DD_HH_MM +0000 Not Committed Yet * Staged changes
2009-02-22 11:53 +0000 A. U. Thor * [master] change b
2009-02-13 23:31 +0000 A. U. Thor I change a and b





[main] Staged changes 100%
EOF

# From a subdirectory the relative file argument must resolve against
# the subdirectory, not the repository root: sub/foo has staged changes
# while the root foo is clean.
work_dir="$work_dir/sub"

test_tig_script 'filtered-subdir' -- foo <<EOF
$YYY_MM_DD_HH_MM +0000 Not Committed Yet * Staged changes
2009-02-13 23:31 +0000 A. U. Thor I change a and b






[main] Staged changes 100%
EOF
Loading