private @NotNull GitCommandResult doPush(final @NotNull GitRepository repository,
                                           final @NotNull String remoteName,
                                           final @NotNull Collection<String> remoteUrls,
                                           final @NotNull String spec,
                                           final boolean force,
                                           final boolean updateTracking,
                                           final boolean skipHook,
                                           final List<? extends GitPushParams.ForceWithLease> forceWithLease,
                                           final @Nullable String tagMode,
                                           final GitLineHandlerListener @NotNull ... listeners) {
    return runCommand(() -> {
      final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.PUSH);
      h.setUrls(remoteUrls);
      h.setSilent(false);
      h.setStdoutSuppressed(false);
      addListeners(h, listeners);
      if(GitVersionSpecialty.ABLE_TO_USE_PROGRESS_IN_REMOTE_COMMANDS.existsIn(repository)) {
        h.addParameters("--progress");
      }
      h.addParameters("--porcelain");
      h.addParameters(remoteName);
      h.addParameters(spec);
      if (updateTracking) {
        h.addParameters("--set-upstream");
      }
      if (GitVersionSpecialty.SUPPORTS_FORCE_PUSH_WITH_LEASE.existsIn(repository) &&
          !forceWithLease.isEmpty()) {
        for (GitPushParams.ForceWithLease lease : forceWithLease) {
          String parameter = lease.getParameter();
          if (parameter != null) {
            h.addParameters("--force-with-lease=" + parameter);
          }
          else {
            h.addParameters("--force-with-lease");
          }
        }
      }
      else if (force) {
        h.addParameters("--force");
      }
      if (tagMode != null) {
        h.addParameters(tagMode);
      }
      if (skipHook) {
        h.addParameters("--no-verify");
      }
      return h;
    });
  }

  @Override
  public @NotNull GitCommandResult show(@NotNull GitRepository repository, String @NotNull ... params) {
    final GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.SHOW);
    handler.addParameters(params);
    return runCommand(handler);
  }

  @Override
  public @NotNull GitCommandResult cherryPick(@NotNull GitRepository repository,
                                              @NotNull String hash,
                                              boolean autoCommit,
                                              boolean addCherryPickedFromSuffix,
                                              GitLineHandlerListener @NotNull ... listeners) {
    final GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.CHERRY_PICK);
    if (addCherryPickedFromSuffix) {
      handler.addParameters("-x");
    }
    if (!autoCommit) {
      handler.addParameters("-n");
    }
    handler.addParameters(hash);
    addListeners(handler, listeners);
    handler.setSilent(false);
    handler.setStdoutSuppressed(false);
    return runCommand(handler);
  }

  @Override
  public @NotNull GitCommandResult getUnmergedFiles(@NotNull GitRepository repository) {
    GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.LS_FILES);
    h.addParameters("--unmerged");
    h.setSilent(true);
    return runCommand(h);
  }

  /**
   * Fetch remote branch
   * {@code git fetch <remote> <params>}
   */
  @Override
  public @NotNull GitCommandResult fetch(final @NotNull GitRepository repository,
                                final @NotNull GitRemote remote,
                                final @NotNull List<? extends GitLineHandlerListener> listeners,
                                final String... params) {
    return fetch(repository, remote, listeners, null, params);
  }

  public @NotNull GitCommandResult fetch(final @NotNull GitRepository repository,
                                         final @NotNull GitRemote remote,
                                         final @NotNull List<? extends GitLineHandlerListener> listeners,
                                         @Nullable AuthenticationGate authenticationGate,
                                         final String... params) {
    return runCommand(() -> {
      GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.FETCH);
      if (authenticationGate != null) {
        h.setAuthenticationGate(authenticationGate);
      }
      h.setSilent(false);
      h.setStdoutSuppressed(false);
      h.setUrls(remote.getUrls());
      h.addParameters(remote.getName());
      h.addParameters(params);
      if(GitVersionSpecialty.ABLE_TO_USE_PROGRESS_IN_REMOTE_COMMANDS.existsIn(repository)) {
        h.addParameters("--progress");
      }
      if (GitVersionSpecialty.SUPPORTS_FETCH_PRUNE.existsIn(repository)) {
        h.addParameters("--prune");
      }
      addListeners(h, listeners);
      return h;
    });
  }
private @NotNull GitCommandResult doPush(final @NotNull GitRepository repository,
                                           final @NotNull String remoteName,
                                           final @NotNull Collection<String> remoteUrls,
                                           final @NotNull String spec,
                                           final boolean force,
                                           final boolean updateTracking,
                                           final boolean skipHook,
                                           final List<? extends GitPushParams.ForceWithLease> forceWithLease,
                                           final @Nullable String tagMode,
                                           final GitLineHandlerListener @NotNull ... listeners) {
    return runCommand(() -> {
      final GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.PUSH);
      h.setUrls(remoteUrls);
      h.setSilent(false);
      h.setStdoutSuppressed(false);
      addListeners(h, listeners);
      if(GitVersionSpecialty.ABLE_TO_USE_PROGRESS_IN_REMOTE_COMMANDS.existsIn(repository)) {
        h.addParameters("--progress");
      }
      h.addParameters("--porcelain");
      h.addParameters(remoteName);
      h.addParameters(spec);
      if (updateTracking) {
        h.addParameters("--set-upstream");
      }
      if (GitVersionSpecialty.SUPPORTS_FORCE_PUSH_WITH_LEASE.existsIn(repository) &&
          !forceWithLease.isEmpty()) {
        for (GitPushParams.ForceWithLease lease : forceWithLease) {
          String parameter = lease.getParameter();
          if (parameter != null) {
            h.addParameters("--force-with-lease=" + parameter);
          }
          else {
            h.addParameters("--force-with-lease");
          }
        }
      }
      else if (force) {
        h.addParameters("--force");
      }
      if (tagMode != null) {
        h.addParameters(tagMode);
      }
      if (skipHook) {
        h.addParameters("--no-verify");
      }
      return h;
    });
  }

  @Override
  public @NotNull GitCommandResult show(@NotNull GitRepository repository, String @NotNull ... params) {
    final GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.SHOW);
    handler.addParameters(params);
    return runCommand(handler);
  }

  @Override
  public @NotNull GitCommandResult cherryPick(@NotNull GitRepository repository,
                                              @NotNull String hash,
                                              boolean autoCommit,
                                              boolean addCherryPickedFromSuffix,
                                              GitLineHandlerListener @NotNull ... listeners) {
    final GitLineHandler handler = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.CHERRY_PICK);
    if (addCherryPickedFromSuffix) {
      handler.addParameters("-x");
    }
    if (!autoCommit) {
      handler.addParameters("-n");
    }
    handler.addParameters(hash);
    addListeners(handler, listeners);
    handler.setSilent(false);
    handler.setStdoutSuppressed(false);
    return runCommand(handler);
  }

  @Override
  public @NotNull GitCommandResult getUnmergedFiles(@NotNull GitRepository repository) {
    GitLineHandler h = new GitLineHandler(repository.getProject(), repository.getRoot(), GitCommand.LS_FILES);
    h.addParameters("--unmerged");
    h.setSilent(true);
    return runCommand(h);
  }

  /**
   * Fetch remote branch
   * {@code git fetch <remote> <params>}
   */
  @Override
  public @NotNull GitCommandResult fetch(final @NotNull GitRepository repository,
                                final @NotNull GitRemote remote,
                                final @NotNull List<? extends GitLineHandlerListener> listeners,
                                final String... params) {
    return fetch(repository, remote, listeners, null, params);
  }

  public @NotNull GitCommandResult fetch(final @NotNull GitRepository repository,
                                         final @NotNull GitRemote remote,
                                         final @NotNull List<? extends GitLineHandlerListener> listeners,
                                         @Nullable AuthenticationGate authenticationGate,
                                         final String... params)