ernal
  public void createAndAddChildren(@NotNull List<ChildInfo> added,
                                   boolean markAllChildrenLoaded,
                                   @NotNull BiConsumer<? super VirtualFile, ? super ChildInfo> callback) {
    if (added.size() <= 1) {
      for (int i = 0; i < added.size(); i++) {
        ChildInfo info = added.get(i);
        assert info.getId() > 0 : info;
        @SuppressWarnings("MagicConstant") @PersistentFS.Attributes int attributes = info.getFileAttributeFlags();
        boolean isEmptyDirectory = info.getChildren() != null && info.getChildren().length == 0;
        synchronized (myData) {
          int[] oldIds = myData.childrenIds;
          if (ArrayUtil.indexOf(oldIds, info.getId()) < 0) {
            VirtualFileSystemEntry file = createChildImpl(info.getId(), info.getNameId(), attributes, isEmptyDirectory);
            addChild(file);
            callback.accept(file, info);
          }
        }
      }
      if (markAllChildrenLoaded) {
        setAllChildrenLoaded();
      }
      return;
    }
    // when there are many children, it's cheaper to merge sorted lists just like in merge sort
    boolean isCaseSensitive = isCaseSensitive();
    Comparator<ChildInfo> byName = (p1, p2) -> compareNames(p1.getName(), p2.getName(), isCaseSensitive);
    added.sort(byName);

    synchronized (myData) {
      int[] oldIds = myData.childrenIds;
      IntList mergedIds = new IntArrayList(oldIds.length + added.size());
      FSRecordsImpl vfsPeer = owningPersistentFS().peer();

      List<ChildInfo> existingChildren = new AbstractList<>() {
        @Override
        public ChildInfo get(int index) {
          int id = oldIds[index];
          assert id > 0 : id;
          int nameId = vfsPeer.getNameIdByFileId(id);
          return new ChildInfoImpl(id, nameId, null, null, null/*irrelevant here*/);
        }

        @Override
        public int size() {
          return oldIds.length;
        }
      };
      ContainerUtil.processSortedListsInOrder(existingChildren, added, byName, /*mergeEqualItems: */ true, (nextInfo, mergeResult) -> {
        //TODO RC: this branch differs from (added.size=1) branch above in many aspects. The most noticeable are
        //         comparison: we merge existing/added lists by-name here, but we look up existing fileId above.
        //         This seems suspicious, and I'm not sure that consequences of the difference could be.
        //         Another difference is that here we call callback.accept() first, and update .childrenIds list with
        //         the new files afterwards, but in 1-item branch above we add new file to .childrenIds list first,
        //         and call the callback afterwards.
        if (mergeResult == ContainerUtil.MergeResult.COPIED_FROM_LIST2) {
          assert nextInfo.getId() > 0 : nextInfo;
          @SuppressWarnings("MagicConstant") @PersistentFS.Attributes int attributes = nextInfo.getFileAttributeFlags();
          boolean isEmptyDirectory = nextInfo.getChildren() != null && nextInfo.getChildren().length == 0;
          myData.removeAdoptedName(nextInfo.getName());
          VirtualFileSystemEntry file = createChildImpl(nextInfo.getId(), nextInfo.getNameId(), attributes, isEmptyDirectory);
          callback.accept(file, nextInfo);
        }
        mergedIds.add(nextInfo.getId());
      });
      myData.childrenIds = mergedIds.toIntArray();

      if (markAllChildrenLoaded) {
        setAllChildrenLoaded();
      }
      assertConsistency(isCaseSensitive, added);
    }
  }

  public voi
ernal
  public void createAndAddChildren(@NotNull List<ChildInfo> added,
                                   boolean markAllChildrenLoaded,
                                   @NotNull BiConsumer<? super VirtualFile, ? super ChildInfo> callback) {
    if (add