Mercurial > hg > mercurial
view mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialIncludeRuleUpdater.java @ 304:e9cdb499350d remote-run/subrepos
Do clean checkout if subrepository URL changed
Hg cannot do update correctly in this situation. The only
thing that helps is a clean checkout.
author | Dmitry Neverov <dmitry.neverov@jetbrains.com> |
---|---|
date | Fri, 09 Sep 2011 11:47:59 +0400 |
parents | |
children | 659287a241c2 |
line wrap: on
line source
package jetbrains.buildServer.buildTriggers.vcs.mercurial; import com.intellij.openapi.util.Pair; import jetbrains.buildServer.agent.AgentRunningBuild; import jetbrains.buildServer.agent.BuildProgressLogger; import jetbrains.buildServer.agent.vcs.IncludeRuleUpdater; import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*; import jetbrains.buildServer.util.FileUtil; import jetbrains.buildServer.vcs.IncludeRule; import jetbrains.buildServer.vcs.VcsException; import jetbrains.buildServer.vcs.VcsRoot; import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandUtil.removePrivateData; /** * @author dmitry.neverov */ public class MercurialIncludeRuleUpdater implements IncludeRuleUpdater { private final MirrorManager myMirrorManager; private final HgPathProvider myHgPathProvider; private final VcsRoot myRoot; private final Settings mySettings; private final String myToVersion; private final BuildProgressLogger myLogger; private final boolean myUseLocalMirrors; public MercurialIncludeRuleUpdater(@NotNull final MirrorManager mirrorManager, @NotNull final HgPathProvider hgPathProvider, @NotNull final VcsRoot root, @NotNull final String toVersion, @NotNull final AgentRunningBuild build) { myMirrorManager = mirrorManager; myHgPathProvider = hgPathProvider; myRoot = root; mySettings = new Settings(myHgPathProvider, myRoot); myToVersion = toVersion; myLogger = build.getBuildLogger(); myUseLocalMirrors = isUseLocalMirrors(build); } public void process(@NotNull IncludeRule rule, @NotNull File workingDir) throws VcsException { try { checkRuleIsValid(rule); if (myUseLocalMirrors) updateLocalMirror(); updateRepository(workingDir); updateWorkingDir(workingDir); } catch (Exception e) { throwVcsException(e); } } public void dispose() throws VcsException { } private void throwVcsException(Exception e) throws VcsException { if (e instanceof VcsException) throw (VcsException) e; else throw new VcsException(e); } private boolean isUseLocalMirrors(AgentRunningBuild build) { String value = build.getSharedConfigParameters().get("teamcity.hg.use.local.mirrors"); return "true".equals(value); } private void initRepository(Settings settings, File workingDir, boolean useLocalMirrors) throws VcsException { try { String defaultPullUrl = getDefaultPullUrl(settings, useLocalMirrors); myLogger.message("Init repository at " + workingDir.getAbsolutePath() + ", remote repository is " + removePrivateData(defaultPullUrl, Collections.singleton(settings.getPassword()))); new Init(settings, workingDir, defaultPullUrl).execute(); } catch (IOException e) { throw new VcsException("Error while initializing repository at " + workingDir.getAbsolutePath(), e); } } private void updateRepository(File workingDir) throws VcsException, IOException { if (!Settings.isValidRepository(workingDir)) { initRepository(mySettings, workingDir, myUseLocalMirrors); } else { ensureUseRightRepository(workingDir); } String defaultPullUrl = getDefaultPullUrl(mySettings, myUseLocalMirrors); myLogger.message("Start pulling changes from " + removePrivateData(defaultPullUrl, Collections.singleton(mySettings.getPassword()))); new PullCommand(mySettings, workingDir).execute(); myLogger.message("Changes successfully pulled"); } private void ensureUseRightRepository(File workingDir) throws VcsException { boolean clonedFromWrongRepository = myUseLocalMirrors && !isClonedFromLocalMirror(workingDir) || !myUseLocalMirrors && isClonedFromLocalMirror(workingDir); if (clonedFromWrongRepository) { String rightRepository = myUseLocalMirrors ? "local mirror" : "remote repository"; String wrongRepository = myUseLocalMirrors ? "remote repository" : "local mirror"; myLogger.message("Repository in working directory is cloned from " + wrongRepository + ", clone it from " + rightRepository); FileUtil.delete(workingDir); initRepository(mySettings, workingDir, myUseLocalMirrors); } } private void updateLocalMirror() throws VcsException, IOException { Settings settings = new Settings(myHgPathProvider, myRoot); File mirrorDir = myMirrorManager.getMirrorDir(settings.getRepositoryUrl()); myLogger.message("Update local mirror at " + mirrorDir); if (!Settings.isValidRepository(mirrorDir)) { initRepository(settings, mirrorDir, false); } final String defaultPullUrl = getDefaultPullUrl(settings, true); myLogger.message("Start pulling changes from " + removePrivateData(defaultPullUrl, Collections.singleton(settings.getPassword()))); new PullCommand(settings, mirrorDir).execute(); myLogger.message("Local mirror changes successfully pulled"); } private void updateWorkingDir(@NotNull final File workingDir) throws VcsException, IOException { String workingDirRevision = getWorkingDirRevision(mySettings, workingDir); if (isInitialClone(workingDirRevision)) { doUpdateWorkingDir(workingDir); } else { Map<String, Pair<String, String>> currentSubrepos = getSubrepositories(workingDir, workingDirRevision); if (currentSubrepos.isEmpty()) { doUpdateWorkingDir(workingDir); } else { Map<String, Pair<String, String>> toVersionSubrepos = getSubrepositories(workingDir, myToVersion); Map<String, Pair<String, String>> subrepositoriesWithChangedUrls = getSubrepositoriesWithChangedUrls(currentSubrepos, toVersionSubrepos); if (subrepositoriesWithChangedUrls.isEmpty()) { doUpdateWorkingDir(workingDir); } else { myLogger.warning("URLs of subrepositories were changed, do clean checkout"); FileUtil.delete(workingDir); updateRepository(workingDir); doUpdateWorkingDir(workingDir); } } } } /*returns map: relative path -> (repository url, repository revision)*/ private Map<String, Pair<String, String>> getSubrepositories(@NotNull final File workingDir, @NotNull final String revision) throws VcsException, IOException { CatCommand cc = new CatCommand(mySettings, workingDir); cc.setRevId(revision); try { File parentDir = cc.execute(Arrays.asList(".hgsub", ".hgsubstate"), false); File hgsub = new File(parentDir, ".hgsub"); File hgsubstate = new File(parentDir, ".hgsubstate"); return readSubrepositories(hgsub, hgsubstate); } catch (VcsException e) { return Collections.emptyMap(); } } private Map<String, Pair<String, String>> readSubrepositories(@NotNull final File hgsub, @NotNull final File hgsubstate) throws IOException { if (hgsub.exists() && hgsubstate.exists()) { Map<String, String> path2repo = readHgsub(hgsub); Map<String, String> path2revision = readHgsubstate(hgsubstate); Map<String, Pair<String, String>> result = new HashMap<String, Pair<String, String>>(); for (Map.Entry<String, String> entry : path2repo.entrySet()) { String path = entry.getKey(); String repo = entry.getValue(); String revision = path2revision.get(path); if (revision != null) { result.put(path, Pair.create(repo, revision)); } else { myLogger.warning("Cannot find revision for subrepository at path " + path + " skip it"); } } return result; } else { return Collections.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]); } else { myLogger.warning("Cannot parse the line '" + line + "' from .hgsub, skip it"); } } 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]); } else { myLogger.warning("Cannot parse the line '" + line + "' from .hgsubstate, skip it"); } } return result; } /*returns map repository path -> (old url, new url)*/ private Map<String, Pair<String, String>> getSubrepositoriesWithChangedUrls(@NotNull final Map<String, Pair<String, String>> subrepos1, @NotNull final Map<String, Pair<String, String>> subrepos2) { Map<String, Pair<String, String>> result = new HashMap<String, Pair<String, String>>(); for (Map.Entry<String, Pair<String, String>> entry : subrepos1.entrySet()) { String path = entry.getKey(); String url1 = entry.getValue().first; Pair<String, String> urlRevision = subrepos2.get(path); if (urlRevision != null && !url1.equals(urlRevision.first)) result.put(path, Pair.create(url1, urlRevision.first)); } return result; } private boolean isInitialClone(@NotNull final String workingDirRevision) { return "000000000000".equals(workingDirRevision); } private String getWorkingDirRevision(@NotNull final Settings settings, @NotNull final File workingDir) throws VcsException { IdentifyCommand id = new IdentifyCommand(settings, workingDir); id.setInLocalRepository(true); return id.execute(); } private void doUpdateWorkingDir(@NotNull final File workingDir) throws VcsException { myLogger.message("Updating folder " + workingDir.getAbsolutePath() + " to revision " + myToVersion); UpdateCommand uc = new UpdateCommand(mySettings, workingDir); ChangeSet cs = new ChangeSet(myToVersion); uc.setToId(cs.getId()); uc.execute(); myLogger.message("Folder successfully updated"); } private String getDefaultPullUrl(Settings settings, boolean useLocalMirror) throws IOException { if (useLocalMirror) { File mirrorDir = myMirrorManager.getMirrorDir(settings.getRepositoryUrl()); return mirrorDir.getCanonicalPath(); } else { return settings.getRepositoryUrl(); } } private void checkRuleIsValid(IncludeRule includeRule) throws VcsException { if (includeRule.getTo() != null && includeRule.getTo().length() > 0) { if (!".".equals(includeRule.getFrom()) && includeRule.getFrom().length() != 0) { throw new VcsException("Invalid include rule: " + includeRule.toString() + ", Mercurial plugin supports mapping of the form: +:.=>dir only."); } } } public boolean isClonedFromLocalMirror(@NotNull final File workingDir) { try { File mirrorDir = myMirrorManager.getMirrorDir(mySettings.getRepositoryUrl()); File hgrc = new File(workingDir, ".hg" + File.separator + "hgrc"); String config = FileUtil.readText(hgrc); return config.contains("default = " + mirrorDir.getCanonicalPath()); } catch (Exception e) { return false; } } }