view mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java @ 520:1ca68585ffce Faradi-7.1.x

Support mirrors for subrepos with relative paths
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Fri, 07 Dec 2012 21:54:45 +0400
parents efba721f9a1d
children 4f5273a54927 d012388935fb
line wrap: on
line source
package jetbrains.buildServer.buildTriggers.vcs.mercurial;

import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*;
import jetbrains.buildServer.log.Loggers;
import jetbrains.buildServer.util.FileUtil;
import jetbrains.buildServer.vcs.VcsException;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.IOException;
import java.util.*;

import static java.util.Collections.emptyMap;
import static jetbrains.buildServer.buildTriggers.vcs.mercurial.HgFileUtil.deleteDir;
import static jetbrains.buildServer.util.FileUtil.isEmptyDir;

/**
* @author dmitry.neverov
*/
public class HgRepo {

  protected final CommandSettingsFactory myCommandSettingsFactory;
  protected final File myWorkingDir;
  protected final String myHgPath;
  protected final AuthSettings myAuthSettings;
  private final Map<String, Map<String, SubRepo>> mySubreposCache = new HashMap<String, Map<String, SubRepo>>();

  public HgRepo(@NotNull CommandSettingsFactory commandSettingsFactory,
                @NotNull File workingDir,
                @NotNull String hgPath,
                @NotNull AuthSettings authSettings) {
    myCommandSettingsFactory = commandSettingsFactory;
    myWorkingDir = workingDir;
    myHgPath = hgPath;
    myAuthSettings = authSettings;
  }

  public PullCommand pull() {
    return new PullCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings);
  }

  public PushCommand push() {
    return new PushCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings);
  }

  public CloneCommand doClone() {
    return new CloneCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings);
  }

  public IdentifyCommand id() {
    return new IdentifyCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings);
  }

  public Init init() {
    return new Init(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings);
  }

  public LogCommand log() {
    return new LogCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings);
  }

  public UpdateCommand update() {
    return new UpdateCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings);
  }

  public BranchesCommand branches() {
    return new BranchesCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings);
  }

  public StatusCommand status() {
    return new StatusCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings);
  }

  public TagCommand tag() {
    return new TagCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings);
  }

  public CatCommand cat() {
    return new CatCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings);
  }

  public ArchiveCommand archive() {
    return new ArchiveCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings);
  }

  public VersionCommand version() {
    return new VersionCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir);
  }

  public String path() {
    return myWorkingDir.getAbsolutePath();
  }

  public File getWorkingDir() {
    return myWorkingDir;
  }

  public boolean isEmpty() {
    return isEmptyDir(myWorkingDir);
  }

  @NotNull
  public List<String> listFiles(@NotNull String revision) throws VcsException {
    List<FileStatus> fileStatuses = status()
            .fromRevision(revision)
            .toRevision(revision)
            .hideStatus()
            .showAllFiles()
            .call();
    List<String> files = new ArrayList<String>(fileStatuses.size());
    for (FileStatus fileStatus : fileStatuses)
      files.add(fileStatus.getPath());
    return files;
  }

  public String getWorkingDirRevision() throws VcsException {
    return id().inLocalRepository().call();
  }

  public boolean containsRevision(@NotNull String revision) {
    return containsRevision(new ChangeSet(revision));
  }

  public boolean containsRevision(@NotNull ChangeSet cset) {
    try {
      id().revision(cset).inLocalRepository().call();
      return true;
    } catch (VcsException e) {
      return false;
    }
  }

  public boolean isValidRepository() {
    // need better way to check that repository copy is ok
    return myWorkingDir.isDirectory() && new File(myWorkingDir, ".hg").isDirectory();
  }

  public void setDefaultPath(@NotNull String defaultPath) throws VcsException {
    try {
      File hgrc = new File(new File(myWorkingDir, ".hg"), "hgrc");
      String content = "[paths]\ndefault = " + defaultPath;
      FileUtil.writeFileAndReportErrors(hgrc, content);
    } catch (IOException e) {
      throw new VcsException(e);
    }
  }

  public boolean hasSubreposAtRevision(@NotNull String revision) {
    return !getSubrepositories(new ChangeSet(revision)).isEmpty();
  }

  public boolean hasSubreposAtRevision(@NotNull ChangeSet cset) {
    return !getSubrepositories(cset).isEmpty();
  }

  public Map<String, SubRepo> getSubrepositories(@NotNull String revision) {
    return getSubrepositories(new ChangeSet(revision));
  }

  public Map<String, SubRepo> getSubrepositories(@NotNull ChangeSet cset) {
    String revId = cset.getId();
    Map<String, SubRepo> subrepos = mySubreposCache.get(revId);
    if (subrepos != null)
      return subrepos;
    CatCommand cc = cat();
    cc.setRevId(revId);
    File catDir = null;
    try {
      catDir = cc.execute(Arrays.asList(".hgsub", ".hgsubstate"), false);
      File hgsub = new File(catDir, ".hgsub");
      File hgsubstate = new File(catDir, ".hgsubstate");
      subrepos = readSubrepositories(hgsub, hgsubstate);
      mySubreposCache.put(revId, subrepos);
      return subrepos;
    } catch (VcsException e) {
      return emptyMap();
    } finally {
      deleteDir(catDir, Loggers.VCS);
    }
  }

  @Override
  public String toString() {
    return myWorkingDir.getAbsolutePath();
  }

  private Map<String, SubRepo> readSubrepositories(@NotNull final File hgsub, @NotNull final File hgsubstate) {
    if (hgsub.exists() && hgsubstate.exists()) {
      try {
        Map<String, String> path2repo = readHgsub(hgsub);
        Map<String, String> path2revision = readHgsubstate(hgsubstate);
        Map<String, SubRepo> result = new HashMap<String, SubRepo>();
        for (Map.Entry<String, String> entry : path2repo.entrySet()) {
          String path = entry.getKey();
          String url = entry.getValue();
          String revision = path2revision.get(path);
          if (revision != null) {
            result.put(path, new SubRepo(this, path, url, revision));
          } else {
//              myLogger.warning("Cannot find revision for subrepository at path " + path + " skip it");
          }
        }
        return result;
      } catch (IOException e) {
//          myLogger.warning("Error while trying to read subrepositories " + e.getMessage());
        return emptyMap();
      }
    } else {
      return emptyMap();
    }
  }

  /*returns map: relative path -> repository url */
  private Map<String, String> readHgsub(@NotNull final File hgsub) throws IOException {
    Map<String, String> result = new HashMap<String, String>();
    for (String line : FileUtil.readFile(hgsub)) {
      String[] parts = line.split(" = ");
      if (parts.length == 2) {
        result.put(parts[0], parts[1]);
      } else {
//          myLogger.warning("Cannot parse the line '" + line + "' from .hgsub, skip it");
      }
    }
    return result;
  }


  /*returns map: relative path -> revision */
  private Map<String, String> readHgsubstate(@NotNull final File hgsubstate) throws IOException {
    Map<String, String> result = new HashMap<String, String>();
    for (String line : FileUtil.readFile(hgsubstate)) {
      String[] parts = line.split(" ");
      if (parts.length == 2) {
        result.put(parts[1], parts[0]);
      } else {
//          myLogger.warning("Cannot parse the line '" + line + "' from .hgsubstate, skip it");
      }
    }
    return result;
  }
}