0001: /*
0002: * ====================================================================
0003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
0004: *
0005: * This software is licensed as described in the file COPYING, which
0006: * you should have received as part of this distribution. The terms
0007: * are also available at http://svnkit.com/license.html
0008: * If newer versions of this license are posted there, you may use a
0009: * newer version instead, at your option.
0010: * ====================================================================
0011: */
0012: package org.tmatesoft.svn.core.wc;
0013:
0014: import java.io.File;
0015: import java.io.OutputStream;
0016: import java.util.HashMap;
0017: import java.util.Iterator;
0018: import java.util.Map;
0019:
0020: import org.tmatesoft.svn.core.SVNErrorCode;
0021: import org.tmatesoft.svn.core.SVNErrorMessage;
0022: import org.tmatesoft.svn.core.SVNException;
0023: import org.tmatesoft.svn.core.SVNNodeKind;
0024: import org.tmatesoft.svn.core.SVNProperty;
0025: import org.tmatesoft.svn.core.SVNURL;
0026: import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
0027: import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
0028: import org.tmatesoft.svn.core.internal.wc.AbstractDiffCallback;
0029: import org.tmatesoft.svn.core.internal.wc.SVNCancellableEditor;
0030: import org.tmatesoft.svn.core.internal.wc.SVNCancellableOutputStream;
0031: import org.tmatesoft.svn.core.internal.wc.SVNDiffCallback;
0032: import org.tmatesoft.svn.core.internal.wc.SVNDiffEditor;
0033: import org.tmatesoft.svn.core.internal.wc.SVNDiffStatusEditor;
0034: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
0035: import org.tmatesoft.svn.core.internal.wc.SVNEventFactory;
0036: import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
0037: import org.tmatesoft.svn.core.internal.wc.SVNMergeCallback;
0038: import org.tmatesoft.svn.core.internal.wc.SVNRemoteDiffEditor;
0039: import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminAreaInfo;
0040: import org.tmatesoft.svn.core.internal.wc.admin.SVNEntry;
0041: import org.tmatesoft.svn.core.internal.wc.admin.SVNReporter;
0042: import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess;
0043: import org.tmatesoft.svn.core.io.ISVNReporter;
0044: import org.tmatesoft.svn.core.io.ISVNReporterBaton;
0045: import org.tmatesoft.svn.core.io.SVNRepository;
0046:
0047: /**
0048: * The <b>SVNDiffClient</b> class provides methods allowing to get differences
0049: * between versioned items ('diff' operation) as well as ones intended for
0050: * merging file contents.
0051: *
0052: * <p>
0053: * Here's a list of the <b>SVNDiffClient</b>'s methods
0054: * matched against corresponing commands of the SVN command line
0055: * client:
0056: *
0057: * <table cellpadding="3" cellspacing="1" border="0" width="40%" bgcolor="#999933">
0058: * <tr bgcolor="#ADB8D9" align="left">
0059: * <td><b>SVNKit</b></td>
0060: * <td><b>Subversion</b></td>
0061: * </tr>
0062: * <tr bgcolor="#EAEAEA" align="left">
0063: * <td>doDiff()</td><td>'svn diff'</td>
0064: * </tr>
0065: * <tr bgcolor="#EAEAEA" align="left">
0066: * <td>doDiffStatus()</td><td>'svn diff --summarize'</td>
0067: * </tr>
0068: * <tr bgcolor="#EAEAEA" align="left">
0069: * <td>doMerge()</td><td>'svn merge'</td>
0070: * </tr>
0071: * </table>
0072: *
0073: * @version 1.1.1
0074: * @author TMate Software Ltd.
0075: */
0076: public class SVNDiffClient extends SVNBasicClient {
0077:
0078: private ISVNDiffGenerator myDiffGenerator;
0079: private SVNDiffOptions myDiffOptions;
0080:
0081: /**
0082: * Constructs and initializes an <b>SVNDiffClient</b> object
0083: * with the specified run-time configuration and authentication
0084: * drivers.
0085: *
0086: * <p>
0087: * If <code>options</code> is <span class="javakeyword">null</span>,
0088: * then this <b>SVNDiffClient</b> will be using a default run-time
0089: * configuration driver which takes client-side settings from the
0090: * default SVN's run-time configuration area but is not able to
0091: * change those settings (read more on {@link ISVNOptions} and {@link SVNWCUtil}).
0092: *
0093: * <p>
0094: * If <code>authManager</code> is <span class="javakeyword">null</span>,
0095: * then this <b>SVNDiffClient</b> will be using a default authentication
0096: * and network layers driver (see {@link SVNWCUtil#createDefaultAuthenticationManager()})
0097: * which uses server-side settings and auth storage from the
0098: * default SVN's run-time configuration area (or system properties
0099: * if that area is not found).
0100: *
0101: * @param authManager an authentication and network layers driver
0102: * @param options a run-time configuration options driver
0103: */
0104: public SVNDiffClient(ISVNAuthenticationManager authManager,
0105: ISVNOptions options) {
0106: super (authManager, options);
0107: }
0108:
0109: public SVNDiffClient(ISVNRepositoryPool repositoryPool,
0110: ISVNOptions options) {
0111: super (repositoryPool, options);
0112: }
0113:
0114: /**
0115: * Sets the specified diff driver for this object to use for
0116: * generating and writing file differences to an otput stream.
0117: *
0118: * <p>
0119: * If no specific diff driver was set in this way, a default one
0120: * will be used (see {@link DefaultSVNDiffGenerator}).
0121: *
0122: * @param diffGenerator a diff driver
0123: * @see #getDiffGenerator()
0124: */
0125: public void setDiffGenerator(ISVNDiffGenerator diffGenerator) {
0126: myDiffGenerator = diffGenerator;
0127: }
0128:
0129: /**
0130: * Returns the diff driver being in use.
0131: *
0132: * <p>
0133: * If no specific diff driver was previously provided, a default one
0134: * will be returned (see {@link DefaultSVNDiffGenerator}).
0135: *
0136: * @return the diff driver being in use
0137: * @see #setDiffGenerator(ISVNDiffGenerator)
0138: */
0139: public ISVNDiffGenerator getDiffGenerator() {
0140: if (myDiffGenerator == null) {
0141: myDiffGenerator = new DefaultSVNDiffGenerator();
0142: }
0143: return myDiffGenerator;
0144: }
0145:
0146: /**
0147: * Sets diff options for this client to use in merge operations.
0148: *
0149: * @param diffOptions diff options object
0150: */
0151: public void setMergeOptions(SVNDiffOptions diffOptions) {
0152: myDiffOptions = diffOptions;
0153: }
0154:
0155: /**
0156: * Gets the diff options that are used in merge operations
0157: * by this client. Creates a new one if none was used before.
0158: *
0159: * @return diff options
0160: */
0161: public SVNDiffOptions getMergeOptions() {
0162: if (myDiffOptions == null) {
0163: myDiffOptions = new SVNDiffOptions();
0164: }
0165: return myDiffOptions;
0166: }
0167:
0168: /**
0169: * Generates the differences for the specified URL taken from the two
0170: * specified revisions and writes the result to the provided output
0171: * stream.
0172: *
0173: * <p>
0174: * Corresponds to the SVN command line client's
0175: * <code>'svn diff -r N:M URL'</code> command.
0176: *
0177: * @param url a repository location
0178: * @param pegRevision a revision in which <code>url</code> is first looked up
0179: * @param rN an old revision
0180: * @param rM a new revision
0181: * @param recursive <span class="javakeyword">true</span> to descend
0182: * recursively
0183: * @param useAncestry if <span class="javakeyword">true</span> then
0184: * the paths ancestry will be noticed while calculating differences,
0185: * otherwise not
0186: * @param result the target {@link java.io.OutputStream} where
0187: * the differences will be written to
0188: * @throws SVNException if one of the following is true:
0189: * <ul>
0190: * <li>at least one of <code>rN</code>, <code>rM</code> and
0191: * <code>pegRevision</code> is invalid
0192: * <li>at least one of <code>rN</code> and <code>rM</code> is
0193: * a local revision (see {@link SVNRevision#isLocal()})
0194: * <li><code>url</code> was not found in <code>rN</code>
0195: * <li><code>url</code> was not found in <code>rM</code>
0196: * </ul>
0197: */
0198: public void doDiff(SVNURL url, SVNRevision pegRevision,
0199: SVNRevision rN, SVNRevision rM, boolean recursive,
0200: boolean useAncestry, OutputStream result)
0201: throws SVNException {
0202: if (!rN.isValid() || !rM.isValid()) {
0203: SVNErrorMessage err = SVNErrorMessage.create(
0204: SVNErrorCode.CLIENT_BAD_REVISION,
0205: "Both rN and rM revisions should be specified");
0206: SVNErrorManager.error(err);
0207: }
0208: if (rN.isLocal() || rM.isLocal()) {
0209: SVNErrorMessage err = SVNErrorMessage.create(
0210: SVNErrorCode.CLIENT_BAD_REVISION,
0211: "Both rN and rM revisions must be non-local for "
0212: + "a pegged diff of an URL");
0213: SVNErrorManager.error(err);
0214: }
0215: getDiffGenerator().init(url.toString(), url.toString());
0216: doDiffURLURL(url, null, rN, url, null, rM, pegRevision,
0217: recursive, useAncestry, result);
0218: }
0219:
0220: /**
0221: * Generates the differences for the specified path taken from the two
0222: * specified revisions and writes the result to the provided output
0223: * stream.
0224: *
0225: * <p>
0226: * If <code>rM</code> is a local revision (see {@link SVNRevision#isLocal()}),
0227: * then the Working Copy <code>path</code> is compared with the corresponding
0228: * repository file at revision <code>rN</code> (that is similar to the SVN command
0229: * line client's <code>'svn diff -r N path'</code> command).
0230: *
0231: * <p>
0232: * Otherwise if both <code>rN</code> and <code>rM</code> are non-local, then
0233: * the repository location of <code>path</code> is compared for these
0234: * revisions (<code>'svn diff -r N:M URL'</code>).
0235: *
0236: * @param path a Working Copy file path
0237: * @param pegRevision a revision in which the repository location of <code>path</code>
0238: * is first looked up
0239: * @param rN an old revision
0240: * @param rM a new revision (or a local one)
0241: * @param recursive <span class="javakeyword">true</span> to descend
0242: * recursively
0243: * @param useAncestry if <span class="javakeyword">true</span> then
0244: * the paths ancestry will be noticed while calculating differences,
0245: * otherwise not
0246: * @param result the target {@link java.io.OutputStream} where
0247: * the differences will be written to
0248: * @throws SVNException if one of the following is true:
0249: * <ul>
0250: * <li>at least one of <code>rN</code>, <code>rM</code> and
0251: * <code>pegRevision</code> is invalid
0252: * <li>both <code>rN</code> and <code>rM</code> are
0253: * local revisions
0254: * <li><code>path</code> was not found in <code>rN</code>
0255: * <li><code>path</code> was not found in <code>rM</code>
0256: * </ul>
0257: */
0258: public void doDiff(File path, SVNRevision pegRevision,
0259: SVNRevision rN, SVNRevision rM, boolean recursive,
0260: boolean useAncestry, OutputStream result)
0261: throws SVNException {
0262: if (!rN.isValid() || !rM.isValid()) {
0263: SVNErrorMessage err = SVNErrorMessage.create(
0264: SVNErrorCode.CLIENT_BAD_REVISION,
0265: "Both rN and rM revisions should be specified");
0266: SVNErrorManager.error(err);
0267: }
0268: if (rN.isLocal() && rM.isLocal()) {
0269: SVNErrorMessage err = SVNErrorMessage.create(
0270: SVNErrorCode.CLIENT_BAD_REVISION,
0271: "At least one revision must be non-local for "
0272: + "a pegged diff");
0273: SVNErrorManager.error(err);
0274: }
0275: path = new File(SVNPathUtil.validateFilePath(path
0276: .getAbsolutePath())).getAbsoluteFile();
0277: getDiffGenerator().init(path.getAbsolutePath(),
0278: path.getAbsolutePath());
0279: if (!(rM == SVNRevision.BASE || rM == SVNRevision.WORKING || rM == SVNRevision.COMMITTED)) {
0280: if ((rN == SVNRevision.BASE || rN == SVNRevision.WORKING || rN == SVNRevision.COMMITTED)) {
0281: doDiffURLWC(path, rM, pegRevision, path, rN, true,
0282: recursive, useAncestry, result);
0283: } else {
0284: doDiffURLURL(null, path, rN, null, path, rM,
0285: pegRevision, recursive, useAncestry, result);
0286: }
0287: } else {
0288: // head, prev,date,number will go here.
0289: doDiffURLWC(path, rN, pegRevision, path, rM, false,
0290: recursive, useAncestry, result);
0291: }
0292: }
0293:
0294: /**
0295: * Generates the differences for the specified URLs taken from the two
0296: * specified revisions and writes the result to the provided output
0297: * stream.
0298: *
0299: * <p>
0300: * Corresponds to the SVN command line client's
0301: * <code>'svn diff -r N:M URL1 URL2'</code> command.
0302: *
0303: * @param url1 the first URL to be compared
0304: * @param rN a revision of <code>url1</code>
0305: * @param url2 the second URL to be compared
0306: * @param rM a revision of <code>url2</code>
0307: * @param recursive <span class="javakeyword">true</span> to descend
0308: * recursively
0309: * @param useAncestry if <span class="javakeyword">true</span> then
0310: * the paths ancestry will be noticed while calculating differences,
0311: * otherwise not
0312: * @param result the target {@link java.io.OutputStream} where
0313: * the differences will be written to
0314: * @throws SVNException if one of the following is true:
0315: * <ul>
0316: * <li>at least one of <code>rN</code> and <code>rM</code> is
0317: * invalid
0318: * <li><code>url1</code> was not found in <code>rN</code>
0319: * <li><code>url2</code> was not found in <code>rM</code>
0320: * </ul>
0321: */
0322: public void doDiff(SVNURL url1, SVNRevision rN, SVNURL url2,
0323: SVNRevision rM, boolean recursive, boolean useAncestry,
0324: OutputStream result) throws SVNException {
0325: if (!rN.isValid() || !rM.isValid()) {
0326: SVNErrorMessage err = SVNErrorMessage.create(
0327: SVNErrorCode.CLIENT_BAD_REVISION,
0328: "Both rN and rM revisions should be specified");
0329: SVNErrorManager.error(err);
0330: }
0331: getDiffGenerator().init(url1.toString(), url2.toString());
0332: doDiffURLURL(url1, null, rN, url2, null, rM,
0333: SVNRevision.UNDEFINED, recursive, useAncestry, result);
0334: }
0335:
0336: /**
0337: * Generates the differences comparing the specified URL in a certain
0338: * revision against either the specified Working Copy path or its repository
0339: * location URL in the specified revision, and writes the result to the provided output
0340: * stream.
0341: *
0342: * <p>
0343: * If <code>rN</code> is not a local revision (see {@link SVNRevision#isLocal()}),
0344: * then its repository location URL as it is in the revision represented by
0345: * <code>rN</code> is taken for comparison with <code>url2</code>.
0346: *
0347: * <p>
0348: * Corresponds to the SVN command line client's
0349: * <code>'svn diff -r N:M PATH URL'</code> command.
0350: *
0351: * @param path1 a WC path
0352: * @param rN a revision of <code>path1</code>
0353: * @param url2 a repository location URL that is to be compared
0354: * against <code>path1</code> (or its repository location)
0355: * @param rM a revision of <code>url2</code>
0356: * @param recursive <span class="javakeyword">true</span> to descend
0357: * recursively
0358: * @param useAncestry if <span class="javakeyword">true</span> then
0359: * the paths ancestry will be noticed while calculating differences,
0360: * otherwise not
0361: * @param result the target {@link java.io.OutputStream} where
0362: * the differences will be written to
0363: * @throws SVNException if one of the following is true:
0364: * <ul>
0365: * <li>at least one of <code>rN</code> and <code>rM</code> is
0366: * invalid
0367: * <li><code>path1</code> is not under version control
0368: * <li><code>path1</code> has no URL
0369: * <li><code>url2</code> was not found in <code>rM</code>
0370: * <li>the repository location of <code>path1</code> was
0371: * not found in <code>rN</code>
0372: * </ul>
0373: */
0374: public void doDiff(File path1, SVNRevision rN, SVNURL url2,
0375: SVNRevision rM, boolean recursive, boolean useAncestry,
0376: OutputStream result) throws SVNException {
0377: if (!rN.isValid() || !rM.isValid()) {
0378: SVNErrorMessage err = SVNErrorMessage.create(
0379: SVNErrorCode.CLIENT_BAD_REVISION,
0380: "Both rN and rM revisions should be specified");
0381: SVNErrorManager.error(err);
0382: }
0383: getDiffGenerator().init(path1.getAbsolutePath(),
0384: url2.toString());
0385: if (rN == SVNRevision.BASE || rN == SVNRevision.WORKING) {
0386: doDiffURLWC(url2, rM, SVNRevision.UNDEFINED, path1, rN,
0387: true, recursive, useAncestry, result);
0388: } else {
0389: doDiffURLURL(null, path1, rN, url2, null, rM,
0390: SVNRevision.UNDEFINED, recursive, useAncestry,
0391: result);
0392: }
0393: }
0394:
0395: /**
0396: * Generates the differences comparing either the specified Working Copy path or
0397: * its repository location URL in the specified revision against the specified URL
0398: * in a certain revision, and writes the result to the provided output stream.
0399: *
0400: * <p>
0401: * If <code>rM</code> is not a local revision (see {@link SVNRevision#isLocal()}),
0402: * then its repository location URL as it is in the revision represented by
0403: * <code>rM</code> is taken for comparison with <code>url1</code>.
0404: *
0405: * <p>
0406: * Corresponds to the SVN command line client's
0407: * <code>'svn diff -r N:M URL PATH'</code> command.
0408: *
0409: * @param url1 a repository location URL
0410: * @param rN a revision of <code>url1</code>
0411: * @param path2 a WC path that is to be compared
0412: * against <code>url1</code>
0413: * @param rM a revision of <code>path2</code>
0414: * @param recursive <span class="javakeyword">true</span> to descend
0415: * recursively
0416: * @param useAncestry if <span class="javakeyword">true</span> then
0417: * the paths ancestry will be noticed while calculating differences,
0418: * otherwise not
0419: * @param result the target {@link java.io.OutputStream} where
0420: * the differences will be written to
0421: * @throws SVNException if one of the following is true:
0422: * <ul>
0423: * <li>at least one of <code>rN</code> and <code>rM</code> is
0424: * invalid
0425: * <li><code>path2</code> is not under version control
0426: * <li><code>path2</code> has no URL
0427: * <li><code>url1</code> was not found in <code>rN</code>
0428: * <li>the repository location of <code>path2</code> was
0429: * not found in <code>rM</code>
0430: * </ul>
0431: */
0432: public void doDiff(SVNURL url1, SVNRevision rN, File path2,
0433: SVNRevision rM, boolean recursive, boolean useAncestry,
0434: OutputStream result) throws SVNException {
0435: if (!rN.isValid() || !rM.isValid()) {
0436: SVNErrorMessage err = SVNErrorMessage.create(
0437: SVNErrorCode.CLIENT_BAD_REVISION,
0438: "Both rN and rM revisions should be specified");
0439: SVNErrorManager.error(err);
0440: }
0441: getDiffGenerator().init(url1.toString(),
0442: path2.getAbsolutePath());
0443: if (rM == SVNRevision.BASE || rM == SVNRevision.WORKING) {
0444: doDiffURLWC(url1, rN, SVNRevision.UNDEFINED, path2, rM,
0445: false, recursive, useAncestry, result);
0446: } else {
0447: doDiffURLURL(url1, null, rN, null, path2, rM,
0448: SVNRevision.UNDEFINED, recursive, useAncestry,
0449: result);
0450: }
0451: }
0452:
0453: /**
0454: * Generates the differences comparing either the specified Working Copy paths or
0455: * their repository location URLs (any combinations are possible) in the specified
0456: * revisions and writes the result to the provided output stream.
0457: *
0458: * <p>
0459: * If both <code>rN</code> and <code>rM</code> are local revisions (see {@link SVNRevision#isLocal()}),
0460: * then a Working Copy <code>path2</code> is compared against a Working Copy <code>path1</code>.
0461: *
0462: * <p>
0463: * If <code>rN</code> is a local revision but <code>rM</code> is not, then
0464: * the repository location URL of <code>path2</code> as it is in the revision
0465: * represented by <code>rM</code> is compared against the Working Copy <code>path1</code>.
0466: *
0467: * <p>
0468: * If <code>rM</code> is a local revision but <code>rN</code> is not, then
0469: * the Working Copy <code>path2</code> is compared against the repository location
0470: * URL of <code>path1</code> as it is in the revision represented by <code>rN</code>.
0471: *
0472: * <p>
0473: * If both <code>rN</code> and <code>rM</code> are non-local revisions, then the
0474: * repository location URL of <code>path2</code> in revision <code>rM</code> is
0475: * compared against the repository location URL of <code>path1</code> in revision
0476: * <code>rN</code>.
0477: *
0478: * @param path1 a WC path
0479: * @param rN a revision of <code>path1</code>
0480: * @param path2 a WC path that is to be compared
0481: * against <code>path1</code>
0482: * @param rM a revision of <code>path2</code>
0483: * @param recursive <span class="javakeyword">true</span> to descend
0484: * recursively
0485: * @param useAncestry if <span class="javakeyword">true</span> then
0486: * the paths ancestry will be noticed while calculating differences,
0487: * otherwise not
0488: * @param result the target {@link java.io.OutputStream} where
0489: * the differences will be written to
0490: * @throws SVNException if one of the following is true:
0491: * <ul>
0492: * <li>at least one of <code>rN</code> and <code>rM</code> is
0493: * invalid
0494: * <li><code>path1</code> is not under version control
0495: * <li><code>path1</code> has no URL
0496: * <li><code>path2</code> is not under version control
0497: * <li><code>path2</code> has no URL
0498: * <li>the repository location of <code>path1</code> was
0499: * not found in <code>rN</code>
0500: * <li>the repository location of <code>path2</code> was
0501: * not found in <code>rM</code>
0502: * <li>both <code>rN</code> and <code>rM</code> are local,
0503: * but either <code>path1</code> does not equal <code>path2</code>,
0504: * or <code>rN</code> is not {@link SVNRevision#BASE}, or
0505: * <code>rM</code> is not {@link SVNRevision#WORKING}
0506: * </ul>
0507: */
0508: public void doDiff(File path1, SVNRevision rN, File path2,
0509: SVNRevision rM, boolean recursive, boolean useAncestry,
0510: OutputStream result) throws SVNException {
0511: if (!rN.isValid() || !rM.isValid()) {
0512: SVNErrorMessage err = SVNErrorMessage.create(
0513: SVNErrorCode.CLIENT_BAD_REVISION,
0514: "Both rN and rM revisions should be specified");
0515: SVNErrorManager.error(err);
0516: }
0517:
0518: boolean isPath1Local = rN == SVNRevision.WORKING
0519: || rN == SVNRevision.BASE;
0520: boolean isPath2Local = rM == SVNRevision.WORKING
0521: || rM == SVNRevision.BASE;
0522: getDiffGenerator().init(path1.getAbsolutePath(),
0523: path2.getAbsolutePath());
0524: if (isPath1Local && isPath2Local) {
0525: doDiffWCWC(path1, rN, path2, rM, recursive, useAncestry,
0526: result);
0527: } else if (isPath1Local) {
0528: doDiffURLWC(path2, rM, SVNRevision.UNDEFINED, path1, rN,
0529: true, recursive, useAncestry, result);
0530: } else if (isPath2Local) {
0531: doDiffURLWC(path1, rN, SVNRevision.UNDEFINED, path2, rM,
0532: false, recursive, useAncestry, result);
0533: } else {
0534: doDiffURLURL(null, path1, rN, null, path2, rM,
0535: SVNRevision.UNDEFINED, recursive, useAncestry,
0536: result);
0537: }
0538: }
0539:
0540: /**
0541: * Diffs one path against another one providing short status-like change information to the provided
0542: * handler. This method functionality is equivalent to the 'svn diff --summarize' command.
0543: *
0544: * @param path1 the path of a left-hand item to diff
0545: * @param rN a revision of <code>path1</code>
0546: * @param path2 the path of a right-hand item to diff
0547: * @param rM a revision of <code>path2</code>
0548: * @param recursive controls whether operation must recurse or not
0549: * @param useAncestry if <span class="javakeyword">true</span> then
0550: * the paths ancestry will be noticed while calculating differences,
0551: * otherwise not
0552: * @param handler a diff status handler
0553: * @throws SVNException
0554: * @since 1.1, new in Subversion 1.4
0555: */
0556: public void doDiffStatus(File path1, SVNRevision rN, File path2,
0557: SVNRevision rM, boolean recursive, boolean useAncestry,
0558: ISVNDiffStatusHandler handler) throws SVNException {
0559: if (handler == null) {
0560: return;
0561: }
0562: if (!rN.isValid() || !rM.isValid()) {
0563: SVNErrorMessage err = SVNErrorMessage.create(
0564: SVNErrorCode.CLIENT_BAD_REVISION,
0565: "Both rN and rM revisions should be specified");
0566: SVNErrorManager.error(err);
0567: }
0568:
0569: boolean isPath1Local = rN == SVNRevision.WORKING
0570: || rN == SVNRevision.BASE;
0571: boolean isPath2Local = rM == SVNRevision.WORKING
0572: || rM == SVNRevision.BASE;
0573: if (isPath1Local || isPath2Local) {
0574: SVNErrorMessage err = SVNErrorMessage
0575: .create(SVNErrorCode.UNSUPPORTED_FEATURE,
0576: "Summarizing diff can only compare repository to repository");
0577: SVNErrorManager.error(err);
0578: }
0579: doDiffURLURL(null, path1, rN, null, path2, rM,
0580: SVNRevision.UNDEFINED, recursive, useAncestry, handler);
0581: }
0582:
0583: /**
0584: * Diffs a path against a url providing short status-like change information to the provided
0585: * handler. This method functionality is equivalent to the 'svn diff --summarize' command.
0586: *
0587: * @param path1 the path of a left-hand item to diff
0588: * @param rN a revision of <code>path1</code>
0589: * @param url2 the url of a right-hand item to diff
0590: * @param rM a revision of <code>url2</code>
0591: * @param recursive controls whether operation must recurse or not
0592: * @param useAncestry if <span class="javakeyword">true</span> then
0593: * the paths ancestry will be noticed while calculating differences,
0594: * otherwise not
0595: * @param handler a diff status handler
0596: * @throws SVNException
0597: * @since 1.1, new in Subversion 1.4
0598: */
0599: public void doDiffStatus(File path1, SVNRevision rN, SVNURL url2,
0600: SVNRevision rM, boolean recursive, boolean useAncestry,
0601: ISVNDiffStatusHandler handler) throws SVNException {
0602: if (handler == null) {
0603: return;
0604: }
0605: if (!rN.isValid() || !rM.isValid()) {
0606: SVNErrorMessage err = SVNErrorMessage.create(
0607: SVNErrorCode.CLIENT_BAD_REVISION,
0608: "Both rN and rM revisions should be specified");
0609: SVNErrorManager.error(err);
0610: }
0611: if (rN == SVNRevision.BASE || rN == SVNRevision.WORKING) {
0612: SVNErrorMessage err = SVNErrorMessage
0613: .create(SVNErrorCode.UNSUPPORTED_FEATURE,
0614: "Summarizing diff can only compare repository to repository");
0615: SVNErrorManager.error(err);
0616: } else {
0617: doDiffURLURL(null, path1, rN, url2, null, rM,
0618: SVNRevision.UNDEFINED, recursive, useAncestry,
0619: handler);
0620: }
0621: }
0622:
0623: /**
0624: * Diffs a url against a path providing short status-like change information to the provided
0625: * handler. This method functionality is equivalent to the 'svn diff --summarize' command.
0626: *
0627: * @param url1 the url of a left-hand item to diff
0628: * @param rN a revision of <code>url1</code>
0629: * @param path2 the path of a right-hand item to diff
0630: * @param rM a revision of <code>path2</code>
0631: * @param recursive controls whether operation must recurse or not
0632: * @param useAncestry if <span class="javakeyword">true</span> then
0633: * the paths ancestry will be noticed while calculating differences,
0634: * otherwise not
0635: * @param handler a diff status handler
0636: * @throws SVNException
0637: * @since 1.1, new in Subversion 1.4
0638: */
0639: public void doDiffStatus(SVNURL url1, SVNRevision rN, File path2,
0640: SVNRevision rM, boolean recursive, boolean useAncestry,
0641: ISVNDiffStatusHandler handler) throws SVNException {
0642: if (handler == null) {
0643: return;
0644: }
0645: if (!rN.isValid() || !rM.isValid()) {
0646: SVNErrorMessage err = SVNErrorMessage.create(
0647: SVNErrorCode.CLIENT_BAD_REVISION,
0648: "Both rN and rM revisions should be specified");
0649: SVNErrorManager.error(err);
0650: }
0651: if (rM == SVNRevision.BASE || rM == SVNRevision.WORKING) {
0652: SVNErrorMessage err = SVNErrorMessage
0653: .create(SVNErrorCode.UNSUPPORTED_FEATURE,
0654: "Summarizing diff can only compare repository to repository");
0655: SVNErrorManager.error(err);
0656: } else {
0657: doDiffURLURL(url1, null, rN, null, path2, rM,
0658: SVNRevision.UNDEFINED, recursive, useAncestry,
0659: handler);
0660: }
0661: }
0662:
0663: /**
0664: * Diffs one url against another one providing short status-like change information to the provided
0665: * handler. This method functionality is equivalent to the 'svn diff --summarize' command.
0666: *
0667: * @param url1 the url of a left-hand item to diff
0668: * @param rN a revision of <code>url1</code>
0669: * @param url2 the url of a right-hand item to diff
0670: * @param rM a revision of <code>url2</code>
0671: * @param recursive controls whether operation must recurse or not
0672: * @param useAncestry if <span class="javakeyword">true</span> then
0673: * the paths ancestry will be noticed while calculating differences,
0674: * otherwise not
0675: * @param handler a diff status handler
0676: * @throws SVNException
0677: * @since 1.1, new in Subversion 1.4
0678: */
0679: public void doDiffStatus(SVNURL url1, SVNRevision rN, SVNURL url2,
0680: SVNRevision rM, boolean recursive, boolean useAncestry,
0681: ISVNDiffStatusHandler handler) throws SVNException {
0682: if (handler == null) {
0683: return;
0684: }
0685: if (!rN.isValid() || !rM.isValid()) {
0686: SVNErrorMessage err = SVNErrorMessage.create(
0687: SVNErrorCode.CLIENT_BAD_REVISION,
0688: "Both rN and rM revisions should be specified");
0689: SVNErrorManager.error(err);
0690: }
0691: doDiffURLURL(url1, null, rN, url2, null, rM,
0692: SVNRevision.UNDEFINED, recursive, useAncestry, handler);
0693: }
0694:
0695: private void doDiffURLWC(SVNURL url1, SVNRevision revision1,
0696: SVNRevision pegRevision, File path2, SVNRevision revision2,
0697: boolean reverse, boolean recursive, boolean useAncestry,
0698: OutputStream result) throws SVNException {
0699: SVNWCAccess wcAccess = createWCAccess();
0700: try {
0701: SVNAdminAreaInfo info = wcAccess.openAnchor(path2, false,
0702: recursive ? SVNWCAccess.INFINITE_DEPTH : 0);
0703: File anchorPath = info.getAnchor().getRoot();
0704: String target = "".equals(info.getTargetName()) ? null
0705: : info.getTargetName();
0706:
0707: SVNEntry anchorEntry = info.getAnchor().getEntry("", false);
0708: if (anchorEntry == null) {
0709: SVNErrorMessage err = SVNErrorMessage.create(
0710: SVNErrorCode.ENTRY_NOT_FOUND,
0711: "''{0}'' is not under version control",
0712: anchorPath);
0713: SVNErrorManager.error(err);
0714: } else if (anchorEntry.getURL() == null) {
0715: SVNErrorMessage err = SVNErrorMessage.create(
0716: SVNErrorCode.ENTRY_MISSING_URL,
0717: "''{0}'' has no URL", anchorPath);
0718: SVNErrorManager.error(err);
0719: }
0720: SVNURL anchorURL = anchorEntry.getSVNURL();
0721: if (pegRevision.isValid()) {
0722: SVNRepositoryLocation[] locations = getLocations(url1,
0723: null, null, pegRevision, revision1,
0724: SVNRevision.UNDEFINED);
0725: url1 = locations[0].getURL();
0726: String anchorPath2 = SVNPathUtil.append(anchorURL
0727: .toString(), target == null ? "" : target);
0728: getDiffGenerator().init(url1.toString(), anchorPath2);
0729: }
0730: SVNRepository repository = createRepository(anchorURL, true);
0731: long revNumber = getRevisionNumber(revision1, repository,
0732: null);
0733: AbstractDiffCallback callback = new SVNDiffCallback(info,
0734: getDiffGenerator(), reverse ? -1 : revNumber,
0735: reverse ? revNumber : -1, result);
0736: SVNDiffEditor editor = new SVNDiffEditor(
0737: wcAccess,
0738: info,
0739: callback,
0740: useAncestry,
0741: reverse /* reverse */,
0742: revision2 == SVNRevision.BASE
0743: || revision2 == SVNRevision.COMMITTED /* compare to base */,
0744: recursive);
0745: SVNReporter reporter = new SVNReporter(info, info
0746: .getAnchor().getFile(info.getTargetName()), false,
0747: recursive, getDebugLog());
0748:
0749: long pegRevisionNumber = getRevisionNumber(revision2,
0750: repository, path2);
0751: try {
0752: repository.diff(url1, revNumber, pegRevisionNumber,
0753: target, !useAncestry, recursive, true,
0754: reporter, SVNCancellableEditor.newInstance(
0755: editor, this , getDebugLog()));
0756: } finally {
0757: editor.cleanup();
0758: }
0759: } finally {
0760: wcAccess.close();
0761: }
0762: }
0763:
0764: private void doDiffURLWC(File path1, SVNRevision revision1,
0765: SVNRevision pegRevision, File path2, SVNRevision revision2,
0766: boolean reverse, boolean recursive, boolean useAncestry,
0767: OutputStream result) throws SVNException {
0768:
0769: SVNWCAccess wcAccess = createWCAccess();
0770: try {
0771: SVNAdminAreaInfo info = wcAccess.openAnchor(path2, false,
0772: recursive ? SVNWCAccess.INFINITE_DEPTH : 0);
0773:
0774: File anchorPath = info.getAnchor().getRoot();
0775: String target = "".equals(info.getTargetName()) ? null
0776: : info.getTargetName();
0777:
0778: SVNEntry anchorEntry = info.getAnchor().getEntry("", false);
0779: if (anchorEntry == null) {
0780: SVNErrorMessage err = SVNErrorMessage.create(
0781: SVNErrorCode.ENTRY_NOT_FOUND,
0782: "''{0}'' is not under version control",
0783: anchorPath);
0784: SVNErrorManager.error(err);
0785: } else if (anchorEntry.getURL() == null) {
0786: SVNErrorMessage err = SVNErrorMessage.create(
0787: SVNErrorCode.ENTRY_MISSING_URL,
0788: "''{0}'' has no URL", anchorPath);
0789: SVNErrorManager.error(err);
0790: }
0791: SVNURL url1;
0792: SVNURL anchorURL = anchorEntry.getSVNURL();
0793: if (pegRevision.isValid()) {
0794: SVNRepositoryLocation[] locations = getLocations(null,
0795: path1, null, pegRevision, revision1,
0796: SVNRevision.UNDEFINED);
0797: url1 = locations[0].getURL();
0798: String anchorPath2 = SVNPathUtil.append(anchorURL
0799: .toString(), target == null ? "" : target);
0800: if (!reverse) {
0801: getDiffGenerator().init(url1.toString(),
0802: anchorPath2);
0803: } else {
0804: getDiffGenerator().init(anchorPath2,
0805: url1.toString());
0806: }
0807: } else {
0808: url1 = getURL(path1);
0809: }
0810: SVNRepository repository = createRepository(anchorURL, true);
0811: long revNumber = getRevisionNumber(revision1, repository,
0812: path1);
0813: AbstractDiffCallback callback = new SVNDiffCallback(info,
0814: getDiffGenerator(), reverse ? -1 : revNumber,
0815: reverse ? revNumber : -1, result);
0816: SVNDiffEditor editor = new SVNDiffEditor(
0817: wcAccess,
0818: info,
0819: callback,
0820: useAncestry,
0821: reverse /* reverse */,
0822: revision2 == SVNRevision.BASE
0823: || revision2 == SVNRevision.COMMITTED /* compare to base */,
0824: recursive);
0825: SVNReporter reporter = new SVNReporter(info, info
0826: .getAnchor().getFile(info.getTargetName()), false,
0827: recursive, getDebugLog());
0828:
0829: // this should be rev2.
0830: long pegRevisionNumber = getRevisionNumber(revision2,
0831: repository, path2);
0832: try {
0833: repository.diff(url1, revNumber, pegRevisionNumber,
0834: target, !useAncestry, recursive, true,
0835: reporter, SVNCancellableEditor.newInstance(
0836: editor, this , getDebugLog()));
0837: } finally {
0838: editor.cleanup();
0839: }
0840: } finally {
0841: wcAccess.close();
0842: }
0843: }
0844:
0845: private void doDiffWCWC(File path1, SVNRevision revision1,
0846: File path2, SVNRevision revision2, boolean recursive,
0847: boolean useAncestry, OutputStream result)
0848: throws SVNException {
0849: if (!path1.equals(path2)
0850: || !(revision1 == SVNRevision.BASE && revision2 == SVNRevision.WORKING)) {
0851: SVNErrorMessage err = SVNErrorMessage
0852: .create(
0853: SVNErrorCode.UNSUPPORTED_FEATURE,
0854: "Only diffs between a path's text-base "
0855: + "and its working files are supported at this time (-rBASE:WORKING)");
0856: SVNErrorManager.error(err);
0857: }
0858:
0859: SVNWCAccess wcAccess = createWCAccess();
0860: try {
0861: SVNAdminAreaInfo info = wcAccess.openAnchor(path1, false,
0862: recursive ? SVNWCAccess.INFINITE_DEPTH : 0);
0863: SVNEntry entry = wcAccess.getEntry(path1, false);
0864: if (entry == null) {
0865: SVNErrorMessage err = SVNErrorMessage.create(
0866: SVNErrorCode.ENTRY_NOT_FOUND,
0867: "''{0}'' is not under version control", path1);
0868: SVNErrorManager.error(err);
0869: }
0870: long rev = getRevisionNumber(revision1, null, path1);
0871: AbstractDiffCallback callback = new SVNDiffCallback(info,
0872: getDiffGenerator(), rev, -1, result);
0873: SVNDiffEditor editor = new SVNDiffEditor(wcAccess, info,
0874: callback, useAncestry, false, false, recursive);
0875: try {
0876: editor.closeEdit();
0877: } finally {
0878: editor.cleanup();
0879: }
0880: } finally {
0881: wcAccess.close();
0882: }
0883: }
0884:
0885: private void doDiffURLURL(SVNURL url1, File path1,
0886: SVNRevision revision1, SVNURL url2, File path2,
0887: SVNRevision revision2, SVNRevision pegRevision,
0888: boolean recursive, boolean useAncestry, OutputStream result)
0889: throws SVNException {
0890: File basePath = null;
0891: if (path1 != null) {
0892: basePath = path1;
0893: }
0894: if (path2 != null) {
0895: basePath = path2;
0896: }
0897: if (pegRevision.isValid()) {
0898: SVNRepositoryLocation[] locations = getLocations(url2,
0899: path2, null, pegRevision, revision1, revision2);
0900: url1 = locations[0].getURL();
0901: url2 = locations[1].getURL();
0902:
0903: getDiffGenerator().init(url1.toString(), url2.toString());
0904: } else {
0905: url1 = url1 == null ? getURL(path1) : url1;
0906: url2 = url2 == null ? getURL(path2) : url2;
0907: }
0908: SVNRepository repository1 = createRepository(url1, true);
0909: SVNRepository repository2 = createRepository(url2, false);
0910:
0911: final long rev1 = getRevisionNumber(revision1, repository1,
0912: path1);
0913: long rev2 = -1;
0914: String target1 = null;
0915: SVNNodeKind kind1 = null;
0916: SVNNodeKind kind2 = null;
0917: try {
0918: rev2 = getRevisionNumber(revision2, repository2, path2);
0919: kind1 = repository1.checkPath("", rev1);
0920: kind2 = repository2.checkPath("", rev2);
0921: if (kind1 == SVNNodeKind.NONE) {
0922: SVNErrorMessage err = SVNErrorMessage
0923: .create(
0924: SVNErrorCode.FS_NOT_FOUND,
0925: "''{0}'' was not found in the repository at revision {1}",
0926: new Object[] { url1, new Long(rev1) });
0927: SVNErrorManager.error(err);
0928: } else if (kind2 == SVNNodeKind.NONE) {
0929: SVNErrorMessage err = SVNErrorMessage
0930: .create(
0931: SVNErrorCode.FS_NOT_FOUND,
0932: "''{0}'' was not found in the repository at revision {1}",
0933: new Object[] { url2, new Long(rev2) });
0934: SVNErrorManager.error(err);
0935: }
0936: } finally {
0937: repository2.closeSession();
0938: }
0939: if (kind1 == SVNNodeKind.FILE || kind2 == SVNNodeKind.FILE) {
0940: target1 = SVNPathUtil.tail(url1.getPath());
0941: if (basePath != null) {
0942: basePath = basePath.getParentFile();
0943: }
0944: url1 = SVNURL.parseURIEncoded(SVNPathUtil.removeTail(url1
0945: .toString()));
0946: repository1 = createRepository(url1, true);
0947: }
0948: repository2 = createRepository(url1, false);
0949: SVNRemoteDiffEditor editor = null;
0950: try {
0951: SVNDiffCallback callback = new SVNDiffCallback(null,
0952: getDiffGenerator(), rev1, rev2, result);
0953: callback.setBasePath(basePath);
0954: editor = new SVNRemoteDiffEditor(null, null, callback,
0955: repository2, rev1, rev2, false, null, this );
0956: ISVNReporterBaton reporter = new ISVNReporterBaton() {
0957: public void report(ISVNReporter reporter)
0958: throws SVNException {
0959: reporter.setPath("", null, rev1, false);
0960: reporter.finishReport();
0961: }
0962: };
0963: repository1.diff(url2, rev2, rev1, target1, !useAncestry,
0964: recursive, true, reporter, SVNCancellableEditor
0965: .newInstance(editor, this , getDebugLog()));
0966: } finally {
0967: if (editor != null) {
0968: editor.cleanup();
0969: }
0970: repository2.closeSession();
0971: }
0972: }
0973:
0974: private void doDiffURLURL(SVNURL url1, File path1,
0975: SVNRevision revision1, SVNURL url2, File path2,
0976: SVNRevision revision2, SVNRevision pegRevision,
0977: boolean recursive, boolean useAncestry,
0978: ISVNDiffStatusHandler handler) throws SVNException {
0979: File basePath = null;
0980: if (path1 != null) {
0981: basePath = path1;
0982: }
0983: if (path2 != null) {
0984: basePath = path2;
0985: }
0986: if (pegRevision.isValid()) {
0987: SVNRepositoryLocation[] locations = getLocations(url2,
0988: path2, null, pegRevision, revision1, revision2);
0989: url1 = locations[0].getURL();
0990: url2 = locations[1].getURL();
0991:
0992: getDiffGenerator().init(url1.toString(), url2.toString());
0993: } else {
0994: url1 = url1 == null ? getURL(path1) : url1;
0995: url2 = url2 == null ? getURL(path2) : url2;
0996: }
0997: SVNRepository repository1 = createRepository(url1, true);
0998: SVNRepository repository2 = createRepository(url2, false);
0999:
1000: final long rev1 = getRevisionNumber(revision1, repository1,
1001: path1);
1002: long rev2 = -1;
1003: SVNNodeKind kind1 = null;
1004: SVNNodeKind kind2 = null;
1005: String target1 = null;
1006:
1007: try {
1008: rev2 = getRevisionNumber(revision2, repository2, path2);
1009:
1010: kind1 = repository1.checkPath("", rev1);
1011: kind2 = repository2.checkPath("", rev2);
1012: if (kind1 == SVNNodeKind.NONE) {
1013: SVNErrorMessage err = SVNErrorMessage
1014: .create(
1015: SVNErrorCode.FS_NOT_FOUND,
1016: "''{0}'' was not found in the repository at revision {1}",
1017: new Object[] { url1, new Long(rev1) });
1018: SVNErrorManager.error(err);
1019: } else if (kind2 == SVNNodeKind.NONE) {
1020: SVNErrorMessage err = SVNErrorMessage
1021: .create(
1022: SVNErrorCode.FS_NOT_FOUND,
1023: "''{0}'' was not found in the repository at revision {1}",
1024: new Object[] { url2, new Long(rev2) });
1025: SVNErrorManager.error(err);
1026: }
1027: if (kind1 == SVNNodeKind.FILE || kind2 == SVNNodeKind.FILE) {
1028: target1 = SVNPathUtil.tail(url1.getPath());
1029: if (basePath != null) {
1030: basePath = basePath.getParentFile();
1031: }
1032: url1 = SVNURL.parseURIEncoded(SVNPathUtil
1033: .removeTail(url1.toString()));
1034: repository1 = createRepository(url1, true);
1035: }
1036: } finally {
1037: repository2.closeSession();
1038: }
1039: repository2 = createRepository(url1, false);
1040: File tmpFile = getDiffGenerator().createTempDirectory();
1041: try {
1042: SVNDiffStatusEditor editor = new SVNDiffStatusEditor(
1043: basePath, repository2, rev1, handler);
1044: ISVNReporterBaton reporter = new ISVNReporterBaton() {
1045: public void report(ISVNReporter reporter)
1046: throws SVNException {
1047: reporter.setPath("", null, rev1, false);
1048: reporter.finishReport();
1049: }
1050: };
1051: repository1.diff(url2, rev2, rev1, target1, !useAncestry,
1052: recursive, false, reporter, SVNCancellableEditor
1053: .newInstance(editor, this , getDebugLog()));
1054: } finally {
1055: if (tmpFile != null) {
1056: SVNFileUtil.deleteAll(tmpFile, true, null);
1057: }
1058: repository2.closeSession();
1059: }
1060: }
1061:
1062: /**
1063: * Applies the differences between two sources (using Working Copy paths to
1064: * get corresponding URLs of the sources) to a Working Copy path.
1065: *
1066: * <p>
1067: * Corresponds to the SVN command line client's
1068: * <code>'svn merge sourceWCPATH1@rev1 sourceWCPATH2@rev2 WCPATH'</code> command.
1069: *
1070: * <p>
1071: * If you need only to try merging your file(s) without actual merging, you
1072: * should set <code>dryRun</code> to <span class="javakeyword">true</span>.
1073: * Your event handler will be dispatched status type information on the target
1074: * path(s). If a path can be successfully merged, the status type will be
1075: * {@link SVNStatusType#MERGED} for that path.
1076: *
1077: * @param path1 the first source path
1078: * @param revision1 a revision of <code>path1</code>
1079: * @param path2 the second source path which URL is to be compared
1080: * against the URL of <code>path1</code>
1081: * @param revision2 a revision of <code>path2</code>
1082: * @param dstPath the target path to which the result should
1083: * be applied
1084: * @param recusrsive <span class="javakeyword">true</span> to descend
1085: * recursively
1086: * @param useAncestry if <span class="javakeyword">true</span> then
1087: * the paths ancestry will be noticed while calculating differences,
1088: * otherwise not
1089: * @param force <span class="javakeyword">true</span> to
1090: * force the operation to run
1091: * @param dryRun if <span class="javakeyword">true</span> then
1092: * only tries the operation to run (to find out
1093: * if a file can be merged successfully)
1094: * @throws SVNException if one of the following is true:
1095: * <ul>
1096: * <li>at least one of <code>revision1</code> and <code>revision2</code> is
1097: * invalid
1098: * <li><code>path1</code> has no URL
1099: * <li><code>path2</code> has no URL
1100: * <li>the repository location of <code>path1</code> was
1101: * not found in <code>revision1</code>
1102: * <li>the repository location of <code>path2</code> was
1103: * not found in <code>revision2</code>
1104: * <li><code>dstPath</code> is not under version control
1105: * </ul>
1106: */
1107: public void doMerge(File path1, SVNRevision revision1, File path2,
1108: SVNRevision revision2, File dstPath, boolean recusrsive,
1109: boolean useAncestry, boolean force, boolean dryRun)
1110: throws SVNException {
1111: path1 = new File(SVNPathUtil.validateFilePath(path1
1112: .getAbsolutePath())).getAbsoluteFile();
1113: path2 = new File(SVNPathUtil.validateFilePath(path2
1114: .getAbsolutePath())).getAbsoluteFile();
1115: dstPath = new File(SVNPathUtil.validateFilePath(dstPath
1116: .getAbsolutePath())).getAbsoluteFile();
1117: /*
1118: * Same as 2. merge sourceWCPATH1@N sourceWCPATH2@M [WCPATH]
1119: * or 3. merge -r N:M SOURCE[@REV] [WCPATH]
1120: * where SOURCE is a path and path1 and path2 are the same.
1121: */
1122: SVNRevision pegRevision = SVNRevision.UNDEFINED;
1123: if (path1.equals(path2)) {
1124: pegRevision = SVNRevision.WORKING;
1125: }
1126: SVNURL url1 = getURL(path1);
1127: if (url1 == null) {
1128: SVNErrorMessage err = SVNErrorMessage.create(
1129: SVNErrorCode.ENTRY_MISSING_URL,
1130: "''{0}'' has no URL", path1);
1131: SVNErrorManager.error(err);
1132: }
1133: SVNURL url2 = getURL(path2);
1134: if (url2 == null) {
1135: SVNErrorMessage err = SVNErrorMessage.create(
1136: SVNErrorCode.ENTRY_MISSING_URL,
1137: "''{0}'' has no URL", path2);
1138: SVNErrorManager.error(err);
1139: }
1140: SVNWCAccess wcAccess = createWCAccess();
1141: dstPath = new File(SVNPathUtil.validateFilePath(dstPath
1142: .getAbsolutePath()));
1143: try {
1144: dstPath = new File(SVNPathUtil.validateFilePath(dstPath
1145: .getAbsolutePath()));
1146: SVNAdminAreaInfo info = wcAccess.openAnchor(dstPath,
1147: !dryRun, recusrsive ? SVNWCAccess.INFINITE_DEPTH
1148: : 0);
1149:
1150: SVNEntry targetEntry = wcAccess.getEntry(dstPath, false);
1151: if (targetEntry == null) {
1152: SVNErrorMessage err = SVNErrorMessage
1153: .create(SVNErrorCode.ENTRY_NOT_FOUND,
1154: "''{0}'' is not under version control",
1155: dstPath);
1156: SVNErrorManager.error(err);
1157: }
1158: if (targetEntry.isFile()) {
1159: doMergeFile(url1, path1, revision1, url2, path2,
1160: revision2, pegRevision, info, force, dryRun);
1161: } else if (targetEntry.isDirectory()) {
1162: doMerge(url1, path1, revision1, url2, path2, revision2,
1163: pegRevision, info, recusrsive, useAncestry,
1164: force, dryRun);
1165: }
1166: } finally {
1167: wcAccess.close();
1168: }
1169: }
1170:
1171: /**
1172: * Applies the differences between two sources (a source URL against the
1173: * repository location URL of a source Working Copy path) to a Working Copy
1174: * path.
1175: *
1176: * <p>
1177: * If you need only to try merging your file(s) without actual merging, you
1178: * should set <code>dryRun</code> to <span class="javakeyword">true</span>.
1179: * Your event handler will be dispatched status type information on the target
1180: * path(s). If a path can be successfully merged, the status type will be
1181: * {@link SVNStatusType#MERGED} for that path.
1182: *
1183: * @param path1 the first source - a WC path
1184: * @param revision1 a revision of <code>path1</code>
1185: * @param url2 the second source - a URL that is to be compared
1186: * against the URL of <code>path1</code>
1187: * @param revision2 a revision of <code>url2</code>
1188: * @param dstPath the target path to which the result should
1189: * be applied
1190: * @param recusrsive <span class="javakeyword">true</span> to descend
1191: * recursively
1192: * @param useAncestry if <span class="javakeyword">true</span> then
1193: * the paths ancestry will be noticed while calculating differences,
1194: * otherwise not
1195: * @param force <span class="javakeyword">true</span> to
1196: * force the operation to run
1197: * @param dryRun if <span class="javakeyword">true</span> then
1198: * only tries the operation to run (to find out
1199: * if a file can be merged successfully)
1200: * @throws SVNException if one of the following is true:
1201: * <ul>
1202: * <li>at least one of <code>revision1</code> and <code>revision2</code> is
1203: * invalid
1204: * <li><code>path1</code> has no URL
1205: * <li>the repository location of <code>path1</code> was
1206: * not found in <code>revision1</code>
1207: * <li><code>url2</code> was not found in
1208: * <code>revision2</code>
1209: * <li><code>dstPath</code> is not under version control
1210: * </ul>
1211: */
1212: public void doMerge(File path1, SVNRevision revision1, SVNURL url2,
1213: SVNRevision revision2, File dstPath, boolean recusrsive,
1214: boolean useAncestry, boolean force, boolean dryRun)
1215: throws SVNException {
1216: path1 = new File(SVNPathUtil.validateFilePath(path1
1217: .getAbsolutePath())).getAbsoluteFile();
1218: dstPath = new File(SVNPathUtil.validateFilePath(dstPath
1219: .getAbsolutePath())).getAbsoluteFile();
1220: SVNURL url1 = getURL(path1);
1221: if (url1 == null) {
1222: SVNErrorMessage err = SVNErrorMessage.create(
1223: SVNErrorCode.ENTRY_MISSING_URL,
1224: "''{0}'' has no URL", path1);
1225: SVNErrorManager.error(err);
1226: }
1227: SVNRevision pegRevision = SVNRevision.UNDEFINED;
1228: if (url1.equals(url2)) {
1229: pegRevision = SVNRevision.HEAD;
1230: }
1231: SVNWCAccess wcAccess = createWCAccess();
1232: try {
1233: dstPath = new File(SVNPathUtil.validateFilePath(dstPath
1234: .getAbsolutePath()));
1235: SVNAdminAreaInfo info = wcAccess.openAnchor(dstPath,
1236: !dryRun, recusrsive ? SVNWCAccess.INFINITE_DEPTH
1237: : 0);
1238:
1239: SVNEntry targetEntry = wcAccess.getEntry(dstPath, false);
1240: if (targetEntry == null) {
1241: SVNErrorMessage err = SVNErrorMessage
1242: .create(SVNErrorCode.ENTRY_NOT_FOUND,
1243: "''{0}'' is not under version control",
1244: dstPath);
1245: SVNErrorManager.error(err);
1246: }
1247: if (targetEntry.isFile()) {
1248: doMergeFile(url1, path1, revision1, url2, null,
1249: revision2, pegRevision, info, force, dryRun);
1250: } else if (targetEntry.isDirectory()) {
1251: doMerge(url1, path1, revision1, url2, null, revision2,
1252: pegRevision, info, recusrsive, useAncestry,
1253: force, dryRun);
1254: }
1255: } finally {
1256: wcAccess.close();
1257: }
1258: }
1259:
1260: /**
1261: * Applies the differences between two sources (the repository location URL of
1262: * a source Working Copy against a source URL) to a Working Copy path.
1263: *
1264: * <p>
1265: * If you need only to try merging your file(s) without actual merging, you
1266: * should set <code>dryRun</code> to <span class="javakeyword">true</span>.
1267: * Your event handler will be dispatched status type information on the target
1268: * path(s). If a path can be successfully merged, the status type will be
1269: * {@link SVNStatusType#MERGED} for that path.
1270: *
1271: * @param url1 the first source - a URL
1272: * @param revision1 a revision of <code>url1</code>
1273: * @param path2 the second source - a WC path that is to be compared
1274: * against <code>url1</code>
1275: * @param revision2 a revision of <code>path2</code>
1276: * @param dstPath the target path to which the result should
1277: * be applied
1278: * @param recusrsive <span class="javakeyword">true</span> to descend
1279: * recursively
1280: * @param useAncestry if <span class="javakeyword">true</span> then
1281: * the paths ancestry will be noticed while calculating differences,
1282: * otherwise not
1283: * @param force <span class="javakeyword">true</span> to
1284: * force the operation to run
1285: * @param dryRun if <span class="javakeyword">true</span> then
1286: * only tries the operation to run (to find out
1287: * if a file can be merged successfully)
1288: * @throws SVNException if one of the following is true:
1289: * <ul>
1290: * <li>at least one of <code>revision1</code> and <code>revision2</code> is
1291: * invalid
1292: * <li><code>path2</code> has no URL
1293: * <li><code>url1</code> was not found in
1294: * <code>revision1</code>
1295: * <li>the repository location of <code>path2</code> was
1296: * not found in <code>revision2</code>
1297: * <li><code>dstPath</code> is not under version control
1298: * </ul>
1299: */
1300: public void doMerge(SVNURL url1, SVNRevision revision1, File path2,
1301: SVNRevision revision2, File dstPath, boolean recusrsive,
1302: boolean useAncestry, boolean force, boolean dryRun)
1303: throws SVNException {
1304: path2 = new File(SVNPathUtil.validateFilePath(path2
1305: .getAbsolutePath())).getAbsoluteFile();
1306: dstPath = new File(SVNPathUtil.validateFilePath(dstPath
1307: .getAbsolutePath())).getAbsoluteFile();
1308: SVNURL url2 = getURL(path2);
1309: if (url2 == null) {
1310: SVNErrorMessage err = SVNErrorMessage.create(
1311: SVNErrorCode.ENTRY_MISSING_URL,
1312: "''{0}'' has no URL", path2);
1313: SVNErrorManager.error(err);
1314: }
1315: SVNRevision pegRevision = SVNRevision.UNDEFINED;
1316: if (url1.equals(url2)) {
1317: pegRevision = SVNRevision.WORKING;
1318: }
1319: SVNWCAccess wcAccess = createWCAccess();
1320: try {
1321: dstPath = new File(SVNPathUtil.validateFilePath(dstPath
1322: .getAbsolutePath()));
1323: SVNAdminAreaInfo info = wcAccess.openAnchor(dstPath,
1324: !dryRun, recusrsive ? SVNWCAccess.INFINITE_DEPTH
1325: : 0);
1326:
1327: SVNEntry targetEntry = wcAccess.getEntry(dstPath, false);
1328: if (targetEntry == null) {
1329: SVNErrorMessage err = SVNErrorMessage
1330: .create(SVNErrorCode.ENTRY_NOT_FOUND,
1331: "''{0}'' is not under version control",
1332: dstPath);
1333: SVNErrorManager.error(err);
1334: }
1335: if (targetEntry.isFile()) {
1336: doMergeFile(url1, null, revision1, url2, path2,
1337: revision2, pegRevision, info, force, dryRun);
1338: } else if (targetEntry.isDirectory()) {
1339: doMerge(url1, null, revision1, url2, path2, revision2,
1340: pegRevision, info, recusrsive, useAncestry,
1341: force, dryRun);
1342: }
1343: } finally {
1344: wcAccess.close();
1345: }
1346: }
1347:
1348: /**
1349: * Applies the differences between two sources (one source URL against another
1350: * source URL) to a Working Copy path.
1351: *
1352: * <p>
1353: * Corresponds to the SVN command line client's
1354: * <code>'svn merge sourceURL1@rev1 sourceURL2@rev2 WCPATH'</code> command.
1355: *
1356: * <p>
1357: * If you need only to try merging your file(s) without actual merging, you
1358: * should set <code>dryRun</code> to <span class="javakeyword">true</span>.
1359: * Your event handler will be dispatched status type information on the target
1360: * path(s). If a path can be successfully merged, the status type will be
1361: * {@link SVNStatusType#MERGED} for that path.
1362: *
1363: * @param url1 the first source URL
1364: * @param revision1 a revision of <code>url1</code>
1365: * @param url2 the second source URL that is to be compared against
1366: * <code>url1</code>
1367: * @param revision2 a revision of <code>url2</code>
1368: * @param dstPath the target path to which the result should
1369: * be applied
1370: * @param recusrsive <span class="javakeyword">true</span> to descend
1371: * recursively
1372: * @param useAncestry if <span class="javakeyword">true</span> then
1373: * the paths ancestry will be noticed while calculating differences,
1374: * otherwise not
1375: * @param force <span class="javakeyword">true</span> to
1376: * force the operation to run
1377: * @param dryRun if <span class="javakeyword">true</span> then
1378: * only tries the operation to run (to find out
1379: * if a file can be merged successfully)
1380: * @throws SVNException if one of the following is true:
1381: * <ul>
1382: * <li>at least one of <code>revision1</code> and <code>revision2</code> is
1383: * invalid
1384: * <li><code>url1</code> was not found in
1385: * <code>revision1</code>
1386: * <li><code>url2</code> was not found in
1387: * <code>revision2</code>
1388: * <li><code>dstPath</code> is not under version control
1389: * </ul>
1390: */
1391: public void doMerge(SVNURL url1, SVNRevision revision1,
1392: SVNURL url2, SVNRevision revision2, File dstPath,
1393: boolean recusrsive, boolean useAncestry, boolean force,
1394: boolean dryRun) throws SVNException {
1395: SVNRevision pegRevision = SVNRevision.UNDEFINED;
1396: if (url1.equals(url2)) {
1397: pegRevision = SVNRevision.HEAD;
1398: }
1399: SVNWCAccess wcAccess = createWCAccess();
1400: try {
1401: dstPath = new File(SVNPathUtil.validateFilePath(dstPath
1402: .getAbsolutePath())).getAbsoluteFile();
1403: SVNAdminAreaInfo info = wcAccess.openAnchor(dstPath,
1404: !dryRun, recusrsive ? SVNWCAccess.INFINITE_DEPTH
1405: : 0);
1406: SVNEntry targetEntry = wcAccess.getEntry(dstPath, false);
1407:
1408: if (targetEntry == null) {
1409: SVNErrorMessage err = SVNErrorMessage
1410: .create(SVNErrorCode.ENTRY_NOT_FOUND,
1411: "''{0}'' is not under version control",
1412: dstPath);
1413: SVNErrorManager.error(err);
1414: }
1415: if (targetEntry.isFile()) {
1416: doMergeFile(url1, null, revision1, url2, null,
1417: revision2, pegRevision, info, force, dryRun);
1418: } else if (targetEntry.isDirectory()) {
1419: doMerge(url1, null, revision1, url2, null, revision2,
1420: pegRevision, info, recusrsive, useAncestry,
1421: force, dryRun);
1422: }
1423: } finally {
1424: wcAccess.close();
1425: }
1426:
1427: }
1428:
1429: /**
1430: * Applies the differences between two sources (a source URL in a particular
1431: * revision against the same source URL in another particular revision) to a
1432: * Working Copy path.
1433: *
1434: * <p>
1435: * Corresponds to the SVN command line client's
1436: * <code>'svn merge -r rev1:rev2 URL@pegRev WCPATH'</code> command.
1437: *
1438: * <p>
1439: * If you need only to try merging your file(s) without actual merging, you
1440: * should set <code>dryRun</code> to <span class="javakeyword">true</span>.
1441: * Your event handler will be dispatched status type information on the target
1442: * path(s). If a path can be successfully merged, the status type will be
1443: * {@link SVNStatusType#MERGED} for that path.
1444: *
1445: * @param url1 a source URL
1446: * @param pegRevision a revision in which code>url1</code>
1447: * is first looked up
1448: * @param revision1 a left-hand revision of <code>url1</code>
1449: * @param revision2 a right-hand revision of <code>url1</code>
1450: * @param dstPath the target path to which the result should
1451: * be applied
1452: * @param recusrsive <span class="javakeyword">true</span> to descend
1453: * recursively
1454: * @param useAncestry if <span class="javakeyword">true</span> then
1455: * the paths ancestry will be noticed while calculating differences,
1456: * otherwise not
1457: * @param force <span class="javakeyword">true</span> to
1458: * force the operation to run
1459: * @param dryRun if <span class="javakeyword">true</span> then
1460: * only tries the operation to run (to find out
1461: * if a file can be merged successfully)
1462: * @throws SVNException if one of the following is true:
1463: * <ul>
1464: * <li>at least one of <code>revision1</code>, <code>revision2</code> and
1465: * <code>pegRevision</code> is invalid
1466: * <li><code>url1</code> was not found in
1467: * <code>revision1</code>
1468: * <li><code>url1</code> was not found in
1469: * <code>revision2</code>
1470: * <li><code>dstPath</code> is not under version control
1471: * </ul>
1472: */
1473: public void doMerge(SVNURL url1, SVNRevision pegRevision,
1474: SVNRevision revision1, SVNRevision revision2, File dstPath,
1475: boolean recusrsive, boolean useAncestry, boolean force,
1476: boolean dryRun) throws SVNException {
1477: if (pegRevision == null || !pegRevision.isValid()) {
1478: pegRevision = SVNRevision.HEAD;
1479: }
1480: SVNWCAccess wcAccess = createWCAccess();
1481: dstPath = new File(SVNPathUtil.validateFilePath(dstPath
1482: .getAbsolutePath())).getAbsoluteFile();
1483: try {
1484: SVNAdminAreaInfo info = wcAccess.openAnchor(dstPath,
1485: !dryRun, recusrsive ? SVNWCAccess.INFINITE_DEPTH
1486: : 0);
1487:
1488: SVNEntry targetEntry = wcAccess.getEntry(dstPath, false);
1489: if (targetEntry == null) {
1490: SVNErrorMessage err = SVNErrorMessage
1491: .create(SVNErrorCode.ENTRY_NOT_FOUND,
1492: "''{0}'' is not under version control",
1493: dstPath);
1494: SVNErrorManager.error(err);
1495: }
1496: if (targetEntry.isFile()) {
1497: doMergeFile(url1, null, revision1, url1, null,
1498: revision2, pegRevision, info, force, dryRun);
1499: } else if (targetEntry.isDirectory()) {
1500: doMerge(url1, null, revision1, url1, null, revision2,
1501: pegRevision, info, recusrsive, useAncestry,
1502: force, dryRun);
1503: }
1504: } finally {
1505: wcAccess.close();
1506: }
1507: }
1508:
1509: /**
1510: * Applies the differences between two sources (the repository location of
1511: * a source Working Copy path in a particular revision against the repository
1512: * location of the same path in another particular revision) to a
1513: * Working Copy path.
1514: *
1515: * <p>
1516: * Corresponds to the SVN command line client's
1517: * <code>'svn merge -r rev1:rev2 sourceWCPATH@pegRev WCPATH'</code> command.
1518: *
1519: * <p>
1520: * If you need only to try merging your file(s) without actual merging, you
1521: * should set <code>dryRun</code> to <span class="javakeyword">true</span>.
1522: * Your event handler will be dispatched status type information on the target
1523: * path(s). If a path can be successfully merged, the status type will be
1524: * {@link SVNStatusType#MERGED} for that path.
1525: *
1526: * @param path1 a source WC path
1527: * @param pegRevision a revision in which the repository location of
1528: * <code>path1</code> is first looked up
1529: * @param revision1 a left-hand revision of <code>path1</code>
1530: * @param revision2 a right-hand revision of <code>path1</code>
1531: * @param dstPath the target path to which the result should
1532: * be applied
1533: * @param recusrsive <span class="javakeyword">true</span> to descend
1534: * recursively
1535: * @param useAncestry if <span class="javakeyword">true</span> then
1536: * the paths ancestry will be noticed while calculating differences,
1537: * otherwise not
1538: * @param force <span class="javakeyword">true</span> to
1539: * force the operation to run
1540: * @param dryRun if <span class="javakeyword">true</span> then
1541: * only tries the operation to run (to find out
1542: * if a file can be merged successfully)
1543: * @throws SVNException if one of the following is true:
1544: * <ul>
1545: * <li>at least one of <code>revision1</code>, <code>revision2</code> and
1546: * <code>pegRevision</code> is invalid
1547: * <li><code>path1</code> has no URL
1548: * <li>the repository location of <code>path1</code> was not found in
1549: * <code>revision1</code>
1550: * <li>the repository location of <code>path1</code> was not found in
1551: * <code>revision2</code>
1552: * <li><code>dstPath</code> is not under version control
1553: * </ul>
1554: */
1555: public void doMerge(File path1, SVNRevision pegRevision,
1556: SVNRevision revision1, SVNRevision revision2, File dstPath,
1557: boolean recusrsive, boolean useAncestry, boolean force,
1558: boolean dryRun) throws SVNException {
1559: SVNURL url1 = getURL(path1);
1560: if (url1 == null) {
1561: SVNErrorMessage err = SVNErrorMessage.create(
1562: SVNErrorCode.ENTRY_MISSING_URL,
1563: "''{0}'' has no URL", path1);
1564: SVNErrorManager.error(err);
1565: }
1566: /*
1567: * Equivalent of 3. merge -r N:M SOURCE[@REV] [WCPATH]
1568: * where SOURCE is a wc path.
1569: */
1570: if (pegRevision == null || !pegRevision.isValid()) {
1571: pegRevision = SVNRevision.WORKING;
1572: }
1573: SVNWCAccess wcAccess = createWCAccess();
1574: try {
1575: dstPath = new File(SVNPathUtil.validateFilePath(dstPath
1576: .getAbsolutePath())).getAbsoluteFile();
1577: path1 = new File(SVNPathUtil.validateFilePath(path1
1578: .getAbsolutePath())).getAbsoluteFile();
1579: SVNAdminAreaInfo info = wcAccess.openAnchor(dstPath
1580: .getAbsoluteFile(), !dryRun,
1581: recusrsive ? SVNWCAccess.INFINITE_DEPTH : 0);
1582:
1583: SVNEntry targetEntry = wcAccess.getEntry(dstPath, false);
1584: if (targetEntry == null) {
1585: SVNErrorMessage err = SVNErrorMessage
1586: .create(SVNErrorCode.ENTRY_NOT_FOUND,
1587: "''{0}'' is not under version control",
1588: dstPath);
1589: SVNErrorManager.error(err);
1590: }
1591: if (targetEntry.isFile()) {
1592: doMergeFile(url1, path1.getAbsoluteFile(), revision1,
1593: url1, path1.getAbsoluteFile(), revision2,
1594: pegRevision, info, force, dryRun);
1595: } else if (targetEntry.isDirectory()) {
1596: doMerge(url1, path1.getAbsoluteFile(), revision1, url1,
1597: path1.getAbsoluteFile(), revision2,
1598: pegRevision, info, recusrsive, useAncestry,
1599: force, dryRun);
1600: }
1601: } finally {
1602: wcAccess.close();
1603: }
1604: }
1605:
1606: private void doMerge(SVNURL url1, File path1,
1607: SVNRevision revision1, SVNURL url2, File path2,
1608: SVNRevision revision2, SVNRevision pegRevision,
1609: SVNAdminAreaInfo info, boolean recursive,
1610: boolean useAncestry, boolean force, boolean dryRun)
1611: throws SVNException {
1612: if (!revision1.isValid() || !revision2.isValid()) {
1613: SVNErrorMessage err = SVNErrorMessage.create(
1614: SVNErrorCode.CLIENT_BAD_REVISION,
1615: "Both rN and rM revisions should be specified");
1616: SVNErrorManager.error(err);
1617: }
1618: if (pegRevision.isValid()) {
1619: SVNRepositoryLocation[] locations = getLocations(url2,
1620: path2, null, pegRevision, revision1, revision2);
1621: url1 = locations[0].getURL();
1622: url2 = locations[1].getURL();
1623: revision1 = SVNRevision.create(locations[0]
1624: .getRevisionNumber());
1625: revision2 = SVNRevision.create(locations[1]
1626: .getRevisionNumber());
1627: path1 = null;
1628: path2 = null;
1629: }
1630: SVNRepository repository1 = createRepository(url1, true);
1631: final long rev1 = getRevisionNumber(revision1, repository1,
1632: path1);
1633: long rev2 = getRevisionNumber(revision2, repository1, path2);
1634: SVNRepository repository2 = createRepository(url1, false);
1635:
1636: SVNMergeCallback callback = new SVNMergeCallback(info, url2,
1637: force, dryRun, getMergeOptions());
1638: SVNRemoteDiffEditor editor = new SVNRemoteDiffEditor(info, info
1639: .getTarget().getRoot(), callback, repository2, rev1,
1640: rev2, dryRun, this , this );
1641:
1642: try {
1643: repository1.diff(url2, rev2, rev1, null, !useAncestry,
1644: recursive, true, new ISVNReporterBaton() {
1645: public void report(ISVNReporter reporter)
1646: throws SVNException {
1647: reporter.setPath("", null, rev1, false);
1648: reporter.finishReport();
1649: }
1650: }, SVNCancellableEditor.newInstance(editor, this ,
1651: getDebugLog()));
1652: } finally {
1653: editor.cleanup();
1654: repository2.closeSession();
1655: }
1656:
1657: }
1658:
1659: private void doMergeFile(SVNURL url1, File path1,
1660: SVNRevision revision1, SVNURL url2, File path2,
1661: SVNRevision revision2, SVNRevision pegRevision,
1662: SVNAdminAreaInfo info, boolean force, boolean dryRun)
1663: throws SVNException {
1664: if (pegRevision.isValid()) {
1665: SVNRepositoryLocation[] locations = getLocations(url2,
1666: path2, null, pegRevision, revision1, revision2);
1667: url1 = locations[0].getURL();
1668: url2 = locations[1].getURL();
1669: revision1 = SVNRevision.create(locations[0]
1670: .getRevisionNumber());
1671: revision2 = SVNRevision.create(locations[1]
1672: .getRevisionNumber());
1673: path1 = null;
1674: path2 = null;
1675: }
1676: long[] rev1 = new long[1];
1677: long[] rev2 = new long[2];
1678: Map props1 = new HashMap();
1679: Map props2 = new HashMap();
1680: File f1 = null;
1681: File f2 = null;
1682: String name = info.getTargetName();
1683: String mimeType2;
1684: String mimeType1;
1685: SVNStatusType[] mergeResult;
1686: try {
1687: f1 = loadFile(url1, path1, revision1, props1, info, rev1);
1688: f2 = loadFile(url2, path2, revision2, props2, info, rev2);
1689:
1690: mimeType1 = (String) props1.get(SVNProperty.MIME_TYPE);
1691: mimeType2 = (String) props2.get(SVNProperty.MIME_TYPE);
1692: props1 = filterProperties(props1, true, false, false);
1693: props2 = filterProperties(props2, true, false, false);
1694: Map propsDiff = computePropsDiff(props1, props2);
1695: // remove non wc props from props1.
1696: for (Iterator names = props1.keySet().iterator(); names
1697: .hasNext();) {
1698: String propertyName = (String) names.next();
1699: if (propertyName
1700: .startsWith(SVNProperty.SVN_ENTRY_PREFIX)
1701: || propertyName
1702: .startsWith(SVNProperty.SVN_WC_PREFIX)) {
1703: names.remove();
1704: }
1705: }
1706: SVNMergeCallback callback = new SVNMergeCallback(info,
1707: url2, force, dryRun, getMergeOptions());
1708: mergeResult = callback.fileChanged(name, f1, f2, rev1[0],
1709: rev2[0], mimeType1, mimeType2, props1, propsDiff);
1710: } finally {
1711: SVNFileUtil.deleteAll(f1, null);
1712: SVNFileUtil.deleteAll(f2, null);
1713: }
1714: handleEvent(SVNEventFactory.createUpdateModifiedEvent(info,
1715: info.getAnchor(), name, SVNNodeKind.FILE,
1716: SVNEventAction.UPDATE_UPDATE, mimeType2,
1717: mergeResult[0], mergeResult[1],
1718: SVNStatusType.LOCK_INAPPLICABLE),
1719: ISVNEventHandler.UNKNOWN);
1720: }
1721:
1722: private File loadFile(SVNURL url, File path, SVNRevision revision,
1723: Map properties, SVNAdminAreaInfo info, long[] revNumber)
1724: throws SVNException {
1725: File tmpDir = info.getAnchor().getRoot();
1726: File result = SVNFileUtil.createUniqueFile(tmpDir, ".merge",
1727: ".tmp");
1728: SVNFileUtil.createEmptyFile(result);
1729:
1730: SVNRepository repository = createRepository(url, true);
1731: long revisionNumber = getRevisionNumber(revision, repository,
1732: path);
1733: OutputStream os = null;
1734: try {
1735: os = SVNFileUtil.openFileForWriting(result);
1736: repository.getFile("", revisionNumber, properties,
1737: new SVNCancellableOutputStream(os, this ));
1738: } finally {
1739: SVNFileUtil.closeFile(os);
1740: }
1741: if (revNumber != null && revNumber.length > 0) {
1742: revNumber[0] = revisionNumber;
1743: }
1744: return result;
1745: }
1746:
1747: private static Map computePropsDiff(Map props1, Map props2) {
1748: Map propsDiff = new HashMap();
1749: for (Iterator names = props2.keySet().iterator(); names
1750: .hasNext();) {
1751: String newPropName = (String) names.next();
1752: if (props1.containsKey(newPropName)) {
1753: // changed.
1754: Object oldValue = props2.get(newPropName);
1755: if (!oldValue.equals(props1.get(newPropName))) {
1756: propsDiff.put(newPropName, props2.get(newPropName));
1757: }
1758: } else {
1759: // added.
1760: propsDiff.put(newPropName, props2.get(newPropName));
1761: }
1762: }
1763: for (Iterator names = props1.keySet().iterator(); names
1764: .hasNext();) {
1765: String oldPropName = (String) names.next();
1766: if (!props2.containsKey(oldPropName)) {
1767: // deleted
1768: propsDiff.put(oldPropName, null);
1769: }
1770: }
1771: return propsDiff;
1772: }
1773:
1774: private static Map filterProperties(Map props1,
1775: boolean leftRegular, boolean leftEntry, boolean leftWC) {
1776: Map result = new HashMap();
1777: for (Iterator names = props1.keySet().iterator(); names
1778: .hasNext();) {
1779: String propName = (String) names.next();
1780: if (!leftEntry
1781: && propName
1782: .startsWith(SVNProperty.SVN_ENTRY_PREFIX)) {
1783: continue;
1784: }
1785: if (!leftWC
1786: && propName.startsWith(SVNProperty.SVN_WC_PREFIX)) {
1787: continue;
1788: }
1789: if (!leftRegular
1790: && !(propName
1791: .startsWith(SVNProperty.SVN_ENTRY_PREFIX) || propName
1792: .startsWith(SVNProperty.SVN_WC_PREFIX))) {
1793: continue;
1794: }
1795: result.put(propName, props1.get(propName));
1796: }
1797: return result;
1798: }
1799: }
|