view mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java @ 285:aeaf4d594967 Eluru-6.0.x

Use customized xml output from the 'hg log' command Do that to parse commit messages correctly (TW-18036). Also 'hg log' can provide information on changed files, so we will not run a 'hg status' for every found cset, that should improve changes collecting performance. Use custom xml format mainly because of the difference in the author output. Default xml splits the author to the person and the email, while default verbose log uses unsplitted author. It is not clear how to make original author from the person and the email, because author|person is not empty even if there is no person in the ui.username config. Also default xml uses date format rfc3339date, which is harder to parse. root: /home/nd/sandbox/hg-plugin/original/ HG: branch: Eluru-6.0.x HG: committing mercurial-common/mercurial-common.iml mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangeSet.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandUtil.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommandTest.java mercurial.ipr mercurial.xml mercurial-server/resources/buildServerResources/log.template HG: Press C-c C-c when you are done editing.
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Mon, 29 Aug 2011 17:31:31 +0400
parents bcc04a74783d
children 9c9b59163e6c
line wrap: on
line source
/*
 * Copyright 2000-2011 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.execution.configurations.GeneralCommandLine;
import com.intellij.openapi.util.JDOMUtil;
import jetbrains.buildServer.ExecResult;
import jetbrains.buildServer.vcs.VcsException;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

public class LogCommand extends BaseCommand {

  private String myFromId;
  private String myToId;
  private static final String DATE_FORMAT = "EEE MMM d HH:mm:ss yyyy Z";
  private final File myTemplate;

  public LogCommand(@NotNull Settings settings, @NotNull final File template) {
    super(settings);
    myTemplate = template;
  }

  public void setFromRevId(String id) {
    myFromId = id;
  }

  public void setToRevId(String id) {
    myToId = id;
  }

  public List<ChangeSet> execute() throws VcsException {
    GeneralCommandLine cli = createCommandLine();
    cli.addParameter("log");
    cli.addParameter("--style=" + myTemplate.getAbsolutePath());
    cli.addParameter("-b");
    cli.addParameter(getSettings().getBranchName());
    cli.addParameter("-r");
    String from = myFromId;
    if (from == null) from = "0";
    String to = myToId;
    if (to == null) to = "tip";
    cli.addParameter(from + ":" + to);

    ExecResult res = runCommand(cli);
    try {
      return parseChangeSetsXml(res.getStdout());
    } catch (Exception e) {
      throw new VcsException("Error while parsing log output:\n" + res.getStdout(), e);
    }
  }


  private List<ChangeSet> parseChangeSetsXml(@NotNull final String xml) throws IOException, JDOMException, ParseException {
    Document doc = JDOMUtil.loadDocument(xml);
    Element log = doc.getRootElement();
    return parseLog(log);
  }


  private List<ChangeSet> parseLog(@NotNull final Element logElement) throws ParseException {
    List<ChangeSet> result = new ArrayList<ChangeSet>();
    for (Object o : logElement.getChildren("logentry")) {
      Element entry = (Element) o;
      result.add(parseLogEntry(entry));
    }
    return result;
  }


  private ChangeSet parseLogEntry(@NotNull final Element logEntry) throws ParseException {
    ChangeSet cset = new ChangeSet(getRevision(logEntry), getId(logEntry));
    addParents(cset, logEntry);
    cset.setUser(getAuthor(logEntry));
    cset.setDescription(getDescription(logEntry));
    cset.setTimestamp(getDate(logEntry));
    cset.setModifiedFiles(getModifiedFiles(logEntry));
    return cset;
  }


  private int getRevision(@NotNull final Element logEntry) {
    return Integer.parseInt(logEntry.getAttribute("revision").getValue());
  }


  private String getId(@NotNull final Element logEntry) {
    return logEntry.getAttribute("shortnode").getValue();
  }


  private void addParents(@NotNull final ChangeSet cset, @NotNull final Element logEntry) {
    List parents = logEntry.getChildren("parent");
    for (Object p : parents) {
      Element parent = (Element) p;
      ChangeSetRevision parentCset = getParent(parent);
      cset.addParent(parentCset);
    }
  }


  private ChangeSetRevision getParent(@NotNull final Element parent) {
    return new ChangeSetRevision(getRevision(parent), getId(parent));
  }


  private String getAuthor(@NotNull final Element logEntry) {
    Element author = logEntry.getChild("author");
    return author.getAttribute("original").getValue();
  }


  private String getDescription(@NotNull final Element logEntry) {
    Element msg = logEntry.getChild("msg");
    return msg.getText();
  }


  private Date getDate(@NotNull final Element logEntry) throws ParseException {
    Element date = logEntry.getChild("date");
    return new SimpleDateFormat(DATE_FORMAT, Locale.ENGLISH).parse(date.getText());
  }


  private List<ModifiedFile> getModifiedFiles(@NotNull final Element logEntry) {
    List<ModifiedFile> result = new ArrayList<ModifiedFile>();
    Element paths = logEntry.getChild("paths");
    for (Object o : paths.getChildren("path")) {
      Element path = (Element) o;
      result.add(getModifiedFile(path));
    }
    return result;
  }


  private ModifiedFile getModifiedFile(@NotNull final Element path) {
    String filePath = path.getText();
    ModifiedFile.Status status = getStatus(path);
    return new ModifiedFile(status, filePath);
  }


  private ModifiedFile.Status getStatus(@NotNull final Element path) {
    String action = path.getAttribute("action").getValue();
    if (action.equals("A")) {
      return ModifiedFile.Status.ADDED;
    } else if (action.equals("M")) {
      return ModifiedFile.Status.MODIFIED;
    } else if (action.equals("R")) {
      return ModifiedFile.Status.REMOVED;
    } else {
      return ModifiedFile.Status.UNKNOWN;
    }
  }
}