mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[hg] parse hg log output line-by-line
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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<String> argsForCmd) {
|
||||
@NotNull
|
||||
private List<String> createArguments(@NotNull String template, int limit, @Nullable HgFile hgFile, @Nullable List<String> argsForCmd) {
|
||||
List<String> arguments = new LinkedList<>();
|
||||
if (myIncludeRemoved) {
|
||||
// There is a bug in mercurial that causes --follow --removed <file> 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<String> argsForCmd) {
|
||||
List<String> 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<String> argsForCmd, @NotNull HgLineProcessListener listener) {
|
||||
List<String> arguments = createArguments(template, limit, hgFile, argsForCmd);
|
||||
HgCommandExecutor commandExecutor = new HgCommandExecutor(myProject);
|
||||
commandExecutor.setOutputAlwaysSuppressed(true);
|
||||
commandExecutor.executeInCurrentThread(repo, "log", arguments, listener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> 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<String> 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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String> hashParameters) throws VcsException {
|
||||
return history(project, root, limit, hashParameters, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Get & parse hg log detailed output with commits, their parents and their changes.
|
||||
* For null destination return log command result</p>
|
||||
@@ -111,25 +101,40 @@ public class HgHistoryUtil {
|
||||
* and it can occupy too much memory. The estimate is ~600Kb for 1000 commits.</p>
|
||||
*/
|
||||
@NotNull
|
||||
public static List<? extends VcsFullCommitDetails> history(@NotNull final Project project,
|
||||
@NotNull final VirtualFile root, final int limit,
|
||||
@NotNull List<String> hashParameters, final boolean silent)
|
||||
public static List<? extends VcsFullCommitDetails> history(@NotNull Project project, @NotNull VirtualFile root, int limit,
|
||||
@NotNull List<String> 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<VcsFullCommitDetails> 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<VcsFullCommitDetails> vcsFullCommitDetailsList = new ArrayList<>();
|
||||
for (HgFileRevision revision : hgRevisions) {
|
||||
|
||||
HgRevisionNumber vcsRevisionNumber = revision.getRevisionNumber();
|
||||
List<HgRevisionNumber> parents = vcsRevisionNumber.getParents();
|
||||
HgRevisionNumber firstParent = parents.isEmpty() ? null : parents.get(0); // can have no parents if it is a root
|
||||
List<Hash> parentsHash = new SmartList<>();
|
||||
for (HgRevisionNumber parent : parents) {
|
||||
parentsHash.add(factory.createHash(parent.getChangeset()));
|
||||
}
|
||||
|
||||
final Collection<Change> 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<String, String> 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<HgRevisionNumber> parents = vcsRevisionNumber.getParents();
|
||||
HgRevisionNumber firstParent = parents.isEmpty() ? null : parents.get(0); // can have no parents if it is a root
|
||||
List<Hash> parentsHash = new SmartList<>();
|
||||
for (HgRevisionNumber parent : parents) {
|
||||
parentsHash.add(factory.createHash(parent.getChangeset()));
|
||||
}
|
||||
|
||||
final Collection<Change> 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<String, String> 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<String> parameters, @NotNull String template) {
|
||||
HgFile originalHgFile = getOriginalHgFile(project, root);
|
||||
HgLogCommand hgLogCommand = new HgLogCommand(project);
|
||||
List<String> args = new ArrayList<>(parameters);
|
||||
hgLogCommand.setLogFile(false);
|
||||
|
||||
List<String> 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<String> hashes, @NotNull String template, @NotNull Consumer<StringBuilder> consumer)
|
||||
throws VcsException {
|
||||
HgLogCommand hgLogCommand = new HgLogCommand(project);
|
||||
hgLogCommand.setLogFile(false);
|
||||
|
||||
ThrowableConsumer<List<String>, VcsException> logRunner = hashesChunk -> {
|
||||
HgLogOutputSplitter splitter = new HgLogOutputSplitter(consumer);
|
||||
List<String> 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<StringBuilder> myConsumer;
|
||||
|
||||
public HgLogOutputSplitter(Consumer<StringBuilder> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> hashes) throws VcsException {
|
||||
return HgHistoryUtil.history(myProject, root, -1, HgHistoryUtil.prepareHashes(hashes));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Set<VcsRef> readAllRefs(@NotNull VirtualFile root) throws VcsException {
|
||||
if (myProject.isDisposed()) {
|
||||
|
||||
@@ -122,7 +122,10 @@ public final class HgErrorUtil {
|
||||
if (result == null) {
|
||||
return false;
|
||||
}
|
||||
List<String> errorLines = result.getErrorLines();
|
||||
return isUnknownEncodingError(result.getErrorLines());
|
||||
}
|
||||
|
||||
public static boolean isUnknownEncodingError(@NotNull List<String> errorLines) {
|
||||
if (errorLines.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user