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