/* * ==================================================================== * Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://svnkit.com/license.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * ==================================================================== */ package org.tmatesoft.svn.cli.svn; import java.io.File; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.tmatesoft.svn.cli.SVNCommandUtil; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil; import org.tmatesoft.svn.core.internal.util.SVNHashMap; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.util.SVNXMLUtil; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.core.internal.wc.SVNPath; import org.tmatesoft.svn.core.internal.wc2.ng.SvnDiffGenerator; import org.tmatesoft.svn.core.internal.wc2.ng.SvnNewDiffGenerator; import org.tmatesoft.svn.core.wc.ISVNDiffStatusHandler; import org.tmatesoft.svn.core.wc.SVNDiffClient; import org.tmatesoft.svn.core.wc.SVNDiffStatus; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNStatusType; import org.tmatesoft.svn.util.SVNLogType; /** * @version 1.3 * @author TMate Software Ltd. */ public class SVNDiffCommand extends SVNXMLCommand implements ISVNDiffStatusHandler { public SVNDiffCommand() { super("diff", new String[] {"di"}); } public boolean acceptsRevisionRange() { return true; } protected Collection createSupportedOptions() { Collection options = new LinkedList(); options.add(SVNOption.REVISION); options.add(SVNOption.CHANGE); options.add(SVNOption.OLD); options.add(SVNOption.NEW); options.add(SVNOption.NON_RECURSIVE); options.add(SVNOption.DEPTH); options.add(SVNOption.DIFF_CMD); options.add(SVNOption.EXTENSIONS); options.add(SVNOption.NO_DIFF_DELETED); options.add(SVNOption.NOTICE_ANCESTRY); options.add(SVNOption.SHOW_COPIES_AS_ADDS); options.add(SVNOption.SUMMARIZE); options.add(SVNOption.CHANGELIST); options.add(SVNOption.FORCE); options.add(SVNOption.XML); options.add(SVNOption.GIT_DIFF_FORMAT); return options; } public void run() throws SVNException { if (getSVNEnvironment().isXML()) { if (!getSVNEnvironment().isSummarize()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_ARG_PARSING_ERROR, "'--xml' option only valid with '--summarize' option"); SVNErrorManager.error(err, SVNLogType.CLIENT); } printXMLHeader("diff"); StringBuffer buffer = openXMLTag("paths", SVNXMLUtil.XML_STYLE_NORMAL, null, null); getSVNEnvironment().getOut().print(buffer.toString()); } List targets = new ArrayList(); if (getSVNEnvironment().getTargets() != null) { targets.addAll(getSVNEnvironment().getTargets()); } targets = getSVNEnvironment().combineTargets(targets, true); SVNPath oldTarget = null; SVNPath newTarget = null; SVNRevision start = getSVNEnvironment().getStartRevision(); SVNRevision end = getSVNEnvironment().getEndRevision(); boolean peggedDiff = false; if (targets.size() == 2 && getSVNEnvironment().getOldTarget() == null && getSVNEnvironment().getNewTarget() == null && SVNCommandUtil.isURL((String) targets.get(0)) && SVNCommandUtil.isURL((String) targets.get(1)) && getSVNEnvironment().getStartRevision() == SVNRevision.UNDEFINED && getSVNEnvironment().getEndRevision() == SVNRevision.UNDEFINED) { oldTarget = new SVNPath((String) targets.get(0), true); newTarget = new SVNPath((String) targets.get(1), true); start = oldTarget.getPegRevision(); end = newTarget.getPegRevision(); targets.clear(); if (start == SVNRevision.UNDEFINED) { start = SVNRevision.HEAD; } if (end == SVNRevision.UNDEFINED) { end = SVNRevision.HEAD; } } else if (getSVNEnvironment().getOldTarget() != null) { targets.clear(); targets.add(getSVNEnvironment().getOldTarget()); targets.add(getSVNEnvironment().getNewTarget() != null ? getSVNEnvironment().getNewTarget() : getSVNEnvironment().getOldTarget()); oldTarget = new SVNPath((String) targets.get(0), true); newTarget = new SVNPath((String) targets.get(1), true); start = getSVNEnvironment().getStartRevision(); end = getSVNEnvironment().getEndRevision(); if (oldTarget.getPegRevision() != SVNRevision.UNDEFINED) { start = oldTarget.getPegRevision(); } if (newTarget.getPegRevision() != SVNRevision.UNDEFINED) { end = newTarget.getPegRevision(); } if (start == SVNRevision.UNDEFINED) { start = oldTarget.isURL() ? SVNRevision.HEAD : SVNRevision.BASE; } if (end == SVNRevision.UNDEFINED) { end = newTarget.isURL() ? SVNRevision.HEAD : SVNRevision.WORKING; } targets = getSVNEnvironment().combineTargets(null, true); } else if (getSVNEnvironment().getNewTarget() != null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_ARG_PARSING_ERROR, "'--new' option only valid with '--old' option"); SVNErrorManager.error(err, SVNLogType.CLIENT); } else { if (targets.isEmpty()) { targets.add(""); } oldTarget = new SVNPath(""); newTarget = new SVNPath(""); boolean hasURLs = false; boolean hasWCs = false; for(int i = 0; i < targets.size(); i++) { SVNPath target = new SVNPath((String) targets.get(i)); hasURLs |= target.isURL(); hasWCs |= target.isFile(); } if (hasURLs && hasWCs) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Target lists to diff may not contain both working copy paths and URLs"); SVNErrorManager.error(err, SVNLogType.CLIENT); } start = getSVNEnvironment().getStartRevision(); end = getSVNEnvironment().getEndRevision(); if (start == SVNRevision.UNDEFINED && hasWCs) { start = SVNRevision.BASE; } if (end == SVNRevision.UNDEFINED) { end = hasWCs ? SVNRevision.WORKING : SVNRevision.HEAD; } peggedDiff = (start != SVNRevision.BASE && start != SVNRevision.WORKING) || (end != SVNRevision.BASE && end != SVNRevision.WORKING); } if (targets.isEmpty()) { targets.add(""); } SVNDiffClient client = getSVNEnvironment().getClientManager().getDiffClient(); SVNCommandEnvironment environment = (SVNCommandEnvironment) getEnvironment(); client.setShowCopiesAsAdds(environment.isShowCopiesAsAdds()); client.setGitDiffFormat(environment.isGitDiffFormat()); SvnNewDiffGenerator generator = createDiffGenerator(getSVNEnvironment()); client.setDiffGenerator(generator); PrintStream ps = getSVNEnvironment().getOut(); Collection changeLists = getSVNEnvironment().getChangelistsCollection(); for(int i = 0; i < targets.size(); i++) { String targetName = (String) targets.get(i); if (!peggedDiff) { SVNPath target1 = new SVNPath(SVNPathUtil.append(oldTarget.getTarget(), targetName)); SVNPath target2 = new SVNPath(SVNPathUtil.append(newTarget.getTarget(), targetName)); if (getSVNEnvironment().isSummarize()) { if (target1.isURL() && target2.isURL()) { client.doDiffStatus(target1.getURL(), start, target2.getURL(), end, getSVNEnvironment().getDepth(), getSVNEnvironment().isNoticeAncestry(), this); } else if (target1.isURL()) { client.doDiffStatus(target1.getURL(), start, target2.getFile(), end, getSVNEnvironment().getDepth(), getSVNEnvironment().isNoticeAncestry(), this); } else if (target2.isURL()) { client.doDiffStatus(target1.getFile(), start, target2.getURL(), end, getSVNEnvironment().getDepth(), getSVNEnvironment().isNoticeAncestry(), this); } else { client.doDiffStatus(target1.getFile(), start, target2.getFile(), end, getSVNEnvironment().getDepth(), getSVNEnvironment().isNoticeAncestry(), this); } } else { if (target1.isURL() && target2.isURL()) { client.doDiff(target1.getURL(), start, target2.getURL(), end, getSVNEnvironment().getDepth(), getSVNEnvironment().isNoticeAncestry(), ps); } else if (target1.isURL()) { client.doDiff(target1.getURL(), start, target2.getFile(), end, getSVNEnvironment().getDepth(), getSVNEnvironment().isNoticeAncestry(), ps, changeLists); } else if (target2.isURL()) { client.doDiff(target1.getFile(), start, target2.getURL(), end, getSVNEnvironment().getDepth(), getSVNEnvironment().isNoticeAncestry(), ps, changeLists); } else { client.doDiff(target1.getFile(), start, target2.getFile(), end, getSVNEnvironment().getDepth(), getSVNEnvironment().isNoticeAncestry(), ps, changeLists); } } } else { SVNPath target = new SVNPath(targetName, true); SVNRevision pegRevision = target.getPegRevision(); if (pegRevision == SVNRevision.UNDEFINED) { pegRevision = target.isURL() ? SVNRevision.HEAD : SVNRevision.WORKING; } if (getSVNEnvironment().isSummarize()) { if (target.isURL()) { client.doDiffStatus(target.getURL(), start, end, pegRevision, getSVNEnvironment().getDepth(), getSVNEnvironment().isNoticeAncestry(), this); } else { client.doDiffStatus(target.getFile(), start, end, pegRevision, getSVNEnvironment().getDepth(), getSVNEnvironment().isNoticeAncestry(), this); } } else { if (target.isURL()) { client.doDiff(target.getURL(), pegRevision, start, end, getSVNEnvironment().getDepth(), getSVNEnvironment().isNoticeAncestry(), ps); } else { client.doDiff(target.getFile(), pegRevision, start, end, getSVNEnvironment().getDepth(), getSVNEnvironment().isNoticeAncestry(), ps, changeLists); } } } } if (getSVNEnvironment().isXML()) { StringBuffer buffer = closeXMLTag("paths", null); getSVNEnvironment().getOut().print(buffer.toString()); printXMLFooter("diff"); } } static SvnNewDiffGenerator createDiffGenerator(SVNCommandEnvironment svnEnvironment) throws SVNException { SvnDiffGenerator diffGenerator = new SvnDiffGenerator(); if (svnEnvironment.getDiffCommand() != null) { diffGenerator.setExternalDiffCommand(svnEnvironment.getDiffCommand()); diffGenerator.setRawDiffOptions((List<String>) svnEnvironment.getExtensions()); } else { diffGenerator.setDiffOptions(svnEnvironment.getDiffOptions()); } diffGenerator.setDiffDeleted(!svnEnvironment.isNoDiffDeleted()); diffGenerator.setForcedBinaryDiff(svnEnvironment.isForce()); diffGenerator.setBasePath(new File("").getAbsoluteFile()); diffGenerator.setFallbackToAbsolutePath(true); diffGenerator.setOptions(svnEnvironment.getOptions()); diffGenerator.setDiffDeleted(!svnEnvironment.isNoDiffDeleted()); return new SvnNewDiffGenerator(diffGenerator); } public void handleDiffStatus(SVNDiffStatus diffStatus) throws SVNException { if (diffStatus.getModificationType() == SVNStatusType.STATUS_NONE && !diffStatus.isPropertiesModified()) { return; } String path = diffStatus.getPath(); if (diffStatus.getFile() != null) { path = getSVNEnvironment().getRelativePath(diffStatus.getFile()); path = SVNCommandUtil.getLocalPath(path); } else if (diffStatus.getURL() != null) { path = diffStatus.getURL().toString(); } else { path = diffStatus.getPath(); if (!SVNCommandUtil.isURL(path)) { path = SVNCommandUtil.getLocalPath(path); } } if (getSVNEnvironment().isXML()) { StringBuffer buffer = new StringBuffer(); Map attrs = new SVNHashMap(); attrs.put("kind", diffStatus.getKind().toString()); String modificationKind = "none"; if (diffStatus.getModificationType() == SVNStatusType.STATUS_MODIFIED) { modificationKind = "modified"; } else if (diffStatus.getModificationType() == SVNStatusType.STATUS_ADDED) { modificationKind = "added"; } else if (diffStatus.getModificationType() == SVNStatusType.STATUS_DELETED) { modificationKind = "deleted"; } attrs.put("item", modificationKind); attrs.put("props", diffStatus.isPropertiesModified() ? "modified" : "none"); buffer = openXMLTag("path", SVNXMLUtil.XML_STYLE_PROTECT_CDATA, attrs, buffer); buffer.append(SVNEncodingUtil.xmlEncodeCDATA(path)); buffer = closeXMLTag("path", buffer); getSVNEnvironment().getOut().print(buffer.toString()); } else { getSVNEnvironment().getOut().print(diffStatus.getModificationType().getCode() + (diffStatus.isPropertiesModified() ? "M" : " ") + " " + path + "\n"); getSVNEnvironment().getOut().flush(); } } }