/**
   * Get history for the file
   *
   * @param project the context project
   * @param path the file path
   * @return the list of the revisions
   * @throws VcsException if there is problem with running git
   */
  public static List<VcsFileRevision> history(
      final Project project, FilePath path, final VirtualFile root, final String... parameters)
      throws VcsException {
    final List<VcsFileRevision> rc = new ArrayList<VcsFileRevision>();
    final List<VcsException> exceptions = new ArrayList<VcsException>();

    history(
        project,
        path,
        root,
        new Consumer<GitFileRevision>() {
          @Override
          public void consume(GitFileRevision gitFileRevision) {
            rc.add(gitFileRevision);
          }
        },
        new Consumer<VcsException>() {
          @Override
          public void consume(VcsException e) {
            exceptions.add(e);
          }
        },
        parameters);
    if (!exceptions.isEmpty()) {
      throw exceptions.get(0);
    }
    return rc;
  }
 /**
  * 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);
 }
  /**
   * 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;
  }
 @Override
 protected void render(ColoredTreeCellRenderer renderer) {
   SimpleTextAttributes rootAttributes;
   SimpleTextAttributes branchAttributes;
   if (remoteName != null && commits.size() != 0 && remoteCommits != 0
       || currentBranch == null) {
     rootAttributes =
         SimpleTextAttributes.ERROR_ATTRIBUTES.derive(
             SimpleTextAttributes.STYLE_BOLD, null, null, null);
     branchAttributes = SimpleTextAttributes.ERROR_ATTRIBUTES;
   } else if (remoteName == null || commits.size() == 0) {
     rootAttributes = SimpleTextAttributes.GRAYED_BOLD_ATTRIBUTES;
     branchAttributes = SimpleTextAttributes.GRAYED_ATTRIBUTES;
   } else {
     branchAttributes = SimpleTextAttributes.REGULAR_ATTRIBUTES;
     rootAttributes = SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES;
   }
   renderer.append(root.getPresentableUrl(), rootAttributes);
   if (currentBranch != null) {
     renderer.append(" [" + currentBranch, branchAttributes);
     if (remoteName != null) {
       renderer.append(" -> " + remoteName + "#" + remoteBranch, branchAttributes);
     }
     renderer.append("]", branchAttributes);
   }
 }
Example #5
0
 /**
  * Given the list of paths converts them to the list of {@link Change Changes} found in the {@link
  * ChangeListManager}, i.e. this works only for local changes. </br> Paths can be absolute or
  * relative to the repository. If a path is not found in the local changes, it is ignored, but the
  * fact is logged.
  */
 @NotNull
 public static List<Change> findLocalChangesForPaths(
     @NotNull Project project,
     @NotNull VirtualFile root,
     @NotNull Collection<String> affectedPaths,
     boolean relativePaths) {
   ChangeListManagerEx changeListManager =
       (ChangeListManagerEx) ChangeListManager.getInstance(project);
   List<Change> affectedChanges = new ArrayList<Change>();
   for (String path : affectedPaths) {
     String absolutePath = relativePaths ? toAbsolute(root, path) : path;
     VirtualFile file = findRefreshFileOrLog(absolutePath);
     if (file != null) {
       Change change = changeListManager.getChange(file);
       if (change != null) {
         affectedChanges.add(change);
       } else {
         String message = "Change is not found for " + file.getPath();
         if (changeListManager.isInUpdate()) {
           message += " because ChangeListManager is being updated.";
         }
         LOG.warn(message);
       }
     }
   }
   return affectedChanges;
 }
  @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;
  }
Example #7
0
 /**
  * Get git roots for the project. The method shows dialogs in the case when roots cannot be
  * retrieved, so it should be called from the event dispatch thread.
  *
  * @param project the project
  * @param vcs the git Vcs
  * @return the list of the roots
  * @deprecated because uses the java.io.File.
  * @use GitRepositoryManager#getRepositoryForFile().
  */
 @NotNull
 public static List<VirtualFile> getGitRoots(Project project, GitVcs vcs) throws VcsException {
   final VirtualFile[] contentRoots =
       ProjectLevelVcsManager.getInstance(project).getRootsUnderVcs(vcs);
   if (contentRoots == null || contentRoots.length == 0) {
     throw new VcsException(
         GitBundle.getString("repository.action.missing.roots.unconfigured.message"));
   }
   final List<VirtualFile> roots =
       new ArrayList<VirtualFile>(gitRootsForPaths(Arrays.asList(contentRoots)));
   if (roots.size() == 0) {
     throw new VcsException(GitBundle.getString("repository.action.missing.roots.misconfigured"));
   }
   Collections.sort(roots, VIRTUAL_FILE_COMPARATOR);
   return roots;
 }
  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;
    }
  }
  /** This is called when rebase is pressed: executes rebase in background. */
  private void rebase() {
    final List<VcsException> exceptions = new ArrayList<VcsException>();
    final RebaseInfo rebaseInfo = collectRebaseInfo();

    ProgressManager.getInstance()
        .runProcessWithProgressSynchronously(
            new Runnable() {
              public void run() {
                executeRebase(exceptions, rebaseInfo);
              }
            },
            GitBundle.getString("push.active.rebasing"),
            true,
            myProject);
    if (!exceptions.isEmpty()) {
      GitUIUtil.showOperationErrors(myProject, exceptions, "git rebase");
    }
    refreshTree(false, rebaseInfo.uncheckedCommits);
    VcsFileUtil.refreshFiles(myProject, rebaseInfo.roots);
  }
 private static String parseRefs(
     SymbolicRefsI refs,
     Collection<String> currentRefs,
     List<String> locals,
     List<String> remotes,
     List<String> tags) {
   if (refs == null) return null;
   for (String ref : currentRefs) {
     final SymbolicRefs.Kind kind = refs.getKind(ref);
     if (SymbolicRefs.Kind.LOCAL.equals(kind)) {
       locals.add(ref);
     } else if (SymbolicRefs.Kind.REMOTE.equals(kind)) {
       remotes.add(ref);
     } else {
       tags.add(ref);
     }
   }
   if (refs.getCurrent() != null && currentRefs.contains(refs.getCurrent().getName()))
     return refs.getCurrent().getName();
   return null;
 }
Example #11
0
 /**
  * Sort files by vcs root
  *
  * @param files files to sort.
  * @param ignoreNonGit if true, non-git files are ignored
  * @return the map from root to the files under the root
  * @throws VcsException if non git files are passed when {@code ignoreNonGit} is false
  */
 @NotNull
 public static Map<VirtualFile, List<FilePath>> sortFilePathsByGitRoot(
     @NotNull Collection<FilePath> files, boolean ignoreNonGit) throws VcsException {
   Map<VirtualFile, List<FilePath>> rc = new HashMap<VirtualFile, List<FilePath>>();
   for (FilePath p : files) {
     VirtualFile root = getGitRootOrNull(p);
     if (root == null) {
       if (ignoreNonGit) {
         continue;
       } else {
         throw new VcsException("The file " + p.getPath() + " is not under Git");
       }
     }
     List<FilePath> l = rc.get(root);
     if (l == null) {
       l = new ArrayList<FilePath>();
       rc.put(root, l);
     }
     l.add(p);
   }
   return rc;
 }
Example #12
0
 /**
  * Sort files by Git root
  *
  * @param virtualFiles files to sort
  * @param ignoreNonGit if true, non-git files are ignored
  * @return sorted files
  * @throws VcsException if non git files are passed when {@code ignoreNonGit} is false
  */
 public static Map<VirtualFile, List<VirtualFile>> sortFilesByGitRoot(
     Collection<VirtualFile> virtualFiles, boolean ignoreNonGit) throws VcsException {
   Map<VirtualFile, List<VirtualFile>> result = new HashMap<VirtualFile, List<VirtualFile>>();
   for (VirtualFile file : virtualFiles) {
     // directory is reported only when it is a submodule => it should be treated in the context of
     // super-root
     final VirtualFile vcsRoot = gitRootOrNull(file.isDirectory() ? file.getParent() : file);
     if (vcsRoot == null) {
       if (ignoreNonGit) {
         continue;
       } else {
         throw new VcsException("The file " + file.getPath() + " is not under Git");
       }
     }
     List<VirtualFile> files = result.get(vcsRoot);
     if (files == null) {
       files = new ArrayList<VirtualFile>();
       result.put(vcsRoot, files);
     }
     files.add(file);
   }
   return result;
 }
  private RebaseInfo collectRebaseInfo() {
    final Set<VirtualFile> roots = new HashSet<VirtualFile>();
    final Set<VirtualFile> rootsWithMerges = new HashSet<VirtualFile>();
    final Map<VirtualFile, List<String>> reorderedCommits =
        new HashMap<VirtualFile, List<String>>();
    final Map<VirtualFile, Set<String>> uncheckedCommits = new HashMap<VirtualFile, Set<String>>();
    for (int i = 0; i < myTreeRoot.getChildCount(); i++) {
      CheckedTreeNode node = (CheckedTreeNode) myTreeRoot.getChildAt(i);
      Root r = (Root) node.getUserObject();
      Set<String> unchecked = new HashSet<String>();
      uncheckedCommits.put(r.root, unchecked);
      if (r.commits.size() == 0) {
        if (r.remoteCommits > 0) {
          roots.add(r.root);
        }
        continue;
      }
      boolean seenCheckedNode = false;
      boolean reorderNeeded = false;
      boolean seenMerges = false;
      for (int j = 0; j < node.getChildCount(); j++) {
        if (node.getChildAt(j) instanceof CheckedTreeNode) {
          CheckedTreeNode commitNode = (CheckedTreeNode) node.getChildAt(j);
          Commit commit = (Commit) commitNode.getUserObject();
          seenMerges |= commit.isMerge;
          if (commitNode.isChecked()) {
            seenCheckedNode = true;
          } else {
            unchecked.add(commit.commitId());
            if (seenCheckedNode) {
              reorderNeeded = true;
            }
          }
        }
      }
      if (seenMerges) {
        rootsWithMerges.add(r.root);
      }
      if (r.remoteCommits > 0 || reorderNeeded) {
        roots.add(r.root);
      }
      if (reorderNeeded) {
        List<String> reordered = new ArrayList<String>();
        for (int j = 0; j < node.getChildCount(); j++) {
          if (node.getChildAt(j) instanceof CheckedTreeNode) {
            CheckedTreeNode commitNode = (CheckedTreeNode) node.getChildAt(j);
            if (!commitNode.isChecked()) {
              Commit commit = (Commit) commitNode.getUserObject();
              reordered.add(commit.revision.asString());
            }
          }
        }
        for (int j = 0; j < node.getChildCount(); j++) {
          if (node.getChildAt(j) instanceof CheckedTreeNode) {
            CheckedTreeNode commitNode = (CheckedTreeNode) node.getChildAt(j);
            if (commitNode.isChecked()) {
              Commit commit = (Commit) commitNode.getUserObject();
              reordered.add(commit.revision.asString());
            }
          }
        }
        Collections.reverse(reordered);
        reorderedCommits.put(r.root, reordered);
      }
    }
    final GitVcsSettings.UpdateChangesPolicy p =
        UpdatePolicyUtils.getUpdatePolicy(myStashRadioButton, myShelveRadioButton);
    assert p == GitVcsSettings.UpdateChangesPolicy.STASH
        || p == GitVcsSettings.UpdateChangesPolicy.SHELVE;

    return new RebaseInfo(reorderedCommits, rootsWithMerges, uncheckedCommits, roots, p);
  }