/** * 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); } }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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); }