# HG changeset patch # User Dmitry Neverov # Date 1326378067 -14400 # Node ID e0464f11206c2324f174f8384665f3624837e8ae # Parent fd56b95248342ba421aca7269f4132b791c53048 TW-19698 Handle unrelated repositories When repository becames unrelated - clone it in different directory on the server. When changes are collected and any of revisions is from unrelated repository - return empty changes collection. diff -r fd56b9524834 -r e0464f11206c mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManager.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManager.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManager.java Thu Jan 12 18:21:07 2012 +0400 @@ -7,13 +7,12 @@ import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import static jetbrains.buildServer.util.FileUtil.isEmptyDir; + /** * Manages local mirrors of remote repositories. * Each unique url get unique local mirror. Each mirror is used for one url only. @@ -73,6 +72,42 @@ } + /** + * Forget specified dir. After call to this method with non-empty dir, + * all urls which were mapped to this dir will be mapped to another. + * If dir is empty, subsequent call getMirrorDir(dir) will return the + * same dir. + * + * @param dir dir of interest + */ + public void forgetDir(@NotNull final File dir) { + myLock.writeLock().lock(); + try { + removeMappingsToDir(dir); + saveMappingToFile(); + } finally { + myLock.writeLock().unlock(); + } + } + + private void removeMappingsToDir(@NotNull final File dir) { + Set keysToRemove = getUrlsMappedToDir(dir); + for (String key : keysToRemove) { + myMirrors.remove(key); + } + } + + private Set getUrlsMappedToDir(@NotNull final File dir) { + Set urlsMappedToDir = new HashSet(); + for (Map.Entry entry : myMirrors.entrySet()) { + File f = entry.getValue(); + if (f.equals(dir)) + urlsMappedToDir.add(entry.getKey()); + } + return urlsMappedToDir; + } + + //for tests only void setHashCalculator(HashCalculator hash) { myHash = hash; @@ -123,7 +158,7 @@ try { String dirName = MIRROR_DIR_PREFIX + hash(normalize(url)); File result = PathUtil.getCanonicalFile(new File(myRootDir, dirName)); - while (isUsedForOtherUrl(result, url)) { + while (isUsedForOtherUrl(result, url) || !isEmptyDir(result)) { dirName = MIRROR_DIR_PREFIX + hash(result.getName()); result = PathUtil.getCanonicalFile(new File(myRootDir, dirName)); } diff -r fd56b9524834 -r e0464f11206c mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ArchiveCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ArchiveCommand.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ArchiveCommand.java Thu Jan 12 18:21:07 2012 +0400 @@ -46,7 +46,7 @@ setDestination(cli); CommandResult res = runCommand(cli); - failIfNotEmptyStdErr(cli, res); + failIfNotEmptyStdErr(res); deleteHgArchival(); } diff -r fd56b9524834 -r e0464f11206c mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java Thu Jan 12 18:21:07 2012 +0400 @@ -104,9 +104,9 @@ return CommandUtil.runCommand(cli, executionTimeout, Collections.emptySet()); } - protected void failIfNotEmptyStdErr(@NotNull GeneralCommandLine cli, @NotNull CommandResult res) throws VcsException { + protected void failIfNotEmptyStdErr(@NotNull CommandResult res) throws VcsException { if (!StringUtil.isEmpty(res.getStderr())) { - CommandUtil.commandFailed(cli.getCommandLineString(), res); + CommandUtil.commandFailed(res); } } } diff -r fd56b9524834 -r e0464f11206c mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java Thu Jan 12 18:21:07 2012 +0400 @@ -1,9 +1,11 @@ package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; import jetbrains.buildServer.ExecResult; +import jetbrains.buildServer.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; import java.util.Set; import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandUtil.removePrivateData; @@ -15,10 +17,16 @@ */ public class CommandResult { + private final String myCommand; private final ExecResult myDelegate; private final Set myPrivateData; - public CommandResult(@NotNull final ExecResult execResult, @NotNull final Set privateData) { + public CommandResult(@NotNull final String command, @NotNull final ExecResult execResult) { + this(command, execResult, Collections.emptySet()); + } + + public CommandResult(@NotNull final String command, @NotNull final ExecResult execResult, @NotNull final Set privateData) { + myCommand = command; myDelegate = execResult; myPrivateData = privateData; } @@ -41,4 +49,54 @@ public int getExitCode() { return myDelegate.getExitCode(); } + + @NotNull + public String getCommand() { + return myCommand; + } + + public boolean isFailure() { + return getExitCode() != 0 || getException() != null; + } + + public boolean hasImportantException() { + Throwable exception = getException(); + return exception instanceof NullPointerException; + } + + @Nullable + public String getExceptionMessage() { + Throwable exception = getException(); + if (exception == null) + return null; + String message = exception.getMessage(); + if (message == null) + message = exception.getClass().getName(); + return message; + } + + public void rethrowDetectedError() throws VcsException { + if (!isFailure()) + return; + String stderr = getStderr(); + checkUnrelatedRepository(stderr); + checkUnknownRevision(stderr); + } + + private void checkUnrelatedRepository(@NotNull final String stderr) throws UnrelatedRepositoryException { + if (stderr.contains("abort: repository is unrelated")) + throw new UnrelatedRepositoryException(); + } + + private void checkUnknownRevision(@NotNull final String stderr) throws UnknownRevisionException { + final String message = "abort: unknown revision '"; + int idx = stderr.indexOf(message); + if (idx != -1) { + int startIdx = idx + message.length(); + int endIdx = stderr.indexOf("'", startIdx); + String revision = stderr.substring(startIdx, endIdx); + throw new UnknownRevisionException(revision); + } + } + } diff -r fd56b9524834 -r e0464f11206c mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandUtil.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandUtil.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandUtil.java Thu Jan 12 18:21:07 2012 +0400 @@ -22,7 +22,6 @@ import jetbrains.buildServer.util.StringUtil; import jetbrains.buildServer.vcs.VcsException; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.Set; @@ -30,55 +29,34 @@ public class CommandUtil { private static final int DEFAULT_COMMAND_TIMEOUT_SEC = 3600; - public static void checkCommandFailed(@NotNull String cmdName, @NotNull CommandResult res) throws VcsException { - if (res.getExitCode() != 0 || res.getException() != null) - commandFailed(cmdName, res); + public static void checkCommandFailed(@NotNull final CommandResult res) throws VcsException { + res.rethrowDetectedError(); + if (res.isFailure()) + commandFailed(res); if (res.getStderr().length() > 0) { - Loggers.VCS.warn("Error output produced by: " + cmdName); + Loggers.VCS.warn("Error output produced by: " + res.getCommand()); Loggers.VCS.warn(res.getStderr()); } } - public static void commandFailed(final String cmdName, final CommandResult res) throws VcsException { - final String message = createCommandLogMessage(cmdName, res); + public static void commandFailed(@NotNull final CommandResult res) throws VcsException { + final String message = createCommandLogMessage(res); Loggers.VCS.warn(message); - if (hasImportantException(res)) - Loggers.VCS.error("Error during executing '" + cmdName + "'", res.getException()); + if (res.hasImportantException()) + Loggers.VCS.error("Error during executing '" + res.getCommand() + "'", res.getException()); throw new VcsException(message); } - private static String createCommandLogMessage(final String cmdName, final CommandResult res) { + private static String createCommandLogMessage(@NotNull final CommandResult res) { String stderr = res.getStderr(); String stdout = res.getStdout(); - String exceptionMessage = getExceptionMessage(res); - return "'" + cmdName + "' command failed.\n" + + String exceptionMessage = res.getExceptionMessage(); + return "'" + res.getCommand() + "' command failed.\n" + (!StringUtil.isEmpty(stdout) ? "stdout: " + stdout + "\n" : "") + (!StringUtil.isEmpty(stderr) ? "stderr: " + stderr + "\n" : "") + (exceptionMessage != null ? "exception: " + exceptionMessage : ""); } - @Nullable - private static String getExceptionMessage(CommandResult result) { - Throwable exception = result.getException(); - String message = null; - if (exception != null) { - message = exception.getMessage(); - if (message == null) { - message = exception.getClass().getName(); - } - } - return message; - } - - private static boolean hasImportantException(CommandResult result) { - Throwable exception = result.getException(); - if (exception != null) { - return exception instanceof NullPointerException; - } else { - return false; - } - } - public static CommandResult runCommand(@NotNull GeneralCommandLine cli) throws VcsException { return runCommand(cli, DEFAULT_COMMAND_TIMEOUT_SEC, Collections.emptySet()); } @@ -100,7 +78,7 @@ Loggers.VCS.debug("Run command: " + cmdStr); CommandResult res = run(cli, executionTimeout, cmdStr,privateData); if (checkFailure) - CommandUtil.checkCommandFailed(cmdStr, res); + CommandUtil.checkCommandFailed(res); Loggers.VCS.debug("Command " + cmdStr + " output:\n" + res.getStdout()); return res; } @@ -118,7 +96,7 @@ Loggers.VCS.debug("Command " + cmdStr + " took " + duration + "ms"); } }); - return new CommandResult(res, privateData); + return new CommandResult(cmdStr, res, privateData); } public static String removePrivateData(final String str, final Set privateData) { diff -r fd56b9524834 -r e0464f11206c mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java Thu Jan 12 18:21:07 2012 +0400 @@ -63,7 +63,7 @@ cli.addParameter(myRevisionNumber.toString()); } CommandResult res = runCommand(cli); - failIfNotEmptyStdErr(cli, res); + failIfNotEmptyStdErr(res); String output = res.getStdout().trim(); return output.contains(" ") ? output.substring(0, output.indexOf(" ")) : output; } diff -r fd56b9524834 -r e0464f11206c mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java Thu Jan 12 18:21:07 2012 +0400 @@ -42,6 +42,7 @@ GeneralCommandLine cli = createCommandLine(); cli.addParameter("pull"); cli.addParameter(myPullUrl); - runCommand(cli, timeout); + CommandResult result = CommandUtil.runCommand(cli, timeout, getPrivateData(), false); + CommandUtil.checkCommandFailed(result); } } diff -r fd56b9524834 -r e0464f11206c mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommand.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommand.java Thu Jan 12 18:21:07 2012 +0400 @@ -43,6 +43,6 @@ } cli.addParameter(getSettings().getRepositoryUrlWithCredentials()); CommandResult res = runCommand(cli); - failIfNotEmptyStdErr(cli, res); + failIfNotEmptyStdErr(res); } } diff -r fd56b9524834 -r e0464f11206c mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnknownRevisionException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnknownRevisionException.java Thu Jan 12 18:21:07 2012 +0400 @@ -0,0 +1,22 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; + +import jetbrains.buildServer.vcs.VcsException; +import org.jetbrains.annotations.NotNull; + +/** + * @author dmitry.neverov + */ +public class UnknownRevisionException extends VcsException { + + private final String myRevision; + + public UnknownRevisionException(@NotNull final String revision) { + myRevision = revision; + } + + @NotNull + public String getRevision() { + return myRevision; + } + +} diff -r fd56b9524834 -r e0464f11206c mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnrelatedRepositoryException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnrelatedRepositoryException.java Thu Jan 12 18:21:07 2012 +0400 @@ -0,0 +1,10 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; + +import jetbrains.buildServer.vcs.VcsException; + +/** + * @author dmitry.neverov + */ +public class UnrelatedRepositoryException extends VcsException { + +} diff -r fd56b9524834 -r e0464f11206c mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VcsRootCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VcsRootCommand.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VcsRootCommand.java Thu Jan 12 18:21:07 2012 +0400 @@ -41,6 +41,9 @@ return CommandUtil.runCommand(cli, executionTimeout, getPrivateData()); } + protected CommandResult runCommand(@NotNull GeneralCommandLine cli, int executionTimeout, boolean checkFailure) throws VcsException { + return CommandUtil.runCommand(cli, executionTimeout, getPrivateData(), checkFailure); + } protected CommandResult runCommand(@NotNull GeneralCommandLine cli, boolean checkFailure) throws VcsException { return CommandUtil.runCommand(cli, getPrivateData(), checkFailure); diff -r fd56b9524834 -r e0464f11206c mercurial-server/src/META-INF/build-server-plugin-mercurial.xml --- a/mercurial-server/src/META-INF/build-server-plugin-mercurial.xml Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-server/src/META-INF/build-server-plugin-mercurial.xml Thu Jan 12 18:21:07 2012 +0400 @@ -4,6 +4,6 @@ - + diff -r fd56b9524834 -r e0464f11206c mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CommandFactory.java --- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CommandFactory.java Wed Jan 11 15:09:52 2012 +0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -package jetbrains.buildServer.buildTriggers.vcs.mercurial; - -import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.LogCommand; -import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.MergeBaseCommand; -import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings; -import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.VersionCommand; -import jetbrains.buildServer.util.FileUtil; -import jetbrains.buildServer.vcs.VcsException; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.io.IOException; - -/** - * @author dmitry.neverov - */ -public final class CommandFactory { - - //hg version which supports revsets - private final static HgVersion REVSET_HG_VERSION = new HgVersion(1, 7, 0); - private final static String LOG_TEMPLATE_NAME = "log.template"; - - private final File myDefaultWorkingDir; - private final File myLogTemplate; - - - public CommandFactory(@NotNull final ServerPluginConfig config) throws IOException { - myDefaultWorkingDir = config.getCachesDir(); - myLogTemplate = createLogTemplate(config.getPluginDataDir()); - } - - - @NotNull - public MergeBaseCommand createMergeBase(@NotNull Settings settings, @NotNull File workingDir) throws VcsException { - HgVersion hgVersion = getHgVersion(settings); - if (hgVersion.isEqualsOrGreaterThan(REVSET_HG_VERSION)) - return new MergeBaseWithRevsets(settings, workingDir, this); - else - return new MergeBaseNoRevsets(settings, workingDir, this); - } - - - @NotNull - public LogCommand createLog(@NotNull final Settings settings, @NotNull final File workingDir) { - return new LogCommand(settings, workingDir, myLogTemplate); - } - - @NotNull - public CollectChangesCommand getCollectChangesCommand(@NotNull final Settings settings, @NotNull final File workingDir) throws VcsException { - HgVersion hgVersion = getHgVersion(settings); - if (hgVersion.isEqualsOrGreaterThan(REVSET_HG_VERSION)) { - return new CollectChangesWithRevsets(settings, workingDir, myLogTemplate); - } else { - return new CollectChangesNoRevsets(settings, workingDir, myLogTemplate); - } - } - - private File createLogTemplate(@NotNull final File templateFileDir) throws IOException { - File template = new File(templateFileDir, LOG_TEMPLATE_NAME); - if (!template.exists()) { - FileUtil.copyResource(CommandFactory.class, "/buildServerResources/log.template", template); - } - return template; - } - - private HgVersion getHgVersion(@NotNull final Settings settings) throws VcsException { - VersionCommand versionCommand = new VersionCommand(settings, myDefaultWorkingDir); - return versionCommand.execute(); - } -} diff -r fd56b9524834 -r e0464f11206c mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CommandFactoryImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CommandFactoryImpl.java Thu Jan 12 18:21:07 2012 +0400 @@ -0,0 +1,70 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial; + +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.LogCommand; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.MergeBaseCommand; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.VersionCommand; +import jetbrains.buildServer.util.FileUtil; +import jetbrains.buildServer.vcs.VcsException; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; + +/** + * @author dmitry.neverov + */ +public final class CommandFactoryImpl implements CommandFactory { + + //hg version which supports revsets + private final static HgVersion REVSET_HG_VERSION = new HgVersion(1, 7, 0); + private final static String LOG_TEMPLATE_NAME = "log.template"; + + private final File myDefaultWorkingDir; + private final File myLogTemplate; + + + public CommandFactoryImpl(@NotNull final ServerPluginConfig config) throws IOException { + myDefaultWorkingDir = config.getCachesDir(); + myLogTemplate = createLogTemplate(config.getPluginDataDir()); + } + + + @NotNull + public MergeBaseCommand createMergeBase(@NotNull Settings settings, @NotNull File workingDir) throws VcsException { + HgVersion hgVersion = getHgVersion(settings); + if (hgVersion.isEqualsOrGreaterThan(REVSET_HG_VERSION)) + return new MergeBaseWithRevsets(settings, workingDir, this); + else + return new MergeBaseNoRevsets(settings, workingDir, this); + } + + + @NotNull + public LogCommand createLog(@NotNull final Settings settings, @NotNull final File workingDir) { + return new LogCommand(settings, workingDir, myLogTemplate); + } + + @NotNull + public CollectChangesCommand getCollectChangesCommand(@NotNull final Settings settings, @NotNull final File workingDir) throws VcsException { + HgVersion hgVersion = getHgVersion(settings); + if (hgVersion.isEqualsOrGreaterThan(REVSET_HG_VERSION)) { + return new CollectChangesWithRevsets(settings, workingDir, myLogTemplate); + } else { + return new CollectChangesNoRevsets(settings, workingDir, myLogTemplate); + } + } + + private File createLogTemplate(@NotNull final File templateFileDir) throws IOException { + File template = new File(templateFileDir, LOG_TEMPLATE_NAME); + if (!template.exists()) { + FileUtil.copyResource(CommandFactoryImpl.class, "/buildServerResources/log.template", template); + } + return template; + } + + private HgVersion getHgVersion(@NotNull final Settings settings) throws VcsException { + VersionCommand versionCommand = new VersionCommand(settings, myDefaultWorkingDir); + return versionCommand.execute(); + } +} diff -r fd56b9524834 -r e0464f11206c mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java --- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Thu Jan 12 18:21:07 2012 +0400 @@ -457,8 +457,14 @@ try { if (Settings.isValidRepository(workingDir)) { if (!isChangeSetExist(settings, workingDir, cset)) { - PullCommand pull = new PullCommand(settings, workingDir); - pull.execute(myConfig.getPullTimeout()); + try { + PullCommand pull = new PullCommand(settings, workingDir); + pull.execute(myConfig.getPullTimeout()); + } catch (UnrelatedRepositoryException e) { + Loggers.VCS.warn("Repository at " + settings.getRepository() + " is unrelated, clone it again"); + myMirrorManager.forgetDir(workingDir); + syncRepository(settings, cset); + } } } else { CloneCommand cl = new CloneCommand(settings, workingDir); @@ -475,8 +481,14 @@ lockWorkDir(workingDir); try { if (Settings.isValidRepository(workingDir)) { - PullCommand pull = new PullCommand(settings, workingDir); - pull.execute(myConfig.getPullTimeout()); + try { + PullCommand pull = new PullCommand(settings, workingDir); + pull.execute(myConfig.getPullTimeout()); + } catch (UnrelatedRepositoryException e) { + Loggers.VCS.warn("Repository at " + settings.getRepository() + " is unrelated, clone it again"); + myMirrorManager.forgetDir(workingDir); + syncRepository(settings); + } } else { CloneCommand cl = new CloneCommand(settings, workingDir); cl.setUpdateWorkingDir(false); @@ -654,18 +666,22 @@ String toCommit = new ChangeSetRevision(toVersion).getId(); File workingDir = getWorkingDir(settings); CollectChangesCommand log = myCommandFactory.getCollectChangesCommand(settings, workingDir); - List changesets = log.execute(fromCommit, toCommit); - Iterator iter = changesets.iterator(); - while (iter.hasNext()) { - ChangeSet cset = iter.next(); - if (cset.getId().equals(fromCommit)) - iter.remove();//skip already reported changes + try { + List changesets = log.execute(fromCommit, toCommit); + Iterator iter = changesets.iterator(); + while (iter.hasNext()) { + ChangeSet cset = iter.next(); + if (cset.getId().equals(fromCommit)) + iter.remove();//skip already reported changes + } + return changesets; + } catch (UnknownRevisionException e) { + Loggers.VCS.warn("Revision '" + e.getRevision() + "' is unknown, will return no changes"); + return Collections.emptyList(); } - return changesets; } - @NotNull public BuildPatchPolicy getBuildPatchPolicy() { return new BuildPatchByCheckoutRules() { diff -r fd56b9524834 -r e0464f11206c mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MergeBaseNoRevsets.java --- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MergeBaseNoRevsets.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MergeBaseNoRevsets.java Thu Jan 12 18:21:07 2012 +0400 @@ -19,9 +19,9 @@ private final Settings mySettings; private final File myWorkingDir; - private final CommandFactory myCommandFactory; + private final CommandFactoryImpl myCommandFactory; - public MergeBaseNoRevsets(@NotNull final Settings settings, @NotNull final File workingDir, @NotNull final CommandFactory commandFactory) { + public MergeBaseNoRevsets(@NotNull final Settings settings, @NotNull final File workingDir, @NotNull final CommandFactoryImpl commandFactory) { mySettings = settings; myWorkingDir = workingDir; myCommandFactory = commandFactory; diff -r fd56b9524834 -r e0464f11206c mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MergeBaseWithRevsets.java --- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MergeBaseWithRevsets.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MergeBaseWithRevsets.java Thu Jan 12 18:21:07 2012 +0400 @@ -15,9 +15,9 @@ private final Settings mySettings; private final File myWorkingDir; - private final CommandFactory myCommandFactory; + private final CommandFactoryImpl myCommandFactory; - public MergeBaseWithRevsets(@NotNull final Settings settings, @NotNull final File workingDir, @NotNull final CommandFactory commandFactory) { + public MergeBaseWithRevsets(@NotNull final Settings settings, @NotNull final File workingDir, @NotNull final CommandFactoryImpl commandFactory) { mySettings = settings; myWorkingDir = workingDir; myCommandFactory = commandFactory; diff -r fd56b9524834 -r e0464f11206c mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManagerTest.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManagerTest.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManagerTest.java Thu Jan 12 18:21:07 2012 +0400 @@ -108,4 +108,15 @@ assertTrue(manager.getMirrors().contains(mirrorDir1)); assertTrue(manager.getMirrors().contains(mirrorDir2)); } + + + public void should_be_able_to_forget_directory() throws Exception { + String url = "hg://some.org/repository"; + File mirror1 = myManager.getMirrorDir(url); + File dotHg = new File(mirror1, ".hg"); + dotHg.mkdirs(); + myManager.forgetDir(mirror1); + File mirror2 = myManager.getMirrorDir(url); + assertFalse(mirror2.equals(mirror1)); + } } diff -r fd56b9524834 -r e0464f11206c mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/UnrelatedResitoriesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/UnrelatedResitoriesTest.java Thu Jan 12 18:21:07 2012 +0400 @@ -0,0 +1,104 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial; + +import jetbrains.buildServer.TempFiles; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.UnknownRevisionException; +import jetbrains.buildServer.util.FileUtil; +import jetbrains.buildServer.vcs.CheckoutRules; +import jetbrains.buildServer.vcs.VcsException; +import jetbrains.buildServer.vcs.impl.VcsRootImpl; +import org.jetbrains.annotations.NotNull; +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; + +import static org.testng.AssertJUnit.assertEquals; +import static org.testng.AssertJUnit.assertTrue; + +/** + * @author dmitry.neverov + */ +@Test +public class UnrelatedResitoriesTest { + + private final static String CURRENT_VERSION_OF_NEW_REPO = "18:df04faa7575a"; + private MercurialVcsSupport myVcs; + private TempFiles myTempFiles; + private File myRepositoryLocation; + private VcsRootImpl myRoot; + private Mockery myContext; + private ServerPluginConfig myPluginConfig; + + @BeforeMethod + public void setUp() throws Exception { + myContext = new Mockery(); + myTempFiles = new TempFiles(); + myPluginConfig = new ServerPluginConfigBuilder() + .cachesDir(myTempFiles.createTempDir()) + .pluginDataDir(myTempFiles.createTempDir()) + .build(); + + myRepositoryLocation = myTempFiles.createTempDir(); + copyRepository(new File("mercurial-tests/testData/rep1")); + myRoot = new VcsRootBuilder().repository(myRepositoryLocation.getCanonicalPath()).build(); + } + + @AfterMethod + public void tearDown() { + myTempFiles.cleanup(); + } + + + public void should_be_able_to_sync_when_repository_became_unrelated() throws Exception { + myVcs = createVcs(); + syncRepository(); + repositoryBecamesUnrelated(); + String currentVersion = syncRepository(); + assertEquals(CURRENT_VERSION_OF_NEW_REPO, currentVersion); + } + + + public void should_return_no_changes_when_fromRevision_is_from_unrelated_repository() throws Exception { + final CommandFactory factory = myContext.mock(CommandFactory.class); + final CollectChangesCommand commandExecutedWithException = myContext.mock(CollectChangesCommand.class); + myVcs = createVcs(factory); + myContext.checking(new Expectations(){{ + allowing(factory).getCollectChangesCommand(with(any(Settings.class)), with(any(File.class))); + will(returnValue(commandExecutedWithException)); + allowing(commandExecutedWithException).execute(with(any(String.class)), with(any(String.class))); + will(throwException(new UnknownRevisionException("1234"))); + }}); + + String currentVersionOfOldRepo = syncRepository(); + repositoryBecamesUnrelated(); + String currentVersionOfNewRepo = syncRepository(); + assertTrue(myVcs.collectChanges(myRoot, currentVersionOfOldRepo, currentVersionOfNewRepo, CheckoutRules.DEFAULT).isEmpty()); + } + + + private String syncRepository() throws VcsException { + return myVcs.getCurrentVersion(myRoot); + } + + private void repositoryBecamesUnrelated() throws IOException { + FileUtil.delete(myRepositoryLocation); + copyRepository(new File("mercurial-tests/testData/rep2")); + } + + private void copyRepository(@NotNull final File src) throws IOException { + LocalRepositoryUtil.copyRepository(src, myRepositoryLocation); + } + + private MercurialVcsSupport createVcs() throws IOException { + return Util.createMercurialServerSupport(myContext, myPluginConfig); + } + + private MercurialVcsSupport createVcs(@NotNull final CommandFactory factory) throws IOException { + return Util.createMercurialServerSupport(myContext, myPluginConfig, factory); + } +} diff -r fd56b9524834 -r e0464f11206c mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Util.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Util.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Util.java Thu Jan 12 18:21:07 2012 +0400 @@ -31,6 +31,11 @@ public static MercurialVcsSupport createMercurialServerSupport(@NotNull Mockery context, ServerPluginConfig config) throws IOException { + return createMercurialServerSupport(context, config, new CommandFactoryImpl(config)); + } + + + public static MercurialVcsSupport createMercurialServerSupport(@NotNull Mockery context, @NotNull ServerPluginConfig config, @NotNull CommandFactory commandFactory) throws IOException { VcsManager vcsManager = context.mock(VcsManager.class); final SBuildServer server = context.mock(SBuildServer.class); final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); @@ -38,6 +43,6 @@ allowing(server).getExecutor(); will(returnValue(executor)); }}); EventDispatcher dispatcher = EventDispatcher.create(BuildServerListener.class); - return new MercurialVcsSupport(vcsManager, server, dispatcher, config, new ServerHgPathProvider(config), new CommandFactory(config)); + return new MercurialVcsSupport(vcsManager, server, dispatcher, config, new ServerHgPathProvider(config), commandFactory); } } diff -r fd56b9524834 -r e0464f11206c mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResultTest.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResultTest.java Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResultTest.java Thu Jan 12 18:21:07 2012 +0400 @@ -2,13 +2,15 @@ import jetbrains.buildServer.ExecResult; import jetbrains.buildServer.StreamGobbler; +import jetbrains.buildServer.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.testng.annotations.Test; import java.io.ByteArrayInputStream; -import java.util.Collections; +import java.util.Arrays; +import java.util.HashSet; -import static org.testng.AssertJUnit.assertFalse; +import static org.testng.AssertJUnit.*; /** * @author dmitry.neverov @@ -18,23 +20,93 @@ public void output_should_not_contain_private_data() { String password = "pass"; - ExecResult result = createExecResult(password, password); - CommandResult commandResult = new CommandResult(result, Collections.singleton(password)); + CommandResult commandResult = commandResultFor(execResult().withStdout(password).withStderr(password), password); assertFalse(commandResult.getStdout().contains(password)); assertFalse(commandResult.getStderr().contains(password)); } - private ExecResult createExecResult(@NotNull final String output, @NotNull final String error) { - ExecResult result = new ExecResult(); - result.setOutputGobbler(createStringGobbler(output)); - result.setErrorGobbler(createStringGobbler(error)); - return result; + @Test(expectedExceptions = UnrelatedRepositoryException.class) + public void should_detect_unrelated_repository_error() throws VcsException { + String unrelatedRepositoryStderr = "abort: repository is unrelated\n"; + CommandResult commandResult = commandResultFor(execResult().withStderr(unrelatedRepositoryStderr).withExitCode(255)); + commandResult.rethrowDetectedError(); + } + + @Test + public void should_detect_unknown_revision_error() throws VcsException { + String unknownRevision = "9c6a6b4aede0"; + String unknownRevisionError = "abort: unknown revision '" + unknownRevision + "'\n"; + CommandResult commandResult = commandResultFor(execResult().withStderr(unknownRevisionError).withExitCode(255)); + try { + commandResult.rethrowDetectedError(); + fail("unknown exception should be thrown"); + } catch (UnknownRevisionException e) { + assertEquals(unknownRevision, e.getRevision()); + } + } + + public void should_detect_failure_when_delegate_has_exception() { + CommandResult commandResult = commandResultFor(execResult().withException(new RuntimeException())); + assertTrue(commandResult.isFailure()); + } + + public void should_detect_failure_with_non_zero_exit_code() { + CommandResult commandResult = commandResultFor(execResult().withExitCode(1)); + assertTrue(commandResult.isFailure()); + } + + + ExecResultBuilder execResult() { + return new ExecResultBuilder(); + } + + CommandResult commandResultFor(ExecResultBuilder builder, String... privateData) { + return new CommandResult("", builder.build(), new HashSet(Arrays.asList(privateData))); } - private StreamGobbler createStringGobbler(@NotNull final String str) { - StreamGobbler gobbler = new StreamGobbler(new ByteArrayInputStream(str.getBytes())); - gobbler.start(); - return gobbler; + private class ExecResultBuilder { + private String myStdout = ""; + private String myStderr = ""; + private Throwable myException = null; + private int myExitCode = 0; + + ExecResultBuilder withStdout(String stdout) { + myStdout = stdout; + return this; + } + ExecResultBuilder withStderr(String stderr) { + myStderr = stderr; + return this; + } + ExecResultBuilder withException(Throwable exception) { + myException = exception; + return this; + } + ExecResultBuilder withExitCode(int exitCode) { + myExitCode = exitCode; + return this; + } + + ExecResult build() { + ExecResult result = new ExecResult(); + result.setOutputGobbler(createStringGobbler(myStdout)); + result.setErrorGobbler(createStringGobbler(myStderr)); + if (myException != null) + result.setException(myException); + if (myExitCode != 0) + result.setExitCode(myExitCode); + return result; + } + + private StreamGobbler createStringGobbler(@NotNull final String str) { + StreamGobbler gobbler = new StreamGobbler(new ByteArrayInputStream(str.getBytes())); + gobbler.start(); + try { + Thread.sleep(10);//wait for gobbler to read string + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + return gobbler; + } } - } diff -r fd56b9524834 -r e0464f11206c mercurial-tests/src/testng.xml --- a/mercurial-tests/src/testng.xml Wed Jan 11 15:09:52 2012 +0400 +++ b/mercurial-tests/src/testng.xml Thu Jan 12 18:21:07 2012 +0400 @@ -18,6 +18,7 @@ +