@Override
  public void rollbackChanges(List<? extends Change> changes,
                              List<VcsException> exceptions,
                              @NotNull RollbackProgressListener listener) {
    HashMap<VirtualFile, List<FilePath>> toUnindex = new HashMap<>();
    HashMap<VirtualFile, List<FilePath>> toUnversion = new HashMap<>();
    HashMap<VirtualFile, List<FilePath>> toRevert = new HashMap<>();
    List<FilePath> toDelete = new ArrayList<>();

    listener.determinate();
    // collect changes to revert
    for (Change c : changes) {
      switch (c.getType()) {
        case NEW ->
          // note that this the only change that could happen
          // for HEAD-less working directories.
          registerFile(toUnversion, c.getAfterRevision().getFile(), exceptions);
        case MOVED -> {
          registerFile(toRevert, c.getBeforeRevision().getFile(), exceptions);
          registerFile(toUnindex, c.getAfterRevision().getFile(), exceptions);
          toDelete.add(c.getAfterRevision().getFile());
        }
        case MODIFICATION ->
          // note that changes are also removed from index, if they got into index somehow
          registerFile(toRevert, c.getBeforeRevision().getFile(), exceptions);
        case DELETED -> registerFile(toRevert, c.getBeforeRevision().getFile(), exceptions);
      }
    }
    // unindex files
    for (Map.Entry<VirtualFile, List<FilePath>> entry : toUnindex.entrySet()) {
      listener.accept(entry.getValue());
      try {
        unindex(entry.getKey(), entry.getValue(), false);
      }
      catch (VcsException e) {
        exceptions.add(e);
      }
    }
    // unversion files
    for (Map.Entry<VirtualFile, List<FilePath>> entry : toUnversion.entrySet()) {
      listener.accept(entry.getValue());
      try {
        unindex(entry.getKey(), entry.getValue(), true);
      }
      catch (VcsException e) {
        exceptions.add(e);
      }
    }
    // delete files
    for (FilePath file : toDelete) {
      listener.accept(file);
      try {
        File ioFile = file.getIOFile();
        if (ioFile.exists()) {
          if (!ioFile.delete()) {
            exceptions.add(new VcsException(GitBundle.message("error.cannot.delete.file", file.getPresentableUrl())));
          }
        }
      }
      catch (Exception e) {
        exceptions.add(new VcsException(GitBundle.message("error.cannot.delete.file", file.getPresentableUrl()), e));
      }
    }
    // revert files from HEAD
    try (AccessToken ignore = DvcsUtil.workingTreeChangeStarted(myProject, VcsBundle.message("activity.name.rollback"))) {
      for (Map.Entry<VirtualFile, List<FilePath>> entry : toRevert.entrySet()) {
        listener.accept(entry.getValue());
        try {
          revert(entry.getKey(), entry.getValue());
        }
        catch (VcsException e) {
          exceptions.add(e);
        }
      }
    }
    LocalFileSystem lfs = LocalFileSystem.getInstance();
    HashSet<File> filesToRefresh = new HashSet<>();
    for (Change c : changes) {
      ContentRevision before = c.getBeforeRevision();
      if (before != null) {
        filesToRefresh.add(new File(before.getFile().getPath()));
      }
      ContentRevision after = c.getAfterRevision();
      if (after != null) {
        filesToRefresh.add(new File(after.getFile().getPath()));
      }
    }
    lfs.refreshIoFiles(filesToRefresh);
    GitIndexFileSystemRefresher.refreshFilePaths(myProject, toUnindex);

    for (GitRepository repo : GitUtil.getRepositoryManager(myProject).getRepositories()) {
      repo.update();
    }
  }
@Override
  public void rollbackChanges(List<? extends Change> changes,
                              List<VcsException> exceptions,
                              @NotNull RollbackProgressListener listener)