Merge branch 'dk/use-nsec-runtime' into seen

The build-time knob 'USE_NSEC' for nanosecond stat precision has been
converted to a runtime configuration 'core.useNanosec', allowing
distributions to bundle one binary that adapts to filesystem
capabilities dynamically.

* dk/use-nsec-runtime:
  core: convert build-time USE_NSEC into runtime core.useNanosec
  environment: align repo_config_values_init with struct declaration
  meson: expose knob for xmlto relative links in manuals
seen
Junio C Hamano 2026-08-31 13:53:27 -07:00
commit 9588992984
12 changed files with 57 additions and 48 deletions

View File

@ -118,6 +118,13 @@ core.trustctime::
crawlers and some backup systems). crawlers and some backup systems).
See linkgit:git-update-index[1]. True by default. 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 respect this option).
This is unsafe on some platforms;
see link:technical/racy-git.html[Racy Git]. False by default.

core.splitIndex:: core.splitIndex::
If true, the split-index feature of the index will be used. If true, the split-index feature of the index will be used.
See linkgit:git-update-index[1]. False by default. See linkgit:git-update-index[1]. False by default.

View File

@ -379,13 +379,18 @@ foreach manpage, category : manpages
output: fs.stem(manpage) + '.xml', output: fs.stem(manpage) + '.xml',
) )


man_base_url = 'file://' + htmldir + '/'
if get_option('man_base_url') != ''
man_base_url = get_option('man_base_url')
endif

doc_targets += custom_target( doc_targets += custom_target(
command: [ command: [
xmlto, xmlto,
'-m', '@INPUT0@', '-m', '@INPUT0@',
'-m', '@INPUT1@', '-m', '@INPUT1@',
'--stringparam', '--stringparam',
'man.base.url.for.relative.links=' + get_option('prefix') / get_option('mandir'), 'man.base.url.for.relative.links=' + man_base_url,
'man', 'man',
manpage_xml_target, manpage_xml_target,
'-o', '-o',

View File

@ -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. timestamps, `st_uid`, `st_gid`, `st_ino`, and `st_size` members.
With a `USE_STDEV` compile-time option, `st_dev` is also With a `USE_STDEV` compile-time option, `st_dev` is also
compared, but this is not enabled by default because this member compared, but this is not enabled by default because this member
is not stable on network filesystems. With `USE_NSEC` is not stable on network filesystems. With 'core.useNanosec'
compile-time option, `st_mtim.tv_nsec` and `st_ctim.tv_nsec` config setting, `st_mtim.tv_nsec` and `st_ctim.tv_nsec`
members are also compared. On Linux, this is not enabled by default members are also compared. On Linux, this is not enabled by default
because in-core timestamps can have finer granularity than because in-core timestamps can have finer granularity than
on-disk timestamps, resulting in meaningless changes when an 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, ([PATCH] Sync in core time granularity with filesystems,
2005-01-04). This patch is included in kernel 2.6.11 and newer, but 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 only fixes the issue for file systems with exactly 1 ns or 1 s
resolution. Other file systems are still broken in current Linux resolution. As of kernel 4.3, other file systems (CEPH, CIFS, NTFS, UFS, FUSE)
kernels (e.g. CEPH, CIFS, NTFS, UDF), see were fixed; see https://public-inbox.org/git/5605D88A.20104%40gmail.com/. FAT
https://lore.kernel.org/lkml/5577240D.7020309@gmail.com/ has been fixed since 2015. The usual suspects (ext2, ext4, XFS) are known to
work, too.


Racy Git Racy Git
-------- --------

View File

@ -202,18 +202,11 @@ include shared.mak
# Define NO_NORETURN if using buggy versions of gcc 4.6+ and profile feedback, # 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) # 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 # Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of
# "st_ctim" # "st_ctim"
# #
# Define NO_NSEC if your "struct stat" does not have "st_ctim.tv_nsec" # 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 # 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. # change being considered an inode change from the update-index perspective.
@ -1940,9 +1933,6 @@ endif
ifdef NO_ST_BLOCKS_IN_STRUCT_STAT ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
endif endif
ifdef USE_NSEC
BASIC_CFLAGS += -DUSE_NSEC
endif
ifdef USE_ST_TIMESPEC ifdef USE_ST_TIMESPEC
BASIC_CFLAGS += -DUSE_ST_TIMESPEC BASIC_CFLAGS += -DUSE_ST_TIMESPEC
endif endif

View File

@ -130,7 +130,7 @@ static void xrmdir(const char *path)
static void avoid_racy(void) 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 enabled. The
* field nsec could be there, but the OS could choose to * field nsec could be there, but the OS could choose to
* ignore it? * ignore it?
*/ */

View File

@ -518,7 +518,6 @@ int git_qsort_s(void *base, size_t nmemb, size_t size,
} while (0) } while (0)


#ifdef NO_NSEC #ifdef NO_NSEC
#undef USE_NSEC
#define ST_ATIME_NSEC(st) 0 #define ST_ATIME_NSEC(st) 0
#define ST_CTIME_NSEC(st) 0 #define ST_CTIME_NSEC(st) 0
#define ST_MTIME_NSEC(st) 0 #define ST_MTIME_NSEC(st) 0

View File

@ -351,12 +351,6 @@ GIT_PARSE_WITH(iconv))


## --enable-FEATURE[=ARG] and --disable-FEATURE ## --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 # 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. # change being considered an inode change from the update-index perspective.



View File

@ -571,6 +571,13 @@ int git_default_core_config(const char *var, const char *value,
return 0; return 0;
} }


#ifndef NO_NSEC
if (!strcmp(var, "core.usenanosec")) {
cfg->use_nanosec = git_config_bool(var, value);
return 0;
}
#endif

/* Add other config variables here and to Documentation/config.adoc. */ /* Add other config variables here and to Documentation/config.adoc. */
return platform_core_config(var, value, ctx, cb); return platform_core_config(var, value, ctx, cb);
} }
@ -745,6 +752,7 @@ int git_default_config(const char *var, const char *value,


void repo_config_values_init(struct repo_config_values *cfg) void repo_config_values_init(struct repo_config_values *cfg)
{ {
/* section "core" config values */
cfg->attributes_file = NULL; cfg->attributes_file = NULL;
cfg->excludes_file = NULL; cfg->excludes_file = NULL;
cfg->editor_program = NULL; cfg->editor_program = NULL;
@ -756,20 +764,25 @@ void repo_config_values_init(struct repo_config_values *cfg)
cfg->autorebase = AUTOREBASE_NEVER; cfg->autorebase = AUTOREBASE_NEVER;
cfg->object_creation_mode = OBJECT_CREATION_MODE; cfg->object_creation_mode = OBJECT_CREATION_MODE;
cfg->apply_sparse_checkout = 0; cfg->apply_sparse_checkout = 0;
cfg->protect_hfs = PROTECT_HFS_DEFAULT;
cfg->protect_ntfs = PROTECT_NTFS_DEFAULT;
cfg->ignore_case = 0;
cfg->trust_executable_bit = 1;
cfg->has_symlinks = platform_has_symlinks();
cfg->branch_track = BRANCH_TRACK_REMOTE;
cfg->trust_ctime = 1; cfg->trust_ctime = 1;
cfg->check_stat = 1; cfg->check_stat = 1;
cfg->zlib_compression_level = Z_BEST_SPEED; cfg->zlib_compression_level = Z_BEST_SPEED;
cfg->pack_compression_level = Z_DEFAULT_COMPRESSION; cfg->pack_compression_level = Z_DEFAULT_COMPRESSION;
cfg->precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */ cfg->precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
cfg->core_sparse_checkout_cone = 0; cfg->core_sparse_checkout_cone = 0;
cfg->sparse_expect_files_outside_of_patterns = 0;
cfg->warn_on_object_refname_ambiguity = 1; cfg->warn_on_object_refname_ambiguity = 1;
cfg->protect_hfs = PROTECT_HFS_DEFAULT;
cfg->protect_ntfs = PROTECT_NTFS_DEFAULT;
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;

/* section "branch" config values */
cfg->branch_track = BRANCH_TRACK_REMOTE;
} }


void repo_config_values_clear(struct repo_config_values *cfg) void repo_config_values_clear(struct repo_config_values *cfg)

View File

@ -138,6 +138,7 @@ struct repo_config_values {
int ignore_case; int ignore_case;
int trust_executable_bit; int trust_executable_bit;
int has_symlinks; int has_symlinks;
int use_nanosec;
int sparse_expect_files_outside_of_patterns; int sparse_expect_files_outside_of_patterns;
enum branch_track branch_track; enum branch_track branch_track;
}; };

View File

@ -115,6 +115,8 @@ option('default_help_format', type: 'combo', choices: ['man', 'html', 'platform'
description: 'Default format used when executing git-help(1).') description: 'Default format used when executing git-help(1).')
option('docs_backend', type: 'combo', choices: ['asciidoc', 'asciidoctor', 'auto'], value: 'auto', option('docs_backend', type: 'combo', choices: ['asciidoc', 'asciidoctor', 'auto'], value: 'auto',
description: 'Which backend to use to generate documentation.') description: 'Which backend to use to generate documentation.')
option('man_base_url', type: 'string', value: '',
description: 'The base URL to use for relative links in manuals')


# Testing. # Testing.
option('benchmarks', type: 'feature', value: 'auto', option('benchmarks', type: 'feature', value: 'auto',

View File

@ -354,15 +354,12 @@ static int is_racy_stat(const struct index_state *istate,
const struct stat_data *sd) const struct stat_data *sd)
{ {
return (istate->timestamp.sec && return (istate->timestamp.sec &&
#ifdef USE_NSEC /* nanosecond timestamped files can also be racy! */
/* nanosecond timestamped files can also be racy! */ (repo_config_values(istate->repo)->use_nanosec
(istate->timestamp.sec < sd->sd_mtime.sec || ? (istate->timestamp.sec < sd->sd_mtime.sec ||
(istate->timestamp.sec == sd->sd_mtime.sec && (istate->timestamp.sec == sd->sd_mtime.sec &&
istate->timestamp.nsec <= sd->sd_mtime.nsec)) istate->timestamp.nsec <= sd->sd_mtime.nsec))
#else : istate->timestamp.sec <= sd->sd_mtime.sec));
istate->timestamp.sec <= sd->sd_mtime.sec
#endif
);
} }


int is_racy_timestamp(const struct index_state *istate, int is_racy_timestamp(const struct index_state *istate,

View File

@ -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) sd->sd_ctime.sec != (unsigned int)st->st_ctime)
changed |= CTIME_CHANGED; changed |= CTIME_CHANGED;


#ifdef USE_NSEC if (cfg->use_nanosec) {
if (cfg->check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st)) if (cfg->check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
changed |= MTIME_CHANGED; changed |= MTIME_CHANGED;
if (cfg->trust_ctime && cfg->check_stat && if (cfg->trust_ctime && cfg->check_stat &&
sd->sd_ctime.nsec != ST_CTIME_NSEC(*st)) sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
changed |= CTIME_CHANGED; changed |= CTIME_CHANGED;
#endif }


if (cfg->check_stat) { if (cfg->check_stat) {
if (sd->sd_uid != (unsigned int) st->st_uid || if (sd->sd_uid != (unsigned int) st->st_uid ||