diff --git a/src/applications/phragment/storage/PhragmentFragment.php b/src/applications/phragment/storage/PhragmentFragment.php --- a/src/applications/phragment/storage/PhragmentFragment.php +++ b/src/applications/phragment/storage/PhragmentFragment.php @@ -201,32 +201,25 @@ $mappings[$path] = $data; } - // We need to detect any directories that are in the ZIP folder that - // aren't explicitly noted in the ZIP. This can happen if the file - // entries in the ZIP look like: + // Sort the mappings by key in the dictionary, so that iterating over it + // will result in entries like: // - // * something/blah.png - // * something/other.png - // * test.png + // a/ => null + // a/e.txt => ... + // b/c.txt => ... + // b/d.txt => ... // - // Where there is no explicit "something/" entry. - foreach ($mappings as $path_key => $data) { - if ($data === null) { - continue; - } - $directory = dirname($path_key); - while ($directory !== ".") { - if (!array_key_exists($directory, $mappings)) { - $mappings[$directory] = null; - } - if (dirname($directory) === $directory) { - // dirname() will not reduce this directory any further; to - // prevent infinite loop we just break out here. - break; - } - $directory = dirname($directory); - } - } + // This is important because if there are explicit directory entries in the + // ZIP, that might be because they are empty directories (in which case + // there's no call to self::createFromFile that would create them) or they + // could be out-of-order explicit directories where an unsorted list + // would cause a duplicate key exception. By sorting this, we ensure that + // explicit directories are always created before files that reside in them. + ksort($mappings); + + // We no longer need to explicitly create directories that are not + // set in the ZIP. The self::createFromFile function automatically + // creates preceding directories for file entries. // Adjust the paths relative to this fragment so we can look existing // fragments up in the DB. @@ -237,8 +230,8 @@ } // FIXME: What happens when a child exists, but the current user - // can't see it. We're going to create a new child with the exact - // same path and then bad things will happen. + // can't see it. We're going to place a new child under a path + // that the current user can't access. $children = id(new PhragmentFragmentQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->needLatestVersion(true)