Mercurial > hg > mercurial
changeset 867:80d695d15cc0
Add CommitSupport
line wrap: on
line diff
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java Mon Sep 15 20:16:55 2014 +0200 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java Thu Sep 18 11:28:51 2014 +0200 @@ -151,6 +151,10 @@ return new CommitCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir); } + public AddRemoveCommand addRemove() { + return new AddRemoveCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir); + } + public String path() { return myWorkingDir.getAbsolutePath(); } @@ -163,6 +167,12 @@ return isEmptyDir(myWorkingDir); } + public boolean isBookmark(@NotNull String branch) throws VcsException { + if (branches().call().keySet().contains(branch)) + return false; + return bookmarks().call().keySet().contains(branch); + } + public void resetBookmarks() { File dotHg = new File(getWorkingDir(), ".hg"); FileUtil.delete(new File(dotHg, "bookmarks"));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/AddRemoveCommand.java Thu Sep 18 11:28:51 2014 +0200 @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2014 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 jetbrains.buildServer.vcs.VcsException; +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +public class AddRemoveCommand extends BaseCommand { + + public AddRemoveCommand(@NotNull CommandSettings commandSettings, + @NotNull String hgPath, + @NotNull File workingDir) { + super(commandSettings, hgPath, workingDir); + } + + public void call() throws VcsException { + MercurialCommandLine cmd = createCommandLine(); + cmd.addParameter("addremove"); + runCommand(cmd); + } +}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommitCommand.java Mon Sep 15 20:16:55 2014 +0200 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommitCommand.java Thu Sep 18 11:28:51 2014 +0200 @@ -42,7 +42,7 @@ return this; } - public void call() throws VcsException { + public int call() throws VcsException { MercurialCommandLine cmd = createCommandLine(); cmd.addParameter("commit"); cmd.addParameter("-S"); @@ -50,6 +50,7 @@ cmd.addParameters("-m", myCommitMessage); if (myUser != null) cmd.addParameters("--user", myUser); - runCommand(cmd); + CommandResult result = runCommand(cmd); + return result.getExitCode(); } }
--- a/mercurial-server/src/META-INF/build-server-plugin-mercurial.xml Mon Sep 15 20:16:55 2014 +0200 +++ b/mercurial-server/src/META-INF/build-server-plugin-mercurial.xml Thu Sep 18 11:28:51 2014 +0200 @@ -28,6 +28,7 @@ <bean id="commandSettingsFactory" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.ServerCommandSettingsFactory"/> <bean id="cleaner" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialCleaner"/> <bean id="mergeSupport" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialMergeSupport"/> + <bean id="commitSupport" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialCommitSupport"/> <bean class="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialCommitsInfoBuilderSupport"/> <bean class="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialModificationInfoBuilder"/>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialCommitSupport.java Thu Sep 18 11:28:51 2014 +0200 @@ -0,0 +1,206 @@ +/* + * Copyright 2000-2014 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; + +import com.intellij.openapi.diagnostic.Logger; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.HgVcsRoot; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.PushCommand; +import jetbrains.buildServer.util.FileUtil; +import jetbrains.buildServer.vcs.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.util.HashSet; +import java.util.Set; + +public class MercurialCommitSupport implements CommitSupport, MercurialServerExtension { + + private final static Logger LOG = Logger.getInstance(MercurialCommitSupport.class.getName()); + + private final MercurialVcsSupport myVcs; + private final MirrorManager myMirrorManager; + private final ServerPluginConfig myConfig; + private final HgVcsRootFactory myHgVcsRootFactory; + private final HgRepoFactory myHgRepoFactory; + + + public MercurialCommitSupport(@NotNull MercurialVcsSupport vcs, + @NotNull MirrorManager mirrorManager, + @NotNull ServerPluginConfig config, + @NotNull HgVcsRootFactory vcsRootFactory, + @NotNull HgRepoFactory hgRepoFactory) { + myVcs = vcs; + myMirrorManager = mirrorManager; + myConfig = config; + myHgVcsRootFactory = vcsRootFactory; + myHgRepoFactory = hgRepoFactory; + myVcs.addExtension(this); + } + + @NotNull + public CommitPatchBuilder getCommitPatchBuilder(@NotNull VcsRoot root) { + File patchDir; + try { + patchDir = HgFileUtil.createTempDir(); + } catch (IOException e) { + return new ErrorCommitPatchBuilder("Error while creating patch dir", e); + } + File checkoutDir; + try { + checkoutDir = HgFileUtil.createTempDir(); + } catch (IOException e) { + return new ErrorCommitPatchBuilder("Error while creating checkout dir", e); + } + + HgVcsRoot hgRoot; + try { + hgRoot = myHgVcsRootFactory.createHgRoot(root); + } catch (VcsException e) { + return new ErrorCommitPatchBuilder("Error while creating mercurial root", e); + } + return new MercurialCommitPatchBuilder(root, hgRoot, patchDir, checkoutDir); + } + + + private class MercurialCommitPatchBuilder implements CommitPatchBuilder { + private final VcsRoot myRoot; + private final HgVcsRoot myHgRoot; + private final File myPatchDir; + private final File myCheckoutDir; + private final Set<String> myCreatedFiles = new HashSet<String>(); + private final Set<String> myDeletedFiles = new HashSet<String>(); + private final Set<String> myDeletedDirs = new HashSet<String>(); + private Exception myError; + + public MercurialCommitPatchBuilder(@NotNull VcsRoot root, @NotNull HgVcsRoot hgRoot, @NotNull File patchDir, @NotNull File checkoutDir) { + myRoot = root; + myHgRoot = hgRoot; + myPatchDir = patchDir; + myCheckoutDir = checkoutDir; + } + + public void createFile(@NotNull String path, @NotNull InputStream content) { + OutputStream out = null; + try { + File f = new File(myPatchDir, path); + f.getParentFile().mkdirs(); + out = new BufferedOutputStream(new FileOutputStream(f)); + FileUtil.copy(content, out); + myCreatedFiles.add(path); + } catch (IOException e) { + LOG.error("Error while creating file " + path, e); + myError = e; + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + LOG.error(e); + } + } + } + } + + public void createDirectory(@NotNull String path) { + //ignore + } + + public void deleteFile(@NotNull String path) { + myDeletedFiles.add(path); + } + + public void deleteDirectory(@NotNull String path) { + myDeletedDirs.add(path); + } + + public void renameFile(@NotNull String oldPath, @NotNull String newPath, @NotNull InputStream content) { + myDeletedFiles.add(oldPath); + createFile(newPath, content); + } + + @Nullable + public String commit(@NotNull String userName, @NotNull String description) throws VcsException { + if (myError != null) + throw new VcsException(myError); + + + try { + RepositoryStateData state = myVcs.getCollectChangesPolicy().getCurrentState(myRoot); + String defaultBranch = state.getDefaultBranchName(); + String defaultBranchRevision = state.getBranchRevisions().get(defaultBranch); + + new CheckoutRepository(myMirrorManager, myHgRepoFactory, myConfig.getPullTimeout(), myConfig.useTagsAsBranches(), + myHgRoot, myCheckoutDir).setRevision(defaultBranchRevision).checkout(); + + HgRepo repo = myHgRepoFactory.createRepo(myHgRoot, myCheckoutDir); + for (String dir : myDeletedDirs) { + FileUtil.delete(new File(myCheckoutDir, dir)); + } + for (String f : myDeletedFiles) { + FileUtil.delete(new File(myCheckoutDir, f)); + } + for (String f : myCreatedFiles) { + FileUtil.copy(new File(myPatchDir, f), new File(myCheckoutDir, f)); + } + + repo.addRemove().call(); + int exitCode = repo.commit().by(userName).message(description).call(); + if (exitCode == 1) + return null; + + boolean bookmark = repo.isBookmark(defaultBranch); + if (bookmark) + repo.updateBookmark().name(defaultBranch).call(); + + PushCommand push = repo.push().toRepository(myHgRoot.getRepository()); + if (bookmark) + push.bookmark(defaultBranch); + push.call(); + + return repo.getWorkingDirRevision(); + } catch (Exception e) { + throw new VcsException(e); + } + } + } + + + private class ErrorCommitPatchBuilder implements CommitPatchBuilder { + private final String myMessage; + private final Exception myError; + public ErrorCommitPatchBuilder(@NotNull String message, @NotNull Exception error) { + myMessage = message; + myError = error; + } + + public void createFile(@NotNull String path, @NotNull InputStream content) { + } + public void createDirectory(@NotNull String path) { + } + public void deleteFile(@NotNull String path) { + } + public void deleteDirectory(@NotNull String path) { + } + public void renameFile(@NotNull String oldPath, @NotNull String newPath, @NotNull InputStream content) { + } + @Nullable + public String commit(@NotNull String userName, @NotNull String description) throws VcsException { + throw new VcsException(myMessage, myError); + } + } +}
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialMergeSupport.java Mon Sep 15 20:16:55 2014 +0200 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialMergeSupport.java Thu Sep 18 11:28:51 2014 +0200 @@ -105,7 +105,7 @@ .setBranch(dstBranch).checkout(); HgRepo repo = myHgRepoFactory.createRepo(hgRoot, tmpDir); - boolean bookmark = isBookmark(repo, dstBranch); + boolean bookmark = repo.isBookmark(dstBranch); try { repo.merge().withMergeTool(myConfig.getMergeTool()).revision(srcRevision).call(); } catch (MergeConflictException e) { @@ -162,13 +162,6 @@ } - private boolean isBookmark(@NotNull HgRepo repo, @NotNull String branch) throws VcsException { - if (repo.branches().call().keySet().contains(branch)) - return false; - return repo.bookmarks().call().keySet().contains(branch); - } - - private List<String> detectConflicts(@NotNull HgVcsRoot root, @NotNull String prefix, @NotNull HgRepo repo) throws VcsException { List<String> conflicts = new ArrayList<String>(); for (String conflict : repo.resolve().call()) {
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/BaseMercurialTestCase.java Mon Sep 15 20:16:55 2014 +0200 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/BaseMercurialTestCase.java Thu Sep 18 11:28:51 2014 +0200 @@ -19,9 +19,15 @@ import jetbrains.buildServer.TempFiles; import jetbrains.buildServer.serverSide.BasePropertiesModel; import jetbrains.buildServer.serverSide.TeamCityProperties; +import org.jetbrains.annotations.NotNull; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; +import java.io.File; +import java.io.IOException; + +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.Util.copyRepository; + public class BaseMercurialTestCase { protected TempFiles myTempFiles; @@ -35,4 +41,12 @@ public void tearDown() { myTempFiles.cleanup(); } + + + @NotNull + protected File createRepo(@NotNull String testRepoPath) throws IOException { + File result = myTempFiles.createTempDir(); + copyRepository(new File(testRepoPath), result); + return result; + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialCommitSupportTest.java Thu Sep 18 11:28:51 2014 +0200 @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2014 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; + +import jetbrains.buildServer.vcs.*; +import org.jetbrains.annotations.NotNull; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.util.List; + +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialSupportBuilder.mercurialSupport; +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsChangeMatcher.vcsChange; +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsRootBuilder.vcsRoot; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.testng.AssertJUnit.assertEquals; + +@Test +public class MercurialCommitSupportTest extends BaseMercurialTestCase { + + private MercurialVcsSupport myVcs; + private MercurialCommitSupport myCommitSupport; + private File myRepo; + + @Override + @BeforeMethod + public void setUp() throws Exception { + super.setUp(); + + myRepo = createRepo("mercurial-tests/testData/commit"); + ServerPluginConfig pluginConfig = new ServerPluginConfigBuilder() + .cachesDir(myTempFiles.createTempDir()) + .build(); + MercurialSupportBuilder mercurialBuilder = mercurialSupport().withConfig(pluginConfig); + myVcs = mercurialBuilder.build(); + myCommitSupport = new MercurialCommitSupport(myVcs, myVcs.getMirrorManager(), pluginConfig, + mercurialBuilder.getHgRootFactory(), mercurialBuilder.getHgRepoFactory()); + } + + + public void test_commit() throws Exception { + VcsRoot root = vcsRoot().withUrl(myRepo).build(); + RepositoryStateData beforeCommit = myVcs.getCollectChangesPolicy().getCurrentState(root); + + String description = "Test commit support"; + String author = "Joe Doe <joe@some.org>"; + CommitPatchBuilder patchBuilder = myCommitSupport.getCommitPatchBuilder(root); + patchBuilder.deleteDirectory("dir"); + patchBuilder.deleteFile("x"); + patchBuilder.createFile("a/b/c", stream("test")); + patchBuilder.renameFile("y", "z", stream("rename")); + String revision = patchBuilder.commit(author, description); + + RepositoryStateData afterCommit = myVcs.getCollectChangesPolicy().getCurrentState(root); + assertEquals(revision, afterCommit.getBranchRevisions().get(afterCommit.getDefaultBranchName())); + + List<ModificationData> changes = myVcs.getCollectChangesPolicy().collectChanges(root, beforeCommit, afterCommit, CheckoutRules.DEFAULT); + ModificationData m = changes.get(0); + assertEquals(description, m.getDescription()); + assertEquals(author, m.getUserName()); + + assertThat(m.getChanges(), hasItem(vcsChange().file("x").type(VcsChangeInfo.Type.REMOVED))); + assertThat(m.getChanges(), hasItem(vcsChange().file("dir/x").type(VcsChangeInfo.Type.REMOVED))); + assertThat(m.getChanges(), hasItem(vcsChange().file("a/b/c").type(VcsChangeInfo.Type.ADDED))); + assertThat(m.getChanges(), hasItem(vcsChange().file("y").type(VcsChangeInfo.Type.REMOVED))); + assertThat(m.getChanges(), hasItem(vcsChange().file("z").type(VcsChangeInfo.Type.ADDED))); + } + + + @NotNull + private InputStream stream(@NotNull String content) { + return new ByteArrayInputStream(content.getBytes()); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/VcsChangeMatcher.java Thu Sep 18 11:28:51 2014 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2014 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; + +import jetbrains.buildServer.vcs.VcsChange; +import jetbrains.buildServer.vcs.VcsChangeInfo; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; +import org.jetbrains.annotations.NotNull; + +public class VcsChangeMatcher extends TypeSafeMatcher<VcsChange> { + + private String myFileName; + private VcsChangeInfo.Type myType; + + public static VcsChangeMatcher vcsChange() { + return new VcsChangeMatcher(); + } + + public VcsChangeMatcher file(@NotNull String fileName) { + myFileName = fileName; + return this; + } + + public VcsChangeMatcher type(@NotNull VcsChangeInfo.Type type) { + myType = type; + return this; + } + + @Override + public boolean matchesSafely(VcsChange vcsChange) { + if (myFileName != null && !myFileName.equals(vcsChange.getFileName())) + return false; + if (myType != null && !myType.equals(vcsChange.getType())) + return false; + return true; + } + + public void describeTo(Description description) { + description.appendText("change"); + if (myFileName != null) + description.appendText(" in file ").appendValue(myFileName); + if (myType != null) + description.appendText(" of type ").appendValue(myType); + } +}
--- a/mercurial-tests/src/testng-via-cmd.xml Mon Sep 15 20:16:55 2014 +0200 +++ b/mercurial-tests/src/testng-via-cmd.xml Thu Sep 18 11:28:51 2014 +0200 @@ -41,6 +41,7 @@ <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialModificationInfoBuilderTest"/> <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialUrlSupportTest"/> <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.PurgeTest"/> + <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialCommitSupportTest"/> </classes> </test> </suite>
--- a/mercurial-tests/src/testng.xml Mon Sep 15 20:16:55 2014 +0200 +++ b/mercurial-tests/src/testng.xml Thu Sep 18 11:28:51 2014 +0200 @@ -56,6 +56,7 @@ <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialUrlSupportTest"/> <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommitsAndMountPointsCommandParserTest"/> <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.PurgeTest"/> + <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialCommitSupportTest"/> </classes> </test> </suite>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/commit/hg/cache/branchheads-served Thu Sep 18 11:28:51 2014 +0200 @@ -0,0 +1,2 @@ +88465be0826dd7e6dfe61c073ac87206183c5fa4 0 +88465be0826dd7e6dfe61c073ac87206183c5fa4 default
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/commit/hg/last-message.txt Thu Sep 18 11:28:51 2014 +0200 @@ -0,0 +1,1 @@ +initial \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/commit/hg/requires Thu Sep 18 11:28:51 2014 +0200 @@ -0,0 +1,3 @@ +revlogv1 +store +fncache
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/commit/hg/store/fncache Thu Sep 18 11:28:51 2014 +0200 @@ -0,0 +1,3 @@ +data/dir/x.i +data/x.i +data/y.i
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/commit/hg/store/phaseroots Thu Sep 18 11:28:51 2014 +0200 @@ -0,0 +1,1 @@ +1 88465be0826dd7e6dfe61c073ac87206183c5fa4
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/testData/commit/hg/undo.branch Thu Sep 18 11:28:51 2014 +0200 @@ -0,0 +1,1 @@ +default \ No newline at end of file