@Nullable
  public static Pair<AbstractHash, AbstractHash> getStashTop(
      @NotNull Project project, @NotNull VirtualFile root) throws VcsException {
    GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.STASH.readLockingCommand());
    GitLogParser parser = new GitLogParser(project, SHORT_HASH, SHORT_PARENTS);
    h.setSilent(true);
    h.setNoSSH(true);
    h.addParameters("list");
    h.addParameters("-n1");
    h.addParameters(parser.getPretty());

    String out;
    h.setCharset(Charset.forName(GitConfigUtil.getLogEncoding(project, root)));
    out = h.run();
    final List<GitLogRecord> gitLogRecords = parser.parse(out);
    for (GitLogRecord gitLogRecord : gitLogRecords) {
      ProgressManager.checkCanceled();

      GitSimpleHandler h1 = new GitSimpleHandler(project, root, GitCommand.LOG);
      GitLogParser parser1 = new GitLogParser(project, SHORT_HASH, SHORT_PARENTS, SUBJECT);
      h1.setSilent(true);
      h1.setNoSSH(true);
      h1.addParameters("-n1");
      h1.addParameters(parser1.getPretty());
      // h1.endOptions();
      h1.addParameters(gitLogRecord.getShortHash());

      String out1;
      out1 = h1.run();
      final List<GitLogRecord> gitLogRecords1 = parser1.parse(out1);
      assert gitLogRecords1.size() == 1;
      final GitLogRecord logRecord = gitLogRecords1.get(0);
      final String[] parentsShortHashes = logRecord.getParentsShortHashes();
      String indexCommit = null;
      // heuristics
      if (parentsShortHashes.length == 2) {
        if (logRecord.getSubject().contains(parentsShortHashes[0])) {
          indexCommit = parentsShortHashes[1];
        }
        if (logRecord.getSubject().contains(parentsShortHashes[1])) {
          indexCommit = parentsShortHashes[0];
        }
      }
      return new Pair<AbstractHash, AbstractHash>(
          AbstractHash.create(gitLogRecord.getShortHash()),
          indexCommit == null ? null : AbstractHash.create(indexCommit));
    }
    return null;
  }
 /**
  * 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);
   }
 }
  // relativePaths are guaranteed to fit into command line length limitations.
  @Override
  @NotNull
  public Collection<VirtualFile> untrackedFilesNoChunk(
      @NotNull Project project, @NotNull VirtualFile root, @Nullable List<String> relativePaths)
      throws VcsException {
    final Set<VirtualFile> untrackedFiles = new HashSet<VirtualFile>();
    GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LS_FILES);
    h.setNoSSH(true);
    h.setSilent(true);
    h.addParameters("--exclude-standard", "--others", "-z");
    h.endOptions();
    if (relativePaths != null) {
      h.addParameters(relativePaths);
    }

    final String output = h.run();
    if (StringUtil.isEmptyOrSpaces(output)) {
      return untrackedFiles;
    }

    for (String relPath : output.split("\u0000")) {
      VirtualFile f = root.findFileByRelativePath(relPath);
      if (f == null) {
        // files was created on disk, but VirtualFile hasn't yet been created,
        // when the GitChangeProvider has already been requested about changes.
        LOG.info(String.format("VirtualFile for path [%s] is null", relPath));
      } else {
        untrackedFiles.add(f);
      }
    }

    return untrackedFiles;
  }
 /**
  * Get current revision for the file under git
  *
  * @param project a project
  * @param filePath a file path
  * @return a revision number or null if the file is unversioned or new
  * @throws VcsException if there is problem with running git
  */
 @Nullable
 public static ItemLatestState getLastRevision(final Project project, FilePath filePath)
     throws VcsException {
   VirtualFile root = GitUtil.getGitRoot(filePath);
   GitBranch c = GitBranch.current(project, root);
   GitBranch t = c == null ? null : c.tracked(project, root);
   if (t == null) {
     return new ItemLatestState(getCurrentRevision(project, filePath, null), true, false);
   }
   filePath = getLastCommitName(project, filePath);
   GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LOG);
   GitLogParser parser =
       new GitLogParser(project, GitLogParser.NameStatus.STATUS, HASH, COMMIT_TIME, SHORT_PARENTS);
   h.setNoSSH(true);
   h.setSilent(true);
   h.addParameters("-n1", parser.getPretty(), "--name-status", t.getFullName());
   h.endOptions();
   h.addRelativePaths(filePath);
   String result = h.run();
   if (result.length() == 0) {
     return null;
   }
   GitLogRecord record = parser.parseOneRecord(result);
   if (record == null) {
     return null;
   }
   final List<Change> changes = record.parseChanges(project, root);
   boolean exists = !FileStatus.DELETED.equals(changes.get(0).getFileStatus());
   record.setUsedHandler(h);
   return new ItemLatestState(
       new GitRevisionNumber(record.getHash(), record.getDate()), exists, false);
 }
 @Nullable
 public static VcsRevisionNumber getCurrentRevision(
     final Project project, FilePath filePath, @Nullable String branch, final boolean shortHash)
     throws VcsException {
   filePath = getLastCommitName(project, filePath);
   GitSimpleHandler h =
       new GitSimpleHandler(project, GitUtil.getGitRoot(filePath), GitCommand.LOG);
   GitLogParser parser =
       shortHash
           ? new GitLogParser(project, SHORT_HASH, COMMIT_TIME)
           : new GitLogParser(project, HASH, COMMIT_TIME);
   h.setNoSSH(true);
   h.setSilent(true);
   h.addParameters("-n1", parser.getPretty());
   if (branch != null && !branch.isEmpty()) {
     h.addParameters(branch);
   } else {
     h.addParameters("--all");
   }
   h.endOptions();
   h.addRelativePaths(filePath);
   String result = h.run();
   if (result.length() == 0) {
     return null;
   }
   final GitLogRecord record = parser.parseOneRecord(result);
   if (record == null) {
     return null;
   }
   record.setUsedHandler(h);
   return shortHash
       ? new GitRevisionNumber(record.getShortHash(), record.getDate())
       : new GitRevisionNumber(record.getHash(), record.getDate());
 }
  /**
   * Gets info of the given commit and checks if it was a RENAME. If yes, returns the older file
   * path, which file was renamed from. If it's not a rename, returns null.
   */
  @Nullable
  private static FilePath getFirstCommitRenamePath(
      Project project, VirtualFile root, String commit, FilePath filePath) throws VcsException {
    // 'git show -M --name-status <commit hash>' returns the information about commit and detects
    // renames.
    // NB: we can't specify the filepath, because then rename detection will work only with the
    // '--follow' option, which we don't wanna use.
    final GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.SHOW);
    final GitLogParser parser =
        new GitLogParser(project, GitLogParser.NameStatus.STATUS, HASH, COMMIT_TIME, SHORT_PARENTS);
    h.setNoSSH(true);
    h.setStdoutSuppressed(true);
    h.addParameters("-M", "--name-status", parser.getPretty(), "--encoding=UTF-8", commit);
    h.endOptions();
    final String output = h.run();
    final List<GitLogRecord> records = parser.parse(output);

    if (records.isEmpty()) return null;
    // we have information about all changed files of the commit. Extracting information about the
    // file we need.
    final List<Change> changes = records.get(0).parseChanges(project, root);
    for (Change change : changes) {
      if ((change.isMoved() || change.isRenamed())
          && filePath.equals(change.getAfterRevision().getFile())) {
        return change.getBeforeRevision().getFile();
      }
    }
    return null;
  }
Ejemplo n.º 7
0
 protected boolean hasRemoteChanges(@NotNull String currentBranch, @NotNull String remoteBranch)
     throws VcsException {
   GitSimpleHandler handler = new GitSimpleHandler(myProject, myRoot, GitCommand.REV_LIST);
   handler.setSilent(true);
   handler.addParameters("-1");
   handler.addParameters(currentBranch + ".." + remoteBranch);
   String output = handler.run();
   return output != null && !output.isEmpty();
 }
 /**
  * Resolve revision number for the specified revision
  *
  * @param project a project
  * @param vcsRoot a vcs root
  * @param rev a revision expression
  * @return a resolved revision number with correct time
  * @throws VcsException if there is a problem with running git
  */
 public static GitRevisionNumber resolve(Project project, VirtualFile vcsRoot, @NonNls String rev)
     throws VcsException {
   GitSimpleHandler h = new GitSimpleHandler(project, vcsRoot, GitCommand.REV_LIST);
   h.setSilent(true);
   h.addParameters("--timestamp", "--max-count=1", rev);
   h.endOptions();
   final String output = h.run();
   return parseRevlistOutputAsRevisionNumber(h, output);
 }
 protected void add() throws ServerRuntimeException {
   try {
     GitSimpleHandler handler = new GitSimpleHandler(getProject(), myContentRoot, GitCommand.ADD);
     handler.setSilent(false);
     handler.addParameters(".");
     handler.run();
   } catch (VcsException e) {
     throw new ServerRuntimeException(e);
   }
 }
 /**
  * Delete files
  *
  * @param project the project
  * @param root a vcs root
  * @param files files to delete
  * @return a result of operation
  * @throws VcsException in case of git problem
  */
 public static void deleteFiles(Project project, VirtualFile root, List<VirtualFile> files)
     throws VcsException {
   for (List<String> paths : VcsFileUtil.chunkFiles(root, files)) {
     GitSimpleHandler handler = new GitSimpleHandler(project, root, GitCommand.RM);
     handler.endOptions();
     handler.addParameters(paths);
     handler.setNoSSH(true);
     handler.run();
   }
 }
Ejemplo n.º 11
0
 /**
  * git diff --name-only [--cached]
  *
  * @return true if there is anything in the unstaged/staging area, false if the unstraed/staging
  *     area is empty.
  * @param staged if true checks the staging area, if false checks unstaged files.
  * @param project
  * @param root
  */
 public static boolean hasLocalChanges(boolean staged, Project project, VirtualFile root)
     throws VcsException {
   final GitSimpleHandler diff = new GitSimpleHandler(project, root, GitCommand.DIFF);
   diff.addParameters("--name-only");
   if (staged) {
     diff.addParameters("--cached");
   }
   diff.setStdoutSuppressed(true);
   diff.setStderrSuppressed(true);
   diff.setSilent(true);
   final String output = diff.run();
   return !output.trim().isEmpty();
 }
 protected void commit() throws ServerRuntimeException {
   try {
     if (GitUtil.hasLocalChanges(true, getProject(), myContentRoot)) {
       GitSimpleHandler handler =
           new GitSimpleHandler(getProject(), myContentRoot, GitCommand.COMMIT);
       handler.setSilent(false);
       handler.addParameters("-a");
       handler.addParameters("-m", "Deploy");
       handler.endOptions();
       handler.run();
     }
   } catch (VcsException e) {
     throw new ServerRuntimeException(e);
   }
 }
 protected void doGitRemote(
     String remoteName, CloudGitApplication application, String subCommand, String failMessage)
     throws ServerRuntimeException {
   try {
     final GitSimpleHandler handler =
         new GitSimpleHandler(myProject, myContentRoot, GitCommand.REMOTE);
     handler.setSilent(false);
     handler.addParameters(subCommand, remoteName, application.getGitUrl());
     handler.run();
     getRepository().update();
     if (handler.getExitCode() != 0) {
       throw new ServerRuntimeException(failMessage);
     }
   } catch (VcsException e) {
     throw new ServerRuntimeException(e);
   }
 }
  private static void addPaths(
      @NotNull Project project, @NotNull VirtualFile root, @NotNull List<List<String>> chunkedPaths)
      throws VcsException {
    for (List<String> paths : chunkedPaths) {
      paths = excludeIgnoredFiles(project, root, paths);

      if (paths.isEmpty()) {
        continue;
      }
      GitSimpleHandler handler = new GitSimpleHandler(project, root, GitCommand.ADD);
      handler.addParameters("--ignore-errors");
      handler.endOptions();
      handler.addParameters(paths);
      handler.setNoSSH(true);
      handler.run();
    }
  }
  public static List<GitCommit> commitsDetails(
      Project project, FilePath path, SymbolicRefsI refs, final Collection<String> commitsIds)
      throws VcsException {
    // adjust path using change manager
    path = getLastCommitName(project, path);
    final VirtualFile root = GitUtil.getGitRoot(path);
    GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.SHOW);
    GitLogParser parser =
        new GitLogParser(
            project,
            GitLogParser.NameStatus.STATUS,
            SHORT_HASH,
            HASH,
            COMMIT_TIME,
            AUTHOR_NAME,
            AUTHOR_TIME,
            AUTHOR_EMAIL,
            COMMITTER_NAME,
            COMMITTER_EMAIL,
            SHORT_PARENTS,
            REF_NAMES,
            SUBJECT,
            BODY,
            RAW_BODY);
    h.setNoSSH(true);
    h.setStdoutSuppressed(true);
    h.addParameters("--name-status", parser.getPretty(), "--encoding=UTF-8");
    h.addParameters(new ArrayList<String>(commitsIds));

    // h.endOptions();
    // h.addRelativePaths(path);
    String output;
    try {
      output = h.run();

      final List<GitCommit> rc = new ArrayList<GitCommit>();
      for (GitLogRecord record : parser.parse(output)) {
        final GitCommit gitCommit = createCommit(project, refs, root, record);
        rc.add(gitCommit);
      }
      return rc;
    } catch (VcsException e) {
      throw e;
    }
  }
 @Nullable
 public static GitRevisionNumber getMergeBase(
     final Project project,
     final VirtualFile root,
     @NotNull final String first,
     @NotNull final String second)
     throws VcsException {
   GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.MERGE_BASE);
   h.setNoSSH(true);
   h.setSilent(true);
   h.addParameters(first, second);
   String output = h.run().trim();
   if (output.length() == 0) {
     return null;
   } else {
     return GitRevisionNumber.resolve(project, root, output);
   }
 }
 @Nullable
 @Override
 protected String getLastCommitMessage(@NotNull VirtualFile root) throws VcsException {
   GitSimpleHandler h = new GitSimpleHandler(myProject, root, GitCommand.LOG);
   h.addParameters("--max-count=1");
   String formatPattern;
   if (GitVersionSpecialty.STARTED_USING_RAW_BODY_IN_FORMAT.existsIn(myVcs.getVersion())) {
     formatPattern = "%B";
   } else {
     // only message: subject + body; "%-b" means that preceding line-feeds will be deleted if
     // the body is empty
     // %s strips newlines from subject; there is no way to work around it before 1.7.2 with %B
     // (unless parsing some fixed format)
     formatPattern = "%s%n%n%-b";
   }
   h.addParameters("--pretty=format:" + formatPattern);
   return h.run();
 }
  @Nullable
  public static List<Pair<String, GitCommit>> loadStashStackAsCommits(
      @NotNull Project project,
      @NotNull VirtualFile root,
      SymbolicRefsI refs,
      final String... parameters)
      throws VcsException {
    GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.STASH.readLockingCommand());
    GitLogParser parser =
        new GitLogParser(
            project,
            GitLogParser.NameStatus.STATUS,
            SHORT_HASH,
            HASH,
            COMMIT_TIME,
            AUTHOR_NAME,
            AUTHOR_TIME,
            AUTHOR_EMAIL,
            COMMITTER_NAME,
            COMMITTER_EMAIL,
            SHORT_PARENTS,
            REF_NAMES,
            SHORT_REF_LOG_SELECTOR,
            SUBJECT,
            BODY,
            RAW_BODY);
    h.setSilent(true);
    h.setNoSSH(true);
    h.addParameters("list");
    h.addParameters(parameters);
    h.addParameters(parser.getPretty());

    String out;
    h.setCharset(Charset.forName(GitConfigUtil.getLogEncoding(project, root)));
    out = h.run();
    final List<GitLogRecord> gitLogRecords = parser.parse(out);
    final List<Pair<String, GitCommit>> result = new ArrayList<Pair<String, GitCommit>>();
    for (GitLogRecord gitLogRecord : gitLogRecords) {
      ProgressManager.checkCanceled();
      final GitCommit gitCommit = createCommit(project, refs, root, gitLogRecord);
      result.add(new Pair<String, GitCommit>(gitLogRecord.getShortenedRefLog(), gitCommit));
    }
    return result;
  }
Ejemplo n.º 19
0
  public static void getLocalCommittedChanges(
      final Project project,
      final VirtualFile root,
      final Consumer<GitSimpleHandler> parametersSpecifier,
      final Consumer<GitCommittedChangeList> consumer,
      boolean skipDiffsForMerge)
      throws VcsException {
    GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LOG);
    h.setSilent(true);
    h.addParameters(
        "--pretty=format:%x04%x01" + GitChangeUtils.COMMITTED_CHANGELIST_FORMAT, "--name-status");
    parametersSpecifier.consume(h);

    String output = h.run();
    LOG.debug("getLocalCommittedChanges output: '" + output + "'");
    StringScanner s = new StringScanner(output);
    final StringBuilder sb = new StringBuilder();
    boolean firstStep = true;
    while (s.hasMoreData()) {
      final String line = s.line();
      final boolean lineIsAStart = line.startsWith("\u0004\u0001");
      if ((!firstStep) && lineIsAStart) {
        final StringScanner innerScanner = new StringScanner(sb.toString());
        sb.setLength(0);
        consumer.consume(
            GitChangeUtils.parseChangeList(
                project, root, innerScanner, skipDiffsForMerge, h, false, false));
      }
      sb.append(lineIsAStart ? line.substring(2) : line).append('\n');
      firstStep = false;
    }
    if (sb.length() > 0) {
      final StringScanner innerScanner = new StringScanner(sb.toString());
      sb.setLength(0);
      consumer.consume(
          GitChangeUtils.parseChangeList(
              project, root, innerScanner, skipDiffsForMerge, h, false, false));
    }
    if (s.hasMoreData()) {
      throw new IllegalStateException("More input is avaialble: " + s.line());
    }
  }
 public static long getHeadTs(final Project project, FilePath filePath) throws VcsException {
   GitSimpleHandler h =
       new GitSimpleHandler(project, GitUtil.getGitRoot(filePath), GitCommand.LOG);
   GitLogParser parser = new GitLogParser(project, SHORT_HASH, COMMIT_TIME);
   h.setNoSSH(true);
   h.setSilent(true);
   h.addParameters("-n1", parser.getPretty());
   h.addParameters("HEAD");
   h.endOptions();
   String result = h.run();
   if (result.length() == 0) {
     return -1;
   }
   final GitLogRecord record = parser.parseOneRecord(result);
   if (record == null) {
     return -1;
   }
   record.setUsedHandler(h);
   return record.getDate().getTime();
 }
 // TODO this is to be removed when tags will be supported by the GitRepositoryReader
 private Collection<? extends VcsRef> readTags(@NotNull VirtualFile root) throws VcsException {
   GitSimpleHandler tagHandler = new GitSimpleHandler(myProject, root, GitCommand.LOG);
   tagHandler.setSilent(true);
   tagHandler.addParameters(
       "--tags", "--no-walk", "--format=%H%d" + GitLogParser.RECORD_START_GIT, "--decorate=full");
   String out = tagHandler.run();
   Collection<VcsRef> refs = new ArrayList<VcsRef>();
   try {
     for (String record : out.split(GitLogParser.RECORD_START)) {
       if (!StringUtil.isEmptyOrSpaces(record)) {
         refs.addAll(new RefParser(myVcsObjectsFactory).parseCommitRefs(record.trim(), root));
       }
     }
   } catch (Exception e) {
     LOG.error(
         "Error during tags parsing",
         new Attachment("stack_trace.txt", ExceptionUtil.getThrowableText(e)),
         new Attachment("git_output.txt", out));
   }
   return refs;
 }
  @NotNull
  private static List<String> excludeIgnoredFiles(
      @NotNull Project project, @NotNull VirtualFile root, @NotNull List<String> paths)
      throws VcsException {
    GitSimpleHandler handler = new GitSimpleHandler(project, root, GitCommand.LS_FILES);
    handler.setNoSSH(true);
    handler.setSilent(true);
    handler.addParameters("--ignored", "--others", "--exclude-standard");
    handler.endOptions();
    handler.addParameters(paths);
    String output = handler.run();

    List<String> nonIgnoredFiles = new ArrayList<String>(paths.size());
    Set<String> ignoredPaths = new HashSet<String>(Arrays.asList(StringUtil.splitByLines(output)));
    for (String pathToCheck : paths) {
      if (!ignoredPaths.contains(pathToCheck)) {
        nonIgnoredFiles.add(pathToCheck);
      }
    }
    return nonIgnoredFiles;
  }
 /**
  * Scan working tree and detect locally modified files
  *
  * @param project the project to scan
  * @param root the root to scan
  * @param files the collection with files
  * @throws VcsException if there problem with running git or working tree is dirty in unsupported
  *     way
  */
 private static void scanFiles(Project project, VirtualFile root, List<String> files)
     throws VcsException {
   String rootPath = root.getPath();
   GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.DIFF);
   h.addParameters("--name-status");
   h.setNoSSH(true);
   h.setSilent(true);
   h.setStdoutSuppressed(true);
   StringScanner s = new StringScanner(h.run());
   while (s.hasMoreData()) {
     if (s.isEol()) {
       s.line();
       continue;
     }
     if (s.tryConsume("M\t")) {
       String path = rootPath + "/" + GitUtil.unescapePath(s.line());
       files.add(path);
     } else {
       throw new VcsException("Working tree is dirty in unsupported way: " + s.line());
     }
   }
 }
  public static List<Pair<SHAHash, Date>> onlyHashesHistory(
      Project project, FilePath path, final VirtualFile root, final String... parameters)
      throws VcsException {
    // adjust path using change manager
    path = getLastCommitName(project, path);
    GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LOG);
    GitLogParser parser = new GitLogParser(project, HASH, COMMIT_TIME);
    h.setNoSSH(true);
    h.setStdoutSuppressed(true);
    h.addParameters(parameters);
    h.addParameters(parser.getPretty(), "--encoding=UTF-8");
    h.endOptions();
    h.addRelativePaths(path);
    String output = h.run();

    final List<Pair<SHAHash, Date>> rc = new ArrayList<Pair<SHAHash, Date>>();
    for (GitLogRecord record : parser.parse(output)) {
      record.setUsedHandler(h);
      rc.add(new Pair<SHAHash, Date>(new SHAHash(record.getHash()), record.getDate()));
    }
    return rc;
  }
  @Nullable
  public static VcsRevisionDescription getCurrentRevisionDescription(
      final Project project, FilePath filePath, @Nullable String branch) throws VcsException {
    filePath = getLastCommitName(project, filePath);
    GitSimpleHandler h =
        new GitSimpleHandler(project, GitUtil.getGitRoot(filePath), GitCommand.LOG);
    GitLogParser parser =
        new GitLogParser(
            project, HASH, COMMIT_TIME, AUTHOR_NAME, COMMITTER_NAME, SUBJECT, BODY, RAW_BODY);
    h.setNoSSH(true);
    h.setSilent(true);
    h.addParameters("-n1", parser.getPretty());
    if (branch != null && !branch.isEmpty()) {
      h.addParameters(branch);
    } else {
      h.addParameters("--all");
    }
    h.endOptions();
    h.addRelativePaths(filePath);
    String result = h.run();
    if (result.length() == 0) {
      return null;
    }
    final GitLogRecord record = parser.parseOneRecord(result);
    if (record == null) {
      return null;
    }
    record.setUsedHandler(h);

    final String author =
        Comparing.equal(record.getAuthorName(), record.getCommitterName())
            ? record.getAuthorName()
            : record.getAuthorName() + " (" + record.getCommitterName() + ")";
    return new VcsRevisionDescriptionImpl(
        new GitRevisionNumber(record.getHash(), record.getDate()),
        record.getDate(),
        author,
        record.getFullMessage());
  }
  public static long getAuthorTime(Project project, FilePath path, final String commitsId)
      throws VcsException {
    // adjust path using change manager
    path = getLastCommitName(project, path);
    final VirtualFile root = GitUtil.getGitRoot(path);
    GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.SHOW);
    GitLogParser parser = new GitLogParser(project, GitLogParser.NameStatus.STATUS, AUTHOR_TIME);
    h.setNoSSH(true);
    h.setStdoutSuppressed(true);
    h.addParameters("--name-status", parser.getPretty(), "--encoding=UTF-8");
    h.addParameters(commitsId);

    String output;
    try {
      output = h.run();

      GitLogRecord logRecord = parser.parseOneRecord(output);
      return logRecord.getAuthorTimeStamp() * 1000;

    } catch (VcsException e) {
      throw e;
    }
  }
  private static boolean performFirstCommitIfRequired(
      @NotNull final Project project,
      @NotNull VirtualFile root,
      @NotNull GitRepository repository,
      @NotNull ProgressIndicator indicator,
      @NotNull String name,
      @NotNull String url) {
    // check if there is no commits
    if (!repository.isFresh()) {
      return true;
    }

    LOG.info("Trying to commit");
    try {
      LOG.info("Adding files for commit");
      indicator.setText("Adding files to git...");

      // ask for files to add
      final List<VirtualFile> trackedFiles =
          ChangeListManager.getInstance(project).getAffectedFiles();
      final Collection<VirtualFile> untrackedFiles =
          filterOutIgnored(project, repository.getUntrackedFilesHolder().retrieveUntrackedFiles());
      trackedFiles.removeAll(untrackedFiles); // fix IDEA-119855

      final List<VirtualFile> allFiles = new ArrayList<VirtualFile>();
      allFiles.addAll(trackedFiles);
      allFiles.addAll(untrackedFiles);

      final Ref<GithubUntrackedFilesDialog> dialogRef = new Ref<GithubUntrackedFilesDialog>();
      ApplicationManager.getApplication()
          .invokeAndWait(
              new Runnable() {
                @Override
                public void run() {
                  GithubUntrackedFilesDialog dialog =
                      new GithubUntrackedFilesDialog(project, allFiles);
                  if (!trackedFiles.isEmpty()) {
                    dialog.setSelectedFiles(trackedFiles);
                  }
                  DialogManager.show(dialog);
                  dialogRef.set(dialog);
                }
              },
              indicator.getModalityState());
      final GithubUntrackedFilesDialog dialog = dialogRef.get();

      final Collection<VirtualFile> files2commit = dialog.getSelectedFiles();
      if (!dialog.isOK() || files2commit.isEmpty()) {
        GithubNotifications.showInfoURL(
            project, "Successfully created empty repository on GitHub", name, url);
        return false;
      }

      Collection<VirtualFile> files2add = ContainerUtil.intersection(untrackedFiles, files2commit);
      Collection<VirtualFile> files2rm = ContainerUtil.subtract(trackedFiles, files2commit);
      Collection<VirtualFile> modified = new HashSet<VirtualFile>(trackedFiles);
      modified.addAll(files2commit);

      GitFileUtils.addFiles(project, root, files2add);
      GitFileUtils.deleteFilesFromCache(project, root, files2rm);

      // commit
      LOG.info("Performing commit");
      indicator.setText("Performing commit...");
      GitSimpleHandler handler = new GitSimpleHandler(project, root, GitCommand.COMMIT);
      handler.addParameters("-m", dialog.getCommitMessage());
      handler.endOptions();
      handler.run();

      VcsFileUtil.refreshFiles(project, modified);
    } catch (VcsException e) {
      LOG.warn(e);
      GithubNotifications.showErrorURL(
          project,
          "Can't finish GitHub sharing process",
          "Successfully created project ",
          "'" + name + "'",
          " on GitHub, but initial commit failed:<br/>" + GithubUtil.getErrorTextFromException(e),
          url);
      return false;
    }
    LOG.info("Successfully created initial commit");
    return true;
  }
  /**
   * Load VCS roots
   *
   * @param project the project
   * @param roots the VCS root list
   * @param exceptions the list of of exceptions to use
   * @param fetchData if true, the data for remote is fetched.
   * @return the loaded information about vcs roots
   */
  private static List<Root> loadRoots(
      final Project project,
      final List<VirtualFile> roots,
      final Collection<VcsException> exceptions,
      final boolean fetchData) {
    final ArrayList<Root> rc = new ArrayList<Root>();
    for (VirtualFile root : roots) {
      try {
        Root r = new Root();
        rc.add(r);
        r.root = root;
        GitBranch b = GitBranch.current(project, root);
        if (b != null) {
          r.currentBranch = b.getFullName();
          r.remoteName = b.getTrackedRemoteName(project, root);
          r.remoteBranch = b.getTrackedBranchName(project, root);
          if (r.remoteName != null) {
            if (fetchData && !r.remoteName.equals(".")) {
              GitLineHandler fetch = new GitLineHandler(project, root, GitCommand.FETCH);
              fetch.addParameters(r.remoteName, "-v");
              Collection<VcsException> exs = GitHandlerUtil.doSynchronouslyWithExceptions(fetch);
              exceptions.addAll(exs);
            }
            GitBranch tracked = b.tracked(project, root);
            assert tracked != null : "Tracked branch cannot be null here";
            final boolean trackedBranchExists = tracked.exists(root);
            if (!trackedBranchExists) {
              LOG.info("loadRoots tracked branch " + tracked + " doesn't exist yet");
            }

            // check what remote commits are not yet merged
            if (trackedBranchExists) {
              GitSimpleHandler toPull = new GitSimpleHandler(project, root, GitCommand.LOG);
              toPull.addParameters(
                  "--pretty=format:%H", r.currentBranch + ".." + tracked.getFullName());
              toPull.setNoSSH(true);
              toPull.setStdoutSuppressed(true);
              StringScanner su = new StringScanner(toPull.run());
              while (su.hasMoreData()) {
                if (su.line().trim().length() != 0) {
                  r.remoteCommits++;
                }
              }
            }

            // check what local commits are to be pushed
            GitSimpleHandler toPush = new GitSimpleHandler(project, root, GitCommand.LOG);
            // if the tracked branch doesn't exist yet (nobody pushed the branch yet), show all
            // commits on this branch.
            final String revisions =
                trackedBranchExists
                    ? tracked.getFullName() + ".." + r.currentBranch
                    : r.currentBranch;
            toPush.addParameters("--pretty=format:%H%x20%ct%x20%at%x20%s%n%P", revisions);
            toPush.setNoSSH(true);
            toPush.setStdoutSuppressed(true);
            StringScanner sp = new StringScanner(toPush.run());
            while (sp.hasMoreData()) {
              if (sp.isEol()) {
                sp.line();
                continue;
              }
              Commit c = new Commit();
              c.root = r;
              String hash = sp.spaceToken();
              String time = sp.spaceToken();
              c.revision = new GitRevisionNumber(hash, new Date(Long.parseLong(time) * 1000L));
              c.authorTime = sp.spaceToken();
              c.message = sp.line();
              c.isMerge = sp.line().indexOf(' ') != -1;
              r.commits.add(c);
            }
          }
        }
      } catch (VcsException e) {
        exceptions.add(e);
      }
    }
    return rc;
  }
  /**
   * 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;
  }