Mercurial > hg > mercurial
view mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java @ 919:16ad8d51b001
fix non-ascii characters support in commandline arguments
author | eugene.petrenko@jetbrains.com |
---|---|
date | Mon, 19 Jan 2015 18:30:08 +0100 |
parents | e467e67d5892 |
children | 7bf4d943d5bb |
line wrap: on
line source
/* * Copyright 2000-2014 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 jetbrains.buildServer.buildTriggers.vcs.mercurial.command; import com.intellij.openapi.diagnostic.Logger; import jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialTemplate; import jetbrains.buildServer.util.StringUtil; import jetbrains.buildServer.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.*; public class LogCommand extends VcsRootCommand { private final static Logger LOG = Logger.getInstance(LogCommand.class.getName()); public final static String ZERO_PARENT_ID = "0000000000000000000000000000000000000000"; public final static String ZERO_PARENT_SHORT_ID = "000000000000"; private final static SAXParserFactory ourSAXFactory = SAXParserFactory.newInstance(); private String myFromId; private String myToId; private Integer myLimit = null; private String myBranchName; private boolean myCalculateParents = true; private String myRevsets; private MercurialTemplate myTemplate; private List<String> myFiles = new ArrayList<String>(); public LogCommand(@NotNull CommandSettings commandSettings, @NotNull String hgPath, @NotNull File workingDir, @NotNull AuthSettings authSettings) { super(commandSettings, hgPath, workingDir, authSettings); } public LogCommand withTemplate(@NotNull MercurialTemplate template) { myTemplate = template; return this; } public LogCommand inBranch(@NotNull String branchName) { myBranchName = branchName; return this; } public LogCommand fromRevision(@Nullable String fromRevision) { myFromId = fromRevision == null ? null : new ChangeSet(fromRevision).getId(); return this; } public LogCommand toRevision(@Nullable String toRevision) { myToId = toRevision == null ? null : new ChangeSet(toRevision).getId(); return this; } public LogCommand toNamedRevision(@Nullable String namedRevision) { myToId = namedRevision; return this; } public LogCommand setLimit(final int limit) { myLimit = limit; return this; } public LogCommand showCommitsFromAllBranches() { myBranchName = null; return this; } public LogCommand dontCalculateParents() { myCalculateParents = false; return this; } public LogCommand withRevsets(String revsets) { myRevsets = revsets; return this; } public LogCommand forFile(@NotNull final String file) { myFiles.add(file); return this; } @NotNull public List<ChangeSet> call() throws VcsException { return myTemplate.withTemplate(new MercurialTemplate.WithTemplate<List<ChangeSet>>() { @NotNull public List<ChangeSet> action(@NotNull File template) throws VcsException { final MercurialCommandLine cli = createCommandLine(); cli.setCharset(Charset.forName("UTF-8")); cli.addParameters("--encoding", "UTF-8"); cli.addParameter("log"); cli.addParameter("-v"); if (myTemplate != null) { cli.addParameter("--style=" + template.getAbsolutePath()); } if (myBranchName != null) { cli.addParameter("-b"); cli.addParameter(myBranchName); } cli.addParameter("-r"); if (myRevsets != null) { cli.addParameter(myRevsets); } else { String from = myFromId != null ? myFromId : "0"; String to = myToId != null ? myToId : "tip"; cli.addParameter(from + ":" + to); } if (myLimit != null) { cli.addParameter("--limit"); cli.addParameter(myLimit.toString()); } cli.addParameters(myFiles); final CommandResult res = runCommand(cli); final String output = res.getRawStdout(); try { List<ChangeSet> changes = parseChangeSetsXml(output); if (myCalculateParents) assignTrivialParents(changes); return changes; } catch (Exception e) { int limit = myCommandSettings.getLogOutputLimit(); if (limit == -1) { LOG.error("Error while parsing log output:\n" + res.getSecureStdout(), e); } else { LOG.error("Error while parsing log output:\n" + StringUtil.truncateStringValueWithDotsAtEnd(res.getSecureStdout(), limit), e); } throw new VcsException("Error while parsing log output, see teamcity-vcs.log for details", e); } } }); } private List<ChangeSet> parseChangeSetsXml(@NotNull final String xml) throws SAXException, ParserConfigurationException, IOException { if ("".equals(xml)) return Collections.emptyList(); String validXml = makeValidXml(xml); MercurialXmlLogParser parser = new MercurialXmlLogParser(); SAXParser saxParser = ourSAXFactory.newSAXParser(); saxParser.parse(new ByteArrayInputStream(validXml.getBytes("UTF-8")), parser); return parser.getChangeSets(); } private String makeValidXml(@NotNull final String xml) { String trimmed = xml.trim(); if (trimmed.endsWith("</log>")) return xml; else return xml + "</log>"; } private void assignTrivialParents(final @NotNull List<ChangeSet> csets) throws VcsException { Map<Integer, ChangeSet> revNumberMap = makeMapByRevNumber(csets); for (ChangeSet cset : csets) { if (cset.getParents().isEmpty()) { int parentRevNumber = cset.getRevNumber() - 1; ChangeSet parent = revNumberMap.get(parentRevNumber); if (parent != null) { cset.addParent(parent); } else { String parentId = getIdOf(parentRevNumber); cset.addParent(new ChangeSetRevision(parentRevNumber, parentId)); } } } } private Map<Integer, ChangeSet> makeMapByRevNumber(@NotNull List<ChangeSet> csets) throws VcsException { Map<Integer, ChangeSet> csetMap = new HashMap<Integer, ChangeSet>(); for (ChangeSet cset : csets) { csetMap.put(cset.getRevNumber(), cset); } return csetMap; } private String getIdOf(int revNumber) throws VcsException { if (revNumber < 0) return ZERO_PARENT_ID; return new IdentifyCommand(myCommandSettings, getHgPath(), getWorkDirectory(), myAuthSettings) .revisionNumber(revNumber) .inLocalRepository() .call(); } @NotNull @Override protected String getDescription() { StringBuilder result = new StringBuilder(); result.append("hg log "); if (myRevsets != null) { result.append(myRevsets); } else { String from = myFromId != null ? myFromId : "0"; String to = myToId != null ? myToId : "tip"; result.append(from).append(":").append(to); } return result.toString(); } }