Mercurial > hg > mercurial
view mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialCollectChangesPolicy.java @ 595:f2a2bf159f4c
Eliminate SubrepoConfigChange usages
author | Dmitry Neverov <dmitry.neverov@jetbrains.com> |
---|---|
date | Mon, 29 Apr 2013 13:44:45 +0400 |
parents | 9cbf9205208e |
children | 6c480513e5c2 |
line wrap: on
line source
package jetbrains.buildServer.buildTriggers.vcs.mercurial; import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*; import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnknownRevisionException; import jetbrains.buildServer.log.Loggers; import jetbrains.buildServer.vcs.*; import jetbrains.buildServer.vcs.impl.VcsRootImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.net.URISyntaxException; import java.util.*; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; public class MercurialCollectChangesPolicy implements CollectChangesBetweenRoots, CollectChangesBetweenRepositories { private final MercurialVcsSupport myVcs; private final ServerPluginConfig myConfig; private final HgVcsRootFactory myHgVcsRootFactory; private final RepoFactory myRepoFactory; private final HgPathProvider myHgPathProvider; public MercurialCollectChangesPolicy(@NotNull MercurialVcsSupport vcs, @NotNull ServerPluginConfig config, @NotNull HgVcsRootFactory hgVcsRootFactory, @NotNull RepoFactory repoFactory, @NotNull HgPathProvider hgPathProvider) { myVcs = vcs; myConfig = config; myHgVcsRootFactory = hgVcsRootFactory; myRepoFactory = repoFactory; myHgPathProvider = hgPathProvider; } @NotNull public RepositoryStateData getCurrentState(@NotNull VcsRoot root) throws VcsException { HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root); myVcs.syncRepository(hgRoot); Map<String, String> revisions = new HashMap<String, String>(); revisions.putAll(getBookmarkRevisions(hgRoot)); revisions.putAll(getBranchesRevisions(hgRoot)); String defaultBranchName = hgRoot.getBranchName(); if (revisions.get(defaultBranchName) == null) { throw new VcsException("Cannot find revision of the default branch '" + defaultBranchName + "' of vcs root " + myVcs.describeVcsRoot(root)); } return RepositoryStateData.createVersionState(defaultBranchName, revisions); } @NotNull private Map<String, String> getBranchesRevisions(@NotNull HgVcsRoot root) throws VcsException { HgRepo repo = myVcs.createRepo(root); return repo.branches().call(); } @NotNull private Map<String, String> getBookmarkRevisions(@NotNull HgVcsRoot root) throws VcsException { ServerHgRepo repo = myVcs.createRepo(root); if (!myConfig.bookmarksEnabled()) return emptyMap(); HgVersion version = repo.version().call(); if (!version.isEqualsOrGreaterThan(BookmarksCommand.REQUIRED_HG_VERSION)) return emptyMap(); return repo.bookmarks().call(); } @NotNull public List<ModificationData> collectChanges(@NotNull VcsRoot fromRoot, @NotNull RepositoryStateData fromState, @NotNull VcsRoot toRoot, @NotNull RepositoryStateData toState, @NotNull CheckoutRules rules) throws VcsException { return collectChanges(toRoot, fromState, toState, rules); } @NotNull public List<ModificationData> collectChanges(@NotNull VcsRoot root, @NotNull RepositoryStateData fromState, @NotNull RepositoryStateData toState, @NotNull CheckoutRules rules) throws VcsException { List<ModificationData> changes = new ArrayList<ModificationData>(); HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root); OperationContext ctx = new OperationContext(myVcs, myRepoFactory, myHgPathProvider, fromState, toState); for (Map.Entry<String, String> entry : toState.getBranchRevisions().entrySet()) { String branch = entry.getKey(); String toRevision = entry.getValue(); String fromRevision = fromState.getBranchRevisions().get(branch); if (fromRevision == null) fromRevision = fromState.getBranchRevisions().get(fromState.getDefaultBranchName()); if (toRevision.equals(fromRevision) || fromRevision == null) continue; Collection<String> fromRevisions = ctx.getFromRevisionsForBranch(hgRoot, fromRevision, toRevision); List<ModificationData> branchChanges = collectChanges(ctx, root, fromRevisions, toRevision, rules); for (ModificationData change : branchChanges) { if (!ctx.isReportedModification(change)) { changes.add(change); ctx.markAsReported(change); } } } changes.addAll(getSubrepoChanges(ctx, root, changes)); return changes; } @NotNull public List<ModificationData> collectChanges(@NotNull VcsRoot fromRoot, @NotNull String fromRootRevision, @NotNull VcsRoot toRoot, @Nullable String toRootRevision, @NotNull CheckoutRules checkoutRules) throws VcsException { HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(toRoot); myVcs.syncRepository(hgRoot); String toRevision = toRootRevision != null ? toRootRevision : myVcs.getCurrentVersion(toRoot); String mergeBase = getMergeBase(hgRoot, fromRootRevision, toRevision); if (mergeBase == null) return Collections.emptyList(); return collectChanges(toRoot, mergeBase, toRootRevision, checkoutRules); } public List<ModificationData> collectChanges(@NotNull VcsRoot root, @NotNull String fromVersion, @Nullable String currentVersion, @NotNull CheckoutRules checkoutRules) throws VcsException { if (currentVersion == null) return emptyList(); OperationContext ctx = new OperationContext(myVcs, myRepoFactory, myHgPathProvider, fromVersion, currentVersion); List<ModificationData> changes = collectChanges(ctx, root, asList(fromVersion), currentVersion, checkoutRules); changes.addAll(getSubrepoChanges(ctx, root, changes)); return changes; } @Nullable private String getMergeBase(@NotNull HgVcsRoot root, @NotNull String revision1, @NotNull String revision2) throws VcsException { String result = myVcs.createRepo(root).mergeBase() .revision1(revision1) .revision2(revision2) .call(); if (result == null) result = getMinusNthCommit(root, 10); return result; } @Nullable private String getMinusNthCommit(@NotNull HgVcsRoot root, int n) throws VcsException { LogCommand log = myVcs.createRepo(root).log() .inBranch(root.getBranchName()) .toNamedRevision(root.getBranchName()); if (n > 0) log.setLimit(n); List<ChangeSet> changeSets = log.call(); if (changeSets.isEmpty()) return null; return changeSets.get(0).getId(); } private List<ModificationData> collectChanges(@NotNull OperationContext ctx, @NotNull VcsRoot root, @NotNull Collection<String> fromVersion, @Nullable String currentVersion, @NotNull CheckoutRules checkoutRules) throws VcsException { HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root); ctx.syncRepository(hgRoot); List<ModificationData> result = new ArrayList<ModificationData>(); for (ChangeSet cset : getChangesets(ctx, hgRoot, fromVersion, currentVersion)) { result.add(createModificationData(ctx, cset, root, checkoutRules)); } return result; } @NotNull private List<ChangeSet> getChangesets(@NotNull OperationContext ctx, @NotNull final HgVcsRoot root, @NotNull final Collection<String> fromVersions, @Nullable final String toVersion) throws VcsException { if (toVersion == null) return emptyList(); List<String> fromCommits = new ArrayList<String>(); for (String fromVersion : fromVersions) { fromCommits.add(new ChangeSetRevision(fromVersion).getId()); } String toCommit = new ChangeSetRevision(toVersion).getId(); try { ServerHgRepo repo = myVcs.createRepo(ctx, root); List<ChangeSet> csets = repo.collectChanges(root) .fromRevision(fromCommits) .toRevision(toCommit) .includeFromRevision(ctx.includeFromRevisions()) .call(); if (!ctx.includeFromRevisions()) { Iterator<ChangeSet> iter = csets.iterator(); while (iter.hasNext()) { ChangeSet cset = iter.next(); if (fromVersions.contains(cset.getId())) iter.remove(); } } return csets; } catch (UnknownRevisionException e) { Loggers.VCS.warn("Revision '" + e.getRevision() + "' is unknown, will return no changes"); return emptyList(); } } private ModificationData createModificationData(@NotNull OperationContext ctx, @NotNull final ChangeSet cset, @NotNull final VcsRoot root, @NotNull final CheckoutRules checkoutRules) throws VcsException { List<ChangeSetRevision> parents = cset.getParents(); if (parents.isEmpty()) throw new IllegalStateException("Commit " + cset.getId() + " has no parents"); String version = ctx.getStringFromPool(cset.getId()); List<VcsChange> files = toVcsChanges(ctx, cset.getModifiedFiles(), ctx.getStringFromPool(parents.get(0).getId()), version, checkoutRules); final ModificationData result = new ModificationData(cset.getTimestamp(), files, cset.getDescription(), ctx.getStringFromPool(cset.getUser()), root, version, version); for (ChangeSetRevision parent : parents) { result.addParentRevision(ctx.getStringFromPool(parent.getId())); } setCanBeIgnored(result, cset); result.setAttributes(getAttributes(ctx, root, cset)); return result; } private void setCanBeIgnored(@NotNull ModificationData md, @NotNull ChangeSet cset) { if (md.getParentRevisions().size() > 1) { //don't ignore merge commits md.setCanBeIgnored(false); } else if (cset.getModifiedFiles().isEmpty()) { //don't ignore empty commits md.setCanBeIgnored(false); } } private List<VcsChange> toVcsChanges(@NotNull OperationContext ctx, @NotNull List<FileStatus> modifiedFiles, @NotNull String prevVer, @NotNull String curVer, @NotNull CheckoutRules rules) { List<VcsChange> files = new ArrayList<VcsChange>(0); for (FileStatus mf: modifiedFiles) { String path = rules.map(mf.getPath()); if (!shouldInclude(path)) continue; String normalizedPath = PathUtil.normalizeSeparator(mf.getPath()); VcsChangeInfo.Type changeType = getChangeType(mf.getStatus()); if (changeType == null) { Loggers.VCS.warn("Unable to convert status: " + mf.getStatus() + " to VCS change type"); changeType = VcsChangeInfo.Type.NOT_CHANGED; } files.add(new VcsChange(changeType, mf.getStatus().getName(), ctx.getStringFromPool(normalizedPath), ctx.getStringFromPool(path), prevVer, curVer)); } if (files.isEmpty()) return emptyList(); return files; } private boolean shouldInclude(String path) { return path != null; } private VcsChangeInfo.Type getChangeType(final Status status) { switch (status) { case ADDED:return VcsChangeInfo.Type.ADDED; case MODIFIED:return VcsChangeInfo.Type.CHANGED; case REMOVED:return VcsChangeInfo.Type.REMOVED; } return null; } @NotNull private List<ModificationData> getSubrepoChanges(@NotNull OperationContext ctx, @NotNull VcsRoot root, @NotNull List<ModificationData> changes) throws VcsException { if (changes.isEmpty()) return emptyList(); ctx.setIncludeFromRevisions(true); HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root); if (!detectSubrepoChanges(hgRoot)) return emptyList(); List<HgSubrepoConfigChange> subrepoConfigChanges = getSubrepoConfigChanges(ctx, changes); List<ModificationData> subrepoChanges = new ArrayList<ModificationData>(); for (HgSubrepoConfigChange configChange : subrepoConfigChanges) { SubRepo current = configChange.getCurrent(); assert current != null; String subrepoUrl = current.url(); String curRevision = current.revision(); List<String> prevRevisions = new ArrayList<String>(0); for (SubRepo prevSubrepo : configChange.getPrevious()) { prevRevisions.add(prevSubrepo.revision()); } String path = configChange.getPath(); Map<String, String> subrepoParams = new HashMap<String, String>(hgRoot.getProperties()); subrepoParams.put(Constants.REPOSITORY_PROP, subrepoUrl); subrepoParams.put("teamcity.internal.subrepo", "true"); subrepoParams.put("teamcity.internal.subrepo.path", path); VcsRootImpl subrepo = new VcsRootImpl(root.getId(), subrepoParams); if (ctx.isProcessedSubrepoChanges(subrepo, prevRevisions, curRevision)) continue; List<ModificationData> subChanges = collectChanges(ctx, subrepo, prevRevisions, curRevision, CheckoutRules.DEFAULT); for (ModificationData m : subChanges) { if (!ctx.isReportedModification(m)) { subrepoChanges.add(m); ctx.markAsReported(m); } } ctx.markProcessedSubrepoChanges(subrepo, prevRevisions, curRevision); } List<ModificationData> subSubrepoChanges = getSubrepoChanges(ctx, root, subrepoChanges); subrepoChanges.addAll(subSubrepoChanges); return subrepoChanges; } private boolean detectSubrepoChanges(@NotNull HgVcsRoot root) { return myConfig.detectSubrepoChanges() && root.detectSubrepoChanges(); } private List<HgSubrepoConfigChange> getSubrepoConfigChanges(@NotNull OperationContext ctx, @NotNull List<ModificationData> mainRootChanges) throws VcsException { List<HgSubrepoConfigChange> subrepoConfigChanges = new ArrayList<HgSubrepoConfigChange>(); for (ModificationData m : mainRootChanges) { subrepoConfigChanges.addAll(getSubrepoConfigChanges(ctx, m)); } return subrepoConfigChanges; } @NotNull private List<HgSubrepoConfigChange> getSubrepoConfigChanges(@NotNull OperationContext ctx, @NotNull ModificationData m) throws VcsException { List<HgSubrepoConfigChange> configChanges = new ArrayList<HgSubrepoConfigChange>(); HgVcsRoot mainRoot = myHgVcsRootFactory.createHgRoot(m.getVcsRoot()); ServerHgRepo repo = myVcs.createRepo(ctx, mainRoot); for (HgSubrepoConfigChange c : repo.getSubrepoConfigChanges(m.getVersion(), m.getParentRevisions())) { if (!(c.subrepoUrlChanged() || c.subrepoAdded() || c.subrepoRemoved())) {//report only changes in revisions, because we collect changes only for such changes //map url and path, relative to the main repository try { SubRepo currentSubrepo = c.getCurrent(); assert currentSubrepo != null; String subrepoUrl = ctx.getStringFromPool(currentSubrepo.resolveUrl(mainRoot.getRepository())); String curRevision = ctx.getStringFromPool(currentSubrepo.revision()); List<SubRepo> prevSubrepos = new ArrayList<SubRepo>(0); String path = ctx.getStringFromPool(mainRoot.expandSubrepoPath(c.getPath())); for (SubRepo prevSubrepo : c.getPrevious()) { prevSubrepos.add(new SubRepo(path, subrepoUrl, ctx.getStringFromPool(prevSubrepo.revision()))); } configChanges.add(new HgSubrepoConfigChange(path, prevSubrepos, new SubRepo(path, subrepoUrl, curRevision))); } catch (URISyntaxException e) { throw new VcsException(e); } } } return configChanges; } @NotNull private Map<String, String> getAttributes(@NotNull OperationContext ctx, @NotNull VcsRoot mainRoot, @NotNull ChangeSet cset) throws VcsException { Map<String, String> attributes = new HashMap<String, String>(); HgVcsRoot root = myHgVcsRootFactory.createHgRoot(mainRoot); if (detectSubrepoChanges(root)) { try { ServerHgRepo repo = myVcs.createRepo(ctx, root); SubrepoRevisionAttributesBuilder attrBuilder = new SubrepoRevisionAttributesBuilder(); for (SubRepo s : repo.getSubrepositories(cset).values()) { attrBuilder.addSubrepo(new SubrepoConfig(root) .setSubrepoPath(ctx.getStringFromPool(root.expandSubrepoPath(s.path()))) .setSubrepoRootParamDiff(Constants.REPOSITORY_PROP, ctx.getStringFromPool(s.resolveUrl(root.getRepository()))) .setSubrepoRootParamDiff("teamcity.internal.subrepo", "true") .setSubrepoRootParamDiff("teamcity.internal.subrepo.path", ctx.getStringFromPool(root.expandSubrepoPath(s.path()))) .setSubrepoRevision(ctx.getStringFromPool(s.revision()))); } attributes.putAll(attrBuilder.buildAttributes()); } catch (Exception e) { Loggers.VCS.warn("Error while reporting subrepo config changes", e); if (e instanceof VcsExtension) throw (VcsException) e; throw new VcsException(e); } } if (attributes.isEmpty()) return emptyMap(); return attributes; } }