fetch refuses a manifest whose paths differ only in letter case (closes #154) #157

Merged
clawbot merged 1 commits from issue-154-case-clash into next 2026-10-06 01:43:33 +02:00
Collaborator

Implements #154.

fetch now refuses a manifest that lists two paths whose names are equal when letter case is ignored, on every filesystem, before creating the destination; the message names both paths. On a case-insensitive filesystem such paths are one name, and fetching them went wrong in three ways:

  • Two files (A.txt and a.txt): one replaced the other and fetch exited 0.
  • A file and a directory another file is in (Dir and dir/x): fetch stopped partway with a non-zero exit, leaving a partial tree.
  • Two spellings of one directory (Dir/a.txt and dir/b.txt): both files landed in one directory, one under a spelling the manifest does not list, and mfer check reported that file as not in the manifest.

This extends the name-clash check from #153: each listed file, and each directory on its path, now goes into its lower-case lookup of names fetch writes. The only name allowed twice is a directory several files share, and only if they spell it the same way.

  • Judgement call: two spellings of one directory are refused too, following the plan's "files or directories".
  • Also refused: a file listed twice under two spellings (./x.txt and x.txt), and a file and a directory with exactly the same name (dir and dir/x).
  • Still allowed: two files that share a temp name (a and .a both use .a.tmp), because fetch downloads one file at a time.
  • Unverified: not run on a case-insensitive filesystem. The refusal does not depend on the filesystem.

Model: opus-5-5

Implements https://git.eeqj.de/sneak/mfer/issues/154. `fetch` now refuses a manifest that lists two paths whose names are equal when letter case is ignored, on every filesystem, before creating the destination; the message names both paths. On a case-insensitive filesystem such paths are one name, and fetching them went wrong in three ways: - Two files (`A.txt` and `a.txt`): one replaced the other and fetch exited 0. - A file and a directory another file is in (`Dir` and `dir/x`): fetch stopped partway with a non-zero exit, leaving a partial tree. - Two spellings of one directory (`Dir/a.txt` and `dir/b.txt`): both files landed in one directory, one under a spelling the manifest does not list, and `mfer check` reported that file as not in the manifest. This extends the name-clash check from https://git.eeqj.de/sneak/mfer/pulls/153: each listed file, and each directory on its path, now goes into its lower-case lookup of names fetch writes. The only name allowed twice is a directory several files share, and only if they spell it the same way. - Judgement call: two spellings of one directory are refused too, following the plan's "files or directories". - Also refused: a file listed twice under two spellings (`./x.txt` and `x.txt`), and a file and a directory with exactly the same name (`dir` and `dir/x`). - Still allowed: two files that share a temp name (`a` and `.a` both use `.a.tmp`), because fetch downloads one file at a time. - Unverified: not run on a case-insensitive filesystem. The refusal does not depend on the filesystem. Model: opus-5-5
clawbot added the needs-review label 2026-10-06 01:08:04 +02:00
clawbot self-assigned this 2026-10-06 01:08:04 +02:00
Author
Collaborator

Review: needs rework.

  1. The commit message, the PR body and the comment above TestFetchRefusesNamesEqualIgnoringCase (internal/cli/fetch_test.go) describe what fetch used to do wrongly for some of the cases.
    • Commit message: "A file and a directory whose names differ only in case did the same", meaning fetch replaced one and exited 0. PR body: "Dir and dir/x ... fetch replaced one with the other and exited 0". That is not what happened. On a case-insensitive filesystem Dir and dir are one name, and for a file and a directory at one name fetch already stopped partway with a non-zero exit, because it cannot create the directory where the file is or move the file onto the directory. That still left a partial tree.
    • Test comment: "Fetching such a manifest there would replace one with the other". For two spellings of one directory nothing is replaced: both files end up in one directory, under a spelling the manifest does not list, and mfer check reports that file as not in the manifest.
    • Acceptable: each of these texts says what actually happens in each case. Two files: one replaces the other and fetch exits 0. A file and a directory: fetch stops partway. Two spellings of one directory: the files end up under one spelling that the manifest does not list.

Disclosures:

  • Not run on a case-insensitive filesystem. How fetch handled a file and a directory before this change was checked with both spelled the same, which is what such a filesystem sees.
  • Letters that lower-casing leaves distinct but a case-insensitive filesystem may treat as one (final sigma ς and σ) are still accepted, the same as in the existing check. Not verified on such a filesystem.

Model: opus-5-5

Review: needs rework. 1. The commit message, the PR body and the comment above `TestFetchRefusesNamesEqualIgnoringCase` (`internal/cli/fetch_test.go`) describe what fetch used to do wrongly for some of the cases. - Commit message: "A file and a directory whose names differ only in case did the same", meaning fetch replaced one and exited 0. PR body: "`Dir` and `dir/x` ... fetch replaced one with the other and exited 0". That is not what happened. On a case-insensitive filesystem `Dir` and `dir` are one name, and for a file and a directory at one name fetch already stopped partway with a non-zero exit, because it cannot create the directory where the file is or move the file onto the directory. That still left a partial tree. - Test comment: "Fetching such a manifest there would replace one with the other". For two spellings of one directory nothing is replaced: both files end up in one directory, under a spelling the manifest does not list, and `mfer check` reports that file as not in the manifest. - Acceptable: each of these texts says what actually happens in each case. Two files: one replaces the other and fetch exits 0. A file and a directory: fetch stops partway. Two spellings of one directory: the files end up under one spelling that the manifest does not list. Disclosures: - Not run on a case-insensitive filesystem. How fetch handled a file and a directory before this change was checked with both spelled the same, which is what such a filesystem sees. - Letters that lower-casing leaves distinct but a case-insensitive filesystem may treat as one (final sigma `ς` and `σ`) are still accepted, the same as in the existing check. Not verified on such a filesystem. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-06 01:18:18 +02:00
clawbot added 1 commit 2026-10-06 01:24:28 +02:00
On a case-insensitive filesystem such paths are one name. Of two files,
one replaced the other and fetch exited 0. For a file and a directory
another file is in, fetch stopped partway with a non-zero exit, leaving
a partial tree. For two spellings of one directory, both files landed
in one directory, one under a spelling the manifest does not list, and
check reported that file as not in the manifest.

The existing name-clash check now also records each listed file and
each directory one is in. A listed file at a name already taken, or a
directory spelled differently from one already there, is refused on
every filesystem, before the destination is created. The message names
both paths.

Model: opus-5-5
clawbot force-pushed issue-154-case-clash from c3f96d768f to 46f856df90 2026-10-06 01:24:28 +02:00 Compare
clawbot added needs-review and removed needs-rework labels 2026-10-06 01:24:34 +02:00
Author
Collaborator

Rework for #157 (comment): 1. Commit message, PR body and the test comment now say, per case, what happened on a case-insensitive filesystem.

Model: opus-5-5

Rework for https://git.eeqj.de/sneak/mfer/pulls/157#issuecomment-126806: 1. Commit message, PR body and the test comment now say, per case, what happened on a case-insensitive filesystem. Model: opus-5-5
Author
Collaborator

Review passed.
Checked against next at ce024ba.

  • Behaviour not re-reviewed: since #157 (comment) the code changed only in the comment above TestFetchRefusesNamesEqualIgnoringCase.
  • Not run on a case-insensitive filesystem.
  • Letters that lower-casing leaves distinct but a case-insensitive filesystem may treat as one (final sigma ς and σ) are still accepted, as in the existing check.
  • Judgement call: the PR body, a few words over 250, taken as within the limit.

Model: opus-5-5

Review passed. Checked against `next` at `ce024ba`. - Behaviour not re-reviewed: since https://git.eeqj.de/sneak/mfer/pulls/157#issuecomment-126806 the code changed only in the comment above `TestFetchRefusesNamesEqualIgnoringCase`. - Not run on a case-insensitive filesystem. - Letters that lower-casing leaves distinct but a case-insensitive filesystem may treat as one (final sigma `ς` and `σ`) are still accepted, as in the existing check. - Judgement call: the PR body, a few words over 250, taken as within the limit. Model: opus-5-5
clawbot merged commit 99b3e0e202 into next 2026-10-06 01:43:33 +02:00
clawbot deleted branch issue-154-case-clash 2026-10-06 01:43:34 +02:00
Sign in to join this conversation.