/**
  * Prepare delete files handler.
  *
  * @param project the project
  * @param root a vcs root
  * @param files a files to commit
  * @param message a message file to use
  * @param nextCommitAuthor a author for the next commit
  * @param nextCommitAmend true, if the commit should be amended
  * @param nextCommitAuthorDate Author date timestamp to override the date of the commit or null if
  *     this overriding is not needed.
  * @return a simple handler that does the task
  * @throws VcsException in case of git problem
  */
 private static void commit(
     Project project,
     VirtualFile root,
     Collection<FilePath> files,
     File message,
     final String nextCommitAuthor,
     boolean nextCommitAmend,
     Date nextCommitAuthorDate)
     throws VcsException {
   boolean amend = nextCommitAmend;
   for (List<String> paths : VcsFileUtil.chunkPaths(root, files)) {
     GitSimpleHandler handler = new GitSimpleHandler(project, root, GitCommand.COMMIT);
     handler.setStdoutSuppressed(false);
     if (amend) {
       handler.addParameters("--amend");
     } else {
       amend = true;
     }
     handler.addParameters("--only", "-F", message.getAbsolutePath());
     if (nextCommitAuthor != null) {
       handler.addParameters("--author=" + nextCommitAuthor);
     }
     if (nextCommitAuthorDate != null) {
       handler.addParameters("--date", COMMIT_DATE_FORMAT.format(nextCommitAuthorDate));
     }
     handler.endOptions();
     handler.addParameters(paths);
     handler.run();
   }
   if (!project.isDisposed()) {
     GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
     manager.updateRepository(root);
   }
 }
  void saveRemainingPatches(
      final ShelvedChangeList changeList,
      final List<FilePatch> remainingPatches,
      final List<ShelvedBinaryFile> remainingBinaries,
      CommitContext commitContext) {
    final File newPath = getPatchPath(changeList.DESCRIPTION);
    try {
      FileUtil.copy(new File(changeList.PATH), newPath);
    } catch (IOException e) {
      // do not delete if cannot recycle
      return;
    }
    final ShelvedChangeList listCopy =
        new ShelvedChangeList(
            newPath.getAbsolutePath(),
            changeList.DESCRIPTION,
            new ArrayList<ShelvedBinaryFile>(changeList.getBinaryFiles()));
    listCopy.DATE = (changeList.DATE == null) ? null : new Date(changeList.DATE.getTime());

    writePatchesToFile(myProject, changeList.PATH, remainingPatches, commitContext);

    changeList.getBinaryFiles().retainAll(remainingBinaries);
    changeList.clearLoadedChanges();
    recycleChangeList(listCopy, changeList);
    notifyStateChanged();
  }
  /**
   * Preform a merge commit
   *
   * @param project a project
   * @param root a vcs root
   * @param added added files
   * @param removed removed files
   * @param messageFile a message file for commit
   * @param author an author
   * @param exceptions the list of exceptions to report
   * @param partialOperation
   * @return true if merge commit was successful
   */
  private static boolean mergeCommit(
      final Project project,
      final VirtualFile root,
      final Set<FilePath> added,
      final Set<FilePath> removed,
      final File messageFile,
      final String author,
      List<VcsException> exceptions,
      @NotNull final PartialOperation partialOperation) {
    HashSet<FilePath> realAdded = new HashSet<FilePath>();
    HashSet<FilePath> realRemoved = new HashSet<FilePath>();
    // perform diff
    GitSimpleHandler diff = new GitSimpleHandler(project, root, GitCommand.DIFF);
    diff.setSilent(true);
    diff.setStdoutSuppressed(true);
    diff.addParameters("--diff-filter=ADMRUX", "--name-status", "HEAD");
    diff.endOptions();
    String output;
    try {
      output = diff.run();
    } catch (VcsException ex) {
      exceptions.add(ex);
      return false;
    }
    String rootPath = root.getPath();
    for (StringTokenizer lines = new StringTokenizer(output, "\n", false);
        lines.hasMoreTokens(); ) {
      String line = lines.nextToken().trim();
      if (line.length() == 0) {
        continue;
      }
      String[] tk = line.split("\t");
      switch (tk[0].charAt(0)) {
        case 'M':
        case 'A':
          realAdded.add(VcsUtil.getFilePath(rootPath + "/" + tk[1]));
          break;
        case 'D':
          realRemoved.add(VcsUtil.getFilePathForDeletedFile(rootPath + "/" + tk[1], false));
          break;
        default:
          throw new IllegalStateException("Unexpected status: " + line);
      }
    }
    realAdded.removeAll(added);
    realRemoved.removeAll(removed);
    if (realAdded.size() != 0 || realRemoved.size() != 0) {

      final List<FilePath> files = new ArrayList<FilePath>();
      files.addAll(realAdded);
      files.addAll(realRemoved);
      final Ref<Boolean> mergeAll = new Ref<Boolean>();
      try {
        GuiUtils.runOrInvokeAndWait(
            new Runnable() {
              public void run() {
                String message =
                    GitBundle.message("commit.partial.merge.message", partialOperation.getName());
                SelectFilePathsDialog dialog =
                    new SelectFilePathsDialog(
                        project,
                        files,
                        message,
                        null,
                        "Commit All Files",
                        CommonBundle.getCancelButtonText(),
                        false);
                dialog.setTitle(GitBundle.getString("commit.partial.merge.title"));
                dialog.show();
                mergeAll.set(dialog.isOK());
              }
            });
      } catch (RuntimeException ex) {
        throw ex;
      } catch (Exception ex) {
        throw new RuntimeException("Unable to invoke a message box on AWT thread", ex);
      }
      if (!mergeAll.get()) {
        return false;
      }
      // update non-indexed files
      if (!updateIndex(project, root, realAdded, realRemoved, exceptions)) {
        return false;
      }
      for (FilePath f : realAdded) {
        VcsDirtyScopeManager.getInstance(project).fileDirty(f);
      }
      for (FilePath f : realRemoved) {
        VcsDirtyScopeManager.getInstance(project).fileDirty(f);
      }
    }
    // perform merge commit
    try {
      GitSimpleHandler handler = new GitSimpleHandler(project, root, GitCommand.COMMIT);
      handler.setStdoutSuppressed(false);
      handler.addParameters("-F", messageFile.getAbsolutePath());
      if (author != null) {
        handler.addParameters("--author=" + author);
      }
      handler.endOptions();
      handler.run();
      GitRepositoryManager manager = GitUtil.getRepositoryManager(project);
      manager.updateRepository(root);
    } catch (VcsException ex) {
      exceptions.add(ex);
      return false;
    }
    return true;
  }