Mercurial > hg > mercurial
diff mercurial/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java @ 0:a530ea876f55
mercurial support sources added
author | Pavel.Sher |
---|---|
date | Mon, 14 Jul 2008 18:22:05 +0400 |
parents | |
children | 56e7f34ca887 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Mon Jul 14 18:22:05 2008 +0400 @@ -0,0 +1,307 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.*; +import jetbrains.buildServer.CollectChangesByIncludeRule; +import jetbrains.buildServer.Used; +import jetbrains.buildServer.buildTriggers.vcs.AbstractVcsPropertiesProcessor; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*; +import jetbrains.buildServer.log.Loggers; +import jetbrains.buildServer.serverSide.InvalidProperty; +import jetbrains.buildServer.serverSide.PropertiesProcessor; +import jetbrains.buildServer.serverSide.ServerPaths; +import jetbrains.buildServer.util.FileUtil; +import jetbrains.buildServer.vcs.*; +import jetbrains.buildServer.vcs.patches.PatchBuilder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class MercurialVcsSupport extends VcsSupport implements CollectChangesByIncludeRule { + private ServerPaths myServerPaths; + + public MercurialVcsSupport(@NotNull VcsManager vcsManager, @NotNull ServerPaths paths) { + vcsManager.registerVcsSupport(this); + myServerPaths = paths; + } + + public List<ModificationData> collectBuildChanges(final VcsRoot root, + @NotNull final String fromVersion, + @NotNull final String currentVersion, + final CheckoutRules checkoutRules) throws VcsException { + updateWorkingDirectory(root); + return VcsSupportUtil.collectBuildChanges(root, fromVersion, currentVersion, checkoutRules, this); + } + + public List<ModificationData> collectBuildChanges(final VcsRoot root, + final String fromVersion, + final String currentVersion, + final IncludeRule includeRule) throws VcsException { + List<ModificationData> result = new ArrayList<ModificationData>(); + Settings settings = new Settings(myServerPaths, root); + LogCommand lc = new LogCommand(settings); + lc.setFromRevId(new ChangeSet(fromVersion).getId()); + lc.setToRevId(new ChangeSet(currentVersion).getId()); + List<ChangeSet> changeSets = lc.execute(); + if (changeSets.isEmpty()) { + return result; + } + + Iterator<ChangeSet> it = changeSets.iterator(); + ChangeSet prev = it.next(); // skip first changeset (cause it was already reported) + StatusCommand st = new StatusCommand(settings); + while (it.hasNext()) { + ChangeSet cur = it.next(); + st.setFromRevId(prev.getId()); + st.setToRevId(cur.getId()); + List<ModifiedFile> modifiedFiles = st.execute(); + List<VcsChange> files = toVcsChanges(modifiedFiles, prev.getFullVersion(), cur.getFullVersion(), includeRule); + if (files.isEmpty()) continue; + ModificationData md = new ModificationData(cur.getTimestamp(), files, cur.getSummary(), cur.getUser(), root, cur.getFullVersion(), cur.getFullVersion()); + result.add(md); + prev = cur; + } + + return result; + } + + private List<VcsChange> toVcsChanges(final List<ModifiedFile> modifiedFiles, String prevVer, String curVer, final IncludeRule includeRule) { + List<VcsChange> files = new ArrayList<VcsChange>(); + for (ModifiedFile mf: modifiedFiles) { + if (!normalizePath(mf.getPath()).startsWith(includeRule.getFrom())) continue; // skip files which do not match include rule + + VcsChangeInfo.Type changeType = getChangeType(mf.getStatus()); + if (changeType == null) { + Loggers.VCS.warn("Unable to convert status: " + mf.getStatus() + " to VCS change type"); + changeType = VcsChangeInfo.Type.NOT_CHANGED; + } + files.add(new VcsChange(changeType, mf.getStatus().getName(), mf.getPath(), mf.getPath(), prevVer, curVer)); + } + return files; + } + + private @NotNull String normalizePath(@NotNull String repPath) { + return repPath.replace('\\', '/'); + } + + private VcsChangeInfo.Type getChangeType(final ModifiedFile.Status status) { + switch (status) { + case ADDED:return VcsChangeInfo.Type.ADDED; + case MODIFIED:return VcsChangeInfo.Type.CHANGED; + case REMOVED:return VcsChangeInfo.Type.REMOVED; + } + return null; + } + + @NotNull + public byte[] getContent(final VcsModification vcsModification, + final VcsChangeInfo change, + final VcsChangeInfo.ContentType contentType, + final VcsRoot vcsRoot) throws VcsException { + return new byte[0]; + } + + @NotNull + public byte[] getContent(final String filePath, final VcsRoot versionedRoot, final String version) throws VcsException { + return new byte[0]; + } + + public String getName() { + return "mercurial"; + } + + @Used("jsp") + public String getDisplayName() { + return "Mercurial"; + } + + @Nullable + public PropertiesProcessor getVcsPropertiesProcessor() { + return new AbstractVcsPropertiesProcessor() { + public Collection<InvalidProperty> process(final Map<String, String> properties) { + List<InvalidProperty> result = new ArrayList<InvalidProperty>(); + if (isEmpty(properties.get(Constants.HG_COMMAND_PATH_PROP))) { + result.add(new InvalidProperty(Constants.HG_COMMAND_PATH_PROP, "Path to 'hg' command must be specified")); + } + if (isEmpty(properties.get(Constants.REPOSITORY_PROP))) { + result.add(new InvalidProperty(Constants.REPOSITORY_PROP, "Repository must be specified")); + } + return result; + } + }; + } + + public String getVcsSettingsJspFilePath() { + return "mercurialSettings.jsp"; + } + + @NotNull + public String getCurrentVersion(final VcsRoot root) throws VcsException { + updateWorkingDirectory(root); + Settings settings = new Settings(myServerPaths, root); + LogCommand lc = new LogCommand(settings); + lc.setFromRevId("tip"); + lc.setToRevId("tip"); + List<ChangeSet> changes = lc.execute(); + if (changes.isEmpty()) { + throw new VcsException("Unable to obtain current version of repository"); + } + final ChangeSet changeSet = changes.get(0); + return changeSet.getFullVersion(); + } + + public String describeVcsRoot(final VcsRoot vcsRoot) { + return "mercurial: " + vcsRoot.getProperty(Constants.REPOSITORY_PROP); + } + + public boolean isTestConnectionSupported() { + return true; + } + + @Nullable + public String testConnection(final VcsRoot vcsRoot) throws VcsException { + getCurrentVersion(vcsRoot); + return null; + } + + @Nullable + public Map<String, String> getDefaultVcsProperties() { + return null; + } + + public String getVersionDisplayName(final String version, final VcsRoot root) throws VcsException { + return version; + } + + @NotNull + public Comparator<String> getVersionComparator() { + return new Comparator<String>() { + public int compare(final String o1, final String o2) { + try { + return new ChangeSet(o1).getRevNumber() - new ChangeSet(o2).getRevNumber(); + } catch (Exception e) { + return 1; + } + } + }; + } + + public void buildPatch(final VcsRoot root, + @Nullable final String fromVersion, + @NotNull final String toVersion, + final PatchBuilder builder, + final CheckoutRules checkoutRules) throws IOException, VcsException { + updateWorkingDirectory(root); + Settings settings = new Settings(myServerPaths, root); + if (fromVersion == null) { + buildFullPatch(settings, new ChangeSet(toVersion), builder); + } else { + buildIncrementalPatch(settings, new ChangeSet(fromVersion), new ChangeSet(toVersion), builder); + } + } + + private void buildIncrementalPatch(final Settings settings, @NotNull final ChangeSet fromVer, @NotNull final ChangeSet toVer, final PatchBuilder builder) + throws VcsException, IOException { + StatusCommand st = new StatusCommand(settings); + st.setFromRevId(fromVer.getId()); + st.setToRevId(toVer.getId()); + List<ModifiedFile> modifiedFiles = st.execute(); + List<String> notDeletedFiles = new ArrayList<String>(); + for (ModifiedFile f: modifiedFiles) { + if (f.getStatus() != ModifiedFile.Status.REMOVED) { + notDeletedFiles.add(f.getPath()); + } + } + + CatCommand cc = new CatCommand(settings); + cc.setRevId(toVer.getId()); + File parentDir = cc.execute(notDeletedFiles); + + try { + for (ModifiedFile f: modifiedFiles) { + final File virtualFile = new File(f.getPath()); + if (f.getStatus() == ModifiedFile.Status.REMOVED) { + builder.deleteFile(virtualFile, true); + } else { + File realFile = new File(parentDir, f.getPath()); + FileInputStream is = new FileInputStream(realFile); + try { + builder.createBinaryFile(virtualFile, null, is, realFile.length()); + } finally { + is.close(); + } + } + } + } finally { + FileUtil.delete(parentDir); + } + } + + private void buildFullPatch(final Settings settings, @NotNull final ChangeSet toVer, final PatchBuilder builder) + throws IOException, VcsException { + CloneCommand cl = new CloneCommand(settings); + cl.setToId(toVer.getId()); + File tempDir = FileUtil.createTempDirectory("mercurial", toVer.getId()); + try { + final File repRoot = new File(tempDir, "rep"); + cl.setDestDir(repRoot.getAbsolutePath()); + cl.execute(); + buildPatchFromDirectory(builder, repRoot, new FileFilter() { + public boolean accept(final File file) { + return !(file.isDirectory() && ".hg".equals(file.getName())); + } + }); + } finally { + FileUtil.delete(tempDir); + } + } + + private void buildPatchFromDirectory(final PatchBuilder builder, final File repRoot, final FileFilter filter) throws IOException { + buildPatchFromDirectory(repRoot, builder, repRoot, filter); + } + + private void buildPatchFromDirectory(File curDir, final PatchBuilder builder, final File repRoot, final FileFilter filter) throws IOException { + File[] files = curDir.listFiles(filter); + if (files != null) { + for (File realFile: files) { + String relPath = realFile.getAbsolutePath().substring(repRoot.getAbsolutePath().length()); + final File virtualFile = new File(relPath); + if (realFile.isDirectory()) { + builder.createDirectory(virtualFile); + buildPatchFromDirectory(realFile, builder, repRoot, filter); + } else { + final FileInputStream is = new FileInputStream(realFile); + try { + builder.createBinaryFile(virtualFile, null, is, realFile.length()); + } finally { + is.close(); + } + } + } + } + } + + private void updateWorkingDirectory(final VcsRoot root) throws VcsException { + Settings settings = new Settings(myServerPaths, root); + String workDir = settings.getWorkingDir(); + synchronized (root) { + if (hasRepositoryCopy(new File(workDir))) { + // update + UpdateCommand up = new UpdateCommand(settings); + up.execute(); + } else { + // clone + CloneCommand cl = new CloneCommand(settings); + cl.setDestDir(workDir); + cl.execute(); + } + } + } + + private boolean hasRepositoryCopy(final File workDir) { + return workDir.isDirectory() && new File(workDir, ".hg").isDirectory(); + } + +}