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 extends VcsFullCommitDetails> 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 extends VcsFullCommitDetails> history(@NotNull final Project project,
- @NotNull final VirtualFile root, final int limit,
- @NotNull List hashParameters, final boolean silent)
+ public static List extends VcsFullCommitDetails> 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 extends VcsFullCommitDetails> 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 extends VcsFullCommitDetails> 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;
}