Mercurial > hg > mercurial
changeset 686:1efd1846f00a
Add an option for using tar archive for building a full patch
This is a workaround for the case when java cannot read some file from
the repository due to encoding used in a file name.
line wrap: on
line diff
--- a/.idea/artifacts/mercurial_vcs_worker.xml Tue Dec 10 18:43:13 2013 +0100 +++ b/.idea/artifacts/mercurial_vcs_worker.xml Tue Dec 17 13:38:24 2013 +0100 @@ -12,6 +12,7 @@ <element id="artifact" artifact-name="mercurial-common.jar" /> <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> <element id="file-copy" path="$PROJECT_DIR$/teamcity-plugin.xml" /> </root>
--- a/.idea/artifacts/plugin.xml Tue Dec 10 18:43:13 2013 +0100 +++ b/.idea/artifacts/plugin.xml Tue Dec 17 13:38:24 2013 +0100 @@ -13,6 +13,7 @@ <element id="artifact" artifact-name="mercurial-common.jar" /> <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> <element id="directory" name="agent"> <element id="archive" name="mercurial.zip">
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.idea/libraries/commons_compress_1_5.xml Tue Dec 17 13:38:24 2013 +0100 @@ -0,0 +1,9 @@ +<component name="libraryTable"> + <library name="commons-compress-1.5"> + <CLASSES> + <root url="jar://$PROJECT_DIR$/lib/commons-compress-1.5.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> +</component> \ No newline at end of file
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Constants.java Tue Dec 10 18:43:13 2013 +0100 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Constants.java Tue Dec 17 13:38:24 2013 +0100 @@ -31,6 +31,7 @@ String DETECT_SUBREPO_CHANGES = "detectSubrepoChanges"; String USE_TAGS_AS_BRANCHES = "useTagsAsBranches"; String INCLUDE_SUBREPOS_IN_PATCH = "includeSubreposInPatch"; + String USE_ARCHIVE_FOR_PATCH = "useArchiveForPatch"; String GLOBAL_DETECT_SUBREPO_CHANGES = "teamcity.hg.detectSubrepoChanges"; }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ArchiveCommand.java Tue Dec 10 18:43:13 2013 +0100 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ArchiveCommand.java Tue Dec 17 13:38:24 2013 +0100 @@ -19,10 +19,16 @@ import org.jetbrains.annotations.NotNull; import java.io.File; +import java.util.ArrayList; +import java.util.List; public class ArchiveCommand extends VcsRootCommand { - private File myDestDir; + private final static int MAX_CMD_LEN = 900; + + private File myDestination; private String myToId; + private String myType = "files"; + private List<String> myIncludeRules = new ArrayList<String>(); public ArchiveCommand(@NotNull CommandSettings commandSettings, @NotNull String hgPath, @@ -31,8 +37,8 @@ super(commandSettings, hgPath, workingDir, authSettings); } - public ArchiveCommand toDir(@NotNull File destDir) { - myDestDir = destDir; + public ArchiveCommand destination(@NotNull File destination) { + myDestination = destination; return this; } @@ -41,21 +47,48 @@ return this; } + public ArchiveCommand type(@NotNull String type) { + myType = type; + return this; + } + + public boolean addIncludeRule(@NotNull String rule) { + MercurialCommandLine cmd = createCmd(); + int cmdSize = cmd.getCommandLineString().length(); + if (cmdSize + rule.length() + (myIncludeRules.isEmpty() ? 0 : "-I ".length()) > MAX_CMD_LEN) + return false; + myIncludeRules.add(rule); + return true; + } + public void call() throws VcsException { - if (myDestDir == null) + if (myDestination == null) throw new IllegalStateException("Destination dir must be specified"); + MercurialCommandLine cli = createCmd(); + + runCommand(cli, myCommandSettings.setFailWhenStderrNotEmpty(true)); + deleteHgArchival(); + } + + private MercurialCommandLine createCmd() { MercurialCommandLine cli = createCommandLine(); cli.addParameter("archive"); setType(cli); setRevision(cli); setDestination(cli); + addIncludeRules(cli); + return cli; + } - runCommand(cli, myCommandSettings.setFailWhenStderrNotEmpty(true)); - deleteHgArchival(); + private void addIncludeRules(@NotNull MercurialCommandLine cli) { + if (myIncludeRules.isEmpty()) + return; + cli.addParameter("-I"); + cli.addParameters(myIncludeRules); } private void setDestination(GeneralCommandLine cli) { - cli.addParameter(myDestDir.getAbsolutePath()); + cli.addParameter(myDestination.getAbsolutePath()); } private void setRevision(GeneralCommandLine cli) { @@ -69,16 +102,16 @@ private void setType(GeneralCommandLine cli) { cli.addParameter("-t"); - cli.addParameter("files"); + cli.addParameter(myType); } /** * hg archive generates .hg_archival.txt, delete it since original repository do not have such file */ private void deleteHgArchival() { - if (myDestDir == null) + if (myDestination == null) throw new IllegalStateException("Destination dir must be specified"); - File hg_arhival = new File(myDestDir, ".hg_archival.txt"); + File hg_arhival = new File(myDestination, ".hg_archival.txt"); FileUtil.delete(hg_arhival); } }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/HgVcsRoot.java Tue Dec 10 18:43:13 2013 +0100 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/HgVcsRoot.java Tue Dec 17 13:38:24 2013 +0100 @@ -44,6 +44,7 @@ private final boolean myDetectSubrepoChanges; private final boolean myUseTagsAsBranches; private final boolean myIncludeSubreposInPatch; + private final boolean myUseArchiveForPatch; public HgVcsRoot(@NotNull final VcsRoot vcsRoot) { this(vcsRoot.getProperties()); @@ -62,6 +63,7 @@ myUseTagsAsBranches = Boolean.parseBoolean(getProperty(Constants.USE_TAGS_AS_BRANCHES)); String includeSubreposProp = getProperty(Constants.INCLUDE_SUBREPOS_IN_PATCH); myIncludeSubreposInPatch = includeSubreposProp == null ? true : Boolean.parseBoolean(includeSubreposProp);//true by default + myUseArchiveForPatch = Boolean.parseBoolean(getProperty(Constants.USE_ARCHIVE_FOR_PATCH)); } public HgVcsRoot withUrl(@NotNull String repositoryUrl) { @@ -82,6 +84,10 @@ return myIncludeSubreposInPatch; } + public boolean useArchiveForPatch() { + return myUseArchiveForPatch; + } + /** * Returns name of the branch to use (returns 'default' if no branch specified) * @return see above
--- a/mercurial-server/mercurial-server.iml Tue Dec 10 18:43:13 2013 +0100 +++ b/mercurial-server/mercurial-server.iml Tue Dec 17 13:38:24 2013 +0100 @@ -22,6 +22,7 @@ <orderEntry type="library" name="TeamCity Vcs Api" level="project" /> <orderEntry type="library" name="trove4j" level="project" /> <orderEntry type="library" name="quartz-1.6.0" level="project" /> + <orderEntry type="library" name="commons-compress-1.5" level="project" /> </component> </module>
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Tue Dec 10 18:43:13 2013 +0100 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Tue Dec 17 13:38:24 2013 +0100 @@ -15,6 +15,7 @@ */ package jetbrains.buildServer.buildTriggers.vcs.mercurial; +import com.intellij.openapi.util.text.StringUtil; import jetbrains.buildServer.Used; import jetbrains.buildServer.buildTriggers.vcs.AbstractVcsPropertiesProcessor; import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*; @@ -25,11 +26,15 @@ import jetbrains.buildServer.serverSide.PropertiesProcessor; import jetbrains.buildServer.serverSide.ServerListener; import jetbrains.buildServer.serverSide.ServerListenerAdapter; +import jetbrains.buildServer.util.ArchiveType; +import jetbrains.buildServer.util.ArchiveUtil; import jetbrains.buildServer.util.EventDispatcher; import jetbrains.buildServer.util.FileUtil; import jetbrains.buildServer.util.cache.ResetCacheRegister; import jetbrains.buildServer.vcs.*; import jetbrains.buildServer.vcs.patches.PatchBuilder; +import org.apache.commons.compress.archivers.ArchiveEntry; +import org.apache.commons.compress.archivers.ArchiveInputStream; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -225,8 +230,7 @@ @NotNull final ChangeSet fromVer, @NotNull final ChangeSet toVer, @NotNull final PatchBuilder builder, - @NotNull final CheckoutRules checkoutRules) - throws VcsException, IOException { + @NotNull final CheckoutRules checkoutRules) throws VcsException, IOException { HgRepo repo = createRepo(root); List<FileStatus> modifiedFiles = repo.status().fromRevision(fromVer).toRevision(toVer).call(); List<String> notDeletedFiles = new ArrayList<String>(); @@ -256,6 +260,11 @@ } if (root.includeSubreposInPatch()) buildSubrepoPatch(root, fromVer, toVer, builder, checkoutRules, repo); + } catch (Exception e) { + Loggers.VCS.warn("Cannot build an incremental patch in repository " + root.getRepository() + + " from revision " + fromVer.getId() + " to revision " + toVer.getId(), e); + builder.deleteDirectory(new File(""), true);//clean patch + buildFullPatch(root, toVer, builder, checkoutRules); } finally { deleteDir(parentDir, Loggers.VCS); } @@ -339,13 +348,33 @@ buildPatchFromDirectory(builder, tempDir, checkoutRules, myIgnoreDotHgFilter); } else { Loggers.VCS.debug("Repository '" + root.getRepository() + "' doesn't have subrepos at revision " + toVer.getId() + ", use 'hg archive' to build clean patch"); - repo.archive().revision(toVer).toDir(tempDir).call(); - buildPatchFromDirectory(builder, tempDir, checkoutRules, myAcceptAllFilter); + if (root.useArchiveForPatch()) { + File archive = new File(tempDir, "arch.tar"); + repo.archive().revision(toVer).type("tar").destination(archive).call(); + buildPatchFromArchive(builder, archive, checkoutRules, new FileFilter() { + public boolean accept(File f) { + return !f.getName().equals(".hg_archival.txt"); + } + }); + } else { + repo.archive().revision(toVer).destination(tempDir).call(); + buildPatchFromDirectory(builder, tempDir, checkoutRules, myAcceptAllFilter); + } } } else { Loggers.VCS.debug("Subrepos disabled in VCS root, use 'hg archive' to build clean patch"); - repo.archive().revision(toVer).toDir(tempDir).call(); - buildPatchFromDirectory(builder, tempDir, checkoutRules, myAcceptAllFilter); + if (root.useArchiveForPatch()) { + File archive = new File(tempDir, "arch.tar"); + repo.archive().revision(toVer).type("tar").destination(archive).call(); + buildPatchFromArchive(builder, archive, checkoutRules, new FileFilter() { + public boolean accept(File f) { + return !f.getName().equals(".hg_archival.txt"); + } + }); + } else { + repo.archive().revision(toVer).destination(tempDir).call(); + buildPatchFromDirectory(builder, tempDir, checkoutRules, myAcceptAllFilter); + } } } finally { deleteDir(tempDir, Loggers.VCS); @@ -385,6 +414,34 @@ } } + private void buildPatchFromArchive(@NotNull PatchBuilder builder, + @NotNull File archive, + @NotNull CheckoutRules checkoutRules, + @NotNull FileFilter filter) throws IOException { + FileInputStream fis = new FileInputStream(archive); + ArchiveInputStream is = null; + try { + is = ArchiveUtil.getArchiveInputStream(ArchiveType.TAR, fis); + if (is == null) + throw new IOException("Unknown archive type"); + ArchiveEntry entry = null; + while ((entry = is.getNextEntry()) != null) { + String fileName = entry.getName(); + if (fileName.startsWith("arch/")) + fileName = fileName.substring(5); + if (!filter.accept(new File(fileName))) + continue; + String mappedFile = checkoutRules.map(fileName); + if (!StringUtil.isEmpty(mappedFile)) + builder.createBinaryFile(new File(mappedFile), null, is, entry.getSize()); + } + } finally { + fis.close(); + if (is != null) + is.close(); + } + } + private void buildPatchFromDirectory(final PatchBuilder builder, final File repRoot, final CheckoutRules checkoutRules, @NotNull final FileFilter filter) throws IOException { buildPatchFromDirectory(repRoot, builder, repRoot, checkoutRules, filter); }
--- a/mercurial-tests/mercurial-tests.iml Tue Dec 10 18:43:13 2013 +0100 +++ b/mercurial-tests/mercurial-tests.iml Tue Dec 17 13:38:24 2013 +0100 @@ -22,6 +22,7 @@ <orderEntry type="module" module-name="mercurial-server-tc" /> <orderEntry type="library" name="quartz-1.6.0" level="project" /> <orderEntry type="library" name="IDEA-openapi" level="project" /> + <orderEntry type="library" name="commons-compress-1.5" level="project" /> </component> </module>
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java Tue Dec 10 18:43:13 2013 +0100 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java Tue Dec 17 13:38:24 2013 +0100 @@ -32,12 +32,10 @@ import java.io.IOException; import java.util.*; -import static com.intellij.openapi.util.io.FileUtil.*; +import static com.intellij.openapi.util.io.FileUtil.delete; import static jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialSupportBuilder.mercurialSupport; import static jetbrains.buildServer.buildTriggers.vcs.mercurial.ModificationDataMatcher.modificationData; -import static jetbrains.buildServer.buildTriggers.vcs.mercurial.Util.buildPatch; -import static jetbrains.buildServer.buildTriggers.vcs.mercurial.Util.copyRepository; -import static jetbrains.buildServer.buildTriggers.vcs.mercurial.Util.getHgPath; +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.Util.*; import static jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsRootBuilder.vcsRoot; import static jetbrains.buildServer.util.Util.map; import static org.hamcrest.MatcherAssert.assertThat; @@ -318,6 +316,32 @@ } + public void unicode_in_filename() throws Exception { + File repo = copyRepository(myTempFiles, new File("mercurial-tests/testData/unicodeFileName").getAbsolutePath()); + VcsRoot root = vcsRoot().withUrl(repo.getAbsolutePath()) + .withSubrepoChanges(false) + .withSubreposInPatch(false) + .withArchiveForPatch(true) + .build(); + + buildPatch(myVcs, root, null, "51677e03dc19", CheckoutRules.DEFAULT); + + buildPatch(myVcs, root, "54044489d391", "51677e03dc19", CheckoutRules.DEFAULT); + } + + + public void clean_patch_from_archive() throws Exception { + setName("patch3"); + File repository = copyRepository(myTempFiles, simpleRepo()); + VcsRoot root = vcsRoot().withUrl(repository.getAbsolutePath()) + .withBranch("test_branch") + .withArchiveForPatch(true) + .build(); + ByteArrayOutputStream output = buildPatch(myVcs, root, null, "7:376dcf05cd2a", CheckoutRules.DEFAULT); + checkPatchResult(output.toByteArray()); + } + + public void labeling_should_not_populate_files_in_local_mirror() throws Exception { VcsRootImpl root = createVcsRoot(simpleRepo()); myVcs.getCurrentVersion(root);
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/VcsRootBuilder.java Tue Dec 10 18:43:13 2013 +0100 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/VcsRootBuilder.java Tue Dec 17 13:38:24 2013 +0100 @@ -25,6 +25,8 @@ private File myCloneRepositoryTo; private boolean myDetectSubrepoChanges = false; private boolean myTagsAsBranches = false; + private boolean myIncludeSubreposInPatch = true; + private boolean myUseArchiveForPatch = false; public static VcsRootBuilder vcsRoot() { return new VcsRootBuilder(); @@ -40,6 +42,8 @@ vcsRoot.addProperty(Constants.USER_FOR_TAG, myUserForTag); vcsRoot.addProperty(Constants.UNCOMPRESSED_TRANSFER, String.valueOf(myUncompressed)); vcsRoot.addProperty(Constants.DETECT_SUBREPO_CHANGES, String.valueOf(myDetectSubrepoChanges)); + vcsRoot.addProperty(Constants.INCLUDE_SUBREPOS_IN_PATCH, String.valueOf(myIncludeSubreposInPatch)); + vcsRoot.addProperty(Constants.USE_ARCHIVE_FOR_PATCH, String.valueOf(myUseArchiveForPatch)); if (myCloneRepositoryTo != null) vcsRoot.addProperty(Constants.SERVER_CLONE_PATH_PROP, String.valueOf(myCloneRepositoryTo.getAbsolutePath())); vcsRoot.addProperty(Constants.USE_TAGS_AS_BRANCHES, String.valueOf(myTagsAsBranches)); @@ -132,6 +136,18 @@ } + public VcsRootBuilder withSubreposInPatch(boolean includeSubreposInPatch) { + myIncludeSubreposInPatch = includeSubreposInPatch; + return this; + } + + + public VcsRootBuilder withArchiveForPatch(boolean useArchiveForPatch) { + myUseArchiveForPatch = useArchiveForPatch; + return this; + } + + public VcsRootBuilder withTagsEnabled(boolean useTagsAsBranches) { myTagsAsBranches = useTagsAsBranches; return this;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/unicodeFileName/hg/branch Tue Dec 17 13:38:24 2013 +0100 @@ -0,0 +1,1 @@ +default
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/unicodeFileName/hg/cache/branchheads-served Tue Dec 17 13:38:24 2013 +0100 @@ -0,0 +1,2 @@ +51677e03dc198765c6e6b5e8698c0855c59ddfac 1 +51677e03dc198765c6e6b5e8698c0855c59ddfac default
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/unicodeFileName/hg/cache/tags Tue Dec 17 13:38:24 2013 +0100 @@ -0,0 +1,2 @@ +0 54044489d391d1f8af85d28a01ca66c399f5c634 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/unicodeFileName/hg/hgrc Tue Dec 17 13:38:24 2013 +0100 @@ -0,0 +1,2 @@ +[paths] +default = http://munit-039:8000/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/unicodeFileName/hg/last-message.txt Tue Dec 17 13:38:24 2013 +0100 @@ -0,0 +1,1 @@ +change strange file \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/unicodeFileName/hg/requires Tue Dec 17 13:38:24 2013 +0100 @@ -0,0 +1,4 @@ +dotencode +fncache +revlogv1 +store
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/unicodeFileName/hg/store/fncache Tue Dec 17 13:38:24 2013 +0100 @@ -0,0 +1,1 @@ +data/à.iml.i
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/unicodeFileName/hg/store/phaseroots Tue Dec 17 13:38:24 2013 +0100 @@ -0,0 +1,1 @@ +1 51677e03dc198765c6e6b5e8698c0855c59ddfac
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/unicodeFileName/hg/undo.branch Tue Dec 17 13:38:24 2013 +0100 @@ -0,0 +1,1 @@ +default \ No newline at end of file