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.InputStream;
0016: import java.util.ArrayList;
0017: import java.util.Collection;
0018: import java.util.HashMap;
0019: import java.util.HashSet;
0020: import java.util.Iterator;
0021: import java.util.List;
0022: import java.util.Map;
0023: import java.util.TreeMap;
0024:
0025: import org.tmatesoft.svn.core.SVNCancelException;
0026: import org.tmatesoft.svn.core.SVNCommitInfo;
0027: import org.tmatesoft.svn.core.SVNErrorCode;
0028: import org.tmatesoft.svn.core.SVNErrorMessage;
0029: import org.tmatesoft.svn.core.SVNException;
0030: import org.tmatesoft.svn.core.SVNNodeKind;
0031: import org.tmatesoft.svn.core.SVNProperty;
0032: import org.tmatesoft.svn.core.SVNURL;
0033: import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
0034: import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
0035: import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
0036: import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
0037: import org.tmatesoft.svn.core.internal.wc.ISVNCommitPathHandler;
0038: import org.tmatesoft.svn.core.internal.wc.SVNCommitMediator;
0039: import org.tmatesoft.svn.core.internal.wc.SVNCommitUtil;
0040: import org.tmatesoft.svn.core.internal.wc.SVNCommitter;
0041: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
0042: import org.tmatesoft.svn.core.internal.wc.SVNEventFactory;
0043: import org.tmatesoft.svn.core.internal.wc.SVNFileListUtil;
0044: import org.tmatesoft.svn.core.internal.wc.SVNFileType;
0045: import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
0046: import org.tmatesoft.svn.core.internal.wc.SVNImportMediator;
0047: import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminArea;
0048: import org.tmatesoft.svn.core.internal.wc.admin.SVNEntry;
0049: import org.tmatesoft.svn.core.internal.wc.admin.SVNTranslator;
0050: import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess;
0051: import org.tmatesoft.svn.core.io.ISVNEditor;
0052: import org.tmatesoft.svn.core.io.SVNRepository;
0053: import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
0054:
0055: /**
0056: * The <b>SVNCommitClient</b> class provides methods to perform operations that relate to
0057: * committing changes to an SVN repository. These operations are similar to
0058: * respective commands of the native SVN command line client
0059: * and include ones which operate on working copy items as well as ones
0060: * that operate only on a repository.
0061: *
0062: * <p>
0063: * Here's a list of the <b>SVNCommitClient</b>'s commit-related methods
0064: * matched against corresponing commands of the SVN command line
0065: * client:
0066: *
0067: * <table cellpadding="3" cellspacing="1" border="0" width="40%" bgcolor="#999933">
0068: * <tr bgcolor="#ADB8D9" align="left">
0069: * <td><b>SVNKit</b></td>
0070: * <td><b>Subversion</b></td>
0071: * </tr>
0072: * <tr bgcolor="#EAEAEA" align="left">
0073: * <td>doCommit()</td><td>'svn commit'</td>
0074: * </tr>
0075: * <tr bgcolor="#EAEAEA" align="left">
0076: * <td>doImport()</td><td>'svn import'</td>
0077: * </tr>
0078: * <tr bgcolor="#EAEAEA" align="left">
0079: * <td>doDelete()</td><td>'svn delete URL'</td>
0080: * </tr>
0081: * <tr bgcolor="#EAEAEA" align="left">
0082: * <td>doMkDir()</td><td>'svn mkdir URL'</td>
0083: * </tr>
0084: * </table>
0085: *
0086: * @version 1.1.1
0087: * @author TMate Software Ltd.
0088: * @see <a target="_top" href="http://svnkit.com/kb/examples/">Examples</a>
0089: *
0090: */
0091: public class SVNCommitClient extends SVNBasicClient {
0092:
0093: private ISVNCommitHandler myCommitHandler;
0094: private ISVNCommitParameters myCommitParameters;
0095:
0096: /**
0097: * Constructs and initializes an <b>SVNCommitClient</b> object
0098: * with the specified run-time configuration and authentication
0099: * drivers.
0100: *
0101: * <p>
0102: * If <code>options</code> is <span class="javakeyword">null</span>,
0103: * then this <b>SVNCommitClient</b> will be using a default run-time
0104: * configuration driver which takes client-side settings from the
0105: * default SVN's run-time configuration area but is not able to
0106: * change those settings (read more on {@link ISVNOptions} and {@link SVNWCUtil}).
0107: *
0108: * <p>
0109: * If <code>authManager</code> is <span class="javakeyword">null</span>,
0110: * then this <b>SVNCommitClient</b> will be using a default authentication
0111: * and network layers driver (see {@link SVNWCUtil#createDefaultAuthenticationManager()})
0112: * which uses server-side settings and auth storage from the
0113: * default SVN's run-time configuration area (or system properties
0114: * if that area is not found).
0115: *
0116: * @param authManager an authentication and network layers driver
0117: * @param options a run-time configuration options driver
0118: */
0119: public SVNCommitClient(ISVNAuthenticationManager authManager,
0120: ISVNOptions options) {
0121: super (authManager, options);
0122: }
0123:
0124: public SVNCommitClient(ISVNRepositoryPool repositoryPool,
0125: ISVNOptions options) {
0126: super (repositoryPool, options);
0127: }
0128:
0129: /**
0130: * @param handler
0131: * @deprecated use {@link #setCommitHandler(ISVNCommitHandler)} instead
0132: */
0133: public void setCommitHander(ISVNCommitHandler handler) {
0134: myCommitHandler = handler;
0135: }
0136:
0137: /**
0138: * Sets an implementation of <b>ISVNCommitHandler</b> to
0139: * the commit handler that will be used during commit operations to handle
0140: * commit log messages. The handler will receive a clien's log message and items
0141: * (represented as <b>SVNCommitItem</b> objects) that will be
0142: * committed. Depending on implementor's aims the initial log message can
0143: * be modified (or something else) and returned back.
0144: *
0145: * <p>
0146: * If using <b>SVNCommitClient</b> without specifying any
0147: * commit handler then a default one will be used - {@link DefaultSVNCommitHandler}.
0148: *
0149: * @param handler an implementor's handler that will be used to handle
0150: * commit log messages
0151: * @see #getCommitHandler()
0152: * @see ISVNCommitHandler
0153: */
0154: public void setCommitHandler(ISVNCommitHandler handler) {
0155: myCommitHandler = handler;
0156: }
0157:
0158: /**
0159: * Returns the specified commit handler (if set) being in use or a default one
0160: * (<b>DefaultSVNCommitHandler</b>) if no special
0161: * implementations of <b>ISVNCommitHandler</b> were
0162: * previousely provided.
0163: *
0164: * @return the commit handler being in use or a default one
0165: * @see #setCommitHander(ISVNCommitHandler)
0166: * @see ISVNCommitHandler
0167: * @see DefaultSVNCommitHandler
0168: */
0169: public ISVNCommitHandler getCommitHandler() {
0170: if (myCommitHandler == null) {
0171: myCommitHandler = new DefaultSVNCommitHandler();
0172: }
0173: return myCommitHandler;
0174: }
0175:
0176: /**
0177: * Sets commit parameters to use.
0178: *
0179: * <p>
0180: * When no parameters are set {@link DefaultSVNCommitParameters default}
0181: * ones are used.
0182: *
0183: * @param parameters commit parameters
0184: * @see #getCommitParameters()
0185: */
0186: public void setCommitParameters(ISVNCommitParameters parameters) {
0187: myCommitParameters = parameters;
0188: }
0189:
0190: /**
0191: * Returns commit parameters.
0192: *
0193: * <p>
0194: * If no user parameters were previously specified, once creates and
0195: * returns {@link DefaultSVNCommitParameters default} ones.
0196: *
0197: * @return commit parameters
0198: * @see #setCommitParameters(ISVNCommitParameters)
0199: */
0200: public ISVNCommitParameters getCommitParameters() {
0201: if (myCommitParameters == null) {
0202: myCommitParameters = new DefaultSVNCommitParameters();
0203: }
0204: return myCommitParameters;
0205: }
0206:
0207: /**
0208: * Committs removing specified URL-paths from the repository.
0209: *
0210: * @param urls an array containing URL-strings that represent
0211: * repository locations to be removed
0212: * @param commitMessage a string to be a commit log message
0213: * @return information on a new revision as the result
0214: * of the commit
0215: * @throws SVNException if one of the following is true:
0216: * <ul>
0217: * <li>a URL does not exist
0218: * <li>probably some of URLs refer to different
0219: * repositories
0220: * </ul>
0221: */
0222: public SVNCommitInfo doDelete(SVNURL[] urls, String commitMessage)
0223: throws SVNException {
0224: if (urls == null || urls.length == 0) {
0225: return SVNCommitInfo.NULL;
0226: }
0227: List paths = new ArrayList();
0228: SVNURL rootURL = SVNURLUtil.condenceURLs(urls, paths, true);
0229: if (rootURL == null) {
0230: SVNErrorMessage err = SVNErrorMessage
0231: .create(SVNErrorCode.RA_ILLEGAL_URL,
0232: "Can not compute common root URL for specified URLs");
0233: SVNErrorManager.error(err);
0234: }
0235: if (paths.isEmpty()) {
0236: // there is just root.
0237: paths.add(SVNPathUtil.tail(rootURL.getURIEncodedPath()));
0238: rootURL = rootURL.removePathTail();
0239: }
0240: SVNCommitItem[] commitItems = new SVNCommitItem[paths.size()];
0241: for (int i = 0; i < commitItems.length; i++) {
0242: String path = (String) paths.get(i);
0243: commitItems[i] = new SVNCommitItem(null, rootURL
0244: .appendPath(path, true), null, SVNNodeKind.NONE,
0245: SVNRevision.UNDEFINED, SVNRevision.UNDEFINED,
0246: false, true, false, false, false, false);
0247: }
0248: commitMessage = getCommitHandler().getCommitMessage(
0249: commitMessage, commitItems);
0250: if (commitMessage == null) {
0251: return SVNCommitInfo.NULL;
0252: }
0253:
0254: List decodedPaths = new ArrayList();
0255: for (Iterator commitPaths = paths.iterator(); commitPaths
0256: .hasNext();) {
0257: String path = (String) commitPaths.next();
0258: decodedPaths.add(SVNEncodingUtil.uriDecode(path));
0259: }
0260: paths = decodedPaths;
0261: SVNRepository repos = createRepository(rootURL, true);
0262: for (Iterator commitPath = paths.iterator(); commitPath
0263: .hasNext();) {
0264: String path = (String) commitPath.next();
0265: SVNNodeKind kind = repos.checkPath(path, -1);
0266: if (kind == SVNNodeKind.NONE) {
0267: SVNURL url = rootURL.appendPath(path, false);
0268: SVNErrorMessage err = SVNErrorMessage.create(
0269: SVNErrorCode.FS_NOT_FOUND,
0270: "URL ''{0}'' does not exist", url);
0271: SVNErrorManager.error(err);
0272: }
0273: }
0274: commitMessage = validateCommitMessage(commitMessage);
0275: ISVNEditor commitEditor = repos.getCommitEditor(commitMessage,
0276: null, false, null);
0277: ISVNCommitPathHandler deleter = new ISVNCommitPathHandler() {
0278: public boolean handleCommitPath(String commitPath,
0279: ISVNEditor commitEditor) throws SVNException {
0280: commitEditor.deleteEntry(commitPath, -1);
0281: return false;
0282: }
0283: };
0284: SVNCommitInfo info;
0285: try {
0286: SVNCommitUtil.driveCommitEditor(deleter, paths,
0287: commitEditor, -1);
0288: info = commitEditor.closeEdit();
0289: } catch (SVNException e) {
0290: try {
0291: commitEditor.abortEdit();
0292: } catch (SVNException inner) {
0293: //
0294: }
0295: throw e;
0296: }
0297: if (info != null && info.getNewRevision() >= 0) {
0298: dispatchEvent(SVNEventFactory.createCommitCompletedEvent(
0299: null, info.getNewRevision()),
0300: ISVNEventHandler.UNKNOWN);
0301: }
0302: return info != null ? info : SVNCommitInfo.NULL;
0303: }
0304:
0305: /**
0306: * Committs a creation of a new directory/directories in the repository.
0307: *
0308: * @param urls an array containing URL-strings that represent
0309: * new repository locations to be created
0310: * @param commitMessage a string to be a commit log message
0311: * @return information on a new revision as the result
0312: * of the commit
0313: * @throws SVNException if some of URLs refer to different
0314: * repositories
0315: */
0316: public SVNCommitInfo doMkDir(SVNURL[] urls, String commitMessage)
0317: throws SVNException {
0318: if (urls == null || urls.length == 0) {
0319: return SVNCommitInfo.NULL;
0320: }
0321: List paths = new ArrayList();
0322: SVNURL rootURL = SVNURLUtil.condenceURLs(urls, paths, false);
0323: if (rootURL == null) {
0324: SVNErrorMessage err = SVNErrorMessage
0325: .create(SVNErrorCode.RA_ILLEGAL_URL,
0326: "Can not compute common root URL for specified URLs");
0327: SVNErrorManager.error(err);
0328: }
0329: if (paths.isEmpty()) {
0330: paths.add(SVNPathUtil.tail(rootURL.getURIEncodedPath()));
0331: rootURL = rootURL.removePathTail();
0332: }
0333:
0334: if (paths.contains("")) {
0335: List convertedPaths = new ArrayList();
0336: String tail = SVNPathUtil.tail(rootURL.getURIEncodedPath());
0337: rootURL = rootURL.removePathTail();
0338: for (Iterator commitPaths = paths.iterator(); commitPaths
0339: .hasNext();) {
0340: String path = (String) commitPaths.next();
0341: if ("".equals(path)) {
0342: convertedPaths.add(tail);
0343: } else {
0344: convertedPaths.add(SVNPathUtil.append(tail, path));
0345: }
0346: }
0347: paths = convertedPaths;
0348: }
0349: SVNCommitItem[] commitItems = new SVNCommitItem[paths.size()];
0350: for (int i = 0; i < commitItems.length; i++) {
0351: String path = (String) paths.get(i);
0352: commitItems[i] = new SVNCommitItem(null, rootURL
0353: .appendPath(path, true), null, SVNNodeKind.DIR,
0354: SVNRevision.UNDEFINED, SVNRevision.UNDEFINED, true,
0355: false, false, false, false, false);
0356: }
0357: commitMessage = getCommitHandler().getCommitMessage(
0358: commitMessage, commitItems);
0359: if (commitMessage == null) {
0360: return SVNCommitInfo.NULL;
0361: }
0362:
0363: List decodedPaths = new ArrayList();
0364: for (Iterator commitPaths = paths.iterator(); commitPaths
0365: .hasNext();) {
0366: String path = (String) commitPaths.next();
0367: decodedPaths.add(SVNEncodingUtil.uriDecode(path));
0368: }
0369: paths = decodedPaths;
0370: SVNRepository repos = createRepository(rootURL, true);
0371: commitMessage = validateCommitMessage(commitMessage);
0372: ISVNEditor commitEditor = repos.getCommitEditor(commitMessage,
0373: null, false, null);
0374: ISVNCommitPathHandler creater = new ISVNCommitPathHandler() {
0375: public boolean handleCommitPath(String commitPath,
0376: ISVNEditor commitEditor) throws SVNException {
0377: commitEditor.addDir(commitPath, null, -1);
0378: return true;
0379: }
0380: };
0381: SVNCommitInfo info;
0382: try {
0383: SVNCommitUtil.driveCommitEditor(creater, paths,
0384: commitEditor, -1);
0385: info = commitEditor.closeEdit();
0386: } catch (SVNException e) {
0387: try {
0388: commitEditor.abortEdit();
0389: } catch (SVNException inner) {
0390: //
0391: }
0392: throw e;
0393: }
0394: if (info != null && info.getNewRevision() >= 0) {
0395: dispatchEvent(SVNEventFactory.createCommitCompletedEvent(
0396: null, info.getNewRevision()),
0397: ISVNEventHandler.UNKNOWN);
0398: }
0399: return info != null ? info : SVNCommitInfo.NULL;
0400: }
0401:
0402: /**
0403: * Committs an addition of a local unversioned file or directory into
0404: * the repository. If the destination URL (<code>dstURL</code>) contains any
0405: * non-existent parent directories they will be automatically created by the
0406: * server.
0407: *
0408: * @param path a local unversioned file or directory to be imported
0409: * into the repository
0410: * @param dstURL a URL-string that represents a repository location
0411: * where the <code>path</code> will be imported
0412: * @param commitMessage a string to be a commit log message
0413: * @param recursive this flag is relevant only when the <code>path</code> is
0414: * a directory: if <span class="javakeyword">true</span> then the entire directory
0415: * tree will be imported including all child directories, otherwise
0416: * only items located in the directory itself
0417: * @return information on a new revision as the result
0418: * of the commit
0419: * @throws SVNException if one of the following is true:
0420: * <ul>
0421: * <li><code>dstURL</code> is invalid
0422: * <li>the path denoted by <code>dstURL</code> already
0423: * exists
0424: * <li><code>path</code> contains a reserved name - <i>'.svn'</i>
0425: * </ul>
0426: */
0427: public SVNCommitInfo doImport(File path, SVNURL dstURL,
0428: String commitMessage, boolean recursive)
0429: throws SVNException {
0430: return doImport(path, dstURL, commitMessage, true, recursive);
0431: }
0432:
0433: /**
0434: * Committs an addition of a local unversioned file or directory into
0435: * the repository. If the destination URL (<code>dstURL</code>) contains any
0436: * non-existent parent directories they will be automatically created by the
0437: * server.
0438: *
0439: * @param path a local unversioned file or directory to be imported
0440: * into the repository
0441: * @param dstURL a URL-string that represents a repository location
0442: * where the <code>path</code> will be imported
0443: * @param commitMessage a string to be a commit log message
0444: * @param useGlobalIgnores if <span class="javakeyword">true</span>
0445: * then those paths that match global ignore patterns controlled
0446: * by a config options driver (see {@link ISVNOptions#isIgnored(String) isIgnored()})
0447: * will not be imported, otherwise global ignore patterns are not
0448: * used
0449: * @param recursive this flag is relevant only when the <code>path</code> is
0450: * a directory: if <span class="javakeyword">true</span> then the entire directory
0451: * tree will be imported including all child directories, otherwise
0452: * only items located in the directory itself
0453: * @return information on a new revision as the result
0454: * of the commit
0455: * @throws SVNException if one of the following is true:
0456: * <ul>
0457: * <li><code>dstURL</code> is invalid
0458: * <li>the path denoted by <code>dstURL</code> already
0459: * exists
0460: * <li><code>path</code> contains a reserved name - <i>'.svn'</i>
0461: * </ul>
0462: */
0463: public SVNCommitInfo doImport(File path, SVNURL dstURL,
0464: String commitMessage, boolean useGlobalIgnores,
0465: boolean recursive) throws SVNException {
0466: // first find dstURL root.
0467: SVNRepository repos = null;
0468: SVNFileType srcKind = SVNFileType.getType(path);
0469: if (srcKind == SVNFileType.NONE) {
0470: SVNErrorMessage err = SVNErrorMessage.create(
0471: SVNErrorCode.ENTRY_NOT_FOUND,
0472: "Path ''{0}'' does not exist", path);
0473: SVNErrorManager.error(err);
0474: }
0475: List newPaths = new ArrayList();
0476: SVNURL rootURL = dstURL;
0477: repos = createRepository(rootURL, true);
0478: SVNURL reposRoot = repos.getRepositoryRoot(true);
0479: while (!reposRoot.equals(rootURL)) {
0480: if (repos.checkPath("", -1) == SVNNodeKind.NONE) {
0481: newPaths.add(SVNPathUtil.tail(rootURL.getPath()));
0482: rootURL = rootURL.removePathTail();
0483: repos = createRepository(rootURL, true);
0484: } else {
0485: break;
0486: }
0487: }
0488: if (newPaths.isEmpty()
0489: && (srcKind == SVNFileType.FILE || srcKind == SVNFileType.SYMLINK)) {
0490: SVNErrorMessage err = SVNErrorMessage.create(
0491: SVNErrorCode.ENTRY_EXISTS,
0492: "Path ''{0}'' already exists", dstURL);
0493: SVNErrorManager.error(err);
0494: }
0495: if (newPaths.contains(SVNFileUtil.getAdminDirectoryName())) {
0496: SVNErrorMessage err = SVNErrorMessage
0497: .create(
0498: SVNErrorCode.CL_ADM_DIR_RESERVED,
0499: "''{0}'' is a reserved name and cannot be imported",
0500: SVNFileUtil.getAdminDirectoryName());
0501: SVNErrorManager.error(err);
0502: }
0503: SVNCommitItem[] items = new SVNCommitItem[1];
0504: items[0] = new SVNCommitItem(path, dstURL, null,
0505: srcKind == SVNFileType.DIRECTORY ? SVNNodeKind.DIR
0506: : SVNNodeKind.FILE, SVNRevision.UNDEFINED,
0507: SVNRevision.UNDEFINED, true, false, false, false,
0508: false, false);
0509: commitMessage = getCommitHandler().getCommitMessage(
0510: commitMessage, items);
0511: if (commitMessage == null) {
0512: return SVNCommitInfo.NULL;
0513: }
0514: commitMessage = validateCommitMessage(commitMessage);
0515: ISVNEditor commitEditor = repos.getCommitEditor(commitMessage,
0516: null, false, new SVNImportMediator());
0517: String filePath = "";
0518: if (srcKind != SVNFileType.DIRECTORY) {
0519: filePath = (String) newPaths.remove(0);
0520: for (int i = 0; i < newPaths.size(); i++) {
0521: String newDir = (String) newPaths.get(i);
0522: filePath = newDir + "/" + filePath;
0523: }
0524: }
0525: checkCancelled();
0526: boolean changed = false;
0527: SVNCommitInfo info = null;
0528: try {
0529: commitEditor.openRoot(-1);
0530: String newDirPath = null;
0531: for (int i = newPaths.size() - 1; i >= 0; i--) {
0532: newDirPath = newDirPath == null ? (String) newPaths
0533: .get(i) : SVNPathUtil.append(newDirPath,
0534: (String) newPaths.get(i));
0535: commitEditor.addDir(newDirPath, null, -1);
0536: }
0537: changed = newPaths.size() > 0;
0538: SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
0539: if (srcKind == SVNFileType.DIRECTORY) {
0540: changed |= importDir(deltaGenerator, path, path,
0541: newDirPath, useGlobalIgnores, recursive,
0542: commitEditor);
0543: } else {
0544: changed |= importFile(deltaGenerator, path
0545: .getParentFile(), path, srcKind, filePath,
0546: commitEditor);
0547: }
0548: if (!changed) {
0549: try {
0550: commitEditor.abortEdit();
0551: } catch (SVNException e) {
0552: }
0553: return SVNCommitInfo.NULL;
0554: }
0555: for (int i = 0; i < newPaths.size(); i++) {
0556: commitEditor.closeDir();
0557: }
0558: info = commitEditor.closeEdit();
0559: } finally {
0560: if (!changed || info == null) {
0561: try {
0562: commitEditor.abortEdit();
0563: } catch (SVNException e) {
0564: //
0565: }
0566: }
0567: }
0568: if (info != null && info.getNewRevision() >= 0) {
0569: dispatchEvent(SVNEventFactory.createCommitCompletedEvent(
0570: null, info.getNewRevision()),
0571: ISVNEventHandler.UNKNOWN);
0572: }
0573: return info != null ? info : SVNCommitInfo.NULL;
0574: }
0575:
0576: /**
0577: * Committs local changes made to the Working Copy items (provided as an array of
0578: * {@link java.io.File}s) to the repository.
0579: *
0580: * @param paths an array of local items which should be traversed
0581: * to commit changes they have to the repository
0582: * @param keepLocks if <span class="javakeyword">true</span> and there are local items that
0583: * were locked then the commit will left them locked,
0584: * otherwise the items will be unlocked after the commit
0585: * succeeds
0586: * @param commitMessage a string to be a commit log message
0587: * @param force <span class="javakeyword">true</span> to force a non-recursive commit; if
0588: * <code>recursive</code> is set to <span class="javakeyword">true</span> the <code>force</code>
0589: * flag is ignored
0590: * @param recursive relevant only for directory items: if <span class="javakeyword">true</span> then
0591: * the entire directory tree will be committed including all child directories,
0592: * otherwise only items located in the directory itself
0593: * @return information on a new revision as the result
0594: * of the commit
0595: * @throws SVNException
0596: * @see #doCommit(SVNCommitPacket, boolean, String)
0597: */
0598: public SVNCommitInfo doCommit(File[] paths, boolean keepLocks,
0599: String commitMessage, boolean force, boolean recursive)
0600: throws SVNException {
0601: SVNCommitPacket packet = doCollectCommitItems(paths, keepLocks,
0602: force, recursive);
0603: try {
0604: packet = packet.removeSkippedItems();
0605: return doCommit(packet, keepLocks, commitMessage);
0606: } finally {
0607: if (packet != null) {
0608: packet.dispose();
0609: }
0610: }
0611: }
0612:
0613: /**
0614: * Committs local changes made to the Working Copy items to the repository.
0615: *
0616: * <p>
0617: * <code>commitPacket</code> contains commit items (<b>SVNCommitItem</b>)
0618: * which represent local Working Copy items that were changed and are to be committed.
0619: * Commit items are gathered in a single <b>SVNCommitPacket</b>
0620: * by invoking {@link #doCollectCommitItems(File[], boolean, boolean, boolean) doCollectCommitItems()}.
0621: *
0622: * @param commitPacket a single object that contains items to be committed
0623: * @param keepLocks if <span class="javakeyword">true</span> and there are local items that
0624: * were locked then the commit will left them locked,
0625: * otherwise the items will be unlocked after the commit
0626: * succeeds
0627: * @param commitMessage a string to be a commit log message
0628: * @return information on a new revision as the result
0629: * of the commit
0630: * @throws SVNException
0631: * @see SVNCommitItem
0632: *
0633: */
0634: public SVNCommitInfo doCommit(SVNCommitPacket commitPacket,
0635: boolean keepLocks, String commitMessage)
0636: throws SVNException {
0637: SVNCommitInfo[] info = doCommit(
0638: new SVNCommitPacket[] { commitPacket }, keepLocks,
0639: commitMessage);
0640: if (info != null && info.length > 0) {
0641: if (info[0].getErrorMessage() != null
0642: && info[0].getErrorMessage().getErrorCode() != SVNErrorCode.REPOS_POST_COMMIT_HOOK_FAILED) {
0643: SVNErrorManager.error(info[0].getErrorMessage());
0644: }
0645: return info[0];
0646: }
0647: return SVNCommitInfo.NULL;
0648: }
0649:
0650: /**
0651: * Committs local changes, made to the Working Copy items, to the repository.
0652: *
0653: * <p>
0654: * <code>commitPackets</code> is an array of packets that contain commit items (<b>SVNCommitItem</b>)
0655: * which represent local Working Copy items that were changed and are to be committed.
0656: * Commit items are gathered in a single <b>SVNCommitPacket</b>
0657: * by invoking {@link #doCollectCommitItems(File[], boolean, boolean, boolean) doCollectCommitItems()}.
0658: *
0659: * <p>
0660: * This allows to commit separate trees of Working Copies "belonging" to different
0661: * repositories. One packet per one repository. If repositories are different (it means more than
0662: * one commit will be done), <code>commitMessage</code> may be replaced by a commit handler
0663: * to be a specific one for each commit.
0664: *
0665: * @param commitPackets logically grouped items to be committed
0666: * @param keepLocks if <span class="javakeyword">true</span> and there are local items that
0667: * were locked then the commit will left them locked,
0668: * otherwise the items will be unlocked after the commit
0669: * succeeds
0670: * @param commitMessage a string to be a commit log message
0671: * @return committed information
0672: * @throws SVNException
0673: */
0674: public SVNCommitInfo[] doCommit(SVNCommitPacket[] commitPackets,
0675: boolean keepLocks, String commitMessage)
0676: throws SVNException {
0677: if (commitPackets == null || commitPackets.length == 0) {
0678: return new SVNCommitInfo[0];
0679: }
0680:
0681: Collection tmpFiles = null;
0682: SVNCommitInfo info = null;
0683: ISVNEditor commitEditor = null;
0684:
0685: Collection infos = new ArrayList();
0686: boolean needsSleepForTimeStamp = false;
0687: for (int p = 0; p < commitPackets.length; p++) {
0688: SVNCommitPacket commitPacket = commitPackets[p]
0689: .removeSkippedItems();
0690: if (commitPacket.getCommitItems().length == 0) {
0691: continue;
0692: }
0693: try {
0694: commitMessage = getCommitHandler().getCommitMessage(
0695: commitMessage, commitPacket.getCommitItems());
0696: if (commitMessage == null) {
0697: infos.add(SVNCommitInfo.NULL);
0698: continue;
0699: }
0700: commitMessage = validateCommitMessage(commitMessage);
0701: Map commitables = new TreeMap();
0702: String baseURL = SVNCommitUtil.translateCommitables(
0703: commitPacket.getCommitItems(), commitables);
0704: Map lockTokens = SVNCommitUtil.translateLockTokens(
0705: commitPacket.getLockTokens(), baseURL);
0706:
0707: SVNRepository repository = createRepository(SVNURL
0708: .parseURIEncoded(baseURL), true);
0709: SVNCommitMediator mediator = new SVNCommitMediator(
0710: commitables);
0711: tmpFiles = mediator.getTmpFiles();
0712: String repositoryRoot = repository.getRepositoryRoot(
0713: true).getPath();
0714: commitEditor = repository.getCommitEditor(
0715: commitMessage, lockTokens, keepLocks, mediator);
0716: // commit.
0717: // set event handler for each wc access.
0718: for (int i = 0; i < commitPacket.getCommitItems().length; i++) {
0719: commitPacket.getCommitItems()[i].getWCAccess()
0720: .setEventHandler(getEventDispatcher());
0721: }
0722: info = SVNCommitter.commit(mediator.getTmpFiles(),
0723: commitables, repositoryRoot, commitEditor);
0724: // update wc.
0725: Collection processedItems = new HashSet();
0726: Collection explicitCommitPaths = new HashSet();
0727: for (Iterator urls = commitables.keySet().iterator(); urls
0728: .hasNext();) {
0729: String url = (String) urls.next();
0730: SVNCommitItem item = (SVNCommitItem) commitables
0731: .get(url);
0732: explicitCommitPaths.add(item.getPath());
0733: }
0734:
0735: for (Iterator urls = commitables.keySet().iterator(); urls
0736: .hasNext();) {
0737: String url = (String) urls.next();
0738: SVNCommitItem item = (SVNCommitItem) commitables
0739: .get(url);
0740: SVNWCAccess wcAccess = item.getWCAccess();
0741: String path = item.getPath();
0742: SVNAdminArea dir = null;
0743: String target = null;
0744:
0745: try {
0746: if (item.getKind() == SVNNodeKind.DIR) {
0747: dir = wcAccess.retrieve(item.getFile());
0748: target = "";
0749: } else {
0750: dir = wcAccess.retrieve(item.getFile()
0751: .getParentFile());
0752: target = SVNPathUtil.tail(path);
0753: }
0754: } catch (SVNException e) {
0755: if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_LOCKED) {
0756: dir = null;
0757: }
0758: }
0759: if (dir == null) {
0760: if (hasProcessedParents(processedItems, path)) {
0761: processedItems.add(path);
0762: continue;
0763: }
0764: if (item.isDeleted()
0765: && item.getKind() == SVNNodeKind.DIR) {
0766: File parentPath = "".equals(path) ? null
0767: : item.getFile().getParentFile();
0768: String nameInParent = "".equals(path) ? null
0769: : SVNPathUtil.tail(path);
0770: if (parentPath != null) {
0771: SVNAdminArea parentDir = wcAccess
0772: .retrieve(parentPath);
0773: if (parentDir != null) {
0774: SVNEntry entryInParent = parentDir
0775: .getEntry(nameInParent,
0776: true);
0777: if (entryInParent != null) {
0778: entryInParent.unschedule();
0779: entryInParent.setDeleted(true);
0780: parentDir.saveEntries(false);
0781: }
0782: }
0783: }
0784: processedItems.add(path);
0785: continue;
0786: }
0787: }
0788: SVNEntry entry = dir.getEntry(target, true);
0789: if (entry == null
0790: && hasProcessedParents(processedItems, path)) {
0791: processedItems.add(path);
0792: continue;
0793: }
0794: boolean recurse = false;
0795: if (item.isAdded() && item.getCopyFromURL() != null
0796: && item.getKind() == SVNNodeKind.DIR) {
0797: recurse = true;
0798: }
0799: boolean removeLock = !keepLocks && item.isLocked();
0800: // update entry in dir.
0801: Map wcPropChanges = mediator.getWCProperties(item);
0802: dir.commit(target, info, wcPropChanges, removeLock,
0803: recurse, explicitCommitPaths,
0804: getCommitParameters());
0805: processedItems.add(path);
0806: }
0807: needsSleepForTimeStamp = true;
0808: // commit completed, include revision number.
0809: dispatchEvent(SVNEventFactory
0810: .createCommitCompletedEvent(null, info
0811: .getNewRevision()),
0812: ISVNEventHandler.UNKNOWN);
0813: } catch (SVNException e) {
0814: if (e instanceof SVNCancelException) {
0815: throw e;
0816: }
0817: SVNErrorMessage err = SVNErrorMessage.create(e
0818: .getErrorMessage().getErrorCode(),
0819: "Commit failed (details follow):");
0820: err.setChildErrorMessage(e.getErrorMessage());
0821: infos.add(new SVNCommitInfo(-1, null, null, err));
0822: dispatchEvent(new SVNEvent(err),
0823: ISVNEventHandler.UNKNOWN);
0824: continue;
0825: } finally {
0826: if (info == null && commitEditor != null) {
0827: try {
0828: commitEditor.abortEdit();
0829: } catch (SVNException e) {
0830: //
0831: }
0832: }
0833: if (tmpFiles != null) {
0834: for (Iterator files = tmpFiles.iterator(); files
0835: .hasNext();) {
0836: File file = (File) files.next();
0837: file.delete();
0838: }
0839: }
0840: if (commitPacket != null) {
0841: commitPacket.dispose();
0842: }
0843: }
0844: infos.add(info != null ? info : SVNCommitInfo.NULL);
0845: }
0846: if (needsSleepForTimeStamp) {
0847: sleepForTimeStamp();
0848: }
0849: return (SVNCommitInfo[]) infos.toArray(new SVNCommitInfo[infos
0850: .size()]);
0851: }
0852:
0853: /**
0854: * Collects commit items (containing detailed information on each Working Copy item
0855: * that was changed and need to be committed to the repository) into a single
0856: * <b>SVNCommitPacket</b>. Further this commit packet can be passed to
0857: * {@link #doCommit(SVNCommitPacket, boolean, String) doCommit()}.
0858: *
0859: * @param paths an array of local items which should be traversed
0860: * to collect information on every changed item (one
0861: * <b>SVNCommitItem</b> per each
0862: * modified local item)
0863: * @param keepLocks if <span class="javakeyword">true</span> and there are local items that
0864: * were locked then these items will be left locked after
0865: * traversing all of them, otherwise the items will be unlocked
0866: * @param force forces collecting commit items for a non-recursive commit
0867: * @param recursive relevant only for directory items: if <span class="javakeyword">true</span> then
0868: * the entire directory tree will be traversed including all child
0869: * directories, otherwise only items located in the directory itself
0870: * will be processed
0871: * @return an <b>SVNCommitPacket</b> containing
0872: * all Working Copy items having local modifications and represented as
0873: * <b>SVNCommitItem</b> objects; if no modified
0874: * items were found then
0875: * {@link SVNCommitPacket#EMPTY} is returned
0876: * @throws SVNException
0877: * @see SVNCommitItem
0878: */
0879: public SVNCommitPacket doCollectCommitItems(File[] paths,
0880: boolean keepLocks, boolean force, boolean recursive)
0881: throws SVNException {
0882: if (paths == null || paths.length == 0) {
0883: return SVNCommitPacket.EMPTY;
0884: }
0885: Collection targets = new ArrayList();
0886: SVNStatusClient statusClient = new SVNStatusClient(
0887: getRepositoryPool(), getOptions());
0888: statusClient.setEventHandler(new ISVNEventHandler() {
0889: public void handleEvent(SVNEvent event, double progress)
0890: throws SVNException {
0891: }
0892:
0893: public void checkCancelled() throws SVNCancelException {
0894: SVNCommitClient.this .checkCancelled();
0895: }
0896: });
0897: SVNWCAccess wcAccess = SVNCommitUtil.createCommitWCAccess(
0898: paths, recursive, force, targets, statusClient);
0899: SVNAdminArea[] areas = wcAccess.getAdminAreas();
0900: for (int i = 0; areas != null && i < areas.length; i++) {
0901: if (areas[i] != null) {
0902: areas[i].setCommitParameters(getCommitParameters());
0903: }
0904: }
0905: try {
0906: Map lockTokens = new HashMap();
0907: checkCancelled();
0908: SVNCommitItem[] commitItems = SVNCommitUtil
0909: .harvestCommitables(wcAccess, targets, lockTokens,
0910: !keepLocks, recursive, force,
0911: getCommitParameters());
0912: boolean hasModifications = false;
0913: checkCancelled();
0914: for (int i = 0; commitItems != null
0915: && i < commitItems.length; i++) {
0916: SVNCommitItem commitItem = commitItems[i];
0917: if (commitItem.isAdded() || commitItem.isDeleted()
0918: || commitItem.isContentsModified()
0919: || commitItem.isPropertiesModified()
0920: || commitItem.isCopied()) {
0921: hasModifications = true;
0922: break;
0923: }
0924: }
0925: if (!hasModifications) {
0926: wcAccess.close();
0927: return SVNCommitPacket.EMPTY;
0928: }
0929: return new SVNCommitPacket(wcAccess, commitItems,
0930: lockTokens);
0931: } catch (SVNException e) {
0932: wcAccess.close();
0933: if (e instanceof SVNCancelException) {
0934: throw e;
0935: }
0936: SVNErrorMessage nestedErr = e.getErrorMessage();
0937: SVNErrorMessage err = SVNErrorMessage.create(nestedErr
0938: .getErrorCode(), "Commit failed (details follow):");
0939: SVNErrorManager.error(err, e);
0940: return null;
0941: }
0942: }
0943:
0944: /**
0945: * Collects commit items (containing detailed information on each Working Copy item
0946: * that was changed and need to be committed to the repository) into different
0947: * <b>SVNCommitPacket</b>s. This allows to prepare commit packets for different
0948: * Working Copies located "belonging" different repositories. Separate packets will
0949: * be committed separately. If the repository is the same for all the paths, then all
0950: * collected commit packets can be combined into a single one and committed in a single
0951: * transaction.
0952: *
0953: * @param paths an array of local items which should be traversed
0954: * to collect information on every changed item (one
0955: * <b>SVNCommitItem</b> per each
0956: * modified local item)
0957: * @param keepLocks if <span class="javakeyword">true</span> and there are local items that
0958: * were locked then these items will be left locked after
0959: * traversing all of them, otherwise the items will be unlocked
0960: * @param force forces collecting commit items for a non-recursive commit
0961: * @param recursive relevant only for directory items: if <span class="javakeyword">true</span> then
0962: * the entire directory tree will be traversed including all child
0963: * directories, otherwise only items located in the directory itself
0964: * will be processed
0965: * @param combinePackets if <span class="javakeyword">true</span> then collected commit
0966: * packets will be joined into a single one, so that to be committed
0967: * in a single transaction
0968: * @return an array of commit packets
0969: * @throws SVNException
0970: * @see SVNCommitItem
0971: */
0972: public SVNCommitPacket[] doCollectCommitItems(File[] paths,
0973: boolean keepLocks, boolean force, boolean recursive,
0974: boolean combinePackets) throws SVNException {
0975: if (paths == null || paths.length == 0) {
0976: return new SVNCommitPacket[0];
0977: }
0978: Collection packets = new ArrayList();
0979: Map targets = new HashMap();
0980: SVNStatusClient statusClient = new SVNStatusClient(
0981: getRepositoryPool(), getOptions());
0982: statusClient.setEventHandler(new ISVNEventHandler() {
0983: public void handleEvent(SVNEvent event, double progress)
0984: throws SVNException {
0985: }
0986:
0987: public void checkCancelled() throws SVNCancelException {
0988: SVNCommitClient.this .checkCancelled();
0989: }
0990: });
0991: SVNWCAccess[] wcAccesses = SVNCommitUtil.createCommitWCAccess2(
0992: paths, recursive, force, targets, statusClient);
0993:
0994: for (int i = 0; i < wcAccesses.length; i++) {
0995: SVNWCAccess wcAccess = wcAccesses[i];
0996: SVNAdminArea[] areas = wcAccess.getAdminAreas();
0997: for (int j = 0; areas != null && j < areas.length; j++) {
0998: if (areas[j] != null) {
0999: areas[j].setCommitParameters(getCommitParameters());
1000: }
1001: }
1002: Collection targetPaths = (Collection) targets.get(wcAccess);
1003: try {
1004: checkCancelled();
1005: Map lockTokens = new HashMap();
1006: SVNCommitItem[] commitItems = SVNCommitUtil
1007: .harvestCommitables(wcAccess, targetPaths,
1008: lockTokens, !keepLocks, recursive,
1009: force, getCommitParameters());
1010: checkCancelled();
1011: boolean hasModifications = false;
1012: for (int j = 0; commitItems != null
1013: && j < commitItems.length; j++) {
1014: SVNCommitItem commitItem = commitItems[j];
1015: if (commitItem.isAdded() || commitItem.isDeleted()
1016: || commitItem.isContentsModified()
1017: || commitItem.isPropertiesModified()
1018: || commitItem.isCopied()) {
1019: hasModifications = true;
1020: break;
1021: }
1022: }
1023: if (!hasModifications) {
1024: wcAccess.close();
1025: continue;
1026: }
1027: packets.add(new SVNCommitPacket(wcAccess, commitItems,
1028: lockTokens));
1029: } catch (SVNException e) {
1030: for (int j = 0; j < wcAccesses.length; j++) {
1031: wcAccesses[j].close();
1032: }
1033: if (e instanceof SVNCancelException) {
1034: throw e;
1035: }
1036: SVNErrorMessage nestedErr = e.getErrorMessage();
1037: SVNErrorMessage err = SVNErrorMessage.create(nestedErr
1038: .getErrorCode(),
1039: "Commit failed (details follow):");
1040: SVNErrorManager.error(err, e);
1041: }
1042: }
1043: SVNCommitPacket[] packetsArray = (SVNCommitPacket[]) packets
1044: .toArray(new SVNCommitPacket[packets.size()]);
1045: if (!combinePackets) {
1046: return packetsArray;
1047: }
1048: Map repoUUIDs = new HashMap();
1049: Map locktokensMap = new HashMap();
1050: try {
1051: // get wc root for each packet and uuid for each root.
1052: // group items by uuid.
1053: for (int i = 0; i < packetsArray.length; i++) {
1054: checkCancelled();
1055: SVNCommitPacket packet = packetsArray[i];
1056: File wcRoot = SVNWCUtil.getWorkingCopyRoot(packet
1057: .getCommitItems()[0].getWCAccess().getAnchor(),
1058: true);
1059: SVNWCAccess rootWCAccess = createWCAccess();
1060: String uuid = null;
1061: SVNURL url = null;
1062: try {
1063: SVNAdminArea rootDir = rootWCAccess.open(wcRoot,
1064: false, 0);
1065: uuid = rootDir.getEntry(rootDir.getThisDirName(),
1066: false).getUUID();
1067: url = rootDir.getEntry(rootDir.getThisDirName(),
1068: false).getSVNURL();
1069: } finally {
1070: rootWCAccess.close();
1071: }
1072: checkCancelled();
1073: if (uuid == null) {
1074: if (url != null) {
1075: SVNRepository repos = createRepository(url,
1076: true);
1077: uuid = repos.getRepositoryUUID(true);
1078: } else {
1079: SVNErrorMessage err = SVNErrorMessage.create(
1080: SVNErrorCode.ENTRY_MISSING_URL,
1081: "''{0}'' has no URL", wcRoot);
1082: SVNErrorManager.error(err);
1083: }
1084: }
1085: // also use protocol, host and port as a key, not only uuid.
1086: uuid += url.getProtocol() + ":" + url.getHost() + ":"
1087: + url.getPort();
1088: if (!repoUUIDs.containsKey(uuid)) {
1089: repoUUIDs.put(uuid, new ArrayList());
1090: locktokensMap.put(uuid, new HashMap());
1091: }
1092: Collection items = (Collection) repoUUIDs.get(uuid);
1093: Map lockTokens = (Map) locktokensMap.get(uuid);
1094: for (int j = 0; j < packet.getCommitItems().length; j++) {
1095: items.add(packet.getCommitItems()[j]);
1096: }
1097: if (packet.getLockTokens() != null) {
1098: lockTokens.putAll(packet.getLockTokens());
1099: }
1100: checkCancelled();
1101: }
1102: packetsArray = new SVNCommitPacket[repoUUIDs.size()];
1103: int index = 0;
1104: for (Iterator roots = repoUUIDs.keySet().iterator(); roots
1105: .hasNext();) {
1106: checkCancelled();
1107: String uuid = (String) roots.next();
1108: Collection items = (Collection) repoUUIDs.get(uuid);
1109: Map lockTokens = (Map) locktokensMap.get(uuid);
1110: SVNCommitItem[] itemsArray = (SVNCommitItem[]) items
1111: .toArray(new SVNCommitItem[items.size()]);
1112: packetsArray[index++] = new SVNCommitPacket(null,
1113: itemsArray, lockTokens);
1114: }
1115: } catch (SVNException e) {
1116: for (int j = 0; j < wcAccesses.length; j++) {
1117: wcAccesses[j].close();
1118: }
1119: if (e instanceof SVNCancelException) {
1120: throw e;
1121: }
1122: SVNErrorMessage nestedErr = e.getErrorMessage();
1123: SVNErrorMessage err = SVNErrorMessage.create(nestedErr
1124: .getErrorCode(), "Commit failed (details follow):");
1125: SVNErrorManager.error(err, e);
1126: }
1127: return packetsArray;
1128: }
1129:
1130: private boolean importDir(SVNDeltaGenerator deltaGenerator,
1131: File rootFile, File dir, String importPath,
1132: boolean useGlobalIgnores, boolean recursive,
1133: ISVNEditor editor) throws SVNException {
1134: checkCancelled();
1135: File[] children = SVNFileListUtil.listFiles(dir);
1136: boolean changed = false;
1137: for (int i = 0; children != null && i < children.length; i++) {
1138: File file = children[i];
1139: if (SVNFileUtil.getAdminDirectoryName().equals(
1140: file.getName())) {
1141: SVNEvent skippedEvent = SVNEventFactory
1142: .createSkipEvent(rootFile, file,
1143: SVNEventAction.SKIP,
1144: SVNEventAction.COMMIT_ADDED,
1145: SVNNodeKind.NONE);
1146: handleEvent(skippedEvent, ISVNEventHandler.UNKNOWN);
1147: continue;
1148: }
1149: if (useGlobalIgnores && getOptions().isIgnored(file)) {
1150: continue;
1151: }
1152: String path = importPath == null ? file.getName()
1153: : SVNPathUtil.append(importPath, file.getName());
1154: SVNFileType fileType = SVNFileType.getType(file);
1155: if (fileType == SVNFileType.DIRECTORY && recursive) {
1156: editor.addDir(path, null, -1);
1157: changed |= true;
1158: SVNEvent event = SVNEventFactory.createCommitEvent(
1159: rootFile, file, SVNEventAction.COMMIT_ADDED,
1160: SVNNodeKind.DIR, null);
1161: handleEvent(event, ISVNEventHandler.UNKNOWN);
1162: importDir(deltaGenerator, rootFile, file, path,
1163: useGlobalIgnores, recursive, editor);
1164: editor.closeDir();
1165: } else if (fileType == SVNFileType.FILE
1166: || fileType == SVNFileType.SYMLINK) {
1167: changed |= importFile(deltaGenerator, rootFile, file,
1168: fileType, path, editor);
1169: }
1170:
1171: }
1172: return changed;
1173: }
1174:
1175: private boolean importFile(SVNDeltaGenerator deltaGenerator,
1176: File rootFile, File file, SVNFileType fileType,
1177: String filePath, ISVNEditor editor) throws SVNException {
1178: if (fileType == null || fileType == SVNFileType.UNKNOWN) {
1179: SVNErrorMessage err = SVNErrorMessage.create(
1180: SVNErrorCode.NODE_UNKNOWN_KIND,
1181: "unknown or unversionable type for ''{0}''", file);
1182: SVNErrorManager.error(err);
1183: }
1184: editor.addFile(filePath, null, -1);
1185: String mimeType = null;
1186: Map autoProperties = new HashMap();
1187: if (fileType != SVNFileType.SYMLINK) {
1188: autoProperties = getOptions().applyAutoProperties(file,
1189: autoProperties);
1190: if (!autoProperties.containsKey(SVNProperty.MIME_TYPE)) {
1191: mimeType = SVNFileUtil.detectMimeType(file);
1192: if (mimeType != null) {
1193: autoProperties.put(SVNProperty.MIME_TYPE, mimeType);
1194: if (SVNProperty.isBinaryMimeType(mimeType)) {
1195: autoProperties.remove(SVNProperty.EOL_STYLE);
1196: }
1197: }
1198: }
1199: if (!autoProperties.containsKey(SVNProperty.EXECUTABLE)
1200: && SVNFileUtil.isExecutable(file)) {
1201: autoProperties.put(SVNProperty.EXECUTABLE, "");
1202: }
1203: } else {
1204: autoProperties.put(SVNProperty.SPECIAL, "*");
1205: }
1206: for (Iterator names = autoProperties.keySet().iterator(); names
1207: .hasNext();) {
1208: String name = (String) names.next();
1209: String value = (String) autoProperties.get(name);
1210: if (SVNProperty.EOL_STYLE.equals(name) && value != null) {
1211: if (SVNProperty
1212: .isBinaryMimeType((String) autoProperties
1213: .get(SVNProperty.MIME_TYPE))) {
1214: continue;
1215: } else if (!SVNTranslator.checkNewLines(file)) {
1216: continue;
1217: }
1218: }
1219: editor.changeFileProperty(filePath, name, value);
1220: }
1221: // send "adding"
1222: SVNEvent addedEvent = SVNEventFactory.createCommitEvent(
1223: rootFile, file, SVNEventAction.COMMIT_ADDED,
1224: SVNNodeKind.FILE, mimeType);
1225: handleEvent(addedEvent, ISVNEventHandler.UNKNOWN);
1226: // translate and send file.
1227: String eolStyle = (String) autoProperties
1228: .get(SVNProperty.EOL_STYLE);
1229: String keywords = (String) autoProperties
1230: .get(SVNProperty.KEYWORDS);
1231: boolean special = autoProperties.get(SVNProperty.SPECIAL) != null;
1232: File tmpFile = null;
1233: if (eolStyle != null || keywords != null || special) {
1234: byte[] eolBytes = SVNTranslator.getBaseEOL(eolStyle);
1235: Map keywordsMap = keywords != null ? SVNTranslator
1236: .computeKeywords(keywords, null, null, null, null,
1237: getOptions()) : null;
1238: tmpFile = SVNFileUtil.createTempFile("import", ".tmp");
1239: SVNTranslator.translate(file, tmpFile, eolBytes,
1240: keywordsMap, special, false);
1241: }
1242: File importedFile = tmpFile != null ? tmpFile : file;
1243: InputStream is = null;
1244: String checksum = null;
1245: try {
1246: is = SVNFileUtil.openFileForReading(importedFile);
1247: editor.applyTextDelta(filePath, null);
1248: checksum = deltaGenerator.sendDelta(filePath, is, editor,
1249: true);
1250: } finally {
1251: SVNFileUtil.closeFile(is);
1252: SVNFileUtil.deleteFile(tmpFile);
1253: }
1254: editor.closeFile(filePath, checksum);
1255: return true;
1256: }
1257:
1258: private static boolean hasProcessedParents(Collection paths,
1259: String path) {
1260: path = SVNPathUtil.removeTail(path);
1261: if (paths.contains(path)) {
1262: return true;
1263: }
1264: if ("".equals(path)) {
1265: return false;
1266: }
1267: return hasProcessedParents(paths, path);
1268: }
1269:
1270: static String validateCommitMessage(String message) {
1271: if (message == null) {
1272: return message;
1273: }
1274: message = message.replaceAll("\r\n", "\n");
1275: message = message.replace('\r', '\n');
1276: return message;
1277: }
1278:
1279: }
|