mirror of
https://github.com/git/git.git
synced 2026-01-26 02:24:27 +00:00
Merge branch 'show-ignored-directory'
This branch introduces an experimental option allowing `git status` to list all untracked files individually, but show ignored directories' names only instead of all ignored files. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
@@ -100,6 +100,11 @@ configuration variable documented in linkgit:git-config[1].
|
|||||||
--ignored::
|
--ignored::
|
||||||
Show ignored files as well.
|
Show ignored files as well.
|
||||||
|
|
||||||
|
--show-ignored-directory::
|
||||||
|
(EXPERIMENTAL) Show directories that are ignored, instead of individual files.
|
||||||
|
Does not recurse into excluded directories when listing all
|
||||||
|
untracked files.
|
||||||
|
|
||||||
-z::
|
-z::
|
||||||
Terminate entries with NUL, instead of LF. This implies
|
Terminate entries with NUL, instead of LF. This implies
|
||||||
the `--porcelain=v1` output format if no other format is given.
|
the `--porcelain=v1` output format if no other format is given.
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ The notable options are:
|
|||||||
Similar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`
|
Similar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`
|
||||||
in addition to untracked files in `entries[]`.
|
in addition to untracked files in `entries[]`.
|
||||||
|
|
||||||
|
`DIR_SHOW_IGNORED_DIRECTORY`:::
|
||||||
|
|
||||||
|
(EXPERIMENTAL) If this is set, non-empty directories that match an
|
||||||
|
ignore pattern are returned. The individual files contained in ignored
|
||||||
|
directories are not included.
|
||||||
|
|
||||||
`DIR_KEEP_UNTRACKED_CONTENTS`:::
|
`DIR_KEEP_UNTRACKED_CONTENTS`:::
|
||||||
|
|
||||||
Only has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if this is set, the
|
Only has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if this is set, the
|
||||||
|
|||||||
@@ -1334,6 +1334,7 @@ static int git_status_config(const char *k, const char *v, void *cb)
|
|||||||
int cmd_status(int argc, const char **argv, const char *prefix)
|
int cmd_status(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
static int no_lock_index = 0;
|
static int no_lock_index = 0;
|
||||||
|
static int show_ignored_directory = 0;
|
||||||
static struct wt_status s;
|
static struct wt_status s;
|
||||||
int fd;
|
int fd;
|
||||||
struct object_id oid;
|
struct object_id oid;
|
||||||
@@ -1365,6 +1366,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
|
|||||||
OPT_COLUMN(0, "column", &s.colopts, N_("list untracked files in columns")),
|
OPT_COLUMN(0, "column", &s.colopts, N_("list untracked files in columns")),
|
||||||
OPT_BOOL(0, "no-lock-index", &no_lock_index,
|
OPT_BOOL(0, "no-lock-index", &no_lock_index,
|
||||||
N_("do not lock the index")),
|
N_("do not lock the index")),
|
||||||
|
OPT_BOOL(0, "show-ignored-directory", &show_ignored_directory,
|
||||||
|
N_("(EXPERIMENTAL) Only show directories that match an ignore pattern name.")),
|
||||||
OPT_END(),
|
OPT_END(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1403,6 +1406,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
|
|||||||
s.ignore_submodule_arg = ignore_submodule_arg;
|
s.ignore_submodule_arg = ignore_submodule_arg;
|
||||||
s.status_format = status_format;
|
s.status_format = status_format;
|
||||||
s.verbose = verbose;
|
s.verbose = verbose;
|
||||||
|
s.show_ignored_directory = show_ignored_directory;
|
||||||
|
|
||||||
wt_status_collect(&s);
|
wt_status_collect(&s);
|
||||||
|
|
||||||
|
|||||||
49
dir.c
49
dir.c
@@ -49,7 +49,7 @@ struct cached_dir {
|
|||||||
static enum path_treatment read_directory_recursive(struct dir_struct *dir,
|
static enum path_treatment read_directory_recursive(struct dir_struct *dir,
|
||||||
struct index_state *istate, const char *path, int len,
|
struct index_state *istate, const char *path, int len,
|
||||||
struct untracked_cache_dir *untracked,
|
struct untracked_cache_dir *untracked,
|
||||||
int check_only, const struct pathspec *pathspec);
|
int check_only, int stop_at_first_file, const struct pathspec *pathspec);
|
||||||
static int get_dtype(struct dirent *de, struct index_state *istate,
|
static int get_dtype(struct dirent *de, struct index_state *istate,
|
||||||
const char *path, int len);
|
const char *path, int len);
|
||||||
|
|
||||||
@@ -1389,6 +1389,21 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
|
|||||||
case index_nonexistent:
|
case index_nonexistent:
|
||||||
if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
|
if (dir->flags & DIR_SHOW_OTHER_DIRECTORIES)
|
||||||
break;
|
break;
|
||||||
|
if (exclude &&
|
||||||
|
(dir->flags & DIR_SHOW_IGNORED_TOO) &&
|
||||||
|
(dir->flags & DIR_SHOW_IGNORED_DIRECTORY)) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is an excluded directory, and we are only
|
||||||
|
* showing the name of a excluded directory.
|
||||||
|
* Check to see if there are any contained files
|
||||||
|
* to determine if the directory is empty or not.
|
||||||
|
*/
|
||||||
|
if (read_directory_recursive(dir, istate, dirname, len,
|
||||||
|
untracked, 1, 1, pathspec) == path_excluded)
|
||||||
|
return path_excluded;
|
||||||
|
return path_none;
|
||||||
|
}
|
||||||
if (!(dir->flags & DIR_NO_GITLINKS)) {
|
if (!(dir->flags & DIR_NO_GITLINKS)) {
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0)
|
if (resolve_gitlink_ref(dirname, "HEAD", sha1) == 0)
|
||||||
@@ -1398,16 +1413,16 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* This is the "show_other_directories" case */
|
/* This is the "show_other_directories" case */
|
||||||
|
|
||||||
if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
|
if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
|
||||||
return exclude ? path_excluded : path_untracked;
|
return exclude ? path_excluded : path_untracked;
|
||||||
|
|
||||||
untracked = lookup_untracked(dir->untracked, untracked,
|
untracked = lookup_untracked(dir->untracked, untracked,
|
||||||
dirname + baselen, len - baselen);
|
dirname + baselen, len - baselen);
|
||||||
return read_directory_recursive(dir, istate, dirname, len,
|
return read_directory_recursive(dir, istate, dirname, len,
|
||||||
untracked, 1, pathspec);
|
untracked, 1, 0, pathspec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is an inexact early pruning of any recursive directory
|
* This is an inexact early pruning of any recursive directory
|
||||||
* reading - if the path cannot possibly be in the pathspec,
|
* reading - if the path cannot possibly be in the pathspec,
|
||||||
@@ -1633,7 +1648,7 @@ static enum path_treatment treat_path_fast(struct dir_struct *dir,
|
|||||||
* with check_only set.
|
* with check_only set.
|
||||||
*/
|
*/
|
||||||
return read_directory_recursive(dir, istate, path->buf, path->len,
|
return read_directory_recursive(dir, istate, path->buf, path->len,
|
||||||
cdir->ucd, 1, pathspec);
|
cdir->ucd, 1, 0, pathspec);
|
||||||
/*
|
/*
|
||||||
* We get path_recurse in the first run when
|
* We get path_recurse in the first run when
|
||||||
* directory_exists_in_index() returns index_nonexistent. We
|
* directory_exists_in_index() returns index_nonexistent. We
|
||||||
@@ -1798,7 +1813,7 @@ static void close_cached_dir(struct cached_dir *cdir)
|
|||||||
static enum path_treatment read_directory_recursive(struct dir_struct *dir,
|
static enum path_treatment read_directory_recursive(struct dir_struct *dir,
|
||||||
struct index_state *istate, const char *base, int baselen,
|
struct index_state *istate, const char *base, int baselen,
|
||||||
struct untracked_cache_dir *untracked, int check_only,
|
struct untracked_cache_dir *untracked, int check_only,
|
||||||
const struct pathspec *pathspec)
|
int stop_at_first_file, const struct pathspec *pathspec)
|
||||||
{
|
{
|
||||||
struct cached_dir cdir;
|
struct cached_dir cdir;
|
||||||
enum path_treatment state, subdir_state, dir_state = path_none;
|
enum path_treatment state, subdir_state, dir_state = path_none;
|
||||||
@@ -1832,12 +1847,32 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
|
|||||||
subdir_state =
|
subdir_state =
|
||||||
read_directory_recursive(dir, istate, path.buf,
|
read_directory_recursive(dir, istate, path.buf,
|
||||||
path.len, ud,
|
path.len, ud,
|
||||||
check_only, pathspec);
|
check_only, stop_at_first_file, pathspec);
|
||||||
if (subdir_state > dir_state)
|
if (subdir_state > dir_state)
|
||||||
dir_state = subdir_state;
|
dir_state = subdir_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_only) {
|
if (check_only) {
|
||||||
|
if (stop_at_first_file) {
|
||||||
|
/*
|
||||||
|
* In general, if we are stopping at the first found file,
|
||||||
|
* We can only signal that a path of at least "excluded" was
|
||||||
|
* found. If the first file we find is "excluded" - there might
|
||||||
|
* be other untracked files later on that will not be searched.
|
||||||
|
*
|
||||||
|
* In current usage of this function, stop_at_first_file will
|
||||||
|
* only be set when called from a directory that matches the
|
||||||
|
* exclude pattern - there should be no untracked files -
|
||||||
|
* all contents should be marked as excluded.
|
||||||
|
*/
|
||||||
|
if (dir_state == path_excluded)
|
||||||
|
break;
|
||||||
|
else if (dir_state > path_excluded) {
|
||||||
|
dir_state = path_excluded;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* abort early if maximum state has been reached */
|
/* abort early if maximum state has been reached */
|
||||||
if (dir_state == path_untracked) {
|
if (dir_state == path_untracked) {
|
||||||
if (cdir.fdir)
|
if (cdir.fdir)
|
||||||
@@ -2108,7 +2143,7 @@ int read_directory(struct dir_struct *dir, struct index_state *istate,
|
|||||||
*/
|
*/
|
||||||
dir->untracked = NULL;
|
dir->untracked = NULL;
|
||||||
if (!len || treat_leading_path(dir, istate, path, len, pathspec))
|
if (!len || treat_leading_path(dir, istate, path, len, pathspec))
|
||||||
read_directory_recursive(dir, istate, path, len, untracked, 0, pathspec);
|
read_directory_recursive(dir, istate, path, len, untracked, 0, 0, pathspec);
|
||||||
QSORT(dir->entries, dir->nr, cmp_dir_entry);
|
QSORT(dir->entries, dir->nr, cmp_dir_entry);
|
||||||
QSORT(dir->ignored, dir->ignored_nr, cmp_dir_entry);
|
QSORT(dir->ignored, dir->ignored_nr, cmp_dir_entry);
|
||||||
|
|
||||||
|
|||||||
3
dir.h
3
dir.h
@@ -152,7 +152,8 @@ struct dir_struct {
|
|||||||
DIR_COLLECT_IGNORED = 1<<4,
|
DIR_COLLECT_IGNORED = 1<<4,
|
||||||
DIR_SHOW_IGNORED_TOO = 1<<5,
|
DIR_SHOW_IGNORED_TOO = 1<<5,
|
||||||
DIR_COLLECT_KILLED_ONLY = 1<<6,
|
DIR_COLLECT_KILLED_ONLY = 1<<6,
|
||||||
DIR_KEEP_UNTRACKED_CONTENTS = 1<<7
|
DIR_KEEP_UNTRACKED_CONTENTS = 1<<7,
|
||||||
|
DIR_SHOW_IGNORED_DIRECTORY = 1<<8
|
||||||
} flags;
|
} flags;
|
||||||
struct dir_entry **entries;
|
struct dir_entry **entries;
|
||||||
struct dir_entry **ignored;
|
struct dir_entry **ignored;
|
||||||
|
|||||||
149
t/t7520-status-show-ignored-directory.sh
Executable file
149
t/t7520-status-show-ignored-directory.sh
Executable file
@@ -0,0 +1,149 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
test_description='git status collapse ignored'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
|
||||||
|
cat >.gitignore <<\EOF
|
||||||
|
*.ign
|
||||||
|
ignored_dir/
|
||||||
|
!*.unignore
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# commit initial ignore file
|
||||||
|
test_expect_success 'setup initial commit and ignore file' '
|
||||||
|
git add . &&
|
||||||
|
test_tick &&
|
||||||
|
git commit -m "Initial commit"
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >expect <<\EOF
|
||||||
|
? expect
|
||||||
|
? output
|
||||||
|
! dir/ignored/ignored_1.ign
|
||||||
|
! dir/ignored/ignored_2.ign
|
||||||
|
! ignored/ignored_1.ign
|
||||||
|
! ignored/ignored_2.ign
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Test status behavior on folder with ignored files
|
||||||
|
test_expect_success 'setup folder with ignored files' '
|
||||||
|
mkdir -p ignored dir/ignored &&
|
||||||
|
touch ignored/ignored_1.ign ignored/ignored_2.ign \
|
||||||
|
dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'Verify behavior of status on folders with ignored files' '
|
||||||
|
test_when_finished "git clean -fdx" &&
|
||||||
|
git status --porcelain=v2 --ignored --untracked-files=all --show-ignored-directory >output &&
|
||||||
|
test_i18ncmp expect output
|
||||||
|
'
|
||||||
|
|
||||||
|
# Test status bahavior on folder with tracked and ignored files
|
||||||
|
cat >expect <<\EOF
|
||||||
|
? expect
|
||||||
|
? output
|
||||||
|
! dir/tracked_ignored/ignored_1.ign
|
||||||
|
! dir/tracked_ignored/ignored_2.ign
|
||||||
|
! tracked_ignored/ignored_1.ign
|
||||||
|
! tracked_ignored/ignored_2.ign
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'setup folder with tracked & ignored files' '
|
||||||
|
mkdir -p tracked_ignored dir/tracked_ignored &&
|
||||||
|
touch tracked_ignored/tracked_1 tracked_ignored/tracked_2 \
|
||||||
|
tracked_ignored/ignored_1.ign tracked_ignored/ignored_2.ign \
|
||||||
|
dir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 \
|
||||||
|
dir/tracked_ignored/ignored_1.ign dir/tracked_ignored/ignored_2.ign &&
|
||||||
|
|
||||||
|
git add tracked_ignored/tracked_1 tracked_ignored/tracked_2 \
|
||||||
|
dir/tracked_ignored/tracked_1 dir/tracked_ignored/tracked_2 &&
|
||||||
|
test_tick &&
|
||||||
|
git commit -m "commit tracked files"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'Verify status on folder with tracked & ignored files' '
|
||||||
|
test_when_finished "git clean -fdx && git reset HEAD~1 --hard" &&
|
||||||
|
git status --porcelain=v2 --ignored --untracked-files=all --show-ignored-directory >output &&
|
||||||
|
test_i18ncmp expect output
|
||||||
|
'
|
||||||
|
|
||||||
|
|
||||||
|
# Test status behavior on folder with untracked and ignored files
|
||||||
|
cat >expect <<\EOF
|
||||||
|
? dir/untracked_ignored/untracked_1
|
||||||
|
? dir/untracked_ignored/untracked_2
|
||||||
|
? expect
|
||||||
|
? output
|
||||||
|
? untracked_ignored/untracked_1
|
||||||
|
? untracked_ignored/untracked_2
|
||||||
|
! dir/untracked_ignored/ignored_1.ign
|
||||||
|
! dir/untracked_ignored/ignored_2.ign
|
||||||
|
! untracked_ignored/ignored_1.ign
|
||||||
|
! untracked_ignored/ignored_2.ign
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'setup folder with tracked & ignored files' '
|
||||||
|
mkdir -p untracked_ignored dir/untracked_ignored &&
|
||||||
|
touch untracked_ignored/untracked_1 untracked_ignored/untracked_2 \
|
||||||
|
untracked_ignored/ignored_1.ign untracked_ignored/ignored_2.ign \
|
||||||
|
dir/untracked_ignored/untracked_1 dir/untracked_ignored/untracked_2 \
|
||||||
|
dir/untracked_ignored/ignored_1.ign dir/untracked_ignored/ignored_2.ign
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'Verify status on folder with tracked & ignored files' '
|
||||||
|
test_when_finished "git clean -fdx" &&
|
||||||
|
git status --porcelain=v2 --ignored --untracked-files=all --show-ignored-directory >output &&
|
||||||
|
test_i18ncmp expect output
|
||||||
|
'
|
||||||
|
|
||||||
|
# Test status behavior on ignored folder
|
||||||
|
cat >expect <<\EOF
|
||||||
|
? expect
|
||||||
|
? output
|
||||||
|
! ignored_dir/
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'setup folder with tracked & ignored files' '
|
||||||
|
mkdir ignored_dir &&
|
||||||
|
touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
|
||||||
|
ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'Verify status on folder with tracked & ignored files' '
|
||||||
|
test_when_finished "git clean -fdx" &&
|
||||||
|
git status --porcelain=v2 --ignored --untracked-files=all --show-ignored-directory >output &&
|
||||||
|
test_i18ncmp expect output
|
||||||
|
'
|
||||||
|
|
||||||
|
# Test status behavior on ignored folder with tracked file
|
||||||
|
cat >expect <<\EOF
|
||||||
|
? expect
|
||||||
|
? output
|
||||||
|
! ignored_dir/ignored_1
|
||||||
|
! ignored_dir/ignored_1.ign
|
||||||
|
! ignored_dir/ignored_2
|
||||||
|
! ignored_dir/ignored_2.ign
|
||||||
|
EOF
|
||||||
|
|
||||||
|
test_expect_success 'setup folder with tracked & ignored files' '
|
||||||
|
mkdir ignored_dir &&
|
||||||
|
touch ignored_dir/ignored_1 ignored_dir/ignored_2 \
|
||||||
|
ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign \
|
||||||
|
ignored_dir/tracked &&
|
||||||
|
git add -f ignored_dir/tracked &&
|
||||||
|
test_tick &&
|
||||||
|
git commit -m "Force add file in ignored directory"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'Verify status on folder with tracked & ignored files' '
|
||||||
|
test_when_finished "git clean -fdx && git reset HEAD~1 --hard" &&
|
||||||
|
git status --porcelain=v2 --ignored --untracked-files=all --show-ignored-directory >output &&
|
||||||
|
test_i18ncmp expect output
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
||||||
|
|
||||||
@@ -664,6 +664,10 @@ static void wt_status_collect_untracked(struct wt_status *s)
|
|||||||
dir.flags |= DIR_SHOW_IGNORED_TOO;
|
dir.flags |= DIR_SHOW_IGNORED_TOO;
|
||||||
else
|
else
|
||||||
dir.untracked = the_index.untracked;
|
dir.untracked = the_index.untracked;
|
||||||
|
|
||||||
|
if (s->show_ignored_directory)
|
||||||
|
dir.flags |= DIR_SHOW_IGNORED_DIRECTORY;
|
||||||
|
|
||||||
setup_standard_excludes(&dir);
|
setup_standard_excludes(&dir);
|
||||||
|
|
||||||
fill_directory(&dir, &the_index, &s->pathspec);
|
fill_directory(&dir, &the_index, &s->pathspec);
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ struct wt_status {
|
|||||||
int submodule_summary;
|
int submodule_summary;
|
||||||
int show_ignored_files;
|
int show_ignored_files;
|
||||||
enum untracked_status_type show_untracked_files;
|
enum untracked_status_type show_untracked_files;
|
||||||
|
int show_ignored_directory;
|
||||||
const char *ignore_submodule_arg;
|
const char *ignore_submodule_arg;
|
||||||
char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];
|
char color_palette[WT_STATUS_MAXSLOT][COLOR_MAXLEN];
|
||||||
unsigned colopts;
|
unsigned colopts;
|
||||||
|
|||||||
Reference in New Issue
Block a user