package org.tmatesoft.svn.core.internal.wc2.remote; import java.io.File; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNNodeKind; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.wc.SVNCancellableEditor; import org.tmatesoft.svn.core.internal.wc.SVNDiffStatusEditor; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.core.internal.wc17.db.Structure; import org.tmatesoft.svn.core.internal.wc2.SvnRemoteOperationRunner; import org.tmatesoft.svn.core.internal.wc2.SvnRepositoryAccess; import org.tmatesoft.svn.core.internal.wc2.compat.SvnCodec; import org.tmatesoft.svn.core.io.ISVNReporter; import org.tmatesoft.svn.core.io.ISVNReporterBaton; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.ISVNDiffStatusHandler; import org.tmatesoft.svn.core.wc.SVNDiffStatus; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.ISvnObjectReceiver; import org.tmatesoft.svn.core.wc2.SvnDiffStatus; import org.tmatesoft.svn.core.wc2.SvnDiffSummarize; import org.tmatesoft.svn.core.wc2.SvnTarget; import org.tmatesoft.svn.util.ISVNDebugLog; import org.tmatesoft.svn.util.SVNDebugLog; import org.tmatesoft.svn.util.SVNLogType; public class SvnRemoteDiffSummarize extends SvnRemoteOperationRunner<SvnDiffStatus, SvnDiffSummarize> { @Override protected SvnDiffStatus run() throws SVNException { final SvnTarget source = getOperation().getSource(); final SvnTarget firstSource = getOperation().getFirstSource(); final SvnTarget secondSource = getOperation().getSecondSource(); final ISVNDiffStatusHandler handler = createHandlerForReceiver(getOperation()); final SVNDepth depth = getOperation().getDepth(); final boolean useAncestry = !getOperation().isIgnoreAncestry(); if (source != null) { doDiffURL(source.getURL(), source.getFile(), getOperation().getStartRevision(), getOperation().getEndRevision(), source.getPegRevision(), depth, useAncestry, handler); } else { doDiffURLURL(firstSource.getURL(), firstSource.getFile(), firstSource.getResolvedPegRevision(), secondSource.getURL(), secondSource.getFile(), secondSource.getResolvedPegRevision(), SVNRevision.UNDEFINED, depth, useAncestry, handler); } return null; } private void doDiffURL(SVNURL url, File path, SVNRevision startRevision, SVNRevision endRevision, SVNRevision pegRevision, SVNDepth depth, boolean useAncestry, ISVNDiffStatusHandler handler) throws SVNException { if (handler == null) { return; } doDiffURLURL(url, path, startRevision, url, path, endRevision, pegRevision, depth, useAncestry, handler); } private void doDiffURLURL(SVNURL url1, File path1, SVNRevision revision1, SVNURL url2, File path2, SVNRevision revision2, SVNRevision pegRevision, SVNDepth depth, boolean useAncestry, ISVNDiffStatusHandler handler) throws SVNException { if (revision1 == SVNRevision.UNDEFINED || revision2 == SVNRevision.UNDEFINED) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_BAD_REVISION, "Not all revisions are specified"); SVNErrorManager.error(err, SVNLogType.WC); } boolean isLocalRev1 = revision1 == SVNRevision.BASE || revision1 == SVNRevision.WORKING; boolean isLocalRev2 = revision2 == SVNRevision.BASE || revision2 == SVNRevision.WORKING; boolean isRepos1; boolean isRepos2; if (pegRevision != SVNRevision.UNDEFINED) { if (isLocalRev1 && isLocalRev2) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_BAD_REVISION, "At least one revision must be non-local for a pegged diff"); SVNErrorManager.error(err, SVNLogType.WC); } isRepos1 = !isLocalRev1; isRepos2 = !isLocalRev2; } else { isRepos1 = !isLocalRev1 || url1 != null; isRepos2 = !isLocalRev2 || url2 != null; } if (!isRepos1 || !isRepos2) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Summarizing diff can only compare repository to repository"); SVNErrorManager.error(err, SVNLogType.WC); } File basePath = null; if (path1 != null) { basePath = path1; } if (path2 != null) { basePath = path2; } if (pegRevision.isValid()) { final Structure<SvnRepositoryAccess.LocationsInfo> locationsInfo = getRepositoryAccess().getLocations(null, url2 == null ? SvnTarget.fromFile(path2) : SvnTarget.fromURL(url2), pegRevision, revision1, revision2); url1 = locationsInfo.get(SvnRepositoryAccess.LocationsInfo.startUrl); url2 = locationsInfo.get(SvnRepositoryAccess.LocationsInfo.endUrl); } else { url1 = url1 == null ? getURL(path1) : url1; url2 = url2 == null ? getURL(path2) : url2; } SVNRepository repository1 = createRepository(url1, null, true); SVNRepository repository2 = createRepository(url2, null, false); final long rev1 = getRevisionNumber(revision1, repository1, url1); long rev2 = -1; SVNNodeKind kind1 = null; SVNNodeKind kind2 = null; String target1 = null; try { rev2 = getRevisionNumber(revision2, repository2, url2); kind1 = repository1.checkPath("", rev1); kind2 = repository2.checkPath("", rev2); if (kind1 == SVNNodeKind.NONE) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_NOT_FOUND, "''{0}'' was not found in the repository at revision {1}", new Object[] { url1, new Long(rev1) }); SVNErrorManager.error(err, SVNLogType.WC); } else if (kind2 == SVNNodeKind.NONE) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_NOT_FOUND, "''{0}'' was not found in the repository at revision {1}", new Object[] { url2, new Long(rev2) }); SVNErrorManager.error(err, SVNLogType.WC); } if (kind1 == SVNNodeKind.FILE || kind2 == SVNNodeKind.FILE) { target1 = SVNPathUtil.tail(url1.getPath()); if (basePath != null) { basePath = basePath.getParentFile(); } url1 = SVNURL.parseURIEncoded(SVNPathUtil.removeTail(url1.toString())); repository1 = createRepository(url1, null, true); } } finally { repository2.closeSession(); } repository2 = createRepository(url1, null, false); try { SVNDiffStatusEditor editor = new SVNDiffStatusEditor(basePath, target1, repository2, rev1, handler); ISVNReporterBaton reporter = new ISVNReporterBaton() { public void report(ISVNReporter reporter) throws SVNException { reporter.setPath("", null, rev1, SVNDepth.INFINITY, false); reporter.finishReport(); } }; repository1.diff(url2, rev2, rev1, target1, !useAncestry, depth, false, reporter, SVNCancellableEditor.newInstance(editor, this, getDebugLog())); } finally { repository2.closeSession(); } } private ISVNDebugLog getDebugLog() { return SVNDebugLog.getDefaultLog(); } private long getRevisionNumber(SVNRevision revision1, SVNRepository repository1, SVNURL url1) throws SVNException { final Structure<SvnRepositoryAccess.RevisionsPair> revisionNumber = getRepositoryAccess().getRevisionNumber(repository1, SvnTarget.fromURL(url1, revision1), revision1, null); return revisionNumber.lng(SvnRepositoryAccess.RevisionsPair.revNumber); } private SVNURL getURL(File path1) throws SVNException { return getRepositoryAccess().getURLFromPath(SvnTarget.fromFile(path1), SVNRevision.UNDEFINED, null).<SVNURL>get(SvnRepositoryAccess.UrlInfo.url); } protected SVNRepository createRepository(SVNURL url, File path, boolean mayReuse) throws SVNException { return getRepositoryAccess().createRepository(url, null, mayReuse); } private static ISVNDiffStatusHandler createHandlerForReceiver(final ISvnObjectReceiver<SvnDiffStatus> receiver) { return new ISVNDiffStatusHandler() { public void handleDiffStatus(SVNDiffStatus diffStatus) throws SVNException { if (receiver != null) { receiver.receive(null, SvnCodec.diffStatus(diffStatus)); } } }; } }