@Override
  protected @Nullable HgFileRevision convertDetails(@NotNull String rev,
                                                    @NotNull String changeset,
                                                    @NotNull SmartList<? extends HgRevisionNumber> parents,
                                                    @NotNull Date revisionDate,
                                                    @NotNull String author,
                                                    @NotNull String email,
                                                    @NotNull List<String> attributes) {
    int numAttributes = attributes.size();
    String commitMessage = parseAdditionalStringAttribute(attributes, MESSAGE_INDEX);
    String branchName = parseAdditionalStringAttribute(attributes, BRANCH_INDEX);
    final HgRevisionNumber vcsRevisionNumber = new HgRevisionNumber(rev, changeset, author, email, commitMessage, parents);

    Set<String> filesAdded = Collections.emptySet();
    Set<String> filesModified = Collections.emptySet();
    Set<String> filesDeleted = Collections.emptySet();
    Map<String, String> copies = Collections.emptyMap();
    boolean shouldParseOldTemplate = !myVersion.isBuiltInFunctionSupported();
    String separator = shouldParseOldTemplate ? " " : HgChangesetUtil.FILE_SEPARATOR;
    // At least in the case of the long template, it's OK that we don't have everything...for example, if there were no
    //  deleted or copied files, then we won't get any attributes for them...
    if (numAttributes > FILES_ADDED_INDEX) {
      filesAdded = parseFileList(attributes.get(FILES_ADDED_INDEX), separator);

      if (numAttributes > FILES_MODIFIED_INDEX) {
        filesModified = parseFileList(attributes.get(FILES_MODIFIED_INDEX), separator);

        if (numAttributes > FILES_DELETED_INDEX) {
          filesDeleted = parseFileList(attributes.get(FILES_DELETED_INDEX), separator);

          if (numAttributes > FILES_COPIED_INDEX) {
            copies = shouldParseOldTemplate
                     ? parseCopiesFileListAsOldVersion(attributes.get(FILES_COPIED_INDEX))
                     : parseCopiesFileList(attributes.get(FILES_COPIED_INDEX));
            // Only keep renames, i.e. copies where the source file is also deleted.
            Iterator<String> keys = copies.keySet().iterator();
            while (keys.hasNext()) {
              String s = keys.next();
              if (filesAdded.contains(copies.get(s)) && filesDeleted.contains(s)) {
                filesAdded.remove(copies.get(s));
                filesDeleted.remove(s);
              }
              else if (!filesDeleted.contains(s)) {
                keys.remove();
              }
            }
          }
        }
      }
    }
    return new HgFileRevision(myProject, myHgFile, vcsRevisionNumber, branchName, revisionDate, vcsRevisionNumber.getAuthor(), commitMessage,
                              filesModified, filesAdded, filesDeleted, copies);
  }
@Override
  protected @Nullable HgFileRevision convertDetails(@NotNull String rev,
                                                    @NotNull String changeset,
                                                    @NotNull SmartList<? extends HgRevisionNumber> parents,
                                                    @NotNull Date revisionDate,
                                                    @NotNull String author,
                                                    @NotNull String email,
                                                    @NotNull List<String> attributes)