Mercurial > hg > mercurial
changeset 730:ae1358e79ee1
Merge
author | eugene.petrenko@jetbrains.com |
---|---|
date | Tue, 14 Jan 2014 11:36:24 +0100 |
parents | 785ab04c78da (diff) d1469a7cc038 (current diff) |
children | 5becb1dfecbd |
files | mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubRepo.java |
diffstat | 23 files changed, 810 insertions(+), 93 deletions(-) [+] |
line wrap: on
line diff
--- a/.idea/artifacts/mercurial_common_jar.xml Mon Jan 13 18:15:41 2014 +0100 +++ b/.idea/artifacts/mercurial_common_jar.xml Tue Jan 14 11:36:24 2014 +0100 @@ -3,6 +3,9 @@ <output-path>$PROJECT_DIR$/out/artifacts/mercurial_common_jar</output-path> <root id="archive" name="mercurial-common.jar"> <element id="module-output" name="mercurial-common" /> + <element id="directory" name="python"> + <element id="dir-copy" path="$PROJECT_DIR$/mercurial-common/src/python" /> + </element> </root> </artifact> </component> \ No newline at end of file
--- a/.idea/artifacts/mercurial_vcs_worker.xml Mon Jan 13 18:15:41 2014 +0100 +++ b/.idea/artifacts/mercurial_vcs_worker.xml Tue Jan 14 11:36:24 2014 +0100 @@ -13,6 +13,7 @@ <element id="artifact" artifact-name="mercurial-server.jar" /> <element id="library" level="project" name="quartz-1.6.0" /> <element id="library" level="project" name="commons-compress-1.5" /> + <element id="library" level="project" name="commons-codec-1.4" /> </element> <element id="file-copy" path="$PROJECT_DIR$/teamcity-plugin.xml" /> </root>
--- a/.idea/artifacts/plugin.xml Mon Jan 13 18:15:41 2014 +0100 +++ b/.idea/artifacts/plugin.xml Tue Jan 14 11:36:24 2014 +0100 @@ -14,6 +14,7 @@ <element id="artifact" artifact-name="mercurial-server-tc.jar" /> <element id="library" level="project" name="quartz-1.6.0" /> <element id="library" level="project" name="commons-compress-1.5" /> + <element id="library" level="project" name="commons-codec-1.4" /> </element> <element id="directory" name="agent"> <element id="archive" name="mercurial.zip"> @@ -21,6 +22,8 @@ <element id="directory" name="lib"> <element id="artifact" artifact-name="mercurial-common.jar" /> <element id="artifact" artifact-name="mercurial-agent.jar" /> + <element id="library" level="project" name="commons-codec-1.4" /> + <element id="library" level="project" name="commons-compress-1.5" /> </element> </element> </element>
--- a/.idea/compiler.xml Mon Jan 13 18:15:41 2014 +0100 +++ b/.idea/compiler.xml Tue Jan 14 11:36:24 2014 +0100 @@ -20,6 +20,7 @@ <entry name="?*.tag" /> <entry name="?*.template" /> <entry name="do-not-load-in-vcs-mode" /> + <entry name="?*.py" /> </wildcardResourcePatterns> <annotationProcessing> <profile default="true" name="Default" enabled="false">
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.idea/libraries/commons_codec_1_4.xml Tue Jan 14 11:36:24 2014 +0100 @@ -0,0 +1,9 @@ +<component name="libraryTable"> + <library name="commons-codec-1.4"> + <CLASSES> + <root url="jar://$PROJECT_DIR$/lib/commons-codec-1.4.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> +</component> \ No newline at end of file
--- a/mercurial-common/mercurial-common.iml Mon Jan 13 18:15:41 2014 +0100 +++ b/mercurial-common/mercurial-common.iml Tue Jan 14 11:36:24 2014 +0100 @@ -11,6 +11,7 @@ <orderEntry type="library" exported="" name="TeamCityAPI-common" level="project" /> <orderEntry type="library" exported="" name="IDEA-openapi" level="project" /> <orderEntry type="library" name="jdom" level="project" /> + <orderEntry type="library" name="commons-codec-1.4" level="project" /> </component> </module>
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java Mon Jan 13 18:15:41 2014 +0100 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java Tue Jan 14 11:36:24 2014 +0100 @@ -61,6 +61,11 @@ return new LogCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings); } + @NotNull + public CommitsAndMountPointsCommand logSubstates() throws VcsException { + return new CommitsAndMountPointsCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings); + } + public UpdateCommand update() { return new UpdateCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings); } @@ -298,9 +303,7 @@ File catDir = null; try { catDir = cc.execute(asList(".hgsub", ".hgsubstate"), false); - File hgsub = new File(catDir, ".hgsub"); - File hgsubstate = new File(catDir, ".hgsubstate"); - subrepos = readSubrepositories(hgsub, hgsubstate); + subrepos = HgSubs.readSubrepositories(new File(catDir, ".hgsub"), new File(catDir, ".hgsubstate")); mySubreposCache.put(revId, subrepos); return new HashMap<String, SubRepo>(subrepos); } catch (VcsException e) { @@ -315,48 +318,10 @@ 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(path, url, revision)); - } - return result; - } catch (IOException e) { - 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]); - } - 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]); - } - return result; + @NotNull + public static String shortId(@NotNull final String s) { + if (s.length() > 12) + return s.substring(0, 12); + return s; } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgSubs.java Tue Jan 14 11:36:24 2014 +0100 @@ -0,0 +1,102 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial; + +import jetbrains.buildServer.util.FileUtil; +import jetbrains.buildServer.util.StringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +import static java.util.Collections.emptyMap; + +/** + * Created 13.01.14 19:51 + * + * @author Eugene Petrenko (eugene.petrenko@jetbrains.com) + */ +public class HgSubs { + + @NotNull + public static Map<String, SubRepo> readSubrepositories(@NotNull final File hgsub, + @NotNull final File hgsubstate) { + if (!hgsub.exists() || !hgsubstate.exists()) { + return emptyMap(); + } + + final Map<String, String> path2repo; + final Map<String, String> path2revision; + try { + path2repo = readHgsub(hgsub); + path2revision = readHgsubstate(hgsubstate); + } catch (IOException e) { + return emptyMap(); + } + + return readSubrepositories(path2repo, path2revision); + } + + @NotNull + public static Map<String, SubRepo> readSubrepositories(@Nullable final String hgsubText, + @Nullable final String hgsubstateText) { + + if (hgsubstateText == null || hgsubText == null) return emptyMap(); + + return readSubrepositories( + readHgsub(Arrays.asList(StringUtil.splitByLines(hgsubText))), + readHgsub(Arrays.asList(StringUtil.splitByLines(hgsubstateText))) + ); + } + + @NotNull + private static Map<String, SubRepo> readSubrepositories(@NotNull final Map<String, String> path2repo, + @NotNull final Map<String, String> path2revision) { + final Map<String, SubRepo> result = new HashMap<String, SubRepo>(); + for (Map.Entry<String, String> entry : path2repo.entrySet()) { + final String path = entry.getKey(); + final String url = entry.getValue(); + final String revision = path2revision.get(path); + if (revision != null) + result.put(path, new SubRepo(path, url, revision)); + } + return result; + } + + @NotNull + /*returns map: relative path -> repository url */ + private static Map<String, String> readHgsub(@NotNull final File hgsub) throws IOException { + return readHgsub(FileUtil.readFile(hgsub)); + } + + @NotNull + /*returns map: relative path -> repository url */ + private static Map<String, String> readHgsub(@NotNull final Collection<String> lines) { + Map<String, String> result = new HashMap<String, String>(); + for (String line : lines) { + String[] parts = line.split(" = "); + if (parts.length == 2) + result.put(parts[0], parts[1]); + } + return result; + } + + + @NotNull + /*returns map: relative path -> revision */ + private static Map<String, String> readHgsubstate(@NotNull final File hgsubstate) throws IOException { + return readHgsubstate(FileUtil.readFile(hgsubstate)); + } + + @NotNull + /*returns map: relative path -> revision */ + private static Map<String, String> readHgsubstate(@NotNull final Collection<String> lines) { + final Map<String, String> result = new HashMap<String, String>(); + for (String line : lines) { + String[] parts = line.split(" "); + if (parts.length == 2) + result.put(parts[1], parts[0]); + } + return result; + } +}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubRepo.java Mon Jan 13 18:15:41 2014 +0100 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubRepo.java Tue Jan 14 11:36:24 2014 +0100 @@ -17,9 +17,18 @@ public SubRepo(@NotNull String path, @NotNull String url, @NotNull String revision) { myPath = path; - myUrl = url.trim(); myRevision = revision; - myVcsType = parseVcsType(); + + if (url.startsWith("[svn]")) { + myVcsType = VcsType.svn; + myUrl = url.substring(5); + } else if (url.startsWith("[git]")) { + myVcsType = VcsType.git; + myUrl = url.substring(5); + } else { + myVcsType = VcsType.hg; + myUrl = url; + } } @NotNull @@ -34,9 +43,7 @@ @NotNull public String revision() { - if (myRevision.length() > 12) - return myRevision.substring(0, 12); - return myRevision; + return HgRepo.shortId(myRevision); } @NotNull @@ -48,7 +55,9 @@ return !myUrl.equals(other.url()); } + @NotNull public String resolveUrl(@NotNull String parentRepoUrl) throws WrongSubrepoUrlException { + //TODO: Handle paths on windows. Those paths are not papable for URL if (!parentRepoUrl.endsWith("/")) parentRepoUrl = parentRepoUrl + "/"; try { @@ -86,16 +95,21 @@ return myPath + " = " + myUrl + "#" + myRevision; } - private VcsType parseVcsType() { - if (myUrl.startsWith("[svn]")) - return VcsType.svn; - if (myUrl.startsWith("[git]")) - return VcsType.git; - return VcsType.hg; - } + public static enum VcsType { + hg(Constants.VCS_NAME), git("jetbrains.git"), svn("svn") + ; - public static enum VcsType { - hg, git, svn + private final String myVcsPluginName; + + VcsType(@NotNull String vcsPluginName) { + myVcsPluginName = vcsPluginName; + } + + @NotNull + public String getVcsPluginName() { + return myVcsPluginName; + } + } @Override
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommitsAndMountPointsCommand.java Tue Jan 14 11:36:24 2014 +0100 @@ -0,0 +1,131 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; + +import com.intellij.openapi.diagnostic.Logger; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.HgFileUtil; +import jetbrains.buildServer.util.FileUtil; +import jetbrains.buildServer.vcs.VcsException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.util.Date; + +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommitsAndMountPointsParser.parseCommits; +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommitsAndMountPointsParser.parseFileLog; + +/** + * Created 03.01.14 14:53 + * + * @author Eugene Petrenko (eugene.petrenko@jetbrains.com) + */ +public class CommitsAndMountPointsCommand extends VcsRootCommand { + private static final Logger LOG = Logger.getInstance(CommitsAndMountPointsCommand.class.getName()); + + public CommitsAndMountPointsCommand(@NotNull final CommandSettings commandSettings, + @NotNull final String hgPath, + @NotNull final File workingDir, + @NotNull final AuthSettings authSettings) { + super(commandSettings.addHgEnv("HGENCODING", "UTF-8"), hgPath, workingDir, authSettings); + } + + @NotNull + private File createTmpDir() throws VcsException { + try { + return HgFileUtil.createTempDir(); + } catch (IOException e) { + throw new VcsException("Unable to create temporary directory", e); + } + } + + @NotNull + private File extractCommandPy(@NotNull final File root) throws VcsException { + try { + final File py = new File(root, "load-substates-command.py"); + + FileUtil.copyResource(getClass(), "/python/load-substates-command.py", py); + + if (py.length() < 100) throw new IOException("Failed to unpack command resource"); + return py; + } catch (IOException e) { + throw new VcsException("Failed to extract .py file: " + e.getMessage(), e); + } + } + + public void call(@NotNull final Callback consumer) throws VcsException { + final File root = createTmpDir(); + + try { + final File py = extractCommandPy(root); + + callImpl(root, py, consumer); + } finally { + FileUtil.delete(root); + } + } + + public interface Callback { + void processHGSubFile(@NotNull final String fileId, @NotNull final String file); + void processHGSubStateFile(@NotNull final String fileId, @NotNull final String file); + void onCommit( + @NotNull String commitNum, + @NotNull String commitId, + @NotNull String[] parents, + @NotNull String branch, + @NotNull String[] tags, + @NotNull String author, + @NotNull String message, + @NotNull final Date timestamp, + @Nullable String hgsubNodeId, + @Nullable String hgsubstateNodeId); + } + + private void callImpl(@NotNull final File root, + @NotNull final File commandPy, + @NotNull final Callback consumer) throws VcsException { + final MercurialCommandLine cli = createCommandLine(); + cli.addParameter("--debug"); + cli.addParameter("--config"); + cli.addParameter("extensions.logextcj=" + commandPy); + cli.addParameter("load-substates"); + cli.addParameter(new File(root, "result").getPath()); + + final CommandResult res = runCommand(cli); + final String output = res.getStdout(); + + if (!output.contains("##Completed##")) throw new VcsException("Command failed: " + output); + + try { + parseFileLog(new File(root, "result.hgsub"), new CommitsAndMountPointsParser.ContentsConsumer() { + public void onCommit(@NotNull final String fileNodeId, @NotNull final String content) { + consumer.processHGSubFile(fileNodeId, content); + } + }); + parseFileLog(new File(root, "result.hgsubstate"), new CommitsAndMountPointsParser.ContentsConsumer() { + public void onCommit(@NotNull final String fileNodeId, @NotNull final String content) { + consumer.processHGSubStateFile(fileNodeId, content); + } + }); + + parseCommits(new File(root, "result.commits"), new CommitsAndMountPointsParser.CommitsConsumer() { + public void onCommit(@NotNull String commitNum, + @NotNull String commitId, + @NotNull String[] parents, + @NotNull String branch, + @NotNull String[] tags, + @NotNull String author, + @NotNull String message, + @NotNull Date timestamp, + @Nullable String hgsubNodeId, + @Nullable String hgsubstateNodeId) { + consumer.onCommit(commitNum, commitId, parents, branch, tags, author, message, timestamp, hgsubNodeId, hgsubstateNodeId); + } + }); + } catch (IOException e) { + throw new VcsException("Failed to parse response files for 'load-substates' command. " + e.getMessage(), e); + } + } + + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommitsAndMountPointsParser.java Tue Jan 14 11:36:24 2014 +0100 @@ -0,0 +1,152 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; + +import org.apache.commons.codec.binary.Base64; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * Created 09.01.14 17:10 + * + * @author Eugene Petrenko (eugene.petrenko@jetbrains.com) + */ +public class CommitsAndMountPointsParser { + + public static void parseFileLog(@NotNull final File dump, + @NotNull final ContentsConsumer consumer) throws IOException { + final InputStream is = new BufferedInputStream(new FileInputStream(dump)); + + final Decoder fileDecoder = new Decoder(5); + final BufferedReader st = new BufferedReader(new InputStreamReader(is, "utf-8")); + String line; + while((line = st.readLine()) != null) { + if (!line.startsWith("$$@@@@ ")) continue; + final String[] items = line.split(" "); + if (items.length != 1 + 2) continue; + + final String commitId = items[1]; + final String content = fileDecoder.decode(items[2]); + + consumer.onCommit(commitId, content == null ? "" : content); + } + } + + public interface ContentsConsumer { + void onCommit( + @NotNull String fileNodeId, + @NotNull String content); + } + + public interface CommitsConsumer { + void onCommit( + @NotNull String commitNum, + @NotNull String commitId, + @NotNull String[] parents, + @NotNull String branch, + @NotNull String[] tags, + @NotNull String author, + @NotNull String message, + @NotNull Date timestamp, + @Nullable String hgsubNodeId, + @Nullable String hgsubstateNodeId); + } + + + public static void parseCommits(@NotNull final File dump, @NotNull final CommitsConsumer consumer) throws IOException { + final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'Z'HH:mm:ss'T'Z", Locale.ENGLISH); + + final InputStream is = new BufferedInputStream(new FileInputStream(dump)); + + final Decoder branchDecoder = new Decoder(250); + final Decoder tagsDecoder = new Decoder(250); + final Decoder authorDecoder = new Decoder(200); + final Decoder messageDecoder = new Decoder(210); + + final BufferedReader st = new BufferedReader(new InputStreamReader(is, "utf-8")); + String line; + while((line = st.readLine()) != null) { + if (!line.startsWith("$$@@@@ ")) continue; + final Iterator<String> items = Arrays.asList(line.split(" ")).iterator(); + items.next(); //$$@@@@ + + try { + final String commitNum = items.next(); + final String commitId = items.next(); + final String[] parents = new String[Integer.parseInt(items.next())]; + for (int i = 0; i < parents.length; i++) { + parents[i] = items.next(); + } + final String branch = branchDecoder.decode(items.next()); + final String[] tags = new String[Integer.parseInt(items.next())]; + for (int i = 0; i < tags.length; i++) { + tags[i] = tagsDecoder.decode(items.next()); + } + + final String author = authorDecoder.decode(items.next()); + final String message = messageDecoder.decode(items.next()); + final Date time = parseTime(dateFormat, items.next()); + final String hgsub = textOrNull(items.next()); + final String hgsubstate = textOrNull(items.next()); + + consumer.onCommit( + commitNum, + commitId, + parents, + branch, + tags, + author == null ? "" : author, + message == null ? "" : message, + time, + hgsub, + hgsubstate); + } catch (NoSuchElementException e) { + //NOP + } + } + } + + @NotNull + private static Date parseTime(@NotNull final SimpleDateFormat dateFormat, + @NotNull final String time) { + try { + return dateFormat.parse(time); + } catch (ParseException e) { + throw new RuntimeException("Failed to parse datetime: " + time + ". " + e, e); + } + } + + @Nullable + private static String textOrNull(@NotNull final String text) { + if (text.equals("=====")) return null; + return text; + } + + private static class Decoder { + private final Map<String, String> myCache; + + public Decoder(final int SZ) { + myCache = new LinkedHashMap<String, String>(SZ, 0.9f) { + @Override + protected boolean removeEldestEntry(Map.Entry<String, String> eldest) { + return size() > SZ; + } + }; + } + + public String decode(@NotNull final String base64) throws UnsupportedEncodingException { + if (textOrNull(base64) == null) return null; + + final String result = myCache.get(base64); + if (result != null) return result; + + final String value = new String(Base64.decodeBase64(base64), "utf-8"); + //noinspection RedundantStringConstructorCall + myCache.put(new String(base64), value); + return value; + } + } +}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java Mon Jan 13 18:15:41 2014 +0100 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java Tue Jan 14 11:36:24 2014 +0100 @@ -28,10 +28,7 @@ import java.io.File; import java.io.IOException; import java.nio.charset.Charset; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public class LogCommand extends VcsRootCommand { @@ -47,6 +44,7 @@ private boolean myCalculateParents = true; private String myRevsets; private File myTemplate; + private List<String> myFiles = new ArrayList<String>(); public LogCommand(@NotNull CommandSettings commandSettings, @NotNull String hgPath, @@ -66,12 +64,12 @@ } public LogCommand fromRevision(@Nullable String fromRevision) { - myFromId = fromRevision == null ? fromRevision : new ChangeSet(fromRevision).getId(); + myFromId = fromRevision == null ? null : new ChangeSet(fromRevision).getId(); return this; } public LogCommand toRevision(@Nullable String toRevision) { - myToId = toRevision == null ? toRevision: new ChangeSet(toRevision).getId(); + myToId = toRevision == null ? null : new ChangeSet(toRevision).getId(); return this; } @@ -100,6 +98,11 @@ return this; } + public LogCommand forFile(@NotNull final String file) { + myFiles.add(file); + return this; + } + public List<ChangeSet> call() throws VcsException { MercurialCommandLine cli = createCommandLine(); cli.setCharset(Charset.forName("UTF-8")); @@ -125,6 +128,8 @@ cli.addParameter(myLimit.toString()); } + cli.addParameters(myFiles); + CommandResult res = runCommand(cli); String output = res.getStdout(); try {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/python/load-substates-command.py Tue Jan 14 11:36:24 2014 +0100 @@ -0,0 +1,148 @@ +#!/usr/bin/env python +## +## Copyright 2000-2014 JetBrains +## +## 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. +## +## +## http://www.gnu.org/licenses/gpl-faq.html#GPLModuleLicense +## http://www.gnu.org/licenses/license-list.html#apache2 +## http://en.wikipedia.org/wiki/Apache_License#GPL_compatibility +## +## +""" +load-substates-command +""" + +import base64 +from mercurial import util, node +from threading import Thread + + +def load_substates_command(ui, repo, outputFile, **opts): + """Tons of docs""" + + ui.write("Fetching commits...") + + NONE = "=====" + + def b64(x): + if x is None or x == "": + return NONE + return base64.b64encode( x ) + + def fetch_commits(): + ui.write("Iterating over commits...\n") + with open(outputFile + ".commits", "w", 5 * 1024 * 1024) as result: + result.write("format: prefix commitID commitHash num_parents parent branch num_tags tag user message date [.hgsub] [.hgsubstate]\n") + result.flush() + + commit_to_substates = {} + + def update_sub_states(ctx): + def filenode(ctx, filename, i): + if filename in ctx.files(): + try: + return node.hex(ctx.filenode(filename)) + except: + # file could have been deleted => so there would be no filenode for it + # this also means we should avoid parents as file source + return NONE + else: + for p in ctx.parents(): + if commit_to_substates.has_key(p.hex()): + v = commit_to_substates[p.hex()][i] + if v != NONE: + return v + return NONE + + best_sub = filenode(ctx, ".hgsub", 0) + best_state = filenode(ctx, ".hgsubstate", 1) + commit_to_substates[ctx.hex()] = (best_sub, best_state) + return best_sub, best_state + + for r in list(repo.changelog): + ctx = repo[r] + + result.write("$$@@@@ ") # magic + result.write( str( ctx.rev() ) ) # commit Num + result.write(" ") + result.write( ctx.hex() ) # commit ID + result.write(" ") + result.write( str( len( ctx.parents()) ) ) # num parents + + for p in ctx.parents(): # parents + result.write(" ") + result.write(p.hex()) + + result.write(" ") + result.write( b64( ctx.branch() ) ) # commit branch + + result.write(" ") + result.write( str( len( ctx.tags() ) ) ) # num tags + + for tag in ctx.tags(): # tags + result.write(" ") + result.write( b64 ( tag ) ) + + result.write(" ") # user + result.write( b64( ctx.user() ) ) + + result.write(" ") # message + result.write( b64( ctx.description() ) ) + + result.write(" ") # date + result.write( util.datestr( ctx.date(), "%Y-%m-%dZ%H:%M:%ST%1%2") ) + + #resolve sub-repo mounts + (sub_node, state_node) = update_sub_states(ctx) + result.write(" " + sub_node + " " + state_node) + result.write("\n") + + ui.write("Commits iteration completed") + + def fetch_file_revisions(filename): + ui.write("Fetching revisions of " + filename + " file\n") + with open(outputFile + filename, "w", 5 * 1024 * 1024) as result: + result.write("format: prefix commitID base64(" + filename + ")\n") + result.flush() + + log = repo.file(filename) + for r in log: + result.write("$$@@@@ " + node.hex(log.node(r)) + " " + b64(log.read(r)) + "\n") + + ui.write("All revisions of file " + filename + " are fetched\n") + + tasks = [ + Thread(target=fetch_commits, args=[], name="Fetch commits graph"), + Thread(target=fetch_file_revisions, args=[".hgsub"], name="Fetch .hgsub"), + Thread(target=fetch_file_revisions, args=[".hgsubstate"], name="Fetch .hgsubstate"), + ] + + for task in tasks: + task.start() + + for task in tasks: + task.join() + + ui.write("\n##Completed##\n") + + +#so here goes command registration and options +cmdtable = { + "load-substates": (load_substates_command, [ ], " [options] OUTPUT_FILE") +} + +testedwith = '2.2.2' +buglink = "@jonnyzzz" +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialCommitsInfoBuilderStates.java Tue Jan 14 11:36:24 2014 +0100 @@ -0,0 +1,38 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Created 13.01.14 19:45 + * + * @author Eugene Petrenko (eugene.petrenko@jetbrains.com) + */ +public class MercurialCommitsInfoBuilderStates { + private final Map<String, String> myHgStateNodes = new HashMap<String, String>(); + private final Map<String, String> myHgSubNodes = new HashMap<String, String>(); + + public void addSubNode(@NotNull final String hash, @NotNull final String text) { + myHgSubNodes.put(hash, text); + } + + public void addStateNode(@NotNull final String hash, @NotNull final String text) { + myHgStateNodes.put(hash, text); + } + + + @NotNull + public Map<String, SubRepo> mounts(@Nullable final String subHash, + @Nullable final String stateHash) { + if (subHash == null || stateHash == null) return Collections.emptyMap(); + + //TODO: may cache parsed contents to make it work faster + return HgSubs.readSubrepositories( + myHgSubNodes.get(subHash), + myHgStateNodes.get(stateHash)); + } +}
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialCommitsInfoBuilderSupport.java Mon Jan 13 18:15:41 2014 +0100 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialCommitsInfoBuilderSupport.java Tue Jan 14 11:36:24 2014 +0100 @@ -1,16 +1,14 @@ package jetbrains.buildServer.buildTriggers.vcs.mercurial; -import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.ChangeSet; -import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.ChangeSetRevision; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommitsAndMountPointsCommand; import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.HgVcsRoot; -import jetbrains.buildServer.util.MultiMap; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.LogCommand; import jetbrains.buildServer.vcs.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.Map; - -import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.LogCommand.ZERO_PARENT_ID; -import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.LogCommand.ZERO_PARENT_SHORT_ID; +import java.net.URISyntaxException; +import java.util.Date; /** * Created 30.09.13 13:05 @@ -36,31 +34,67 @@ final ServerHgRepo repo = mySupport.createRepo(hgRoot); mySupport.syncRepository(hgRoot); - MultiMap<String, String> commitToTag = new MultiMap<String, String>(); - for (Map.Entry<String, String> e : repo.tags().call().entrySet()) { - commitToTag.putValue(e.getValue(), e.getKey()); - } - //TODO: make stream parsing of commits instead of parsing of collected string output - for (ChangeSet set : repo.logNoFiles().showCommitsFromAllBranches().call()) { - final CommitDataBean change = new CommitDataBean(set.getId(), set.getFullVersion(), set.getTimestamp()); - for (ChangeSetRevision p : set.getParents()) { - final String commitId = p.getId(); + repo.logSubstates().call(new CommitsAndMountPointsCommand.Callback() { + private final MercurialCommitsInfoBuilderStates subs = new MercurialCommitsInfoBuilderStates(); - if (ZERO_PARENT_ID.equals(commitId)) continue; - if (ZERO_PARENT_SHORT_ID.equals(commitId)) continue; - change.addParentRevision(commitId); + public void processHGSubFile(@NotNull String fileId, @NotNull String text) { + subs.addSubNode(fileId, text); } - change.setCommitMessage(set.getDescription()); - change.setCommitAuthor(set.getUser()); - change.addBranch(set.getBranch()); - - for (String tag : commitToTag.get(set.getId())) { - change.addTag(tag); + public void processHGSubStateFile(@NotNull String fileId, @NotNull String text) { + subs.addStateNode(fileId, text); } - consumer.consumeCommit(change); - } + public void onCommit(@NotNull String commitNum, + @NotNull String commitId, + @NotNull String[] parents, + @NotNull String branch, + @NotNull String[] tags, + @NotNull String author, + @NotNull String message, + @NotNull Date timestamp, + @Nullable String hgsubNodeId, + @Nullable String hgsubstateNodeId) { + + final CommitDataBean bean = new CommitDataBean( + HgRepo.shortId(commitId), + HgRepo.shortId(commitId), + timestamp + ); + + for (String parent : parents) { + if (LogCommand.ZERO_PARENT_ID.equals(parent)) continue; + if (LogCommand.ZERO_PARENT_SHORT_ID.equals(parent)) continue; + bean.addParentRevision(HgRepo.shortId(parent)); + } + + for (String tag : tags) { + if ("tip".equals(tag)) continue; + + bean.addTag(tag); + } + + bean.addBranch(branch); + bean.setCommitAuthor(author); + bean.setCommitMessage(message); + + for (SubRepo subRepo : subs.mounts(hgsubNodeId, hgsubstateNodeId).values()) { + try { + bean.addMountPoint(new CommitMountPointDataBean( + subRepo.vcsType().getVcsPluginName(), + subRepo.resolveUrl(hgRoot.getRepository()), + subRepo.path(), + subRepo.revision() + )); + } catch (URISyntaxException e) { + //NOP + } + } + + consumer.consumeCommit(bean); + } + }); } + }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/BaseMercurialTestCase.java Mon Jan 13 18:15:41 2014 +0100 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/BaseMercurialTestCase.java Tue Jan 14 11:36:24 2014 +0100 @@ -1,6 +1,8 @@ package jetbrains.buildServer.buildTriggers.vcs.mercurial; import jetbrains.buildServer.TempFiles; +import jetbrains.buildServer.serverSide.BasePropertiesModel; +import jetbrains.buildServer.serverSide.TeamCityProperties; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; @@ -9,6 +11,7 @@ @BeforeMethod public void setUp() throws Exception { + new TeamCityProperties() {{ setModel(new BasePropertiesModel() {});}}; myTempFiles = new TempFiles(); }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CommitsInfoBuilderSupportTest.java Mon Jan 13 18:15:41 2014 +0100 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CommitsInfoBuilderSupportTest.java Tue Jan 14 11:36:24 2014 +0100 @@ -103,4 +103,20 @@ } } + @Test(enabled = false) + public void should_return_graphcommits() throws Exception { + VcsRoot root = vcsRoot().withLocalRepository(new File("F:\\Work\\ReSharper")).build(); + + final long start = System.currentTimeMillis(); + mySupport.collectCommits(root, CheckoutRules.DEFAULT, new CommitsInfoBuilder.CommitsConsumer() { + public void consumeCommit(@NotNull CommitInfo commit) { + + } + }); + + final long actual = System.currentTimeMillis() - start; + System.out.println("computed in " + actual + " ms"); + } + + }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/VcsRootBuilder.java Mon Jan 13 18:15:41 2014 +0100 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/VcsRootBuilder.java Tue Jan 14 11:36:24 2014 +0100 @@ -82,6 +82,11 @@ } + public VcsRootBuilder withLocalRepository(@NotNull final File repo) { + return withUrl(repo.getPath()).withCloneRepositoryTo(repo.getParentFile()); + } + + public VcsRootBuilder withUrl(@NotNull File repository) { myRepository = repository.getAbsolutePath(); return this;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommitsAndMountPointsCommandParserTest.java Tue Jan 14 11:36:24 2014 +0100 @@ -0,0 +1,72 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; + +import org.jetbrains.annotations.NotNull; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +/** + * Created 08.01.14 22:39 + * + * @author Eugene Petrenko (eugene.petrenko@jetbrains.com) + */ +public class CommitsAndMountPointsCommandParserTest { + + @Test + public void test_parse_commits_a() throws IOException { + final List<String> log = new ArrayList<String>(); + CommitsAndMountPointsParser.parseCommits(new File("./mercurial-tests/testData/subst/a.commits"), proxy(CommitsAndMountPointsParser.CommitsConsumer.class, log)); + + for (String s : log) { + System.out.println(s); + } + Assert.assertEquals(log, Arrays.asList( + "onCommit [0, 4cc56f5e7eee469e4b33db02a22baaac46c1ecee, [0000000000000000000000000000000000000000], default, [], dsha, API changes, 1184063759000, null, null]", + "onCommit [5, 90ca28808a205909463386921939f59eb4259fcf, [e7298836dbcbbf3a79b255db718eca04f5b4824f], default, [], dsl, Switch to Orcas, 1184687116000, null, null]", + "onCommit [2490, 6a4484ecc639cc0e3783a86f40a3ad87405970fc, [1d30763d0f9f94ba7368c36a186119c11c453f4c, 549dca11e6a1080a81ce711849e20c23ec1b4e05], default, [], Sergey.Shkredov, Merge with serjic, 1334149579000, 9a12f93d5158a5cd75962d4ebd24fbe7e45ace53, e64b208e4ae1f3b3302731e358fc4c494dbe881d]", + "onCommit [2513, 6cef50ceefad10da64d256a01d2b2ac3153b465a, [afb1278c857e2dbfe1a6cd4e7e646e5fe7c48b51, 3cc6eef56b1ed4a49b39c0fb785334ca618c5e9c], SDKReferences, [], Kirill.Skrygan, Merge two SDKReferences heads, 1334240172000, 9a12f93d5158a5cd75962d4ebd24fbe7e45ace53, 35b0fe2dd3e1e2553a6407cf732e0fc7cd4c2e08]", + "onCommit [2592, 6f096ed0264b2378f98f125c545f01b227cdf23d, [9816adffa38d039da651b902a08d3ad45bc35a12, 9de1e1e88af7b440c6f4c55cb0410afdad2ce89f], default, [], Alexander.Shvedov, Merge with XamlWorkshop, 1334582015000, 9a12f93d5158a5cd75962d4ebd24fbe7e45ace53, 0b2f91b87bb6b3fc848111b9ddc6c250d6b06453]", + "onCommit [2593, b4047e0d64ecb070d2826166d1b4b9d25672e023, [c3205f71291d525b6d9282b8491abe20ec8373d0], serjic, [], Sergey.Shkredov, fix some problems., 1334574266000, 9a12f93d5158a5cd75962d4ebd24fbe7e45ace53, d8c99c4baa952506946dadd627ca918ffce24bc7]", + "onCommit [2600, ddd190f94b157f1ac215df16f530e51a91b4c7c2, [66f3b250e585ec69f9396bf311ce8fe986e4c1e3], xvost, [], xvost, RSRP-294634, 1334569744000, 9a12f93d5158a5cd75962d4ebd24fbe7e45ace53, c6a6c6fb5b932c1c89e0c411c150859ec4906078]", + "onCommit [812, 68f94180a341a6c174d015cd0fa0b10a66e2615b, [cf898c0dac48a20985a5b72dbc801a03300d4a3c], Subplatforms, [MsiReady], baltic, ++, 1328895848000, 9a12f93d5158a5cd75962d4ebd24fbe7e45ace53, 8418205f67a695ae82b4b4d4946cec9082202553]" + )); + } + + @Test + public void test_parse_files_a() throws IOException { + final List<String> log = new ArrayList<String>(); + CommitsAndMountPointsParser.parseFileLog(new File("./mercurial-tests/testData/subst/a.files"), proxy(CommitsAndMountPointsParser.ContentsConsumer.class, log)); + + for (String s : log) { + System.out.println(s); + } + Assert.assertEquals(log, Arrays.asList( + "onCommit [b80de5d138758541c5f05265ad144ab9fa86d1db, ]", + "onCommit [05a11677a1346201e93dee1968178c019b42bbb0, 0000000000000000000000000000000000000000 Platform\n]", + "onCommit [d0ae0cceebc043697902c66e9fec0ecf621d954c, 0c4b35f8765c032a4101fa2024267be31f3c8343 Platform\n]" + )); + } + + @NotNull + private <T> T proxy(@NotNull final Class<T> clazz, @NotNull final List<String> log) { + return clazz.cast(Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[]{clazz}, new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + for (int i = 0; i < args.length; i++) { + Object arg = args[i]; + if (arg instanceof Date) args[i] = ((Date) arg).getTime(); + } + log.add(method.getName() + " " + Arrays.deepToString(args)); + return null; + } + })); + } +}
--- a/mercurial-tests/src/testng.xml Mon Jan 13 18:15:41 2014 +0100 +++ b/mercurial-tests/src/testng.xml Tue Jan 14 11:36:24 2014 +0100 @@ -37,6 +37,7 @@ <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.CommitsInfoBuilderSupportTest"/> <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialModificationInfoBuilderTest"/> <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialUrlSupportTest"/> + <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommitsAndMountPointsCommandParserTest"/> </classes> </test> </suite>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/subst/a.commits Tue Jan 14 11:36:24 2014 +0100 @@ -0,0 +1,9 @@ +format: prefix commitID commitHash num_parents parent branch num_tags tag user message date [.hgsub] [.hgsubstate] +$$@@@@ 0 4cc56f5e7eee469e4b33db02a22baaac46c1ecee 1 0000000000000000000000000000000000000000 ZGVmYXVsdA== 0 ZHNoYQ== QVBJIGNoYW5nZXM= 2007-07-10Z10:35:59T+0000 ===== ===== +$$@@@@ 5 90ca28808a205909463386921939f59eb4259fcf 1 e7298836dbcbbf3a79b255db718eca04f5b4824f ZGVmYXVsdA== 0 ZHNs U3dpdGNoIHRvIE9yY2Fz 2007-07-17Z15:45:16T+0000 ===== ===== +$$@@@@ 2490 6a4484ecc639cc0e3783a86f40a3ad87405970fc 2 1d30763d0f9f94ba7368c36a186119c11c453f4c 549dca11e6a1080a81ce711849e20c23ec1b4e05 ZGVmYXVsdA== 0 U2VyZ2V5LlNoa3JlZG92 TWVyZ2Ugd2l0aCBzZXJqaWM= 2012-04-11Z17:06:19T+0400 9a12f93d5158a5cd75962d4ebd24fbe7e45ace53 e64b208e4ae1f3b3302731e358fc4c494dbe881d +$$@@@@ 2513 6cef50ceefad10da64d256a01d2b2ac3153b465a 2 afb1278c857e2dbfe1a6cd4e7e646e5fe7c48b51 3cc6eef56b1ed4a49b39c0fb785334ca618c5e9c U0RLUmVmZXJlbmNlcw== 0 S2lyaWxsLlNrcnlnYW4= TWVyZ2UgdHdvIFNES1JlZmVyZW5jZXMgaGVhZHM= 2012-04-12Z18:16:12T+0400 9a12f93d5158a5cd75962d4ebd24fbe7e45ace53 35b0fe2dd3e1e2553a6407cf732e0fc7cd4c2e08 +$$@@@@ 2592 6f096ed0264b2378f98f125c545f01b227cdf23d 2 9816adffa38d039da651b902a08d3ad45bc35a12 9de1e1e88af7b440c6f4c55cb0410afdad2ce89f ZGVmYXVsdA== 0 QWxleGFuZGVyLlNodmVkb3Y= TWVyZ2Ugd2l0aCBYYW1sV29ya3Nob3A= 2012-04-16Z17:13:35T+0400 9a12f93d5158a5cd75962d4ebd24fbe7e45ace53 0b2f91b87bb6b3fc848111b9ddc6c250d6b06453 +$$@@@@ 2593 b4047e0d64ecb070d2826166d1b4b9d25672e023 1 c3205f71291d525b6d9282b8491abe20ec8373d0 c2Vyamlj 0 U2VyZ2V5LlNoa3JlZG92 Zml4IHNvbWUgcHJvYmxlbXMu 2012-04-16Z15:04:26T+0400 9a12f93d5158a5cd75962d4ebd24fbe7e45ace53 d8c99c4baa952506946dadd627ca918ffce24bc7 +$$@@@@ 2600 ddd190f94b157f1ac215df16f530e51a91b4c7c2 1 66f3b250e585ec69f9396bf311ce8fe986e4c1e3 eHZvc3Q= 0 eHZvc3Q= UlNSUC0yOTQ2MzQ= 2012-04-16Z11:49:04T+0200 9a12f93d5158a5cd75962d4ebd24fbe7e45ace53 c6a6c6fb5b932c1c89e0c411c150859ec4906078 +$$@@@@ 812 68f94180a341a6c174d015cd0fa0b10a66e2615b 1 cf898c0dac48a20985a5b72dbc801a03300d4a3c U3VicGxhdGZvcm1z 1 TXNpUmVhZHk= YmFsdGlj Kys= 2012-02-10Z21:44:08T+0400 9a12f93d5158a5cd75962d4ebd24fbe7e45ace53 8418205f67a695ae82b4b4d4946cec9082202553 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/subst/a.files Tue Jan 14 11:36:24 2014 +0100 @@ -0,0 +1,4 @@ +format: prefix commitID base64(.hgsubstate) +$$@@@@ b80de5d138758541c5f05265ad144ab9fa86d1db ===== +$$@@@@ 05a11677a1346201e93dee1968178c019b42bbb0 MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCBQbGF0Zm9ybQo= +$$@@@@ d0ae0cceebc043697902c66e9fec0ecf621d954c MGM0YjM1Zjg3NjVjMDMyYTQxMDFmYTIwMjQyNjdiZTMxZjNjODM0MyBQbGF0Zm9ybQo=