{"thread":{"id":"65693","subject":"[PATCH 0/2] update-index: add --refresh-stat-only","startedAt":"2026-05-26T08:03:13Z","lastAt":"2026-05-26T08:03:15Z","messageCount":3,"participants":["George Giorgidze via GitGitGadget"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"544102","messageId":"pull.2125.git.1779782591.gitgitgadget@gmail.com","threadId":"65693","inReplyTo":null,"subject":"[PATCH 0/2] update-index: add --refresh-stat-only","fromName":"George Giorgidze via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-05-26T08:03:08Z","receivedAt":"2026-05-26T08:03:13Z","isPatch":true,"body":"This two-patch series adds \"git update-index --refresh-stat-only\", a\none-shot way to update the index's cached stat data to match the current\nfilesystem without rehashing file contents.\n\nWhen a working tree is produced or restored by means other than a normal\ncheckout -- a CI cache restore, container provisioning, a tarball\nextraction, or a copy from another machine -- the files may be byte-for-byte\nidentical while filesystem-local stat fields like inode and device numbers\nno longer match. Today the available workarounds are to (a) pay for full\ncontent rehashing on the next \"git status\", or (b) set\ncore.checkStat=minimal, which is sticky and weakens every subsequent\noperation. Neither composes well with modern container-based CI, where every\njob step would otherwise need to set and preserve the configuration.\n\nA similar idea (\"--assume-content-unchanged\") was discussed in January 2017;\nsee the thread starting at 20170105112359.GN8116@chrystal.oracle.com. The\nconcern raised there was that exposing a way to update cached stat data\nwithout content comparison opens the index to abuse. The flag in this series\nis deliberately narrower than the 2017 proposal:\n\n * one-shot action, not a sticky config or per-entry bit;\n * the name describes what the invocation does, not a trust state attached\n   to entries (contrast --assume-unchanged);\n * intended for closed-loop callers (CI cache restore, container\n   provisioning, backup/restore tooling) that produced or verified the\n   worktree atomically;\n * the failure mode -- stale object IDs becoming invisible until the next\n   content check -- is named directly in the docs, and the flag must be\n   typed explicitly.\n\nThe series is organised so the bug fix is reviewable on its own:\n\n1/2 preload-index: respect --really-refresh override of assume-unchanged\n\n   A latent bug observable today via GIT_TEST_PRELOAD_INDEX=1:\n   preload_thread() never sets CE_MATCH_IGNORE_VALID, so it\n   honours the \"assume unchanged\" bit and marks modified\n   assume-unchanged entries as uptodate before the\n   --really-refresh loop sees them. Plumb refresh flags\n   through to preload threads and add a regression test under\n   t2106.\n\n\n2/2 update-index: add --refresh-stat-only\n\n   Add the new flag, extend the preload mask to also recognise\n   REFRESH_STAT_ONLY, document the assume-unchanged override\n   behaviour alongside the flag, and add coverage for object\n   ID preservation, missing-file handling (with and without\n   --ignore-missing), assume-unchanged override, and quiet\n   output under t2109.\n\n\nGeorge Giorgidze (2):\n  preload-index: respect --really-refresh override of assume-unchanged\n  update-index: add --refresh-stat-only\n\n Documentation/git-update-index.adoc       | 19 ++++++++\n builtin/update-index.c                    | 12 +++++\n preload-index.c                           |  7 ++-\n read-cache-ll.h                           |  3 ++\n read-cache.c                              | 24 +++++----\n t/meson.build                             |  1 +\n t/t2106-update-index-assume-unchanged.sh  | 11 +++++\n t/t2109-update-index-refresh-stat-only.sh | 59 +++++++++++++++++++++++\n 8 files changed, 126 insertions(+), 10 deletions(-)\n create mode 100755 t/t2109-update-index-refresh-stat-only.sh\n\n\nbase-commit: 56a4f3c3a221adf1df9b39da69b8a6890f803157\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2125%2Fgiorgidze%2Fmaster-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2125/giorgidze/master-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/2125\n-- \ngitgitgadget\n"},{"id":"544103","messageId":"13063f998a43a9b3904cf8b338c038a51fb80150.1779782591.git.gitgitgadget@gmail.com","threadId":"65693","inReplyTo":"pull.2125.git.1779782591.gitgitgadget@gmail.com","subject":"[PATCH 1/2] preload-index: respect --really-refresh override of assume-unchanged","fromName":"George Giorgidze via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-05-26T08:03:09Z","receivedAt":"2026-05-26T08:03:14Z","isPatch":true,"body":"From: George Giorgidze <giorgidze@meta.com>\n\nWhen refresh_index() is invoked with REFRESH_REALLY (e.g. via\n\"git update-index --really-refresh\"), the documented behaviour is that\nthe \"assume unchanged\" bit on cache entries is disregarded so that\nstale stat data on those entries is still refreshed.\n\nThe preload pass runs before the single-threaded refresh loop and is\nintended to mark up-to-date entries quickly so the slow path only has\nto deal with the leftovers. However, preload_thread() unconditionally\ncalled ie_match_stat() with CE_MATCH_RACY_IS_DIRTY|CE_MATCH_IGNORE_FSMONITOR\nand never with CE_MATCH_IGNORE_VALID, so it honoured the \"assume\nunchanged\" bit. When a modified file's entry was marked\nassume-unchanged, preload would conclude the entry was clean and call\nce_mark_uptodate(); the subsequent --really-refresh loop would then\nskip the entry (because ce_uptodate(ce) is true) and never report it\nas needing an update.\n\nThis only manifests when preload is active, so it has been latent in\ndefault configurations. It is observable today via GIT_TEST_PRELOAD_INDEX=1.\n\nPlumb the refresh flags through to the preload threads via a new\nrefresh_flags field on struct thread_data, and have preload_thread()\nadd CE_MATCH_IGNORE_VALID to its match options when REFRESH_REALLY is\nin effect. Update refresh_index() to pass \"flags & REFRESH_REALLY\" to\npreload_index() instead of a bare 0.\n\nAdd a regression test under t2106 that forces preload on and confirms\nthat \"update-index --really-refresh\" reports a modified\nassume-unchanged entry as needing update.\n\nSigned-off-by: George Giorgidze <giorgidze@meta.com>\n---\n preload-index.c                          |  7 ++++++-\n read-cache.c                             |  2 +-\n t/t2106-update-index-assume-unchanged.sh | 11 +++++++++++\n 3 files changed, 18 insertions(+), 2 deletions(-)\n\ndiff --git a/preload-index.c b/preload-index.c\nindex b222821b44..88bb486329 100644\n--- a/preload-index.c\n+++ b/preload-index.c\n@@ -42,6 +42,7 @@ struct thread_data {\n \tstruct progress_data *progress;\n \tint offset, nr;\n \tint t2_nr_lstat;\n+\tunsigned int refresh_flags;\n };\n \n static void *preload_thread(void *_data)\n@@ -60,6 +61,7 @@ static void *preload_thread(void *_data)\n \tdo {\n \t\tstruct cache_entry *ce = *cep++;\n \t\tstruct stat st;\n+\t\tunsigned int ce_option = CE_MATCH_RACY_IS_DIRTY | CE_MATCH_IGNORE_FSMONITOR;\n \n \t\tif (ce_stage(ce))\n \t\t\tcontinue;\n@@ -87,7 +89,9 @@ static void *preload_thread(void *_data)\n \t\tp->t2_nr_lstat++;\n \t\tif (lstat(ce->name, &st))\n \t\t\tcontinue;\n-\t\tif (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY|CE_MATCH_IGNORE_FSMONITOR))\n+\t\tif (p->refresh_flags & REFRESH_REALLY)\n+\t\t\tce_option |= CE_MATCH_IGNORE_VALID;\n+\t\tif (ie_match_stat(index, ce, &st, ce_option))\n \t\t\tcontinue;\n \t\tce_mark_uptodate(ce);\n \t\tmark_fsmonitor_valid(index, ce);\n@@ -150,6 +154,7 @@ void preload_index(struct index_state *index,\n \t\t\tcopy_pathspec(&p->pathspec, pathspec);\n \t\tp->offset = offset;\n \t\tp->nr = work;\n+\t\tp->refresh_flags = refresh_flags;\n \t\tif (pd.progress)\n \t\t\tp->progress = &pd;\n \t\toffset += work;\ndiff --git a/read-cache.c b/read-cache.c\nindex 38a04b8de3..f5023b9a8b 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -1520,7 +1520,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,\n \t * cache entries quickly then in the single threaded loop below,\n \t * we only have to do the special cases that are left.\n \t */\n-\tpreload_index(istate, pathspec, 0);\n+\tpreload_index(istate, pathspec, flags & REFRESH_REALLY);\n \ttrace2_region_enter(\"index\", \"refresh\", NULL);\n \n \tfor (i = 0; i < istate->cache_nr; i++) {\ndiff --git a/t/t2106-update-index-assume-unchanged.sh b/t/t2106-update-index-assume-unchanged.sh\nindex 6b2ccc21a9..266c88d45d 100755\n--- a/t/t2106-update-index-assume-unchanged.sh\n+++ b/t/t2106-update-index-assume-unchanged.sh\n@@ -24,4 +24,15 @@ test_expect_success 'do not switch branches with dirty file' '\n \ttest_grep overwritten err\n '\n \n+test_expect_success '--really-refresh overrides assume-unchanged under preload' '\n+\tgit reset --hard &&\n+\ttest_commit really-refresh really-refresh original &&\n+\tgit update-index --assume-unchanged really-refresh &&\n+\tprintf \"modified\\n\" >really-refresh &&\n+\ttest-tool chmtime -100000 really-refresh &&\n+\ttest_must_fail env GIT_TEST_PRELOAD_INDEX=1 \\\n+\t\tgit update-index --really-refresh >out 2>err &&\n+\ttest_grep \"needs update\" out\n+'\n+\n test_done\n-- \ngitgitgadget\n\n"},{"id":"544104","messageId":"a235b9e31d74a04c28701b11dee8e74ff7dc4d9e.1779782591.git.gitgitgadget@gmail.com","threadId":"65693","inReplyTo":"pull.2125.git.1779782591.gitgitgadget@gmail.com","subject":"[PATCH 2/2] update-index: add --refresh-stat-only","fromName":"George Giorgidze via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-05-26T08:03:10Z","receivedAt":"2026-05-26T08:03:15Z","isPatch":true,"body":"From: George Giorgidze <giorgidze@meta.com>\n\nWhen a working tree is copied from another machine, or restored from\na tarball, container image, or CI cache on the same machine, the\nfiles may be byte-for-byte identical while cached stat data in the\nindex no longer matches. Backup and sync tools can preserve mtimes,\nbut fields like inode and device numbers are filesystem-local, so\nlarge repositories can still end up paying for expensive refresh\nchecks on every \"git status\".\n\nGit already has runtime configuration for reducing which stat fields\nare checked, such as core.checkStat=<minimal|default>. That affects\nhow future checks interpret cached stat data, but it does not provide\na one-shot way to update the index's cached stat data to match the\ncurrent filesystem without also rehashing file contents. Setting\ncore.checkStat=minimal is \"sticky\": it weakens every subsequent\noperation in the repository for the duration of the configuration,\nrather than performing a single, bounded correction at a well-defined\npoint.\n\nA similar idea was discussed on the list in January 2017 under the\nname \"--assume-content-unchanged\"; see the thread starting at\n<20170105112359.GN8116@chrystal.oracle.com>. The concern raised there\nwas that exposing a way to update cached stat data without content\ncomparison opens the index to abuse: an interactive user could skip a\nslow refresh, lie to Git about the worktree, then file a bug after a\nlater merge corrupts a file. That concern is taken seriously here,\nand this proposal is deliberately narrower than the 2017 one:\n\n  * It is a one-shot action, not a sticky configuration or per-entry\n    bit. The name --refresh-stat-only reflects that: it describes\n    what the command does in a single invocation, not a trust state\n    attached to entries (contrast with --assume-unchanged).\n\n  * The trust assertion is intended for closed-loop callers (CI cache\n    restore, container provisioning, backup/restore tooling) where\n    the worktree and the index were produced or verified together by\n    the same process. It is not a knob for interactive users to reach\n    for when \"git status\" feels slow.\n\n  * The failure mode is named directly in the documentation: if the\n    worktree does not in fact match the index, affected entries will\n    appear clean while the recorded object ID remains stale. The user\n    must type the flag, having read the warning. This is a narrower\n    contract than core.checkStat=minimal, which silently affects\n    every subsequent operation.\n\nContainer-based CI has become the dominant deployment model in the\nyears since that 2017 discussion. The current workaround -- setting\ncore.checkStat=minimal in every job step, or accepting the cost of\nfull content rehashing -- is operationally fragile: it requires every\nstep in every pipeline to set and preserve the configuration, and it\npermanently weakens stat semantics for every command those steps\nrun. A single explicit invocation at restore time is a tighter, more\nlocal fix.\n\nTeach git update-index --refresh-stat-only to refresh only cached\nstat information. It follows the existing refresh machinery, but\nskips ie_modified() and treats racy entries as dirty by stat instead\nof resolving them by content. Like --really-refresh, it ignores the\n\"assume unchanged\" setting, so stale stat data on those entries is\nstill updated; that behaviour is documented alongside the flag.\n\nThe preload pass is extended to recognise REFRESH_STAT_ONLY (on top\nof REFRESH_REALLY, which was wired up in the preceding commit) so\nthat assume-unchanged entries are not marked uptodate before the main\nrefresh path can update them.\n\nAdd tests covering object ID preservation, missing-file handling with\nand without --ignore-missing, assume-unchanged override, and quiet\noutput.\n\nSigned-off-by: George Giorgidze <giorgidze@meta.com>\n---\n Documentation/git-update-index.adoc       | 19 ++++++++\n builtin/update-index.c                    | 12 +++++\n preload-index.c                           |  2 +-\n read-cache-ll.h                           |  3 ++\n read-cache.c                              | 24 +++++----\n t/meson.build                             |  1 +\n t/t2109-update-index-refresh-stat-only.sh | 59 +++++++++++++++++++++++\n 7 files changed, 110 insertions(+), 10 deletions(-)\n create mode 100755 t/t2109-update-index-refresh-stat-only.sh\n\ndiff --git a/Documentation/git-update-index.adoc b/Documentation/git-update-index.adoc\nindex 9bea9fab9a..2840a52ae4 100644\n--- a/Documentation/git-update-index.adoc\n+++ b/Documentation/git-update-index.adoc\n@@ -109,6 +109,25 @@ you will need to handle the situation manually.\n \tLike `--refresh`, but checks stat information unconditionally,\n \twithout regard to the \"assume unchanged\" setting.\n \n+--refresh-stat-only::\n+\tLike `--refresh`, but updates only the stat information\n+\tin the index, without rehashing the file contents. This is\n+\tuseful for large repositories after a working tree has been\n+\tproduced or restored by means other than a normal checkout --\n+\tfor example, a CI cache restore, container provisioning, or\n+\tcopying a working tree from another machine -- when the file\n+\tcontents are known to be correct but the cached stat\n+\tinformation no longer matches. Some backup and syncing tools\n+\tpreserve mtimes, but inode numbers, device identifiers, and\n+\tother filesystem-specific stat fields generally cannot be\n+\tpreserved across machines or even across mounts on the same\n+\tmachine. Like `--really-refresh`, this option disregards the\n+\t\"assume unchanged\" setting so that stale stat data on those\n+\tentries is still updated. Use with care: if the worktree\n+\tcontent does not actually match what the index records, the\n+\taffected entries will appear clean while the recorded object\n+\tID remains stale.\n+\n --skip-worktree::\n --no-skip-worktree::\n \tWhen one of these flags is specified, the object names recorded\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 8a5907767b..5e5d2e77c9 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -766,6 +766,14 @@ static int really_refresh_callback(const struct option *opt,\n \treturn refresh(opt->value, REFRESH_REALLY);\n }\n \n+static int refresh_stat_only_callback(const struct option *opt,\n+\t\t\t\tconst char *arg, int unset)\n+{\n+\tBUG_ON_OPT_NEG(unset);\n+\tBUG_ON_OPT_ARG(arg);\n+\treturn refresh(opt->value, REFRESH_STAT_ONLY);\n+}\n+\n static int chmod_callback(const struct option *opt,\n \t\t\t\tconst char *arg, int unset)\n {\n@@ -957,6 +965,10 @@ int cmd_update_index(int argc,\n \t\t\tN_(\"like --refresh, but ignore assume-unchanged setting\"),\n \t\t\tPARSE_OPT_NOARG | PARSE_OPT_NONEG,\n \t\t\treally_refresh_callback),\n+\t\tOPT_CALLBACK_F(0, \"refresh-stat-only\", &refresh_args, NULL,\n+\t\t\tN_(\"refresh stat information without checking content\"),\n+\t\t\tPARSE_OPT_NOARG | PARSE_OPT_NONEG,\n+\t\t\trefresh_stat_only_callback),\n \t\t{\n \t\t\t.type = OPTION_LOWLEVEL_CALLBACK,\n \t\t\t.long_name = \"cacheinfo\",\ndiff --git a/preload-index.c b/preload-index.c\nindex 88bb486329..9ecc6e4af0 100644\n--- a/preload-index.c\n+++ b/preload-index.c\n@@ -89,7 +89,7 @@ static void *preload_thread(void *_data)\n \t\tp->t2_nr_lstat++;\n \t\tif (lstat(ce->name, &st))\n \t\t\tcontinue;\n-\t\tif (p->refresh_flags & REFRESH_REALLY)\n+\t\tif (p->refresh_flags & (REFRESH_REALLY | REFRESH_STAT_ONLY))\n \t\t\tce_option |= CE_MATCH_IGNORE_VALID;\n \t\tif (ie_match_stat(index, ce, &st, ce_option))\n \t\t\tcontinue;\ndiff --git a/read-cache-ll.h b/read-cache-ll.h\nindex 2c8b4b21b1..7e4b555a31 100644\n--- a/read-cache-ll.h\n+++ b/read-cache-ll.h\n@@ -425,6 +425,8 @@ void *read_blob_data_from_index(struct index_state *, const char *, unsigned lon\n #define CE_MATCH_REFRESH\t\t0x10\n /* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */\n #define CE_MATCH_IGNORE_FSMONITOR 0X20\n+/* update stat info without checking content */\n+#define CE_MATCH_STAT_ONLY\t\t0x40\n int is_racy_timestamp(const struct index_state *istate,\n \t\t      const struct cache_entry *ce);\n int has_racy_timestamp(struct index_state *istate);\n@@ -452,6 +454,7 @@ int fake_lstat(const struct cache_entry *ce, struct stat *st);\n #define REFRESH_IN_PORCELAIN             (1 << 5) /* user friendly output, not \"needs update\" */\n #define REFRESH_PROGRESS                 (1 << 6) /* show progress bar if stderr is tty */\n #define REFRESH_IGNORE_SKIP_WORKTREE     (1 << 7) /* ignore skip_worktree entries */\n+#define REFRESH_STAT_ONLY                (1 << 8) /* update stat info without checking content */\n int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);\n /*\n  * Refresh the index and write it to disk.\ndiff --git a/read-cache.c b/read-cache.c\nindex f5023b9a8b..ec9419cbe2 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -1350,6 +1350,7 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,\n \tint ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;\n \tint ignore_missing = options & CE_MATCH_IGNORE_MISSING;\n \tint ignore_fsmonitor = options & CE_MATCH_IGNORE_FSMONITOR;\n+\tint stat_only = options & CE_MATCH_STAT_ONLY;\n \n \tif (!refresh || ce_uptodate(ce))\n \t\treturn ce;\n@@ -1420,12 +1421,14 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,\n \t\t}\n \t}\n \n-\tif (t2_did_scan)\n-\t\t*t2_did_scan = 1;\n-\tif (ie_modified(istate, ce, &st, options)) {\n-\t\tif (err)\n-\t\t\t*err = EINVAL;\n-\t\treturn NULL;\n+\tif (!stat_only) {\n+\t\tif (t2_did_scan)\n+\t\t\t*t2_did_scan = 1;\n+\t\tif (ie_modified(istate, ce, &st, options)) {\n+\t\t\tif (err)\n+\t\t\t\t*err = EINVAL;\n+\t\t\treturn NULL;\n+\t\t}\n \t}\n \n \tupdated = make_empty_cache_entry(istate, ce_namelen(ce));\n@@ -1490,11 +1493,14 @@ int refresh_index(struct index_state *istate, unsigned int flags,\n \tint not_new = (flags & REFRESH_IGNORE_MISSING) != 0;\n \tint ignore_submodules = (flags & REFRESH_IGNORE_SUBMODULES) != 0;\n \tint ignore_skip_worktree = (flags & REFRESH_IGNORE_SKIP_WORKTREE) != 0;\n+\tint stat_only = (flags & REFRESH_STAT_ONLY) != 0;\n \tint first = 1;\n \tint in_porcelain = (flags & REFRESH_IN_PORCELAIN);\n \tunsigned int options = (CE_MATCH_REFRESH |\n-\t\t\t\t(really ? CE_MATCH_IGNORE_VALID : 0) |\n-\t\t\t\t(not_new ? CE_MATCH_IGNORE_MISSING : 0));\n+\t\t\t\t((really || stat_only) ? CE_MATCH_IGNORE_VALID : 0) |\n+\t\t\t\t(not_new ? CE_MATCH_IGNORE_MISSING : 0) |\n+\t\t\t\t(stat_only ? (CE_MATCH_STAT_ONLY |\n+\t\t\t\t\t      CE_MATCH_RACY_IS_DIRTY) : 0));\n \tconst char *modified_fmt;\n \tconst char *deleted_fmt;\n \tconst char *typechange_fmt;\n@@ -1520,7 +1526,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,\n \t * cache entries quickly then in the single threaded loop below,\n \t * we only have to do the special cases that are left.\n \t */\n-\tpreload_index(istate, pathspec, flags & REFRESH_REALLY);\n+\tpreload_index(istate, pathspec, flags & (REFRESH_REALLY | REFRESH_STAT_ONLY));\n \ttrace2_region_enter(\"index\", \"refresh\", NULL);\n \n \tfor (i = 0; i < istate->cache_nr; i++) {\ndiff --git a/t/meson.build b/t/meson.build\nindex fd955f44ef..e1e68921b4 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -291,6 +291,7 @@ integration_tests = [\n   't2106-update-index-assume-unchanged.sh',\n   't2107-update-index-basic.sh',\n   't2108-update-index-refresh-racy.sh',\n+  't2109-update-index-refresh-stat-only.sh',\n   't2200-add-update.sh',\n   't2201-add-update-typechange.sh',\n   't2202-add-addremove.sh',\ndiff --git a/t/t2109-update-index-refresh-stat-only.sh b/t/t2109-update-index-refresh-stat-only.sh\nnew file mode 100755\nindex 0000000000..404d2a6fb2\n--- /dev/null\n+++ b/t/t2109-update-index-refresh-stat-only.sh\n@@ -0,0 +1,59 @@\n+#!/bin/sh\n+\n+test_description='git update-index --refresh-stat-only'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\ttest_commit initial base-file base\n+'\n+\n+test_expect_success '--refresh-stat-only updates stat info without rehashing' '\n+\ttest_commit refresh-stat refresh-stat original &&\n+\tgit ls-files --stage -- refresh-stat >expect &&\n+\tgit ls-files --debug refresh-stat | grep mtime >before &&\n+\tprintf \"modified\\n\" >refresh-stat &&\n+\ttest-tool chmtime -100000 refresh-stat &&\n+\ttest_must_fail git diff-files --quiet -- refresh-stat &&\n+\tgit update-index --refresh-stat-only &&\n+\tgit ls-files --debug refresh-stat | grep mtime >after &&\n+\t! test_cmp before after &&\n+\tgit ls-files --stage -- refresh-stat >actual &&\n+\ttest_cmp expect actual &&\n+\tgit diff-files --quiet -- refresh-stat\n+'\n+\n+test_expect_success '--refresh-stat-only ignores assume-unchanged' '\n+\ttest_commit assume-unchanged assume-unchanged old &&\n+\tgit update-index --assume-unchanged assume-unchanged &&\n+\tprintf \"new\\n\" >assume-unchanged &&\n+\ttest-tool chmtime -100000 assume-unchanged &&\n+\tGIT_TEST_PRELOAD_INDEX=1 git update-index --refresh-stat-only &&\n+\tgit update-index --no-assume-unchanged assume-unchanged &&\n+\tgit diff-files --quiet -- assume-unchanged\n+'\n+\n+test_expect_success '--refresh-stat-only with missing file and --ignore-missing' '\n+\ttest_commit missing-ignore missing-ignore content &&\n+\trm missing-ignore &&\n+\tgit update-index --ignore-missing --refresh-stat-only &&\n+\tgit checkout -- missing-ignore\n+'\n+\n+test_expect_success '--refresh-stat-only reports error on missing file without --ignore-missing' '\n+\ttest_commit missing-error missing-error content &&\n+\trm missing-error &&\n+\ttest_must_fail git update-index --refresh-stat-only >out 2>err &&\n+\ttest_grep \"needs update\" out &&\n+\tgit checkout -- missing-error\n+'\n+\n+test_expect_success '--refresh-stat-only with -q is quiet' '\n+\ttest_commit missing-quiet missing-quiet content &&\n+\trm missing-quiet &&\n+\tgit update-index -q --ignore-missing --refresh-stat-only >out 2>err &&\n+\ttest_must_be_empty out &&\n+\ttest_must_be_empty err\n+'\n+\n+test_done\n-- \ngitgitgadget\n"}]}