git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 3/3] core: convert build-time USE_NSEC into runtime core.useNanosec

From
D. Ben Knoble <ben.knoble@gmail.com>
Date
Aug 7, 2026, 11:56 UTC
Message-ID
<dbbd96d50811e4c2decb6f754b56dc1f7ee0944a.1786103607.git.ben.knoble@gmail.com>
In-Reply-To
<cover.1786103607.git.ben.knoble@gmail.com>

Racy Git problems persist today, manifesting themselves in the performance of commands like "git diff" in new worktrees [1]. We have long had a build knob "USE_NSEC" to tell Git to use in-core nanosecond precision when available, which mitigates most if not all racy issues, but most builds we know about it don't use it. In part, that's because someone distributing Git can't safely enable it at compile-time if they don't know exactly what platforms their distribution will be used on.

[1]: https://lore.kernel.org/git/CALnO6CADMJSixqYvL1Yo8qKX5rWhKQ+2OoSEuPUh-yoeK9TseQ@mail.gmail.com

These days, most platforms are likely to be safe for the USE_NSEC code. Regardless, we want to give users the ability to benefit from it. This requires exposing the compile-time gated code as a runtime option.

In addition, update the Racy Git documentation and other mentions of USE_NSEC in the code.

Best-viewed-with: --ignore-space-change
Signed-off-by: D. Ben Knoble <ben.knoble@gmail.com>
---
Notes (benknoble/commits):
    Repeating the benchmark from my original mail [1] shows an improvement
    
    # git worktree add -d ../perf-test HEAD
    # hyperfine -N --warmup=10 './build/bin-wrappers/git diff'
    Benchmark 1: ./build/bin-wrappers/git diff
      Time (mean ± σ):       3.8 ms ±   0.4 ms    [User: 4.7 ms, System: 4.4 ms]
      Range (min … max):     3.2 ms …   5.6 ms    780 runs
    # (pushd ../perf-test && hyperfine -N --warmup=10 $OLDPWD/'./build/bin-wrappers/git diff')
    Benchmark 1: /home/benknoble/code/git/./build/bin-wrappers/git diff
      Time (mean ± σ):     217.5 ms ±   2.9 ms    [User: 202.1 ms, System: 23.4 ms]
      Range (min … max):   213.9 ms … 223.3 ms    13 runs
    # (pushd ../perf-test && hyperfine -N --warmup=10 $OLDPWD/'./build/bin-wrappers/git -c core.useNanosec=true diff')
    Benchmark 1: /home/benknoble/code/git/./build/bin-wrappers/git -c core.useNanosec=true diff
      Time (mean ± σ):       3.8 ms ±   0.4 ms    [User: 5.3 ms, System: 4.2 ms]
      Range (min … max):     3.2 ms …   6.9 ms    541 runs
    
    [1]: <CALnO6CADMJSixqYvL1Yo8qKX5rWhKQ+2OoSEuPUh-yoeK9TseQ@mail.gmail.com>
    
    Passing CI: https://github.com/benknoble/git/actions/runs/31104581195
 Documentation/config/core.adoc        |  6 ++++++
 Documentation/technical/racy-git.adoc | 11 ++++++-----
 Makefile                              | 12 +-----------
 builtin/update-index.c                |  2 +-
 compat/posix.h                        |  1 -
 configure.ac                          |  6 ------
 environment.c                         |  6 ++++++
 environment.h                         |  1 +
 read-cache.c                          | 17 +++++++++--------
 statinfo.c                            | 14 +++++++-------
 10 files changed, 37 insertions(+), 39 deletions(-)
diff --git a/Documentation/config/core.adoc b/Documentation/config/core.adoc
index 340329edc3..33104444ab 100644
--- a/Documentation/config/core.adoc
+++ b/Documentation/config/core.adoc
@@ -118,6 +118,12 @@ core.trustctime::
 	crawlers and some backup systems).
 	See linkgit:git-update-index[1]. True by default.
 
+core.useNanosec::
+	If true, use nanosecond precision for ctime and mtime
+	comparisions between the index and the working tree (if Git
+	was compiled to store it).
+	See link:technical/racy-git.html[Racy Git]. False by default.
+
 core.splitIndex::
 	If true, the split-index feature of the index will be used.
 	See linkgit:git-update-index[1]. False by default.
diff --git a/Documentation/technical/racy-git.adoc b/Documentation/technical/racy-git.adoc
index 59bea66c0f..499231585b 100644
--- a/Documentation/technical/racy-git.adoc
+++ b/Documentation/technical/racy-git.adoc
@@ -39,8 +39,8 @@ files) from `st_mode` member, `st_mtime` and `st_ctime`
 timestamps, `st_uid`, `st_gid`, `st_ino`, and `st_size` members.
 With a `USE_STDEV` compile-time option, `st_dev` is also
 compared, but this is not enabled by default because this member
-is not stable on network filesystems.  With `USE_NSEC`
-compile-time option, `st_mtim.tv_nsec` and `st_ctim.tv_nsec`
+is not stable on network filesystems.  With 'core.useNanosec'
+config setting, `st_mtim.tv_nsec` and `st_ctim.tv_nsec`
 members are also compared. On Linux, this is not enabled by default
 because in-core timestamps can have finer granularity than
 on-disk timestamps, resulting in meaningless changes when an
@@ -49,9 +49,10 @@ of git://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git
 ([PATCH] Sync in core time granularity with filesystems,
 2005-01-04). This patch is included in kernel 2.6.11 and newer, but
 only fixes the issue for file systems with exactly 1 ns or 1 s
-resolution. Other file systems are still broken in current Linux
-kernels (e.g. CEPH, CIFS, NTFS, UDF), see
-https://lore.kernel.org/lkml/5577240D.7020309@gmail.com/
+resolution.  As of kernel 4.3, other file systems (CEPH, CIFS, NTFS, UFS, FUSE)
+were fixed; see https://public-inbox.org/git/5605D88A.20104%40gmail.com/.  FAT
+has been fixed since 2015.  The usual suspects (ext2, ext4, XFS) are known to
+work, too.
 
 Racy Git
 --------
diff --git a/Makefile b/Makefile
index fac3e8879c..b4ebcb9e83 100644
--- a/Makefile
+++ b/Makefile
@@ -197,18 +197,11 @@ include shared.mak
 # Define NO_NORETURN if using buggy versions of gcc 4.6+ and profile feedback,
 # as the compiler can crash (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
 #
-# Define USE_NSEC below if you want git to care about sub-second file mtimes
-# and ctimes. Note that you need recent glibc (at least 2.2.4) for this. On
-# Linux, kernel 2.6.11 or newer is required for reliable sub-second file times
-# on file systems with exactly 1 ns or 1 s resolution. If you intend to use Git
-# on other file systems (e.g. CEPH, CIFS, NTFS, UDF), don't enable USE_NSEC. See
-# Documentation/technical/racy-git.adoc for details.
-#
 # Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of
 # "st_ctim"
 #
 # Define NO_NSEC if your "struct stat" does not have "st_ctim.tv_nsec"
-# available.  This automatically turns USE_NSEC off.
+# available.
 #
 # Define USE_STDEV below if you want git to care about the underlying device
 # change being considered an inode change from the update-index perspective.
@@ -1935,9 +1928,6 @@ endif
 ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
 	BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
 endif
-ifdef USE_NSEC
-	BASIC_CFLAGS += -DUSE_NSEC
-endif
 ifdef USE_ST_TIMESPEC
 	BASIC_CFLAGS += -DUSE_ST_TIMESPEC
 endif
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 241abd4332..8e0c25655f 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -130,7 +130,7 @@ static void xrmdir(const char *path)
 static void avoid_racy(void)
 {
 	/*
-	 * not use if we could usleep(10) if USE_NSEC is defined. The
+	 * not use if we could usleep(10) if core.useNanosec is defined. The
 	 * field nsec could be there, but the OS could choose to
 	 * ignore it?
 	 */
diff --git a/compat/posix.h b/compat/posix.h
index e2e794cad7..51ee03233b 100644
--- a/compat/posix.h
+++ b/compat/posix.h
@@ -487,7 +487,6 @@ int git_qsort_s(void *base, size_t nmemb, size_t size,
 } while (0)
 
 #ifdef NO_NSEC
-#undef USE_NSEC
 #define ST_CTIME_NSEC(st) 0
 #define ST_MTIME_NSEC(st) 0
 #else
diff --git a/configure.ac b/configure.ac
index cfb50112bf..fc956776ab 100644
--- a/configure.ac
+++ b/configure.ac
@@ -351,12 +351,6 @@ GIT_PARSE_WITH(iconv))
 
 ## --enable-FEATURE[=ARG] and --disable-FEATURE
 #
-# Define USE_NSEC below if you want git to care about sub-second file mtimes
-# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
-# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
-# randomly break unless your underlying filesystem supports those sub-second
-# times (my ext3 doesn't).
-#
 # Define USE_STDEV below if you want git to care about the underlying device
 # change being considered an inode change from the update-index perspective.
 
diff --git a/environment.c b/environment.c
index 6676e6f5ae..e6a50060e8 100644
--- a/environment.c
+++ b/environment.c
@@ -571,6 +571,11 @@ int git_default_core_config(const char *var, const char *value,
 		return 0;
 	}
 
+	if (!strcmp(var, "core.usenanosec")) {
+		cfg->use_nanosec = git_config_bool(var, value);
+		return 0;
+	}
+
 	/* Add other config variables here and to Documentation/config.adoc. */
 	return platform_core_config(var, value, ctx, cb);
 }
@@ -769,6 +774,7 @@ void repo_config_values_init(struct repo_config_values *cfg)
 	cfg->ignore_case = 0;
 	cfg->trust_executable_bit = 1;
 	cfg->has_symlinks = platform_has_symlinks();
+	cfg->use_nanosec = 0;
 
 	/* section "sparse" config values */
 	cfg->sparse_expect_files_outside_of_patterns = 0;
diff --git a/environment.h b/environment.h
index e7ec5b0437..a35534afe5 100644
--- a/environment.h
+++ b/environment.h
@@ -139,6 +139,7 @@ struct repo_config_values {
 	int ignore_case;
 	int trust_executable_bit;
 	int has_symlinks;
+	int use_nanosec;
 
 	/* section "sparse" config values */
 	int sparse_expect_files_outside_of_patterns;
diff --git a/read-cache.c b/read-cache.c
index 6c449f393d..297646c357 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -353,15 +353,16 @@ static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
 static int is_racy_stat(const struct index_state *istate,
 			const struct stat_data *sd)
 {
+	int use_nsec = 0;
+	repo_config_get_bool(the_repository, "core.useNanosec", &use_nsec);
+
 	return (istate->timestamp.sec &&
-#ifdef USE_NSEC
-		 /* nanosecond timestamped files can also be racy! */
-		(istate->timestamp.sec < sd->sd_mtime.sec ||
-		 (istate->timestamp.sec == sd->sd_mtime.sec &&
-		  istate->timestamp.nsec <= sd->sd_mtime.nsec))
-#else
-		istate->timestamp.sec <= sd->sd_mtime.sec
-#endif
+		/* nanosecond timestamped files can also be racy! */
+		use_nsec
+		? (istate->timestamp.sec < sd->sd_mtime.sec ||
+		   (istate->timestamp.sec == sd->sd_mtime.sec &&
+		    istate->timestamp.nsec <= sd->sd_mtime.nsec))
+		: istate->timestamp.sec <= sd->sd_mtime.sec
 		);
 }
 
diff --git a/statinfo.c b/statinfo.c
index 5e00af127d..d9ddcf9382 100644
--- a/statinfo.c
+++ b/statinfo.c
@@ -72,13 +72,13 @@ int match_stat_data(const struct stat_data *sd, struct stat *st)
 	    sd->sd_ctime.sec != (unsigned int)st->st_ctime)
 		changed |= CTIME_CHANGED;
 
-#ifdef USE_NSEC
-	if (cfg->check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
-		changed |= MTIME_CHANGED;
-	if (cfg->trust_ctime && cfg->check_stat &&
-	    sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
-		changed |= CTIME_CHANGED;
-#endif
+	if (cfg->use_nanosec) {
+		if (cfg->check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
+			changed |= MTIME_CHANGED;
+		if (cfg->trust_ctime && cfg->check_stat &&
+		    sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
+			changed |= CTIME_CHANGED;
+	}
 
 	if (cfg->check_stat) {
 		if (sd->sd_uid != (unsigned int) st->st_uid ||
-- 
2.55.0.340.g8e2bf96aa5.dirty
Previous: D. Ben KnobleNext: Junio C Hamano
Message 5 of 74 in “Convert USE_NSEC to runtime config”
  1. 0/3 Convert USE_NSEC to runtime configD. Ben Knoble, Aug 7, 2026
  2. 1/3 meson: expose knob for xmlto relative links in manualsD. Ben Knoble, Aug 7, 2026
  3. Patrick SteinhardtAug 10, 2026
  4. 2/3 environment: align repo_config_values_init with struct declarationD. Ben Knoble, Aug 7, 2026
  5. 3/3 core: convert build-time USE_NSEC into runtime core.useNanosecD. Ben Knoble, Aug 7, 2026
  6. Junio C HamanoAug 7, 2026
  7. SZEDER GáborAug 8, 2026
  8. D. Ben KnobleAug 10, 2026
  9. D. Ben KnobleAug 10, 2026
  10. Patrick SteinhardtAug 10, 2026
  11. Ben KnobleAug 11, 2026
  12. D. Ben KnobleAug 13, 2026
  13. Patrick SteinhardtAug 14, 2026
  14. Ben KnobleAug 14, 2026
  15. Patrick SteinhardtAug 10, 2026
  16. 0/3 Convert USE_NSEC to runtime configD. Ben Knoble, Aug 14, 2026
  17. 1/3 meson: expose knob for xmlto relative links in manualsD. Ben Knoble, Aug 14, 2026
  18. 2/3 environment: align repo_config_values_init with struct declarationD. Ben Knoble, Aug 14, 2026
  19. 3/3 core: convert build-time USE_NSEC into runtime core.useNanosecD. Ben Knoble, Aug 14, 2026
  20. Junio C HamanoAug 14, 2026
  21. D. Ben KnobleAug 14, 2026
  22. 0/3 Convert USE_NSEC to runtime configD. Ben Knoble, Aug 18, 2026
  23. 1/3 meson: expose knob for xmlto relative links in manualsD. Ben Knoble, Aug 18, 2026
  24. 2/3 environment: align repo_config_values_init with struct declarationD. Ben Knoble, Aug 18, 2026
  25. 3/3 core: convert build-time USE_NSEC into runtime core.useNanosecD. Ben Knoble, Aug 18, 2026
  26. Junio C HamanoAug 18, 2026
  27. D. Ben KnobleAug 19, 2026
  28. Patrick SteinhardtAug 19, 2026
  29. D. Ben KnobleAug 19, 2026
  30. Patrick SteinhardtAug 20, 2026
  31. D. Ben KnobleAug 20, 2026
  32. Junio C HamanoAug 19, 2026
  33. D. Ben KnobleAug 19, 2026
  34. Patrick SteinhardtAug 20, 2026
  35. 0/3 Convert USE_NSEC to runtime configD. Ben Knoble, Aug 20, 2026
  36. 1/3 meson: expose knob for xmlto relative links in manualsD. Ben Knoble, Aug 20, 2026
  37. 2/3 environment: align repo_config_values_init with struct declarationD. Ben Knoble, Aug 20, 2026
  38. Junio C HamanoAug 20, 2026
  39. D. Ben KnobleAug 21, 2026
  40. 3/3 core: convert build-time USE_NSEC into runtime core.useNanosecD. Ben Knoble, Aug 20, 2026
  41. 0/3 Convert USE_NSEC to runtime configD. Ben Knoble, Aug 29, 2026
  42. 1/3 meson: expose knob for xmlto relative links in manualsD. Ben Knoble, Aug 29, 2026
  43. 2/3 environment: align repo_config_values_init with struct declarationD. Ben Knoble, Aug 29, 2026
  44. 3/3 core: convert build-time USE_NSEC into runtime core.useNanosecD. Ben Knoble, Aug 29, 2026
  45. Junio C HamanoAug 30, 2026
  46. D. Ben KnobleAug 31, 2026
  47. Patrick SteinhardtAug 31, 2026
  48. D. Ben KnobleAug 31, 2026
  49. Patrick SteinhardtAug 31, 2026
  50. Ben KnobleAug 31, 2026
  51. 0/3 Convert USE_NSEC to runtime configD. Ben Knoble, Aug 31, 2026
  52. 1/3 meson: expose knob for xmlto relative links in manualsD. Ben Knoble, Aug 31, 2026
  53. 2/3 environment: align repo_config_values_init with struct declarationD. Ben Knoble, Aug 31, 2026
  54. 3/3 core: convert build-time USE_NSEC into runtime core.useNanosecD. Ben Knoble, Aug 31, 2026
  55. Junio C HamanoSep 1, 2026
  56. D. Ben KnobleSep 1, 2026
  57. Junio C HamanoSep 1, 2026
  58. Jeff KingSep 1, 2026
  59. D. Ben KnobleSep 1, 2026
  60. Jeff KingSep 2, 2026
  61. Ben KnobleSep 2, 2026
  62. Junio C HamanoSep 2, 2026
  63. Ben KnobleSep 3, 2026
  64. Junio C HamanoSep 3, 2026
  65. Ben KnobleSep 3, 2026
  66. D. Ben KnobleAug 31, 2026
  67. 0/3 Convert USE_NSEC to runtime configD. Ben Knoble, Sep 11, 2026
  68. 1/3 meson: expose knob for xmlto relative links in manualsD. Ben Knoble, Sep 11, 2026
  69. 2/3 environment: align repo_config_values_init with struct declarationD. Ben Knoble, Sep 11, 2026
  70. 3/3 core: convert build-time USE_NSEC into runtime core.useNanosecD. Ben Knoble, Sep 11, 2026
  71. Junio C HamanoSep 11, 2026
  72. Ben KnobleSep 11, 2026
  73. Patrick SteinhardtAug 31, 2026
  74. Ben KnobleSep 1, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.