From 2869ec0017070088c9d6114785b3778ce772893f Mon Sep 17 00:00:00 2001 From: Julia Beliaeva Date: Tue, 11 Jul 2017 22:49:06 +0300 Subject: [PATCH] [hg] parse hg log output line-by-line --- .../src/com/intellij/vcsUtil/VcsFileUtil.java | 3 +- .../zmlx/hg4idea/command/HgLogCommand.java | 24 ++- .../hg4idea/execution/HgCommandExecutor.java | 35 +++- .../execution/HgLineProcessListener.java | 52 +++++ .../zmlx/hg4idea/execution/ShellCommand.java | 36 ++++ .../org/zmlx/hg4idea/log/HgHistoryUtil.java | 191 ++++++++++++------ .../org/zmlx/hg4idea/log/HgLogProvider.java | 36 ++-- .../org/zmlx/hg4idea/util/HgErrorUtil.java | 5 +- 8 files changed, 295 insertions(+), 87 deletions(-) create mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgLineProcessListener.java diff --git a/platform/vcs-impl/src/com/intellij/vcsUtil/VcsFileUtil.java b/platform/vcs-impl/src/com/intellij/vcsUtil/VcsFileUtil.java index 98461e6818ca..3fb691a0f17a 100644 --- a/platform/vcs-impl/src/com/intellij/vcsUtil/VcsFileUtil.java +++ b/platform/vcs-impl/src/com/intellij/vcsUtil/VcsFileUtil.java @@ -149,12 +149,13 @@ public class VcsFileUtil { // to avoid empty chunks rc.add(arguments.subList(i, i + groupSize)); start = i + groupSize; + size = 0; } else { rc.add(arguments.subList(start, i)); start = i; + size = length; } - size = 0; } else { size += length; diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgLogCommand.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgLogCommand.java index 38abf992449b..fee25206e50b 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgLogCommand.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgLogCommand.java @@ -25,6 +25,7 @@ import org.zmlx.hg4idea.HgVcs; import org.zmlx.hg4idea.execution.HgCommandException; import org.zmlx.hg4idea.execution.HgCommandExecutor; import org.zmlx.hg4idea.execution.HgCommandResult; +import org.zmlx.hg4idea.execution.HgLineProcessListener; import org.zmlx.hg4idea.log.HgBaseLogParser; import org.zmlx.hg4idea.log.HgFileRevisionLogParser; import org.zmlx.hg4idea.log.HgHistoryUtil; @@ -96,13 +97,12 @@ public class HgLogCommand { HgFile originalHgFile = new HgFile(hgFile.getRepo(), originalFileName); HgCommandResult result = execute(hgFile.getRepo(), template, limit, originalHgFile, argsForCmd); - return HgHistoryUtil.getCommitRecords(myProject, result, - new HgFileRevisionLogParser(myProject, originalHgFile, myVersion)); + return HgHistoryUtil.getCommitRecords(myProject, result, + new HgFileRevisionLogParser(myProject, originalHgFile, myVersion)); } - @Nullable - public HgCommandResult execute(@NotNull VirtualFile repo, @NotNull String template, int limit, @Nullable HgFile hgFile, - @Nullable List argsForCmd) { + @NotNull + private List createArguments(@NotNull String template, int limit, @Nullable HgFile hgFile, @Nullable List argsForCmd) { List arguments = new LinkedList<>(); if (myIncludeRemoved) { // There is a bug in mercurial that causes --follow --removed to cause @@ -134,9 +134,23 @@ public class HgLogCommand { if (myLogFile && hgFile != null) { arguments.add(hgFile.getRelativePath()); } + return arguments; + } + + @Nullable + public HgCommandResult execute(@NotNull VirtualFile repo, @NotNull String template, int limit, @Nullable HgFile hgFile, + @Nullable List argsForCmd) { + List arguments = createArguments(template, limit, hgFile, argsForCmd); HgCommandExecutor commandExecutor = new HgCommandExecutor(myProject); commandExecutor.setOutputAlwaysSuppressed(true); return commandExecutor.executeInCurrentThread(repo, "log", arguments); } + public void execute(@NotNull VirtualFile repo, @NotNull String template, int limit, @Nullable HgFile hgFile, + @Nullable List argsForCmd, @NotNull HgLineProcessListener listener) { + List arguments = createArguments(template, limit, hgFile, argsForCmd); + HgCommandExecutor commandExecutor = new HgCommandExecutor(myProject); + commandExecutor.setOutputAlwaysSuppressed(true); + commandExecutor.executeInCurrentThread(repo, "log", arguments, listener); + } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgCommandExecutor.java b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgCommandExecutor.java index 596b02fbb472..4b7b492dfc2e 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgCommandExecutor.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgCommandExecutor.java @@ -20,6 +20,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.SystemProperties; import com.intellij.vcsUtil.VcsImplUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -153,6 +154,38 @@ public class HgCommandExecutor { } } + public void executeInCurrentThread(@Nullable VirtualFile repo, + @NotNull String operation, + @Nullable List arguments, + @NotNull HgLineProcessListener listener) { + executeInCurrentThreadAndLog(repo, operation, arguments, listener); + if (HgErrorUtil.isUnknownEncodingError(StringUtil.split(listener.getErrorOutput().toString(), SystemProperties.getLineSeparator()))) { + setCharset(Charset.forName("utf8")); + executeInCurrentThreadAndLog(repo, operation, arguments, listener); + } + } + + public void executeInCurrentThreadAndLog(@Nullable VirtualFile repo, + @NotNull String operation, + @Nullable List arguments, + @NotNull HgLineProcessListener listener) { + if (myProject == null || myProject.isDisposed() || myVcs == null) return; + + ShellCommand shellCommand = createShellCommandWithArgs(repo, operation, arguments, false); + try { + long startTime = System.currentTimeMillis(); + LOG.debug(String.format("hg %s started", operation)); + shellCommand.execute(myShowOutput, listener); + LOG.debug(String.format("hg %s finished. Took %s ms", operation, System.currentTimeMillis() - startTime)); + } + catch (ShellCommandException e) { + processError(e); + } + catch (InterruptedException e) { // this may happen during project closing, no need to notify the user. + LOG.info(e.getMessage(), e); + } + } + private void processError(@NotNull ShellCommandException e) { if (myVcs.getExecutableValidator().checkExecutableAndNotifyIfNeeded()) { // if the problem was not with invalid executable - show error. @@ -203,7 +236,7 @@ public class HgCommandExecutor { final int lastSlashIndex = settings.getHgExecutable().lastIndexOf(File.separator); exeName = settings.getHgExecutable().substring(lastSlashIndex + 1); - String str = String.format("%s %s %s", exeName, operation, arguments == null ? "" : StringUtil.join(arguments, " ")); + String str = String.format("%s %s %s", exeName, operation, arguments == null ? "" : StringUtil.escapeStringCharacters(StringUtil.join(arguments, " "))); //remove password from path before log final String cmdString = myDestination != null ? HgUtil.removePasswordIfNeeded(str) : str; // log command diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgLineProcessListener.java b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgLineProcessListener.java new file mode 100644 index 000000000000..2bbe313d94a0 --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgLineProcessListener.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.execution; + +import com.intellij.execution.process.ProcessOutputTypes; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vcs.VcsException; +import org.jetbrains.annotations.NotNull; + +public abstract class HgLineProcessListener { + @NotNull private final StringBuilder myErrorOutput = new StringBuilder(); + private int myExitCode; + + public void onLineAvailable(String line, Key outputType) { + if (ProcessOutputTypes.STDOUT == outputType) { + processOutputLine(line); + } + else if (ProcessOutputTypes.STDERR == outputType) { + myErrorOutput.append(line).append("\n"); + } + } + + protected abstract void processOutputLine(@NotNull String line); + + @NotNull + public StringBuilder getErrorOutput() { + return myErrorOutput; + } + + public void finish() throws VcsException { + if (myExitCode != 0 && myErrorOutput.length() != 0) { + throw new VcsException(myErrorOutput.toString()); + } + } + + public void setExitCode(int exitCode) { + myExitCode = exitCode; + } +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/execution/ShellCommand.java b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/ShellCommand.java index 6234cb291cc3..3150c5bcee40 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/execution/ShellCommand.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/ShellCommand.java @@ -90,4 +90,40 @@ public final class ShellCommand { throw new ShellCommandException(e); } } + + public void execute(boolean showTextOnIndicator, @NotNull HgLineProcessListener listener) + throws ShellCommandException, InterruptedException { + final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); + try { + OSProcessHandler processHandler = new OSProcessHandler(myCommandLine); + ProcessAdapter outputAdapter = new ProcessAdapter() { + @Override + public void onTextAvailable(ProcessEvent event, Key outputType) { + for (String line : LineHandlerHelper.splitText(event.getText())) { + if (ProcessOutputTypes.STDOUT == outputType && indicator != null && showTextOnIndicator) { + indicator.setText2(line); + } + listener.onLineAvailable(line, outputType); + } + } + + @Override + public void processTerminated(ProcessEvent event) { + listener.setExitCode(event.getExitCode()); + } + }; + processHandler.addProcessListener(outputAdapter); + processHandler.startNotify(); + while (!processHandler.waitFor(300)) { + if (indicator != null && indicator.isCanceled()) { + processHandler.destroyProcess(); + listener.setExitCode(255); + break; + } + } + } + catch (ExecutionException e) { + throw new ShellCommandException(e); + } + } } \ No newline at end of file diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgHistoryUtil.java b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgHistoryUtil.java index 2a99649405de..6207594ee5d0 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgHistoryUtil.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgHistoryUtil.java @@ -28,10 +28,7 @@ import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.changes.CurrentContentRevision; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ArrayUtil; -import com.intellij.util.Consumer; -import com.intellij.util.Function; -import com.intellij.util.SmartList; +import com.intellij.util.*; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.*; import com.intellij.vcsUtil.VcsFileUtil; @@ -41,6 +38,7 @@ import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.*; import org.zmlx.hg4idea.command.HgLogCommand; import org.zmlx.hg4idea.execution.HgCommandResult; +import org.zmlx.hg4idea.execution.HgLineProcessListener; import org.zmlx.hg4idea.provider.HgChangeProvider; import org.zmlx.hg4idea.util.HgChangesetUtil; import org.zmlx.hg4idea.util.HgUtil; @@ -95,14 +93,6 @@ public class HgHistoryUtil { return getCommitRecords(project, result, baseParser); } - @NotNull - public static List history(@NotNull final Project project, - @NotNull final VirtualFile root, - int limit, - @NotNull List hashParameters) throws VcsException { - return history(project, root, limit, hashParameters, false); - } - /** *

Get & parse hg log detailed output with commits, their parents and their changes. * For null destination return log command result

@@ -111,25 +101,40 @@ public class HgHistoryUtil { * and it can occupy too much memory. The estimate is ~600Kb for 1000 commits.

*/ @NotNull - public static List history(@NotNull final Project project, - @NotNull final VirtualFile root, final int limit, - @NotNull List hashParameters, final boolean silent) + public static List history(@NotNull Project project, @NotNull VirtualFile root, int limit, + @NotNull List hashParameters, boolean silent) throws VcsException { HgVcs hgvcs = HgVcs.getInstance(project); assert hgvcs != null; - final HgVersion version = hgvcs.getVersion(); - final String[] templates = HgBaseLogParser.constructFullTemplateArgument(true, version); + HgVersion version = hgvcs.getVersion(); + String[] templates = HgBaseLogParser.constructFullTemplateArgument(true, version); - return VcsFileUtil.foreachChunk(hashParameters, 2, - strings -> { - HgCommandResult logResult = - getLogResult(project, root, version, limit, strings, HgChangesetUtil.makeTemplate(templates)); - if (logResult == null) return Collections.emptyList(); - if (!logResult.getErrorLines().isEmpty()) throw new VcsException(logResult.getRawError()); - return createFullCommitsFromResult(project, root, logResult, version, silent); - }); + ArrayList result = ContainerUtil.newArrayList(); + VcsLogObjectsFactory factory = getObjectsFactoryWithDisposeCheck(project); + if (factory == null) { + return Collections.emptyList(); + } + + HgFileRevisionLogParser parser = new HgFileRevisionLogParser(project, getOriginalHgFile(project, root), hgvcs.getVersion()); + try { + readLog(project, root, hgvcs.getVersion(), limit, hashParameters, HgChangesetUtil.makeTemplate(templates), + stringBuilder -> { + HgFileRevision revision = parser.convert(stringBuilder.toString()); + if (revision != null) { + result.add(createDetails(project, root, factory, revision)); + } + }); + } + catch (VcsException e) { + if (!silent) { + VcsNotifier.getInstance(project).notifyError(HgVcsMessages.message("hg4idea.error.log.command.execution"), e.getMessage()); + } + throw e; + } + return result; } + @NotNull public static List createFullCommitsFromResult(@NotNull Project project, @NotNull VirtualFile root, @Nullable HgCommandResult result, @@ -142,58 +147,89 @@ public class HgHistoryUtil { getCommitRecords(project, result, new HgFileRevisionLogParser(project, getOriginalHgFile(project, root), version), silent); List vcsFullCommitDetailsList = new ArrayList<>(); for (HgFileRevision revision : hgRevisions) { - - HgRevisionNumber vcsRevisionNumber = revision.getRevisionNumber(); - List parents = vcsRevisionNumber.getParents(); - HgRevisionNumber firstParent = parents.isEmpty() ? null : parents.get(0); // can have no parents if it is a root - List parentsHash = new SmartList<>(); - for (HgRevisionNumber parent : parents) { - parentsHash.add(factory.createHash(parent.getChangeset())); - } - - final Collection changes = new ArrayList<>(); - for (String file : revision.getModifiedFiles()) { - changes.add(createChange(project, root, file, firstParent, file, vcsRevisionNumber, FileStatus.MODIFIED)); - } - for (String file : revision.getAddedFiles()) { - changes.add(createChange(project, root, null, null, file, vcsRevisionNumber, FileStatus.ADDED)); - } - for (String file : revision.getDeletedFiles()) { - changes.add(createChange(project, root, file, firstParent, null, vcsRevisionNumber, FileStatus.DELETED)); - } - for (Map.Entry copiedFile : revision.getMovedFiles().entrySet()) { - changes.add(createChange(project, root, copiedFile.getKey(), firstParent, copiedFile.getValue(), vcsRevisionNumber, - HgChangeProvider.RENAMED)); - } - - vcsFullCommitDetailsList.add(factory.createFullDetails(factory.createHash(vcsRevisionNumber.getChangeset()), parentsHash, - revision.getRevisionDate().getTime(), root, - vcsRevisionNumber.getSubject(), - vcsRevisionNumber.getName(), vcsRevisionNumber.getEmail(), - vcsRevisionNumber.getCommitMessage(), vcsRevisionNumber.getName(), - vcsRevisionNumber.getEmail(), revision.getRevisionDate().getTime(), - () -> changes - )); + vcsFullCommitDetailsList.add(createDetails(project, root, factory, revision)); } return vcsFullCommitDetailsList; } + @NotNull + public static VcsFullCommitDetails createDetails(@NotNull Project project, + @NotNull VirtualFile root, + @NotNull VcsLogObjectsFactory factory, + @NotNull HgFileRevision revision) { + HgRevisionNumber vcsRevisionNumber = revision.getRevisionNumber(); + List parents = vcsRevisionNumber.getParents(); + HgRevisionNumber firstParent = parents.isEmpty() ? null : parents.get(0); // can have no parents if it is a root + List parentsHash = new SmartList<>(); + for (HgRevisionNumber parent : parents) { + parentsHash.add(factory.createHash(parent.getChangeset())); + } + + final Collection changes = new ArrayList<>(); + for (String file : revision.getModifiedFiles()) { + changes.add(createChange(project, root, file, firstParent, file, vcsRevisionNumber, FileStatus.MODIFIED)); + } + for (String file : revision.getAddedFiles()) { + changes.add(createChange(project, root, null, null, file, vcsRevisionNumber, FileStatus.ADDED)); + } + for (String file : revision.getDeletedFiles()) { + changes.add(createChange(project, root, file, firstParent, null, vcsRevisionNumber, FileStatus.DELETED)); + } + for (Map.Entry copiedFile : revision.getMovedFiles().entrySet()) { + changes.add(createChange(project, root, copiedFile.getKey(), firstParent, copiedFile.getValue(), vcsRevisionNumber, + HgChangeProvider.RENAMED)); + } + + return factory.createFullDetails(factory.createHash(vcsRevisionNumber.getChangeset()), parentsHash, + revision.getRevisionDate().getTime(), root, + vcsRevisionNumber.getSubject(), + vcsRevisionNumber.getName(), vcsRevisionNumber.getEmail(), + vcsRevisionNumber.getCommitMessage(), vcsRevisionNumber.getName(), + vcsRevisionNumber.getEmail(), revision.getRevisionDate().getTime(), + () -> changes); + } + @Nullable public static HgCommandResult getLogResult(@NotNull final Project project, @NotNull final VirtualFile root, @NotNull HgVersion version, int limit, @NotNull List parameters, @NotNull String template) { - HgFile originalHgFile = getOriginalHgFile(project, root); HgLogCommand hgLogCommand = new HgLogCommand(project); - List args = new ArrayList<>(parameters); hgLogCommand.setLogFile(false); + + List args = new ArrayList<>(parameters); if (!version.isParentRevisionTemplateSupported()) { args.add("--debug"); } - return hgLogCommand.execute(root, template, limit, originalHgFile, args); + return hgLogCommand.execute(root, template, limit, getOriginalHgFile(project, root), args); } - public static HgFile getOriginalHgFile(Project project, VirtualFile root) { + public static void readLog(@NotNull Project project, @NotNull VirtualFile root, @NotNull HgVersion version, int limit, + @NotNull List hashes, @NotNull String template, @NotNull Consumer consumer) + throws VcsException { + HgLogCommand hgLogCommand = new HgLogCommand(project); + hgLogCommand.setLogFile(false); + + ThrowableConsumer, VcsException> logRunner = hashesChunk -> { + HgLogOutputSplitter splitter = new HgLogOutputSplitter(consumer); + List args = new ArrayList<>(hashesChunk); + if (!version.isParentRevisionTemplateSupported()) { + args.add("--debug"); + } + hgLogCommand.execute(root, template, limit, getOriginalHgFile(project, root), args, splitter); + splitter.finish(); + }; + + if (hashes.isEmpty()) { + // no hashes provided means need to read the whole thing + logRunner.consume(hashes); + } + else { + VcsFileUtil.foreachChunk(hashes, 2, logRunner); + } + } + + public static HgFile getOriginalHgFile(@NotNull Project project, @NotNull VirtualFile root) { HgFile hgFile = new HgFile(root, VcsUtil.getFilePath(root.getPath())); if (project.isDisposed()) { return hgFile; @@ -316,7 +352,8 @@ public class HgHistoryUtil { }); } - private static VcsLogObjectsFactory getObjectsFactoryWithDisposeCheck(Project project) { + @Nullable + static VcsLogObjectsFactory getObjectsFactoryWithDisposeCheck(Project project) { if (!project.isDisposed()) { return ServiceManager.getService(project, VcsLogObjectsFactory.class); } @@ -388,4 +425,34 @@ public class HgHistoryUtil { public static String prepareParameter(String paramName, String value) { return "--" + paramName + "=" + value; // no value escaping needed, because the parameter itself will be quoted by GeneralCommandLine } + + private static class HgLogOutputSplitter extends HgLineProcessListener { + @NotNull private final StringBuilder myOutput; + private final Consumer myConsumer; + + public HgLogOutputSplitter(Consumer consumer) { + myConsumer = consumer; + myOutput = new StringBuilder(); + } + + @Override + protected void processOutputLine(@NotNull String line) { + int separatorIndex; + while ((separatorIndex = line.indexOf(HgChangesetUtil.CHANGESET_SEPARATOR)) >= 0) { + myOutput.append(line.substring(0, separatorIndex)); + myConsumer.consume(myOutput); + myOutput.setLength(0); // maybe also call myOutput.trimToSize() to free some memory ? + line = line.substring(separatorIndex + 1); + } + myOutput.append(line); + } + + public void finish() throws VcsException { + super.finish(); + if (myOutput.length() != 0) { + myConsumer.consume(myOutput); + myOutput.setLength(0); + } + } + } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java index f9b150cc972a..1b525c56dfd8 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java @@ -35,20 +35,21 @@ import com.intellij.vcs.log.util.UserNameRegex; import com.intellij.vcs.log.util.VcsUserUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.zmlx.hg4idea.HgFileRevision; import org.zmlx.hg4idea.HgNameWithHashInfo; import org.zmlx.hg4idea.HgUpdater; import org.zmlx.hg4idea.HgVcs; -import org.zmlx.hg4idea.execution.HgCommandResult; import org.zmlx.hg4idea.repo.HgConfig; import org.zmlx.hg4idea.repo.HgRepository; import org.zmlx.hg4idea.repo.HgRepositoryManager; import org.zmlx.hg4idea.util.HgChangesetUtil; import org.zmlx.hg4idea.util.HgUtil; -import org.zmlx.hg4idea.util.HgVersion; import java.text.SimpleDateFormat; import java.util.*; +import static org.zmlx.hg4idea.log.HgHistoryUtil.getObjectsFactoryWithDisposeCheck; +import static org.zmlx.hg4idea.log.HgHistoryUtil.getOriginalHgFile; import static org.zmlx.hg4idea.util.HgUtil.HEAD_REFERENCE; import static org.zmlx.hg4idea.util.HgUtil.TIP_REFERENCE; @@ -103,18 +104,25 @@ public class HgLogProvider implements VcsLogProvider { // parameter fast is currently not used // since this method is not called from index yet, fast always is false // but when implementing indexing mercurial commits, we'll need to avoid rename/move detection when fast = true - // also we'll need to process commits one by one - + HgVcs hgvcs = HgVcs.getInstance(myProject); assert hgvcs != null; - final HgVersion version = hgvcs.getVersion(); - final String[] templates = HgBaseLogParser.constructFullTemplateArgument(true, version); + String[] templates = HgBaseLogParser.constructFullTemplateArgument(true, hgvcs.getVersion()); + VcsLogObjectsFactory factory = getObjectsFactoryWithDisposeCheck(myProject); + if (factory == null) { + return; + } - HgCommandResult logResult = HgHistoryUtil.getLogResult(myProject, root, version, -1, - HgHistoryUtil.prepareHashes(hashes), HgChangesetUtil.makeTemplate(templates)); - if (logResult == null) return; - if (!logResult.getErrorLines().isEmpty()) throw new VcsException(logResult.getRawError()); - HgHistoryUtil.createFullCommitsFromResult(myProject, root, logResult, version, false).forEach(commitConsumer::consume); + HgFileRevisionLogParser parser = new HgFileRevisionLogParser(myProject, getOriginalHgFile(myProject, root), hgvcs.getVersion()); + HgHistoryUtil.readLog(myProject, root, hgvcs.getVersion(), -1, + HgHistoryUtil.prepareHashes(hashes), + HgChangesetUtil.makeTemplate(templates), + stringBuilder -> { + HgFileRevision revision = parser.convert(stringBuilder.toString()); + if (revision != null) { + commitConsumer.consume(HgHistoryUtil.createDetails(myProject, root, factory, revision)); + } + }); } @NotNull @@ -124,12 +132,6 @@ public class HgLogProvider implements VcsLogProvider { return HgHistoryUtil.readMiniDetails(myProject, root, hashes); } - @NotNull - @Override - public List readFullDetails(@NotNull VirtualFile root, @NotNull List hashes) throws VcsException { - return HgHistoryUtil.history(myProject, root, -1, HgHistoryUtil.prepareHashes(hashes)); - } - @NotNull private Set readAllRefs(@NotNull VirtualFile root) throws VcsException { if (myProject.isDisposed()) { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgErrorUtil.java b/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgErrorUtil.java index cef752587cf5..7fd646bba13a 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgErrorUtil.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgErrorUtil.java @@ -122,7 +122,10 @@ public final class HgErrorUtil { if (result == null) { return false; } - List errorLines = result.getErrorLines(); + return isUnknownEncodingError(result.getErrorLines()); + } + + public static boolean isUnknownEncodingError(@NotNull List errorLines) { if (errorLines.isEmpty()) { return false; }