Skip to content

Commit 0e35fcb

Browse files
committed
Merge branch 'cc/untracked'
Update the untracked cache subsystem and change its primary UI from "git update-index" to "git config". * cc/untracked: t7063: add tests for core.untrackedCache test-dump-untracked-cache: don't modify the untracked cache config: add core.untrackedCache dir: simplify untracked cache "ident" field dir: add remove_untracked_cache() dir: add {new,add}_untracked_cache() update-index: move 'uc' var declaration update-index: add untracked cache notifications update-index: add --test-untracked-cache update-index: use enum for untracked cache options dir: free untracked cache when removing it
2 parents 81ad6a9 + 7c12178 commit 0e35fcb

File tree

12 files changed

+307
-56
lines changed

12 files changed

+307
-56
lines changed

Documentation/config.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,15 @@ core.trustctime::
308308
crawlers and some backup systems).
309309
See linkgit:git-update-index[1]. True by default.
310310

311+
core.untrackedCache::
312+
Determines what to do about the untracked cache feature of the
313+
index. It will be kept, if this variable is unset or set to
314+
`keep`. It will automatically be added if set to `true`. And
315+
it will automatically be removed, if set to `false`. Before
316+
setting it to `true`, you should check that mtime is working
317+
properly on your system.
318+
See linkgit:git-update-index[1]. `keep` by default.
319+
311320
core.checkStat::
312321
Determines which stat fields to match between the index
313322
and work tree. The user can set this to 'default' or

Documentation/git-update-index.txt

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ SYNOPSIS
1818
[--[no-]skip-worktree]
1919
[--ignore-submodules]
2020
[--[no-]split-index]
21-
[--[no-|force-]untracked-cache]
21+
[--[no-|test-|force-]untracked-cache]
2222
[--really-refresh] [--unresolve] [--again | -g]
2323
[--info-only] [--index-info]
2424
[-z] [--stdin] [--index-version <n>]
@@ -174,17 +174,30 @@ may not support it yet.
174174

175175
--untracked-cache::
176176
--no-untracked-cache::
177-
Enable or disable untracked cache extension. This could speed
178-
up for commands that involve determining untracked files such
179-
as `git status`. The underlying operating system and file
180-
system must change `st_mtime` field of a directory if files
181-
are added or deleted in that directory.
177+
Enable or disable untracked cache feature. Please use
178+
`--test-untracked-cache` before enabling it.
179+
+
180+
These options take effect whatever the value of the `core.untrackedCache`
181+
configuration variable (see linkgit:git-config[1]). But a warning is
182+
emitted when the change goes against the configured value, as the
183+
configured value will take effect next time the index is read and this
184+
will remove the intended effect of the option.
185+
186+
--test-untracked-cache::
187+
Only perform tests on the working directory to make sure
188+
untracked cache can be used. You have to manually enable
189+
untracked cache using `--untracked-cache` or
190+
`--force-untracked-cache` or the `core.untrackedCache`
191+
configuration variable afterwards if you really want to use
192+
it. If a test fails the exit code is 1 and a message
193+
explains what is not working as needed, otherwise the exit
194+
code is 0 and OK is printed.
182195

183196
--force-untracked-cache::
184-
For safety, `--untracked-cache` performs tests on the working
185-
directory to make sure untracked cache can be used. These
186-
tests can take a few seconds. `--force-untracked-cache` can be
187-
used to skip the tests.
197+
Same as `--untracked-cache`. Provided for backwards
198+
compatibility with older versions of Git where
199+
`--untracked-cache` used to imply `--test-untracked-cache` but
200+
this option would enable the extension unconditionally.
188201

189202
\--::
190203
Do not interpret any more arguments as options.
@@ -375,6 +388,37 @@ Although this bit looks similar to assume-unchanged bit, its goal is
375388
different from assume-unchanged bit's. Skip-worktree also takes
376389
precedence over assume-unchanged bit when both are set.
377390

391+
Untracked cache
392+
---------------
393+
394+
This cache is meant to speed up commands that involve determining
395+
untracked files such as `git status`.
396+
397+
This feature works by recording the mtime of the working tree
398+
directories and then omitting reading directories and stat calls
399+
against files in those directories whose mtime hasn't changed. For
400+
this to work the underlying operating system and file system must
401+
change the `st_mtime` field of directories if files in the directory
402+
are added, modified or deleted.
403+
404+
You can test whether the filesystem supports that with the
405+
`--test-untracked-cache` option. The `--untracked-cache` option used
406+
to implicitly perform that test in older versions of Git, but that's
407+
no longer the case.
408+
409+
If you want to enable (or disable) this feature, it is easier to use
410+
the `core.untrackedCache` configuration variable (see
411+
linkgit:git-config[1]) than using the `--untracked-cache` option to
412+
`git update-index` in each repository, especially if you want to do so
413+
across all repositories you use, because you can set the configuration
414+
variable to `true` (or `false`) in your `$HOME/.gitconfig` just once
415+
and have it affect all repositories you touch.
416+
417+
When the `core.untrackedCache` configuration variable is changed, the
418+
untracked cache is added to or removed from the index the next time a
419+
command reads the index; while when `--[no-|force-]untracked-cache`
420+
are used, the untracked cache is immediately added to or removed from
421+
the index.
378422

379423
Configuration
380424
-------------
@@ -400,6 +444,9 @@ It can be useful when the inode change time is regularly modified by
400444
something outside Git (file system crawlers and backup systems use
401445
ctime for marking files processed) (see linkgit:git-config[1]).
402446

447+
The untracked cache extension can be enabled by the
448+
`core.untrackedCache` configuration variable (see
449+
linkgit:git-config[1]).
403450

404451
SEE ALSO
405452
--------

builtin/update-index.c

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ static int mark_skip_worktree_only;
3535
#define UNMARK_FLAG 2
3636
static struct strbuf mtime_dir = STRBUF_INIT;
3737

38+
/* Untracked cache mode */
39+
enum uc_mode {
40+
UC_UNSPECIFIED = -1,
41+
UC_DISABLE = 0,
42+
UC_ENABLE,
43+
UC_TEST,
44+
UC_FORCE
45+
};
46+
3847
__attribute__((format (printf, 1, 2)))
3948
static void report(const char *fmt, ...)
4049
{
@@ -121,7 +130,7 @@ static int test_if_untracked_cache_is_supported(void)
121130
if (!mkdtemp(mtime_dir.buf))
122131
die_errno("Could not make temporary directory");
123132

124-
fprintf(stderr, _("Testing "));
133+
fprintf(stderr, _("Testing mtime in '%s' "), xgetcwd());
125134
atexit(remove_test_directory);
126135
xstat_mtime_dir(&st);
127136
fill_stat_data(&base, &st);
@@ -904,7 +913,7 @@ static int reupdate_callback(struct parse_opt_ctx_t *ctx,
904913
int cmd_update_index(int argc, const char **argv, const char *prefix)
905914
{
906915
int newfd, entries, has_errors = 0, nul_term_line = 0;
907-
int untracked_cache = -1;
916+
enum uc_mode untracked_cache = UC_UNSPECIFIED;
908917
int read_from_stdin = 0;
909918
int prefix_length = prefix ? strlen(prefix) : 0;
910919
int preferred_index_format = 0;
@@ -999,8 +1008,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
9991008
N_("enable or disable split index")),
10001009
OPT_BOOL(0, "untracked-cache", &untracked_cache,
10011010
N_("enable/disable untracked cache")),
1011+
OPT_SET_INT(0, "test-untracked-cache", &untracked_cache,
1012+
N_("test if the filesystem supports untracked cache"), UC_TEST),
10021013
OPT_SET_INT(0, "force-untracked-cache", &untracked_cache,
1003-
N_("enable untracked cache without testing the filesystem"), 2),
1014+
N_("enable untracked cache without testing the filesystem"), UC_FORCE),
10041015
OPT_END()
10051016
};
10061017

@@ -1109,27 +1120,32 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
11091120
the_index.split_index = NULL;
11101121
the_index.cache_changed |= SOMETHING_CHANGED;
11111122
}
1112-
if (untracked_cache > 0) {
1113-
struct untracked_cache *uc;
11141123

1115-
if (untracked_cache < 2) {
1116-
setup_work_tree();
1117-
if (!test_if_untracked_cache_is_supported())
1118-
return 1;
1119-
}
1120-
if (!the_index.untracked) {
1121-
uc = xcalloc(1, sizeof(*uc));
1122-
strbuf_init(&uc->ident, 100);
1123-
uc->exclude_per_dir = ".gitignore";
1124-
/* should be the same flags used by git-status */
1125-
uc->dir_flags = DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
1126-
the_index.untracked = uc;
1127-
}
1128-
add_untracked_ident(the_index.untracked);
1129-
the_index.cache_changed |= UNTRACKED_CHANGED;
1130-
} else if (!untracked_cache && the_index.untracked) {
1131-
the_index.untracked = NULL;
1132-
the_index.cache_changed |= UNTRACKED_CHANGED;
1124+
switch (untracked_cache) {
1125+
case UC_UNSPECIFIED:
1126+
break;
1127+
case UC_DISABLE:
1128+
if (git_config_get_untracked_cache() == 1)
1129+
warning("core.untrackedCache is set to true; "
1130+
"remove or change it, if you really want to "
1131+
"disable the untracked cache");
1132+
remove_untracked_cache(&the_index);
1133+
report(_("Untracked cache disabled"));
1134+
break;
1135+
case UC_TEST:
1136+
setup_work_tree();
1137+
return !test_if_untracked_cache_is_supported();
1138+
case UC_ENABLE:
1139+
case UC_FORCE:
1140+
if (git_config_get_untracked_cache() == 0)
1141+
warning("core.untrackedCache is set to false; "
1142+
"remove or change it, if you really want to "
1143+
"enable the untracked cache");
1144+
add_untracked_cache(&the_index);
1145+
report(_("Untracked cache enabled for '%s'"), get_git_work_tree());
1146+
break;
1147+
default:
1148+
die("Bug: bad untracked_cache value: %d", untracked_cache);
11331149
}
11341150

11351151
if (active_cache_changed) {

cache.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,6 +1624,14 @@ extern int git_config_get_bool(const char *key, int *dest);
16241624
extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest);
16251625
extern int git_config_get_maybe_bool(const char *key, int *dest);
16261626
extern int git_config_get_pathname(const char *key, const char **dest);
1627+
extern int git_config_get_untracked_cache(void);
1628+
1629+
/*
1630+
* This is a hack for test programs like test-dump-untracked-cache to
1631+
* ensure that they do not modify the untracked cache when reading it.
1632+
* Do not use it otherwise!
1633+
*/
1634+
extern int ignore_untracked_cache_config;
16271635

16281636
struct key_value_info {
16291637
const char *filename;

config.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,6 +1594,30 @@ int git_config_get_pathname(const char *key, const char **dest)
15941594
return ret;
15951595
}
15961596

1597+
int git_config_get_untracked_cache(void)
1598+
{
1599+
int val = -1;
1600+
const char *v;
1601+
1602+
/* Hack for test programs like test-dump-untracked-cache */
1603+
if (ignore_untracked_cache_config)
1604+
return -1;
1605+
1606+
if (!git_config_get_maybe_bool("core.untrackedcache", &val))
1607+
return val;
1608+
1609+
if (!git_config_get_value("core.untrackedcache", &v)) {
1610+
if (!strcasecmp(v, "keep"))
1611+
return -1;
1612+
1613+
error("unknown core.untrackedCache value '%s'; "
1614+
"using 'keep' default value", v);
1615+
return -1;
1616+
}
1617+
1618+
return -1; /* default value */
1619+
}
1620+
15971621
NORETURN
15981622
void git_die_config_linenr(const char *key, const char *filename, int linenr)
15991623
{

contrib/completion/git-completion.bash

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2060,6 +2060,7 @@ _git_config ()
20602060
core.sparseCheckout
20612061
core.symlinks
20622062
core.trustctime
2063+
core.untrackedCache
20632064
core.warnAmbiguousRefs
20642065
core.whitespace
20652066
core.worktree

dir.c

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1839,31 +1839,67 @@ static const char *get_ident_string(void)
18391839
return sb.buf;
18401840
if (uname(&uts) < 0)
18411841
die_errno(_("failed to get kernel name and information"));
1842-
strbuf_addf(&sb, "Location %s, system %s %s %s", get_git_work_tree(),
1843-
uts.sysname, uts.release, uts.version);
1842+
strbuf_addf(&sb, "Location %s, system %s", get_git_work_tree(),
1843+
uts.sysname);
18441844
return sb.buf;
18451845
}
18461846

18471847
static int ident_in_untracked(const struct untracked_cache *uc)
18481848
{
1849-
const char *end = uc->ident.buf + uc->ident.len;
1850-
const char *p = uc->ident.buf;
1849+
/*
1850+
* Previous git versions may have saved many NUL separated
1851+
* strings in the "ident" field, but it is insane to manage
1852+
* many locations, so just take care of the first one.
1853+
*/
18511854

1852-
for (p = uc->ident.buf; p < end; p += strlen(p) + 1)
1853-
if (!strcmp(p, get_ident_string()))
1854-
return 1;
1855-
return 0;
1855+
return !strcmp(uc->ident.buf, get_ident_string());
18561856
}
18571857

1858-
void add_untracked_ident(struct untracked_cache *uc)
1858+
static void set_untracked_ident(struct untracked_cache *uc)
18591859
{
1860-
if (ident_in_untracked(uc))
1861-
return;
1860+
strbuf_reset(&uc->ident);
18621861
strbuf_addstr(&uc->ident, get_ident_string());
1863-
/* this strbuf contains a list of strings, save NUL too */
1862+
1863+
/*
1864+
* This strbuf used to contain a list of NUL separated
1865+
* strings, so save NUL too for backward compatibility.
1866+
*/
18641867
strbuf_addch(&uc->ident, 0);
18651868
}
18661869

1870+
static void new_untracked_cache(struct index_state *istate)
1871+
{
1872+
struct untracked_cache *uc = xcalloc(1, sizeof(*uc));
1873+
strbuf_init(&uc->ident, 100);
1874+
uc->exclude_per_dir = ".gitignore";
1875+
/* should be the same flags used by git-status */
1876+
uc->dir_flags = DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
1877+
set_untracked_ident(uc);
1878+
istate->untracked = uc;
1879+
istate->cache_changed |= UNTRACKED_CHANGED;
1880+
}
1881+
1882+
void add_untracked_cache(struct index_state *istate)
1883+
{
1884+
if (!istate->untracked) {
1885+
new_untracked_cache(istate);
1886+
} else {
1887+
if (!ident_in_untracked(istate->untracked)) {
1888+
free_untracked_cache(istate->untracked);
1889+
new_untracked_cache(istate);
1890+
}
1891+
}
1892+
}
1893+
1894+
void remove_untracked_cache(struct index_state *istate)
1895+
{
1896+
if (istate->untracked) {
1897+
free_untracked_cache(istate->untracked);
1898+
istate->untracked = NULL;
1899+
istate->cache_changed |= UNTRACKED_CHANGED;
1900+
}
1901+
}
1902+
18671903
static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *dir,
18681904
int base_len,
18691905
const struct pathspec *pathspec)
@@ -1921,7 +1957,7 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
19211957
return NULL;
19221958

19231959
if (!ident_in_untracked(dir->untracked)) {
1924-
warning(_("Untracked cache is disabled on this system."));
1960+
warning(_("Untracked cache is disabled on this system or location."));
19251961
return NULL;
19261962
}
19271963

dir.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,5 +307,6 @@ void untracked_cache_add_to_index(struct index_state *, const char *);
307307
void free_untracked_cache(struct untracked_cache *);
308308
struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz);
309309
void write_untracked_extension(struct strbuf *out, struct untracked_cache *untracked);
310-
void add_untracked_ident(struct untracked_cache *);
310+
void add_untracked_cache(struct index_state *istate);
311+
void remove_untracked_cache(struct index_state *istate);
311312
#endif

environment.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ int auto_comment_line_char;
8787
/* Parallel index stat data preload? */
8888
int core_preload_index = 1;
8989

90+
/*
91+
* This is a hack for test programs like test-dump-untracked-cache to
92+
* ensure that they do not modify the untracked cache when reading it.
93+
* Do not use it otherwise!
94+
*/
95+
int ignore_untracked_cache_config;
96+
9097
/* This is set by setup_git_dir_gently() and/or git_default_config() */
9198
char *git_work_tree_cfg;
9299
static char *work_tree;

0 commit comments

Comments
 (0)