Skip to content

Commit f3d9bc8

Browse files
committed
Merge branch 'rr/status-untracked-advice'
The advice message given by "git status" when it takes long time to enumerate untracked paths has been updated. * rr/status-untracked-advice: status: modernize git-status "slow untracked files" advice
2 parents 053650d + ecbc23e commit f3d9bc8

File tree

3 files changed

+153
-5
lines changed

3 files changed

+153
-5
lines changed

Documentation/git-status.txt

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,66 @@ during the write may conflict with other simultaneous processes, causing
457457
them to fail. Scripts running `status` in the background should consider
458458
using `git --no-optional-locks status` (see linkgit:git[1] for details).
459459

460+
UNTRACKED FILES AND PERFORMANCE
461+
-------------------------------
462+
463+
`git status` can be very slow in large worktrees if/when it
464+
needs to search for untracked files and directories. There are
465+
many configuration options available to speed this up by either
466+
avoiding the work or making use of cached results from previous
467+
Git commands. There is no single optimum set of settings right
468+
for everyone. We'll list a summary of the relevant options to help
469+
you, but before going into the list, you may want to run `git status`
470+
again, because your configuration may already be caching `git status`
471+
results, so it could be faster on subsequent runs.
472+
473+
* The `--untracked-files=no` flag or the
474+
`status.showUntrackedfiles=false` config (see above for both):
475+
indicate that `git status` should not report untracked
476+
files. This is the fastest option. `git status` will not list
477+
the untracked files, so you need to be careful to remember if
478+
you create any new files and manually `git add` them.
479+
480+
* `advice.statusUoption=false` (see linkgit:git-config[1]):
481+
setting this variable to `false` disables the warning message
482+
given when enumerating untracked files takes more than 2
483+
seconds. In a large project, it may take longer and the user
484+
may have already accepted the trade off (e.g. using "-uno" may
485+
not be an acceptable option for the user), in which case, there
486+
is no point issuing the warning message, and in such a case,
487+
disabling the warning may be the best.
488+
489+
* `core.untrackedCache=true` (see linkgit:git-update-index[1]):
490+
enable the untracked cache feature and only search directories
491+
that have been modified since the previous `git status` command.
492+
Git remembers the set of untracked files within each directory
493+
and assumes that if a directory has not been modified, then
494+
the set of untracked files within has not changed. This is much
495+
faster than enumerating the contents of every directory, but still
496+
not without cost, because Git still has to search for the set of
497+
modified directories. The untracked cache is stored in the
498+
`.git/index` file. The reduced cost of searching for untracked
499+
files is offset slightly by the increased size of the index and
500+
the cost of keeping it up-to-date. That reduced search time is
501+
usually worth the additional size.
502+
503+
* `core.untrackedCache=true` and `core.fsmonitor=true` or
504+
`core.fsmonitor=<hook_command_pathname>` (see
505+
linkgit:git-update-index[1]): enable both the untracked cache
506+
and FSMonitor features and only search directories that have
507+
been modified since the previous `git status` command. This
508+
is faster than using just the untracked cache alone because
509+
Git can also avoid searching for modified directories. Git
510+
only has to enumerate the exact set of directories that have
511+
changed recently. While the FSMonitor feature can be enabled
512+
without the untracked cache, the benefits are greatly reduced
513+
in that case.
514+
515+
Note that after you turn on the untracked cache and/or FSMonitor
516+
features it may take a few `git status` commands for the various
517+
caches to warm up before you see improved command times. This is
518+
normal.
519+
460520
SEE ALSO
461521
--------
462522
linkgit:gitignore[5]

t/t7508-status.sh

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,4 +1676,74 @@ test_expect_success 'racy timestamps will be fixed for dirty worktree' '
16761676
! test_is_magic_mtime .git/index
16771677
'
16781678

1679+
test_expect_success 'setup slow status advice' '
1680+
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main git init slowstatus &&
1681+
(
1682+
cd slowstatus &&
1683+
cat >.gitignore <<-\EOF &&
1684+
/actual
1685+
/expected
1686+
/out
1687+
EOF
1688+
git add .gitignore &&
1689+
git commit -m "Add .gitignore" &&
1690+
git config advice.statusuoption true
1691+
)
1692+
'
1693+
1694+
test_expect_success 'slow status advice when core.untrackedCache and fsmonitor are unset' '
1695+
(
1696+
cd slowstatus &&
1697+
git config core.untrackedCache false &&
1698+
git config core.fsmonitor false &&
1699+
GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
1700+
cat >expected <<-\EOF &&
1701+
On branch main
1702+
1703+
It took 3.25 seconds to enumerate untracked files.
1704+
See '\''git help status'\'' for information on how to improve this.
1705+
1706+
nothing to commit, working tree clean
1707+
EOF
1708+
test_cmp expected actual
1709+
)
1710+
'
1711+
1712+
test_expect_success 'slow status advice when core.untrackedCache true, but not fsmonitor' '
1713+
(
1714+
cd slowstatus &&
1715+
git config core.untrackedCache true &&
1716+
git config core.fsmonitor false &&
1717+
GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
1718+
cat >expected <<-\EOF &&
1719+
On branch main
1720+
1721+
It took 3.25 seconds to enumerate untracked files.
1722+
See '\''git help status'\'' for information on how to improve this.
1723+
1724+
nothing to commit, working tree clean
1725+
EOF
1726+
test_cmp expected actual
1727+
)
1728+
'
1729+
1730+
test_expect_success 'slow status advice when core.untrackedCache true, and fsmonitor' '
1731+
(
1732+
cd slowstatus &&
1733+
git config core.untrackedCache true &&
1734+
git config core.fsmonitor true &&
1735+
GIT_TEST_UF_DELAY_WARNING=1 git status >actual &&
1736+
cat >expected <<-\EOF &&
1737+
On branch main
1738+
1739+
It took 3.25 seconds to enumerate untracked files,
1740+
but the results were cached, and subsequent runs may be faster.
1741+
See '\''git help status'\'' for information on how to improve this.
1742+
1743+
nothing to commit, working tree clean
1744+
EOF
1745+
test_cmp expected actual
1746+
)
1747+
'
1748+
16791749
test_done

wt-status.c

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
#include "worktree.h"
1919
#include "lockfile.h"
2020
#include "sequencer.h"
21+
#include "fsmonitor-settings.h"
2122

2223
#define AB_DELAY_WARNING_IN_MS (2 * 1000)
24+
#define UF_DELAY_WARNING_IN_MS (2 * 1000)
2325

2426
static const char cut_line[] =
2527
"------------------------ >8 ------------------------\n";
@@ -1205,6 +1207,13 @@ static void wt_longstatus_print_tracking(struct wt_status *s)
12051207
strbuf_release(&sb);
12061208
}
12071209

1210+
static int uf_was_slow(struct wt_status *s)
1211+
{
1212+
if (getenv("GIT_TEST_UF_DELAY_WARNING"))
1213+
s->untracked_in_ms = 3250;
1214+
return UF_DELAY_WARNING_IN_MS < s->untracked_in_ms;
1215+
}
1216+
12081217
static void show_merge_in_progress(struct wt_status *s,
12091218
const char *color)
12101219
{
@@ -1814,6 +1823,7 @@ static void wt_longstatus_print(struct wt_status *s)
18141823
{
18151824
const char *branch_color = color(WT_STATUS_ONBRANCH, s);
18161825
const char *branch_status_color = color(WT_STATUS_HEADER, s);
1826+
enum fsmonitor_mode fsm_mode = fsm_settings__get_mode(s->repo);
18171827

18181828
if (s->branch) {
18191829
const char *on_what = _("On branch ");
@@ -1870,13 +1880,21 @@ static void wt_longstatus_print(struct wt_status *s)
18701880
wt_longstatus_print_other(s, &s->untracked, _("Untracked files"), "add");
18711881
if (s->show_ignored_mode)
18721882
wt_longstatus_print_other(s, &s->ignored, _("Ignored files"), "add -f");
1873-
if (advice_enabled(ADVICE_STATUS_U_OPTION) && 2000 < s->untracked_in_ms) {
1883+
if (advice_enabled(ADVICE_STATUS_U_OPTION) && uf_was_slow(s)) {
18741884
status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
1885+
if (fsm_mode > FSMONITOR_MODE_DISABLED) {
1886+
status_printf_ln(s, GIT_COLOR_NORMAL,
1887+
_("It took %.2f seconds to enumerate untracked files,\n"
1888+
"but the results were cached, and subsequent runs may be faster."),
1889+
s->untracked_in_ms / 1000.0);
1890+
} else {
1891+
status_printf_ln(s, GIT_COLOR_NORMAL,
1892+
_("It took %.2f seconds to enumerate untracked files."),
1893+
s->untracked_in_ms / 1000.0);
1894+
}
18751895
status_printf_ln(s, GIT_COLOR_NORMAL,
1876-
_("It took %.2f seconds to enumerate untracked files. 'status -uno'\n"
1877-
"may speed it up, but you have to be careful not to forget to add\n"
1878-
"new files yourself (see 'git help status')."),
1879-
s->untracked_in_ms / 1000.0);
1896+
_("See 'git help status' for information on how to improve this."));
1897+
status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
18801898
}
18811899
} else if (s->committable)
18821900
status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),

0 commit comments

Comments
 (0)