@Override
    public @NotNull GistData<Data> getProjectData(@Nullable Project project,
                                                  @NotNull VirtualFile file,
                                                  int expectedGistStamp) throws IOException {
      FileAttribute fileAttribute = fileAttributeFor(id, version);
      String projectId = projectIdOf(project);

      CancellationUtil.lockMaybeCancellable(lock.readLock());
      try {
        try (AttributeInputStream stream = fileAttribute.readFileAttribute(file)) {
          if (stream == null) {
            return GistData.empty();
          }
          //iterate through Gists records, look for Gist for the project given:
          while (true) {
            GistRecord<Data> gistRecord = nextRecordOrNull(stream, externalizer);
            if (gistRecord == null) {
              return GistData.empty();
            }
            if (!gistRecord.matchProjectId(projectId)) {
              continue;
            }
            if (gistRecord.gistStamp != expectedGistStamp) {
              return GistData.outdated(gistRecord.gistStamp);
            }
            if (gistRecord.externalFileSuffix == null) {
              return GistData.valid(
                gistRecord.payload,
                gistRecord.gistStamp
              );
            }
            else {
              Path gistPath = dedicatedGistFilePath(file, gistRecord.externalFileSuffix);
              if (!Files.exists(gistPath)) {
                if (VfsLog.isVfsTrackingEnabled()) {
                  // maybe there was a recovery: gists were lost, but attributes were recovered
                  LOG.warn("Gist file [" + gistPath + "] doesn't exist, probably a vfs recovery has happened recently?");
                  return GistData.empty();
                }
                //looks like data corruption: if gist value was indeed null, we would have stored it as VALUE_KIND_NULL
                throw new IOException("Gist file [" + gistPath + "] doesn't exist -> looks like data corruption?");
              }
              try (DataInputStream gistStream = new DataInputStream(Files.newInputStream(gistPath, READ))) {
                return GistData.valid(
                  externalizer.read(gistStream),
                  gistRecord.gistStamp
                );
              }
            }
          }
        }
        catch (ProcessCanceledException pce) {
          throw pce;
        }
        catch (Exception e) {
          throw new IOException("Can't read " + this, e);
        }
      }
      finally {
        lock.readLock().unlock();
      }
    }
@Override
    public @NotNull GistData<Data> getProjectData(@Nullable Project project,
                                                  @NotNull VirtualFile file,
                                                  int expectedGistStamp)