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

Empty directory showing as untracked with untrackedCache (with reproduction script)

From
NLNikita Leshenko <nikita@island.io>
Date
Oct 21, 2024, 16:58 UTC
Message-ID
<CAEXts1vjiFsMCL9ANsqnz-mwFCZQyM6S5u_qXz8v1P=z1o3gsQ@mail.gmail.com>
Summary: Enabling untrackedCache sometimes causes an empty directory to show as
untracked (as if the directory contains content). This is racy but there's a
script to reproduce it deterministically.
What did you do before the bug happened? (Steps to reproduce your issue)
- Create repo with some content
- Enable untrackedCache
- git status, shows nothing
- create aa/bb
- git status, shows '?? aa/'
- rm aa/bb (leaving aa/ empty)
- git status
What did you expect to happen? (Expected behavior)
git status should show nothing.
What happened instead? (Actual behavior)
git status shows aa/ as having untracked content.
Anything else you want to add:

Here's a Python script to reproduce: ------------------------ >8 ------------------------ import os from subprocess import check_output, check_call

check_call(['git', 'init'])
# Populate directory for some load, makes reproduction faster.
for a in range(40):
  os.mkdir(f'z_{a}')
  for b in range(40):
    os.mkdir(f'z_{a}/{b}')
    for c in range(40):
      with open(f'z_{a}/{b}/{c}', 'w') as f:
        pass
check_call(['git', 'add', '.'])
check_call(['git', 'commit', '-m', '.', '-q'])

# Enable untracked cache. check_call(['git', 'update-index', '--test-untracked-cache']) check_call(['git', 'config', 'core.untrackedCache', 'true'])

# Repeatedly create/delete same file. for i in range(1, 1000):

  s = check_output(['git', 'status', '--porcelain'])
  assert not s, "Expected clean git status"
  os.mkdir(f'aa')
  with open('aa/bb', 'w') as f:
    pass
  s = check_output(['git', 'status', '--porcelain'])
  assert s == b'?? aa/\n', "Expected aa dir to show up"
  os.unlink('aa/bb')
  s = check_output(['git', 'status', '--porcelain'])
  if s:
    print("\n\n=============== REPRODUCED! ===============\n")
    print(f'Reproduced in {i} attempts')
    print(f'\n$ ls -la aa/')
    check_call(['ls', '-la', 'aa/'])
    print(f'\n$ git status')
    check_call(['git', 'status'])
    print(f'\n$ git --version')
    check_call(['git', '--version'])
    break
  os.rmdir(f'aa')
else:
  print('Failed to reproduce')
------------------------ >8 ------------------------

Example output of this script: ------------------------ >8 ------------------------ Initialized empty Git repository in /home/user/tmp/repro/.git/ Testing mtime in '/home/user/tmp/repro' ...... OK

=============== REPRODUCED! ===============
Reproduced in 90 attempts

$ ls -la aa/ total 8 drwxr-xr-x. 2 user user 4096 Oct 21 19:46 . drwxr-xr-x. 44 user user 4096 Oct 21 19:46 ..

$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)
aa/
nothing added to commit but untracked files present (use "git add" to track)

$ git --version git version 2.47.0.107.g34b6ce9b30 ------------------------ >8 ------------------------

This reproduced in the following Git versions:
- Latest master (34b6ce9b30)
- Latest shipping in Fedora (2.47.0)
- Ubuntu 22 LTS (2.34.1)
[System Info]
git version:
git version 2.47.0.107.g34b6ce9b30
cpu: x86_64
built from commit: 34b6ce9b30747131b6e781ff718a45328aa887d0
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/sh
libcurl: 8.6.0
OpenSSL: OpenSSL 3.2.2 4 Jun 2024
zlib: 1.3.1.zlib-ng
uname: Linux 6.11.3-200.fc40.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Oct 10
22:31:19 UTC 2024 x86_64
compiler info: gnuc: 14.2
libc info: glibc: 2.39
$SHELL (typically, interactive shell): /bin/zsh
[Enabled Hooks]
Message 1 of 1 in “Empty directory showing as untracked with untrackedCache (with reproduction script)”
  1. Nikita LeshenkoOct 21, 2024

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.