@RequiresEdt
  static @NotNull GitPushResultNotification create(@NotNull Project project,
                                                   @NotNull GitPushResult pushResult,
                                                   @Nullable GitPushOperation pushOperation,
                                                   boolean multiRepoProject,
                                                   @Nullable GitUpdateInfoAsLog.NotificationData notificationData,
                                                   @NotNull Map<String, VcsPushOptionValue> customParams) {
    GroupedPushResult grouped = GroupedPushResult.group(pushResult.getResults());

    String title;
    NotificationType type;
    boolean singleRepoSuccess = false;
    if (!grouped.errors.isEmpty()) {
      if (!grouped.successful.isEmpty()) {
        title = GitBundle.message("push.notification.partially.failed.title");
      }
      else {
        title = GitBundle.message("push.notification.push.failed.title");
      }
      type = NotificationType.ERROR;
    }
    else if (!grouped.rejected.isEmpty() || !grouped.customRejected.isEmpty()) {
      if (!grouped.successful.isEmpty()) {
        title = GitBundle.message("push.notification.partially.rejected.title");
      }
      else {
        title = GitBundle.message("push.notification.rejected.title");
      }
      type = NotificationType.WARNING;
    }
    else {
      type = NotificationType.INFORMATION;
      if (!multiRepoProject) {
        singleRepoSuccess = true;
        GitPushRepoResult result = getFirstItem(grouped.successful.values());
        title = formRepoDescription(result);
      }
      else {
        title = GitBundle.message("push.notification.successful.title");
      }
    }

    String description;
    if (singleRepoSuccess) {
      if (notificationData != null) {
        int receivedCommitsCount = notificationData.getReceivedCommitsCount();
        description = GitBundle.message("push.notification.single.repo.success.description", receivedCommitsCount);
      }
      else { // nothing was updated
        description = "";
      }
    }
    else {
      description = formDescription(pushResult.getResults(), multiRepoProject);
    }

    NotificationGroup group = type == NotificationType.INFORMATION ?
                              VcsNotifier.standardNotification() :
                              VcsNotifier.importantNotification();

    GitPushResultNotification notification = new GitPushResultNotification(group.getDisplayId(), title, description, type);

    if (AbstractCommonUpdateAction.showsCustomNotification(singletonList(GitVcs.getInstance(project)))) {
      if (notificationData != null && notificationData.getReceivedCommitsCount() > 0) {
        Integer filteredCommitsCount = notificationData.getFilteredCommitsCount();
        String actionText;
        if (filteredCommitsCount == null || filteredCommitsCount == 0) {
          actionText = GitBundle.message("push.notification.view.received.commits.action", notificationData.getReceivedCommitsCount());
        }
        else {
          actionText = GitBundle.message("push.notification.view.filtered.commits.actions", filteredCommitsCount);
        }
        notification.addAction(NotificationAction.createSimple(actionText, notificationData.getViewCommitAction()));
      }
    }
    else {
      UpdatedFiles updatedFiles = pushResult.getUpdatedFiles();
      if (!updatedFiles.isEmpty()) {
        UpdateInfoTree tree = ProjectLevelVcsManagerEx.getInstanceEx(project).showUpdateProjectInfo(
          updatedFiles,
          GitBundle.message("push.notification.update.action"),
          UPDATE,
          false
        );
        if (tree != null) {
          tree.setBefore(pushResult.getBeforeUpdateLabel());
          tree.setAfter(pushResult.getAfterUpdateLabel());
          notification.addAction(new ViewUpdateInfoNotification(
            project,
            tree,
            GitBundle.message("push.notification.view.files.action"),
            notification
          ));
        }
      }
    }

    List<GitRepository> staleInfoRejected = EntryStream.of(pushResult.getResults())
      .filterValues(result -> result.getType() == GitPushRepoResult.Type.REJECTED_STALE_INFO)
      .keys().toList();
    if (!staleInfoRejected.isEmpty()) {
      notification.setContextHelpAction(new AnAction(
        GitBundle.message("push.notification.force.with.lease.help"),
        new HtmlBuilder()
          .append(GitBundle.message("push.notification.force.with.lease.help.description.first")).br()
          .append(GitBundle.message("push.notification.force.with.lease.help.description.second"))
          .toString(),
        null
      ) {
        @Override
        public void actionPerformed(@NotNull AnActionEvent e) {
        }
      });
      if (pushOperation != null) {
        notification.addAction(new ForcePushNotificationAction(project, pushOperation, staleInfoRejected, customParams));
      }
    }

    if (!grouped.errors.isEmpty() ||
        !grouped.rejected.isEmpty() ||
        !grouped.customRejected.isEmpty()) {
      VcsNotifier.addShowDetailsAction(project, notification);
    }

    return notification;
  }
@RequiresEdt
  static @NotNull GitPushResultNotification create(@NotNull Project project,
                                                   @NotNull GitPushResult pushResult,
                                                   @Nullable GitPushOperation pushOperation,
                                                   boolean multiRepoProject,
                                                   @Nullable GitUpdateInfoAsLog.NotificationData notificationData,
                                                   @NotNull Map<String, VcsPushOptionValue> customParams)