Skip to content

fsfork -l: List the forks faster, and each as soon as it is looked into - #12

Open
penberg wants to merge 3 commits into
mainfrom
fsfork-list-faster
Open

penberg wants to merge 3 commits into
mainfrom
fsfork-list-faster

Conversation

@penberg

@penberg penberg commented Nov 6, 2026

Copy link
Copy Markdown
Owner

fsfork -l in a directory with 21 forks of turso took over ten minutes, with nothing printed until the end. It now takes about 20 seconds, and prints the header at once and each fork as soon as it is looked into:

$ fsfork -l turso
FORK                  UPDATED          STATUS
turso.6               6 days ago       unchanged
turso.7               6 days ago       73 files only here
turso.8               6 days ago       merged
turso.maint           7 hours ago      1180 files only here
...

One git apply for all of a fork's files

When a fork's whole patch did not undo in the directory, each changed file was checked on its own, with a git diff and a git apply --check each. Forks with an upstream pull in them change hundreds or thousands of files: turso.maint alone took about two minutes.

Git checks every file in one git apply --reverse --check --verbose, saying which file it is checking before any error about it, so the files that do not undo are counted from one run. The counts are the same as checking each file on its own, as checked on turso.7 and turso.rls.

Hashing again only the files that changed

A fork's files are clones of its base's, with new inodes and change times, so git hashed all 4,500 files of every fork and base again: a few seconds each.

Git now compares only a file's size and modification time, which clones keep. It compares only whole seconds then, which missed a file changed within the same second to the same size, as the fsdiff tests do. So a file whose time differs from the one the index was made from only within the second is hashed again first, comparing all its stat, staying in the index so that git does not ignore it. This makes fsdiff faster too.

Each fork as soon as it is looked into

The forks are listed in the order they were made, rather than those left alone longest first, as sorting them would mean waiting for all. --prune still looks into all before removing any.

Not done

Every run still hashes every base again, though bases never change: about two thirds of what is left. A cache that outlives the run, of each base's index and tree, would take that away.

When a fork's whole patch did not undo in the directory, each changed
file was checked on its own: a `git diff` and a `git apply --check` for
each. A fork with an upstream pull in it changes a thousand files, and
listing the forks of turso took over ten minutes.

Git checks every file's changes in one `git apply --reverse --check
--verbose`, saying which file it is checking before any error about it,
so the files that do not undo are counted from one run, without reading
their paths back from what git says.
Every fork's files are clones of its base's, with new inodes and change
times, so git hashed every file of every fork and base again: a few
seconds each, for 4,500 files in turso.

Git now compares only a file's size and modification time, which clones
keep. It compares only the whole seconds of the time then, so a file
whose time differs from the one the index was made from only within the
second is hashed again first, comparing all its stat. It stays in the
index, so that git does not ignore it.
The list was printed only once every fork had been looked into, after
seconds with nothing to show. The header is printed at once, and each
fork as soon as it is looked into, in the order they were made, with
the columns as wide as the forks' paths, known before any is looked
into, and the longest age.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant