fetch refuses a manifest whose paths differ only in letter case (closes #154)
check / check (push) Failing after 2s
check / check (push) Failing after 2s
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
This commit was merged in pull request #157.
This commit is contained in:
+25
-12
@@ -466,17 +466,18 @@ func fetchManifest(
|
||||
}
|
||||
|
||||
// checkNoNameClash returns an error if files lists a file, or a directory
|
||||
// a file is in, under a name where fetch writes another file: the temp
|
||||
// file it downloads a listed file to, or, at the top of the tree, the
|
||||
// saved manifest or its temp file. fetch would remove or replace what is
|
||||
// a file is in, under a name where fetch writes another file: another
|
||||
// listed file, a directory another listed file is in, the temp file it
|
||||
// downloads a listed file to, or, at the top of the tree, the saved
|
||||
// manifest or its temp file. fetch would remove or replace what is
|
||||
// listed there, or fail partway, leaving a tree check rejects. Names are
|
||||
// compared ignoring case, since on a case-insensitive filesystem INDEX.MF
|
||||
// and index.mf are one file.
|
||||
// compared ignoring case, on every filesystem, since on a
|
||||
// case-insensitive one A.txt and a.txt are one file.
|
||||
func checkNoNameClash(files []*mfer.MFFilePath) error {
|
||||
sep := string(filepath.Separator)
|
||||
|
||||
// written maps each name fetch writes, other than the listed files
|
||||
// themselves, in lower case, to the file it writes there.
|
||||
// written maps each name fetch writes, in lower case, to the file or
|
||||
// directory it writes there.
|
||||
written := map[string]string{
|
||||
defaultManifestName: "the saved manifest",
|
||||
tempPathFor(defaultManifestName): "the saved manifest's temp file",
|
||||
@@ -488,14 +489,26 @@ func checkNoNameClash(files []*mfer.MFFilePath) error {
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
// Look up each directory on the file's path, then the file itself.
|
||||
parts := strings.Split(strings.ToLower(filepath.Clean(f.GetPath())), sep)
|
||||
// Look up and add each directory on the file's path, then the
|
||||
// file itself. Only the same directory, spelled alike, may
|
||||
// already be there.
|
||||
parts := strings.Split(filepath.Clean(f.GetPath()), sep)
|
||||
last := len(parts) - 1
|
||||
|
||||
for i := range parts {
|
||||
what, ok := written[strings.Join(parts[:i+1], sep)]
|
||||
if ok {
|
||||
return fmt.Errorf("%w: %s (%s)", errNameClash, f.GetPath(), what)
|
||||
name := strings.Join(parts[:i+1], sep)
|
||||
|
||||
what := "the directory " + name
|
||||
if i == last {
|
||||
what = "the file " + f.GetPath()
|
||||
}
|
||||
|
||||
other, ok := written[strings.ToLower(name)]
|
||||
if ok && (i == last || other != what) {
|
||||
return fmt.Errorf("%w: %s (%s)", errNameClash, f.GetPath(), other)
|
||||
}
|
||||
|
||||
written[strings.ToLower(name)] = what
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user