/* * ==================================================================== * 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.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; 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.internal.util.SVNEncodingUtil; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; import org.tmatesoft.svn.core.internal.wc.SVNFileType; import org.tmatesoft.svn.core.internal.wc.SVNPath; import org.tmatesoft.svn.core.wc.SVNDiffClient; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNRevisionRange; import org.tmatesoft.svn.util.SVNLogType; /** * @version 1.3 * @author TMate Software Ltd. */ public class SVNMergeCommand extends SVNCommand { public SVNMergeCommand() { super("merge", null); } protected Collection createSupportedOptions() { Collection options = new LinkedList(); options.add(SVNOption.REVISION); options.add(SVNOption.CHANGE); options.add(SVNOption.NON_RECURSIVE); options.add(SVNOption.DEPTH); options.add(SVNOption.QUIET); options.add(SVNOption.FORCE); options.add(SVNOption.DRY_RUN); options.add(SVNOption.DIFF3_CMD); options.add(SVNOption.RECORD_ONLY); options.add(SVNOption.EXTENSIONS); options.add(SVNOption.IGNORE_ANCESTRY); options.add(SVNOption.ACCEPT); options.add(SVNOption.REINTEGRATE); options.add(SVNOption.ALLOW_MIXED_REVISIONS); return options; } public boolean acceptsRevisionRange() { return true; } public void run() throws SVNException { List targets = getSVNEnvironment().combineTargets(new ArrayList(), true); SVNPath source1 = null; SVNPath source2 = null; SVNPath target = null; SVNRevision pegRevision1 = null; SVNRevision pegRevision2 = null; if (targets.size() >= 1) { source1 = new SVNPath((String) targets.get(0), true); pegRevision1 = source1.getPegRevision(); if (targets.size() >= 2) { source2 = new SVNPath((String) targets.get(1), true); pegRevision2 = source2.getPegRevision(); } } boolean twoSourcesSpecified = true; if (targets.size() <= 1) { twoSourcesSpecified = false; } else if (targets.size() == 2) { if (source1.isURL() && !source2.isURL()) { twoSourcesSpecified = false; } } List rangesToMerge = getSVNEnvironment().getRevisionRanges(); SVNRevision firstRangeStart = SVNRevision.UNDEFINED; SVNRevision firstRangeEnd = SVNRevision.UNDEFINED; if (!rangesToMerge.isEmpty()) { SVNRevisionRange range = (SVNRevisionRange) rangesToMerge.get(0); firstRangeStart = range.getStartRevision(); firstRangeEnd = range.getEndRevision(); } if (firstRangeStart != SVNRevision.UNDEFINED) { if (firstRangeEnd == SVNRevision.UNDEFINED) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_INSUFFICIENT_ARGS, "Second revision required"); SVNErrorManager.error(err, SVNLogType.CLIENT); } twoSourcesSpecified = false; } if (!twoSourcesSpecified) { if (targets.size() > 2) { SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.CL_ARG_PARSING_ERROR, "Too many arguments given"), SVNLogType.CLIENT); } if (targets.isEmpty()) { pegRevision1 = SVNRevision.HEAD; } else { source2 = source1; if (pegRevision1 == null || pegRevision1 == SVNRevision.UNDEFINED) { pegRevision1 = source1.isURL() ? SVNRevision.HEAD : SVNRevision.WORKING; } if (targets.size() == 2) { target = new SVNPath((String) targets.get(1)); if (target.isURL()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_ARG_PARSING_ERROR, "Cannot specifify a revision range with two URLs"); SVNErrorManager.error(err, SVNLogType.CLIENT); } } } } else { if (targets.size() < 2) { SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.CL_INSUFFICIENT_ARGS), SVNLogType.CLIENT); } else if (targets.size() > 3) { SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.CL_ARG_PARSING_ERROR, "Too many arguments given"), SVNLogType.CLIENT); } firstRangeStart = pegRevision1; firstRangeEnd = pegRevision2; if (((firstRangeStart == null || firstRangeStart == SVNRevision.UNDEFINED) && !source1.isURL()) || ((pegRevision2 == null || pegRevision2 == SVNRevision.UNDEFINED) && !source2.isURL())) { SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.CLIENT_BAD_REVISION, "A working copy merge source needs an explicit revision"), SVNLogType.CLIENT); } if (firstRangeStart == null || firstRangeStart == SVNRevision.UNDEFINED) { firstRangeStart = SVNRevision.HEAD; } if (firstRangeEnd == null || firstRangeEnd == SVNRevision.UNDEFINED) { firstRangeEnd = SVNRevision.HEAD; } if (targets.size() >= 3) { target = new SVNPath((String) targets.get(2)); } } if (source1 != null && source2 != null && target == null) { if (source1.isURL()) { String name1 = SVNPathUtil.tail(source1.getTarget()); String name2 = SVNPathUtil.tail(source2.getTarget()); if (name1.equals(name2)) { String decodedPath = SVNEncodingUtil.uriDecode(name1); SVNPath decodedPathTarget = new SVNPath(decodedPath); if (SVNFileType.getType(decodedPathTarget.getFile()) == SVNFileType.FILE) { target = decodedPathTarget; } } } else if (source1.equals(source2)) { String decodedPath = SVNEncodingUtil.uriDecode(source1.getTarget()); SVNPath decodedPathTarget = new SVNPath(decodedPath); if (SVNFileType.getType(decodedPathTarget.getFile()) == SVNFileType.FILE) { target = decodedPathTarget; } } } if (target == null) { target = new SVNPath(""); } SVNDiffClient client = getSVNEnvironment().getClientManager().getDiffClient(); SVNNotifyPrinter printer = new SVNNotifyPrinter(getSVNEnvironment()); if (!getSVNEnvironment().isQuiet()) { client.setEventHandler(printer); } client.setAllowMixedRevisionsWCForMerge(getSVNEnvironment().isAllowMixedRevisions()); try { client.setMergeOptions(getSVNEnvironment().getDiffOptions()); try { if (!twoSourcesSpecified) { if (firstRangeStart == SVNRevision.UNDEFINED && firstRangeEnd == SVNRevision.UNDEFINED) { SVNRevisionRange range = new SVNRevisionRange(SVNRevision.create(1), pegRevision1); rangesToMerge = new LinkedList(); rangesToMerge.add(range); } if (source1 == null) { source1 = target; } if (getSVNEnvironment().isReIntegrate()) { if (getSVNEnvironment().getDepth() != SVNDepth.UNKNOWN) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_MUTUALLY_EXCLUSIVE_ARGS, "--depth cannot be used with --reintegrate"); SVNErrorManager.error(err, SVNLogType.CLIENT); } if (getSVNEnvironment().isForce()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CL_MUTUALLY_EXCLUSIVE_ARGS, "--force cannot be used with --reintegrate"); SVNErrorManager.error(err, SVNLogType.CLIENT); } if (source1.isURL()) { client.doMergeReIntegrate(source1.getURL(), pegRevision1, target.getFile(), getSVNEnvironment().isDryRun()); } else { client.doMergeReIntegrate(source1.getFile(), pegRevision1, target.getFile(), getSVNEnvironment().isDryRun()); } } else { if (source1.isURL()) { client.doMerge(source1.getURL(), pegRevision1, rangesToMerge, target.getFile(), getSVNEnvironment().getDepth(), !getSVNEnvironment().isIgnoreAncestry(), getSVNEnvironment().isForce(), getSVNEnvironment().isDryRun(), getSVNEnvironment().isRecordOnly()); } else { client.doMerge(source1.getFile(), pegRevision1, rangesToMerge, target.getFile(), getSVNEnvironment().getDepth(), !getSVNEnvironment().isIgnoreAncestry(), getSVNEnvironment().isForce(), getSVNEnvironment().isDryRun(), getSVNEnvironment().isRecordOnly()); } } } else { if (source1.isURL() && source2.isURL()) { client.doMerge(source1.getURL(), firstRangeStart, source2.getURL(), firstRangeEnd, target.getFile(), getSVNEnvironment().getDepth(), !getSVNEnvironment().isIgnoreAncestry(), getSVNEnvironment().isForce(), getSVNEnvironment().isDryRun(), getSVNEnvironment().isRecordOnly()); } else if (source1.isURL() && source2.isFile()) { client.doMerge(source1.getURL(), firstRangeStart, source2.getFile(), firstRangeEnd, target.getFile(), getSVNEnvironment().getDepth(), !getSVNEnvironment().isIgnoreAncestry(), getSVNEnvironment().isForce(), getSVNEnvironment().isDryRun(), getSVNEnvironment().isRecordOnly()); } else if (source1.isFile() && source2.isURL()) { client.doMerge(source1.getFile(), firstRangeStart, source2.getURL(), firstRangeEnd, target.getFile(), getSVNEnvironment().getDepth(), !getSVNEnvironment().isIgnoreAncestry(), getSVNEnvironment().isForce(), getSVNEnvironment().isDryRun(), getSVNEnvironment().isRecordOnly()); } else { client.doMerge(source1.getFile(), firstRangeStart, source2.getFile(), firstRangeEnd, target.getFile(), getSVNEnvironment().getDepth(), !getSVNEnvironment().isIgnoreAncestry(), getSVNEnvironment().isForce(), getSVNEnvironment().isDryRun(), getSVNEnvironment().isRecordOnly()); } } } finally { if (!getSVNEnvironment().isQuiet()) { StringBuffer status = new StringBuffer(); printer.printConflictStatus(status); getSVNEnvironment().getOut().print(status); } } } catch (SVNException e) { SVNErrorMessage err = e.getErrorMessage(); if (err != null && !getSVNEnvironment().isReIntegrate()) { SVNErrorCode code = err.getErrorCode(); if (code == SVNErrorCode.UNVERSIONED_RESOURCE || code == SVNErrorCode.CLIENT_MODIFIED) { err = err.wrap("Use --force to override this restriction"); SVNErrorManager.error(err, SVNLogType.CLIENT); } } throw e; } } }