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.examples.wc;
0013:
0014: import java.io.File;
0015: import java.io.FileNotFoundException;
0016: import java.io.FileOutputStream;
0017: import java.io.IOException;
0018:
0019: import org.tmatesoft.svn.core.SVNCommitInfo;
0020: import org.tmatesoft.svn.core.SVNException;
0021: import org.tmatesoft.svn.core.SVNURL;
0022: import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
0023: import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
0024: import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
0025: import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
0026: import org.tmatesoft.svn.core.wc.ISVNOptions;
0027: import org.tmatesoft.svn.core.wc.SVNClientManager;
0028: import org.tmatesoft.svn.core.wc.SVNRevision;
0029: import org.tmatesoft.svn.core.wc.SVNUpdateClient;
0030: import org.tmatesoft.svn.core.wc.SVNWCUtil;
0031: import org.tmatesoft.svn.core.wc.ISVNEventHandler;
0032:
0033: /*
0034: * This is a complex example program that demonstrates how you can manage local
0035: * working copies as well as URLs (that is, items located in the repository) by
0036: * means of the API provided in the org.tmatesoft.svn.core.wc package. The package
0037: * itself represents a high-level API consisting of classes and interfaces which
0038: * allow to perform operations compatible with ones of the native Subversion command
0039: * line client. These version control operations are logically grouped in a set of
0040: * classes which names meet 'SVN*Client' pattern. For example, the package has the
0041: * SVNUpdateClient class which is responsible for update-related operations (update,
0042: * switch, check out). Most of the methods of these 'client' classes are named like
0043: * 'doSomething(..)' where 'Something' corresponds to the name of a version control
0044: * operation (usually similar to the name of the corresponding Subversion command
0045: * line client's command). So, for users familiar with the Subversion command line
0046: * client it won't take much effort and time to match a 'do*' method against an
0047: * appropriate Subversion client's operation (or command, in other words).
0048: *
0049: * Surely, it may seem not quite handy to deal with a number of classes that all
0050: * need to be instantiated, initialized, kept.. For example, if a developer is going
0051: * to use all (or several) of the SVN*Client classes and most of them will access a
0052: * repository (in that way when authentication is demanded), it becomes tiresome to
0053: * provide authentication info to every one of them. So, for managing purposes like
0054: * the previous one the package has got the class called SVNClientManager whose
0055: * get*Client() methods provide all necessary SVN*Client objects to a caller.
0056: *
0057: * A developer once creates an instance of SVNClientManager providing (if needed)
0058: * his authentication info/options (options are similar to the SVN run-time
0059: * configuration settings that can be found in the config file) into an appropriate
0060: * SVNClientManager.newInstance(..) method. Further all SVN*Client objects provided
0061: * by the instance of SVNClientManager will use these authentication info/options.
0062: *
0063: * The program illustrates a number of main operations usually carried out upon
0064: * working copies and URLs. Here's a brief description of what the program does
0065: * (main steps):
0066: *
0067: * 0)first of all initializes the SVNKit library (it must be done prior to using
0068: * the library);
0069: *
0070: * 1)if the program was run with some in parameters, it fetches them out of the args
0071: * parameter; the program expects the following input parameters:
0072: *
0073: * repositoryURL wcRootPath name password
0074: *
0075: * 2)next instantiates an SVNClientManager providing default options and auth info
0076: * to it - these parameters will be used by all SVN*Client objects that will be
0077: * created, kept and provided by the manager; default run-time options correspond to
0078: * the client-side run-time settings found in the 'config' file within the default
0079: * SVN configuration area; also in this case the client manager will use the server-
0080: * side run-time settings found in the 'servers' file within the same area;
0081: *
0082: * 3)the first operation - making an empty directory in a repository; that is like
0083: * 'svn mkdir URL' which creates a new directory given all the intermediate
0084: * directories created); this operation is immediately committed to the repository;
0085: *
0086: * 4)the next operation - creating a new local directory (importDir) and a new file
0087: * (importFile) in it and then importing the directory into the repository;
0088: *
0089: * 5)the next operation - checking out the directory created in the previous step to
0090: * a local directory defined by the myWorkingCopyPath parameter ;
0091: *
0092: * 6)the next operation - recursively getting and displaying info for each item at
0093: * the working revision in the working copy that was checked out in the previous
0094: * step;
0095: *
0096: * 7)the next operation - creating a new directory (newDir) with a file (newFile) in
0097: * the working copy and then recursively scheduling (if any subdirictories existed
0098: * they would be also added:)) the created directory for addition;
0099: *
0100: * 8)the next operation - recursively getting and displaying the working copy status
0101: * not including unchanged (normal) paths; the result must include those paths which
0102: * were scheduled for addition in the previous step;
0103: *
0104: * 9)the next operation - recursively updating the working copy; if any local items
0105: * are out of date they will be updated to the latest revision;
0106: *
0107: * 10)the next operation - committing local changes to the repository; this will add
0108: * the directory with the file (that were scheduled for addition two steps before)
0109: * to the repository;
0110: *
0111: * 11)the next operation - locking the file committed in the previous step (for
0112: * example, if you temporarily need to keep a file locked to prevent someone's else
0113: * modifications);
0114: *
0115: * 12)the next operation - showing status once again (here, to see that the file
0116: * was locked);
0117: *
0118: * 13)the next operation - copying with history one URL (url) to another one
0119: * (copyURL) within the same repository;
0120: *
0121: * 14)the next operation - switching the working copy to a different URL (copyURL)
0122: * where url was copied to in the previous step;
0123: *
0124: * 15)the next operation - recursively getting and displaying info on the root
0125: * directory of the working copy to demonstrate that the working copy is now really
0126: * switched against copyURL;
0127: *
0128: * 16)the next operation - scheduling the newDir directory for deletion;
0129: *
0130: * 17)the next operation - showing status once again (here, to see that the
0131: * directory with all its entries were scheduled for deletion);
0132: *
0133: * 18)the next operation - committing local changes to the repository; this operation
0134: * will delete the directory (newDir) with the file (newFile) that were scheduled for
0135: * deletion from the repository;
0136: *
0137: * * * *
0138: *
0139: * This example can be run for a locally installed Subversion repository via the
0140: * svn:// protocol. This is how you can do it:
0141: *
0142: * 1)after you install the Subversion on your machine (SVN is available for download
0143: * at http://subversion.tigris.org/), you should create a new repository in a
0144: * directory, like this (in a command line under a Windows OS):
0145: *
0146: * >svnadmin create X:\path\to\rep
0147: *
0148: * 2)after the repository is created you can add a new account: open X:\path\to\rep\,
0149: * then move into \conf and open the file - 'passwd'. In the file you'll see the
0150: * section [users]. Uncomment it and add a new account below the section name, like:
0151: *
0152: * [users]
0153: * userName = userPassword
0154: *
0155: * In the program you may further use this account as user's credentials.
0156: *
0157: * 3)the next step is to launch the custom Subversion server (svnserve) in a
0158: * background mode for the just created repository:
0159: *
0160: * >svnserve -d -r X:\path\to
0161: *
0162: * That's all. The repository is now available via svn://localhost/rep.
0163: *
0164: * * * *
0165: *
0166: * While the program is running, in your console you'll see something like this:
0167:
0168: Making a new directory at 'svn://localhost/testRep/MyRepos'...
0169: Committed to revision 70
0170:
0171: Importing a new directory into 'svn://localhost/testRep/MyRepos/importDir'...
0172: Adding importFile.txt
0173: Committed to revision 71
0174:
0175: Checking out a working copy from 'svn://localhost/testRep/MyRepos'...
0176: A importDir
0177: A importDir/importFile.txt
0178: At revision 71
0179:
0180: -----------------INFO-----------------
0181: Local Path: N:\MyWorkingCopy
0182: URL: svn://localhost/testRep/MyRepos
0183: Repository UUID: dbe83c44-f5aa-e043-94ec-ecdf6c56480f
0184: Revision: 71
0185: Node Kind: dir
0186: Schedule: normal
0187: Last Changed Author: userName
0188: Last Changed Revision: 71
0189: Last Changed Date: Thu Jul 21 23:43:15 NOVST 2005
0190: -----------------INFO-----------------
0191: Local Path: N:\MyWorkingCopy\importDir
0192: URL: svn://localhost/testRep/MyRepos/importDir
0193: Repository UUID: dbe83c44-f5aa-e043-94ec-ecdf6c56480f
0194: Revision: 71
0195: Node Kind: dir
0196: Schedule: normal
0197: Last Changed Author: userName
0198: Last Changed Revision: 71
0199: Last Changed Date: Thu Jul 21 23:43:15 NOVST 2005
0200: -----------------INFO-----------------
0201: Local Path: N:\MyWorkingCopy\importDir\importFile.txt
0202: URL: svn://localhost/testRep/MyRepos/importDir/importFile.txt
0203: Repository UUID: dbe83c44-f5aa-e043-94ec-ecdf6c56480f
0204: Revision: 71
0205: Node Kind: file
0206: Schedule: normal
0207: Last Changed Author: userName
0208: Last Changed Revision: 71
0209: Last Changed Date: Thu Jul 21 23:43:15 NOVST 2005
0210: Properties Last Updated: Thu Jul 21 23:43:16 NOVST 2005
0211: Text Last Updated: Thu Jul 21 23:43:16 NOVST 2005
0212: Checksum: 75e9e68e21ae4453f318424738aef57e
0213:
0214: Recursively scheduling a new directory 'N:\MyWorkingCopy\newDir' for addition...
0215: A newDir
0216: A newDir/newFile.txt
0217:
0218: Status for 'N:\MyWorkingCopy':
0219: A 0 ? ? N:\MyWorkingCopy\newDir\newFile.txt
0220: A 0 ? ? N:\MyWorkingCopy\newDir
0221:
0222: Updating 'N:\MyWorkingCopy'...
0223: At revision 71
0224:
0225: Committing changes for 'N:\MyWorkingCopy'...
0226: Adding newDir
0227: Adding newDir/newFile.txt
0228: Transmitting file data....
0229: Committed to revision 72
0230:
0231: Locking (with stealing if the entry is already locked) 'N:\MyWorkingCopy\newDir\newFile.txt'.
0232: L newFile.txt
0233:
0234: Status for 'N:\MyWorkingCopy':
0235: K 72 72 userName N:\MyWorkingCopy\newDir\newFile.txt
0236:
0237: Copying 'svn://localhost/testRep/MyRepos' to 'svn://localhost/testRep/MyReposCopy'...
0238: Committed to revision 73
0239:
0240: Switching 'N:\MyWorkingCopy' to 'svn://localhost/testRep/MyReposCopy'...
0241: B newDir/newFile.txt
0242: At revision 73
0243:
0244: -----------------INFO-----------------
0245: Local Path: N:\MyWorkingCopy
0246: URL: svn://localhost/testRep/MyReposCopy
0247: Repository UUID: dbe83c44-f5aa-e043-94ec-ecdf6c56480f
0248: Revision: 73
0249: Node Kind: dir
0250: Schedule: normal
0251: Last Changed Author: userName
0252: Last Changed Revision: 73
0253: Last Changed Date: Thu Jul 21 23:43:19 NOVST 2005
0254: -----------------INFO-----------------
0255: Local Path: N:\MyWorkingCopy\importDir
0256: URL: svn://localhost/testRep/MyReposCopy/importDir
0257: Repository UUID: dbe83c44-f5aa-e043-94ec-ecdf6c56480f
0258: Revision: 73
0259: Node Kind: dir
0260: Schedule: normal
0261: Last Changed Author: userName
0262: Last Changed Revision: 71
0263: Last Changed Date: Thu Jul 21 23:43:15 NOVST 2005
0264: -----------------INFO-----------------
0265: Local Path: N:\MyWorkingCopy\importDir\importFile.txt
0266: URL: svn://localhost/testRep/MyReposCopy/importDir/importFile.txt
0267: Repository UUID: dbe83c44-f5aa-e043-94ec-ecdf6c56480f
0268: Revision: 73
0269: Node Kind: file
0270: Schedule: normal
0271: Last Changed Author: userName
0272: Last Changed Revision: 71
0273: Last Changed Date: Thu Jul 21 23:43:15 NOVST 2005
0274: Properties Last Updated: Thu Jul 21 23:43:16 NOVST 2005
0275: Text Last Updated: Thu Jul 21 23:43:16 NOVST 2005
0276: Checksum: 75e9e68e21ae4453f318424738aef57e
0277: -----------------INFO-----------------
0278: Local Path: N:\MyWorkingCopy\newDir
0279: URL: svn://localhost/testRep/MyReposCopy/newDir
0280: Repository UUID: dbe83c44-f5aa-e043-94ec-ecdf6c56480f
0281: Revision: 73
0282: Node Kind: dir
0283: Schedule: normal
0284: Last Changed Author: userName
0285: Last Changed Revision: 72
0286: Last Changed Date: Thu Jul 21 23:43:18 NOVST 2005
0287: -----------------INFO-----------------
0288: Local Path: N:\MyWorkingCopy\newDir\newFile.txt
0289: URL: svn://localhost/testRep/MyReposCopy/newDir/newFile.txt
0290: Repository UUID: dbe83c44-f5aa-e043-94ec-ecdf6c56480f
0291: Revision: 73
0292: Node Kind: file
0293: Schedule: normal
0294: Last Changed Author: userName
0295: Last Changed Revision: 72
0296: Last Changed Date: Thu Jul 21 23:43:18 NOVST 2005
0297: Properties Last Updated: Thu Jul 21 23:43:20 NOVST 2005
0298: Text Last Updated: Thu Jul 21 23:43:18 NOVST 2005
0299: Checksum: 023b67e9660b2faabaf84b10ba32c6cf
0300:
0301: Scheduling 'N:\MyWorkingCopy\newDir' for deletion ...
0302: D newDir/newFile.txt
0303: D newDir
0304:
0305: Status for 'N:\MyWorkingCopy':
0306: D 73 72 userName N:\MyWorkingCopy\newDir\newFile.txt
0307: D 73 72 userName N:\MyWorkingCopy\newDir
0308:
0309: Committing changes for 'N:\MyWorkingCopy'...
0310: Deleting newDir
0311: Committed to revision 74
0312: *
0313: */
0314: public class WorkingCopy {
0315:
0316: private static SVNClientManager ourClientManager;
0317: private static ISVNEventHandler myCommitEventHandler;
0318: private static ISVNEventHandler myUpdateEventHandler;
0319: private static ISVNEventHandler myWCEventHandler;
0320:
0321: public static void main(String[] args) throws SVNException {
0322: /*
0323: * Initializes the library (it must be done before ever using the library
0324: * itself)
0325: */
0326: setupLibrary();
0327:
0328: /*
0329: * Default values:
0330: */
0331:
0332: /*
0333: * Assuming that 'svn://localhost/testRep' is an existing repository path.
0334: * SVNURL is a wrapper for URL strings that refer to repository locations.
0335: */
0336: SVNURL repositoryURL = null;
0337: try {
0338: repositoryURL = SVNURL
0339: .parseURIEncoded("svn://localhost/testRep");
0340: } catch (SVNException e) {
0341: //
0342: }
0343: String name = "userName";
0344: String password = "userPassword";
0345: String myWorkingCopyPath = "/MyWorkingCopy";
0346:
0347: String importDir = "/importDir";
0348: String importFile = importDir + "/importFile.txt";
0349: String importFileText = "This unversioned file is imported into a repository";
0350:
0351: String newDir = "/newDir";
0352: String newFile = newDir + "/newFile.txt";
0353: String fileText = "This is a new file added to the working copy";
0354:
0355: if (args != null) {
0356: /*
0357: * Obtains a URL that represents an already existing repository
0358: */
0359: try {
0360: repositoryURL = (args.length >= 1) ? SVNURL
0361: .parseURIEncoded(args[0]) : repositoryURL;
0362: } catch (SVNException e) {
0363: System.err.println("'" + args[0]
0364: + "' is not a valid URL");
0365: System.exit(1);
0366: }
0367: /*
0368: * Obtains a path to be a working copy root directory
0369: */
0370: myWorkingCopyPath = (args.length >= 2) ? args[1]
0371: : myWorkingCopyPath;
0372: /*
0373: * Obtains an account name
0374: */
0375: name = (args.length >= 3) ? args[2] : name;
0376: /*
0377: * Obtains a password
0378: */
0379: password = (args.length >= 4) ? args[3] : password;
0380: }
0381:
0382: /*
0383: * That's where a new directory will be created
0384: */
0385: SVNURL url = repositoryURL.appendPath("MyRepos", false);
0386: /*
0387: * That's where '/MyRepos' will be copied to (branched)
0388: */
0389: SVNURL copyURL = repositoryURL.appendPath("MyReposCopy", false);
0390: /*
0391: * That's where a local directory will be imported into.
0392: * Note that it's not necessary that the '/importDir' directory must already
0393: * exist - the SVN repository server will take care of creating it.
0394: */
0395: SVNURL importToURL = url.appendPath(importDir, false);
0396:
0397: /*
0398: * Creating custom handlers that will process events
0399: */
0400: myCommitEventHandler = new CommitEventHandler();
0401:
0402: myUpdateEventHandler = new UpdateEventHandler();
0403:
0404: myWCEventHandler = new WCEventHandler();
0405:
0406: /*
0407: * Creates a default run-time configuration options driver. Default options
0408: * created in this way use the Subversion run-time configuration area (for
0409: * instance, on a Windows platform it can be found in the '%APPDATA%\Subversion'
0410: * directory).
0411: *
0412: * readonly = true - not to save any configuration changes that can be done
0413: * during the program run to a config file (config settings will only
0414: * be read to initialize; to enable changes the readonly flag should be set
0415: * to false).
0416: *
0417: * SVNWCUtil is a utility class that creates a default options driver.
0418: */
0419: ISVNOptions options = SVNWCUtil.createDefaultOptions(true);
0420:
0421: /*
0422: * Creates an instance of SVNClientManager providing authentication
0423: * information (name, password) and an options driver
0424: */
0425: ourClientManager = SVNClientManager.newInstance(options, name,
0426: password);
0427:
0428: /*
0429: * Sets a custom event handler for operations of an SVNCommitClient
0430: * instance
0431: */
0432: ourClientManager.getCommitClient().setEventHandler(
0433: myCommitEventHandler);
0434:
0435: /*
0436: * Sets a custom event handler for operations of an SVNUpdateClient
0437: * instance
0438: */
0439: ourClientManager.getUpdateClient().setEventHandler(
0440: myUpdateEventHandler);
0441:
0442: /*
0443: * Sets a custom event handler for operations of an SVNWCClient
0444: * instance
0445: */
0446: ourClientManager.getWCClient()
0447: .setEventHandler(myWCEventHandler);
0448:
0449: long committedRevision = -1;
0450: System.out
0451: .println("Making a new directory at '" + url + "'...");
0452: try {
0453: /*
0454: * creates a new version comtrolled directory in a repository and
0455: * displays what revision the repository was committed to
0456: */
0457: committedRevision = makeDirectory(url,
0458: "making a new directory at '" + url + "'")
0459: .getNewRevision();
0460: } catch (SVNException svne) {
0461: error(
0462: "error while making a new directory at '" + url
0463: + "'", svne);
0464: }
0465: System.out
0466: .println("Committed to revision " + committedRevision);
0467: System.out.println();
0468:
0469: File anImportDir = new File(importDir);
0470: File anImportFile = new File(anImportDir, SVNPathUtil
0471: .tail(importFile));
0472: /*
0473: * creates a new local directory - './importDir' and a new file -
0474: * './importDir/importFile.txt' that will be imported into the repository
0475: * into the '/MyRepos/importDir' directory
0476: */
0477: createLocalDir(anImportDir, new File[] { anImportFile },
0478: new String[] { importFileText });
0479:
0480: System.out.println("Importing a new directory into '"
0481: + importToURL + "'...");
0482: try {
0483: /*
0484: * recursively imports an unversioned directory into a repository
0485: * and displays what revision the repository was committed to
0486: */
0487: boolean isRecursive = true;
0488: committedRevision = importDirectory(
0489: anImportDir,
0490: importToURL,
0491: "importing a new directory '"
0492: + anImportDir.getAbsolutePath() + "'",
0493: isRecursive).getNewRevision();
0494: } catch (SVNException svne) {
0495: error("error while importing a new directory '"
0496: + anImportDir.getAbsolutePath() + "' into '"
0497: + importToURL + "'", svne);
0498: }
0499: System.out
0500: .println("Committed to revision " + committedRevision);
0501: System.out.println();
0502:
0503: /*
0504: * creates a local directory where the working copy will be checked out into
0505: */
0506: File wcDir = new File(myWorkingCopyPath);
0507: if (wcDir.exists()) {
0508: error("the destination directory '"
0509: + wcDir.getAbsolutePath() + "' already exists!",
0510: null);
0511: }
0512: wcDir.mkdirs();
0513:
0514: System.out.println("Checking out a working copy from '" + url
0515: + "'...");
0516: try {
0517: /*
0518: * recursively checks out a working copy from url into wcDir.
0519: * SVNRevision.HEAD means the latest revision to be checked out.
0520: */
0521: checkout(url, SVNRevision.HEAD, wcDir, true);
0522: } catch (SVNException svne) {
0523: error(
0524: "error while checking out a working copy for the location '"
0525: + url + "'", svne);
0526: }
0527: System.out.println();
0528:
0529: /*
0530: * recursively displays info for wcDir at the current working revision
0531: * in the manner of 'svn info -R' command
0532: */
0533: try {
0534: showInfo(wcDir, SVNRevision.WORKING, true);
0535: } catch (SVNException svne) {
0536: error(
0537: "error while recursively getting info for the working copy at'"
0538: + wcDir.getAbsolutePath() + "'", svne);
0539: }
0540: System.out.println();
0541:
0542: File aNewDir = new File(wcDir, newDir);
0543: File aNewFile = new File(aNewDir, SVNPathUtil.tail(newFile));
0544: /*
0545: * creates a new local directory - 'wcDir/newDir' and a new file -
0546: * '/MyWorkspace/newDir/newFile.txt'
0547: */
0548: createLocalDir(aNewDir, new File[] { aNewFile },
0549: new String[] { fileText });
0550:
0551: System.out.println("Recursively scheduling a new directory '"
0552: + aNewDir.getAbsolutePath() + "' for addition...");
0553: try {
0554: /*
0555: * recursively schedules aNewDir for addition
0556: */
0557: addEntry(aNewDir);
0558: } catch (SVNException svne) {
0559: error("error while recursively adding the directory '"
0560: + aNewDir.getAbsolutePath() + "'", svne);
0561: }
0562: System.out.println();
0563:
0564: boolean isRecursive = true;
0565: boolean isRemote = true;
0566: boolean isReportAll = false;
0567: boolean isIncludeIgnored = true;
0568: boolean isCollectParentExternals = false;
0569: System.out.println("Status for '" + wcDir.getAbsolutePath()
0570: + "':");
0571: try {
0572: /*
0573: * gets and shows status information for the WC directory.
0574: * status will be recursive on wcDir, will also cover the repository,
0575: * won't cover unmodified entries, will disregard 'svn:ignore' property
0576: * ignores (if any), will ignore externals definitions.
0577: */
0578: showStatus(wcDir, isRecursive, isRemote, isReportAll,
0579: isIncludeIgnored, isCollectParentExternals);
0580: } catch (SVNException svne) {
0581: error("error while recursively performing status for '"
0582: + wcDir.getAbsolutePath() + "'", svne);
0583: }
0584: System.out.println();
0585:
0586: System.out.println("Updating '" + wcDir.getAbsolutePath()
0587: + "'...");
0588: try {
0589: /*
0590: * recursively updates wcDir to the latest revision (SVNRevision.HEAD)
0591: */
0592: update(wcDir, SVNRevision.HEAD, true);
0593: } catch (SVNException svne) {
0594: error(
0595: "error while recursively updating the working copy at '"
0596: + wcDir.getAbsolutePath() + "'", svne);
0597: }
0598: System.out.println("");
0599:
0600: System.out.println("Committing changes for '"
0601: + wcDir.getAbsolutePath() + "'...");
0602: try {
0603: /*
0604: * commits changes in wcDir to the repository with not leaving items
0605: * locked (if any) after the commit succeeds; this will add aNewDir &
0606: * aNewFile to the repository.
0607: */
0608: committedRevision = commit(wcDir, false,
0609: "'/newDir' with '/newDir/newFile.txt' were added")
0610: .getNewRevision();
0611: } catch (SVNException svne) {
0612: error(
0613: "error while committing changes to the working copy at '"
0614: + wcDir.getAbsolutePath() + "'", svne);
0615: }
0616: System.out
0617: .println("Committed to revision " + committedRevision);
0618: System.out.println();
0619:
0620: System.out
0621: .println("Locking (with stealing if the entry is already locked) '"
0622: + aNewFile.getAbsolutePath() + "'.");
0623: try {
0624: /*
0625: * locks aNewFile with stealing (if it has been already locked by someone
0626: * else), providing a lock comment
0627: */
0628: lock(aNewFile, true, "locking '/newDir/newFile.txt'");
0629: } catch (SVNException svne) {
0630: error("error while locking the working copy file '"
0631: + aNewFile.getAbsolutePath() + "'", svne);
0632: }
0633: System.out.println();
0634:
0635: System.out.println("Status for '" + wcDir.getAbsolutePath()
0636: + "':");
0637: try {
0638: /*
0639: * displays status once again to see the file is really locked
0640: */
0641: showStatus(wcDir, isRecursive, isRemote, isReportAll,
0642: isIncludeIgnored, isCollectParentExternals);
0643: } catch (SVNException svne) {
0644: error("error while recursively performing status for '"
0645: + wcDir.getAbsolutePath() + "'", svne);
0646: }
0647: System.out.println();
0648:
0649: System.out.println("Copying '" + url + "' to '" + copyURL
0650: + "'...");
0651: try {
0652: /*
0653: * makes a branch of url at copyURL - that is URL->URL copying
0654: * with history
0655: */
0656: committedRevision = copy(
0657: url,
0658: copyURL,
0659: false,
0660: "remotely copying '" + url + "' to '" + copyURL
0661: + "'").getNewRevision();
0662: } catch (SVNException svne) {
0663: error("error while copying '" + url + "' to '" + copyURL
0664: + "'", svne);
0665: }
0666: /*
0667: * displays what revision the repository was committed to
0668: */
0669: System.out
0670: .println("Committed to revision " + committedRevision);
0671: System.out.println();
0672:
0673: System.out.println("Switching '" + wcDir.getAbsolutePath()
0674: + "' to '" + copyURL + "'...");
0675: try {
0676: /*
0677: * recursively switches wcDir to copyURL in the latest revision
0678: * (SVNRevision.HEAD)
0679: */
0680: switchToURL(wcDir, copyURL, SVNRevision.HEAD, true);
0681: } catch (SVNException svne) {
0682: error("error while switching '" + wcDir.getAbsolutePath()
0683: + "' to '" + copyURL + "'", svne);
0684: }
0685: System.out.println();
0686:
0687: /*
0688: * recursively displays info for the working copy once again to see
0689: * it was really switched to a new URL
0690: */
0691: try {
0692: showInfo(wcDir, SVNRevision.WORKING, true);
0693: } catch (SVNException svne) {
0694: error(
0695: "error while recursively getting info for the working copy at'"
0696: + wcDir.getAbsolutePath() + "'", svne);
0697: }
0698: System.out.println();
0699:
0700: System.out.println("Scheduling '" + aNewDir.getAbsolutePath()
0701: + "' for deletion ...");
0702: try {
0703: /*
0704: * schedules aNewDir for deletion (with forcing)
0705: */
0706: delete(aNewDir, true);
0707: } catch (SVNException svne) {
0708: error("error while schediling '" + wcDir.getAbsolutePath()
0709: + "' for deletion", svne);
0710: }
0711: System.out.println();
0712:
0713: System.out.println("Status for '" + wcDir.getAbsolutePath()
0714: + "':");
0715: try {
0716: /*
0717: * recursively displays status once more to see whether aNewDir
0718: * was really scheduled for deletion
0719: */
0720: showStatus(wcDir, isRecursive, isRemote, isReportAll,
0721: isIncludeIgnored, isCollectParentExternals);
0722: } catch (SVNException svne) {
0723: error("error while recursively performing status for '"
0724: + wcDir.getAbsolutePath() + "'", svne);
0725: }
0726: System.out.println();
0727:
0728: System.out.println("Committing changes for '"
0729: + wcDir.getAbsolutePath() + "'...");
0730: try {
0731: /*
0732: * lastly commits changes in wcDir to the repository; all items that
0733: * were locked by the user (if any) will be unlocked after the commit
0734: * succeeds; this commit will remove aNewDir from the repository.
0735: */
0736: committedRevision = commit(
0737: wcDir,
0738: false,
0739: "deleting '"
0740: + aNewDir.getAbsolutePath()
0741: + "' from the filesystem as well as from the repository")
0742: .getNewRevision();
0743: } catch (SVNException svne) {
0744: error(
0745: "error while committing changes to the working copy '"
0746: + wcDir.getAbsolutePath() + "'", svne);
0747: }
0748: System.out
0749: .println("Committed to revision " + committedRevision);
0750: System.exit(0);
0751: }
0752:
0753: /*
0754: * Initializes the library to work with a repository via
0755: * different protocols.
0756: */
0757: private static void setupLibrary() {
0758: /*
0759: * For using over http:// and https://
0760: */
0761: DAVRepositoryFactory.setup();
0762: /*
0763: * For using over svn:// and svn+xxx://
0764: */
0765: SVNRepositoryFactoryImpl.setup();
0766:
0767: /*
0768: * For using over file:///
0769: */
0770: FSRepositoryFactory.setup();
0771: }
0772:
0773: /*
0774: * Creates a new version controlled directory (doesn't create any intermediate
0775: * directories) right in a repository. Like 'svn mkdir URL -m "some comment"'
0776: * command. It's done by invoking
0777: *
0778: * SVNCommitClient.doMkDir(SVNURL[] urls, String commitMessage)
0779: *
0780: * which takes the following parameters:
0781: *
0782: * urls - an array of URLs that are to be created;
0783: *
0784: * commitMessage - a commit log message since a URL-based directory creation is
0785: * immediately committed to a repository.
0786: */
0787: private static SVNCommitInfo makeDirectory(SVNURL url,
0788: String commitMessage) throws SVNException {
0789: /*
0790: * Returns SVNCommitInfo containing information on the new revision committed
0791: * (revision number, etc.)
0792: */
0793: return ourClientManager.getCommitClient().doMkDir(
0794: new SVNURL[] { url }, commitMessage);
0795: }
0796:
0797: /*
0798: * Imports an unversioned directory into a repository location denoted by a
0799: * destination URL (all necessary parent non-existent paths will be created
0800: * automatically). This operation commits the repository to a new revision.
0801: * Like 'svn import PATH URL (-N) -m "some comment"' command. It's done by
0802: * invoking
0803: *
0804: * SVNCommitClient.doImport(File path, SVNURL dstURL, String commitMessage, boolean recursive)
0805: *
0806: * which takes the following parameters:
0807: *
0808: * path - a local unversioned directory or singal file that will be imported into a
0809: * repository;
0810: *
0811: * dstURL - a repository location where the local unversioned directory/file will be
0812: * imported into; this URL path may contain non-existent parent paths that will be
0813: * created by the repository server;
0814: *
0815: * commitMessage - a commit log message since the new directory/file are immediately
0816: * created in the repository;
0817: *
0818: * recursive - if true and path parameter corresponds to a directory then the directory
0819: * will be added with all its child subdirictories, otherwise the operation will cover
0820: * only the directory itself (only those files which are located in the directory).
0821: */
0822: private static SVNCommitInfo importDirectory(File localPath,
0823: SVNURL dstURL, String commitMessage, boolean isRecursive)
0824: throws SVNException {
0825: /*
0826: * Returns SVNCommitInfo containing information on the new revision committed
0827: * (revision number, etc.)
0828: */
0829: return ourClientManager.getCommitClient().doImport(localPath,
0830: dstURL, commitMessage, isRecursive);
0831:
0832: }
0833:
0834: /*
0835: * Committs changes in a working copy to a repository. Like
0836: * 'svn commit PATH -m "some comment"' command. It's done by invoking
0837: *
0838: * SVNCommitClient.doCommit(File[] paths, boolean keepLocks, String commitMessage,
0839: * boolean force, boolean recursive)
0840: *
0841: * which takes the following parameters:
0842: *
0843: * paths - working copy paths which changes are to be committed;
0844: *
0845: * keepLocks - if true then doCommit(..) won't unlock locked paths; otherwise they will
0846: * be unlocked after a successful commit;
0847: *
0848: * commitMessage - a commit log message;
0849: *
0850: * force - if true then a non-recursive commit will be forced anyway;
0851: *
0852: * recursive - if true and a path corresponds to a directory then doCommit(..) recursively
0853: * commits changes for the entire directory, otherwise - only for child entries of the
0854: * directory;
0855: */
0856: private static SVNCommitInfo commit(File wcPath, boolean keepLocks,
0857: String commitMessage) throws SVNException {
0858: /*
0859: * Returns SVNCommitInfo containing information on the new revision committed
0860: * (revision number, etc.)
0861: */
0862: return ourClientManager.getCommitClient().doCommit(
0863: new File[] { wcPath }, keepLocks, commitMessage, false,
0864: true);
0865: }
0866:
0867: /*
0868: * Checks out a working copy from a repository. Like 'svn checkout URL[@REV] PATH (-r..)'
0869: * command; It's done by invoking
0870: *
0871: * SVNUpdateClient.doCheckout(SVNURL url, File dstPath, SVNRevision pegRevision,
0872: * SVNRevision revision, boolean recursive)
0873: *
0874: * which takes the following parameters:
0875: *
0876: * url - a repository location from where a working copy is to be checked out;
0877: *
0878: * dstPath - a local path where the working copy will be fetched into;
0879: *
0880: * pegRevision - an SVNRevision representing a revision to concretize
0881: * url (what exactly URL a user means and is sure of being the URL he needs); in other
0882: * words that is the revision in which the URL is first looked up;
0883: *
0884: * revision - a revision at which a working copy being checked out is to be;
0885: *
0886: * recursive - if true and url corresponds to a directory then doCheckout(..) recursively
0887: * fetches out the entire directory, otherwise - only child entries of the directory;
0888: */
0889: private static long checkout(SVNURL url, SVNRevision revision,
0890: File destPath, boolean isRecursive) throws SVNException {
0891:
0892: SVNUpdateClient updateClient = ourClientManager
0893: .getUpdateClient();
0894: /*
0895: * sets externals not to be ignored during the checkout
0896: */
0897: updateClient.setIgnoreExternals(false);
0898: /*
0899: * returns the number of the revision at which the working copy is
0900: */
0901: return updateClient.doCheckout(url, destPath, revision,
0902: revision, isRecursive);
0903: }
0904:
0905: /*
0906: * Updates a working copy (brings changes from the repository into the working copy).
0907: * Like 'svn update PATH' command; It's done by invoking
0908: *
0909: * SVNUpdateClient.doUpdate(File file, SVNRevision revision, boolean recursive)
0910: *
0911: * which takes the following parameters:
0912: *
0913: * file - a working copy entry that is to be updated;
0914: *
0915: * revision - a revision to which a working copy is to be updated;
0916: *
0917: * recursive - if true and an entry is a directory then doUpdate(..) recursively
0918: * updates the entire directory, otherwise - only child entries of the directory;
0919: */
0920: private static long update(File wcPath,
0921: SVNRevision updateToRevision, boolean isRecursive)
0922: throws SVNException {
0923:
0924: SVNUpdateClient updateClient = ourClientManager
0925: .getUpdateClient();
0926: /*
0927: * sets externals not to be ignored during the update
0928: */
0929: updateClient.setIgnoreExternals(false);
0930: /*
0931: * returns the number of the revision wcPath was updated to
0932: */
0933: return updateClient.doUpdate(wcPath, updateToRevision,
0934: isRecursive);
0935: }
0936:
0937: /*
0938: * Updates a working copy to a different URL. Like 'svn switch URL' command.
0939: * It's done by invoking
0940: *
0941: * SVNUpdateClient.doSwitch(File file, SVNURL url, SVNRevision revision, boolean recursive)
0942: *
0943: * which takes the following parameters:
0944: *
0945: * file - a working copy entry that is to be switched to a new url;
0946: *
0947: * url - a target URL a working copy is to be updated against;
0948: *
0949: * revision - a revision to which a working copy is to be updated;
0950: *
0951: * recursive - if true and an entry (file) is a directory then doSwitch(..) recursively
0952: * switches the entire directory, otherwise - only child entries of the directory;
0953: */
0954: private static long switchToURL(File wcPath, SVNURL url,
0955: SVNRevision updateToRevision, boolean isRecursive)
0956: throws SVNException {
0957: SVNUpdateClient updateClient = ourClientManager
0958: .getUpdateClient();
0959: /*
0960: * sets externals not to be ignored during the switch
0961: */
0962: updateClient.setIgnoreExternals(false);
0963: /*
0964: * returns the number of the revision wcPath was updated to
0965: */
0966: return updateClient.doSwitch(wcPath, url, updateToRevision,
0967: isRecursive);
0968: }
0969:
0970: /*
0971: * Collects status information on local path(s). Like 'svn status (-u) (-N)'
0972: * command. It's done by invoking
0973: *
0974: * SVNStatusClient.doStatus(File path, boolean recursive,
0975: * boolean remote, boolean reportAll, boolean includeIgnored,
0976: * boolean collectParentExternals, ISVNStatusHandler handler)
0977: *
0978: * which takes the following parameters:
0979: *
0980: * path - an entry which status info to be gathered;
0981: *
0982: * recursive - if true and an entry is a directory then doStatus(..) collects status
0983: * info not only for that directory but for each item inside stepping down recursively;
0984: *
0985: * remote - if true then doStatus(..) will cover the repository (not only the working copy)
0986: * as well to find out what entries are out of date;
0987: *
0988: * reportAll - if true then doStatus(..) will also include unmodified entries;
0989: *
0990: * includeIgnored - if true then doStatus(..) will also include entries being ignored;
0991: *
0992: * collectParentExternals - if true then externals definitions won't be ignored;
0993: *
0994: * handler - an implementation of ISVNStatusHandler to process status info per each entry
0995: * doStatus(..) traverses; such info is collected in an SVNStatus object and
0996: * is passed to a handler's handleStatus(SVNStatus status) method where an implementor
0997: * decides what to do with it.
0998: */
0999: private static void showStatus(File wcPath, boolean isRecursive,
1000: boolean isRemote, boolean isReportAll,
1001: boolean isIncludeIgnored, boolean isCollectParentExternals)
1002: throws SVNException {
1003: /*
1004: * StatusHandler displays status information for each entry in the console (in the
1005: * manner of the native Subversion command line client)
1006: */
1007: ourClientManager.getStatusClient().doStatus(wcPath,
1008: isRecursive, isRemote, isReportAll, isIncludeIgnored,
1009: isCollectParentExternals, new StatusHandler(isRemote));
1010: }
1011:
1012: /*
1013: * Collects information on local path(s). Like 'svn info (-R)' command.
1014: * It's done by invoking
1015: *
1016: * SVNWCClient.doInfo(File path, SVNRevision revision,
1017: * boolean recursive, ISVNInfoHandler handler)
1018: *
1019: * which takes the following parameters:
1020: *
1021: * path - a local entry for which info will be collected;
1022: *
1023: * revision - a revision of an entry which info is interested in; if it's not
1024: * WORKING then info is got from a repository;
1025: *
1026: * recursive - if true and an entry is a directory then doInfo(..) collects info
1027: * not only for that directory but for each item inside stepping down recursively;
1028: *
1029: * handler - an implementation of ISVNInfoHandler to process info per each entry
1030: * doInfo(..) traverses; such info is collected in an SVNInfo object and
1031: * is passed to a handler's handleInfo(SVNInfo info) method where an implementor
1032: * decides what to do with it.
1033: */
1034: private static void showInfo(File wcPath, SVNRevision revision,
1035: boolean isRecursive) throws SVNException {
1036: /*
1037: * InfoHandler displays information for each entry in the console (in the manner of
1038: * the native Subversion command line client)
1039: */
1040: ourClientManager.getWCClient().doInfo(wcPath, revision,
1041: isRecursive, new InfoHandler());
1042: }
1043:
1044: /*
1045: * Puts directories and files under version control scheduling them for addition
1046: * to a repository. They will be added in a next commit. Like 'svn add PATH'
1047: * command. It's done by invoking
1048: *
1049: * SVNWCClient.doAdd(File path, boolean force,
1050: * boolean mkdir, boolean climbUnversionedParents, boolean recursive)
1051: *
1052: * which takes the following parameters:
1053: *
1054: * path - an entry to be scheduled for addition;
1055: *
1056: * force - set to true to force an addition of an entry anyway;
1057: *
1058: * mkdir - if true doAdd(..) creates an empty directory at path and schedules
1059: * it for addition, like 'svn mkdir PATH' command;
1060: *
1061: * climbUnversionedParents - if true and the parent of the entry to be scheduled
1062: * for addition is not under version control, then doAdd(..) automatically schedules
1063: * the parent for addition, too;
1064: *
1065: * recursive - if true and an entry is a directory then doAdd(..) recursively
1066: * schedules all its inner dir entries for addition as well.
1067: */
1068: private static void addEntry(File wcPath) throws SVNException {
1069: ourClientManager.getWCClient().doAdd(wcPath, false, false,
1070: false, true);
1071: }
1072:
1073: /*
1074: * Locks working copy paths, so that no other user can commit changes to them.
1075: * Like 'svn lock PATH' command. It's done by invoking
1076: *
1077: * SVNWCClient.doLock(File[] paths, boolean stealLock, String lockMessage)
1078: *
1079: * which takes the following parameters:
1080: *
1081: * paths - an array of local entries to be locked;
1082: *
1083: * stealLock - set to true to steal the lock from another user or working copy;
1084: *
1085: * lockMessage - an optional lock comment string.
1086: */
1087: private static void lock(File wcPath, boolean isStealLock,
1088: String lockComment) throws SVNException {
1089: ourClientManager.getWCClient().doLock(new File[] { wcPath },
1090: isStealLock, lockComment);
1091: }
1092:
1093: /*
1094: * Schedules directories and files for deletion from version control upon the next
1095: * commit (locally). Like 'svn delete PATH' command. It's done by invoking
1096: *
1097: * SVNWCClient.doDelete(File path, boolean force, boolean dryRun)
1098: *
1099: * which takes the following parameters:
1100: *
1101: * path - an entry to be scheduled for deletion;
1102: *
1103: * force - a boolean flag which is set to true to force a deletion even if an entry
1104: * has local modifications;
1105: *
1106: * dryRun - set to true not to delete an entry but to check if it can be deleted;
1107: * if false - then it's a deletion itself.
1108: */
1109: private static void delete(File wcPath, boolean force)
1110: throws SVNException {
1111: ourClientManager.getWCClient().doDelete(wcPath, force, false);
1112: }
1113:
1114: /*
1115: * Duplicates srcURL to dstURL (URL->URL)in a repository remembering history.
1116: * Like 'svn copy srcURL dstURL -m "some comment"' command. It's done by
1117: * invoking
1118: *
1119: * doCopy(SVNURL srcURL, SVNRevision srcRevision, SVNURL dstURL,
1120: * boolean isMove, String commitMessage)
1121: *
1122: * which takes the following parameters:
1123: *
1124: * srcURL - a source URL that is to be copied;
1125: *
1126: * srcRevision - a definite revision of srcURL
1127: *
1128: * dstURL - a URL where srcURL will be copied; if srcURL & dstURL are both
1129: * directories then there are two cases:
1130: * a) dstURL already exists - then doCopy(..) will duplicate the entire source
1131: * directory and put it inside dstURL (for example,
1132: * consider srcURL = svn://localhost/rep/MyRepos,
1133: * dstURL = svn://localhost/rep/MyReposCopy, in this case if doCopy(..) succeeds
1134: * MyRepos will be in MyReposCopy - svn://localhost/rep/MyReposCopy/MyRepos);
1135: * b) dstURL doesn't exist yet - then doCopy(..) will create a directory and
1136: * recursively copy entries from srcURL into dstURL (for example, consider the same
1137: * srcURL = svn://localhost/rep/MyRepos, dstURL = svn://localhost/rep/MyReposCopy,
1138: * in this case if doCopy(..) succeeds MyRepos entries will be in MyReposCopy, like:
1139: * svn://localhost/rep/MyRepos/Dir1 -> svn://localhost/rep/MyReposCopy/Dir1...);
1140: *
1141: * isMove - if false then srcURL is only copied to dstURL what
1142: * corresponds to 'svn copy srcURL dstURL -m "some comment"'; but if it's true then
1143: * srcURL will be copied and deleted - 'svn move srcURL dstURL -m "some comment"';
1144: *
1145: * commitMessage - a commit log message since URL->URL copying is immediately
1146: * committed to a repository.
1147: */
1148: private static SVNCommitInfo copy(SVNURL srcURL, SVNURL dstURL,
1149: boolean isMove, String commitMessage) throws SVNException {
1150: /*
1151: * SVNRevision.HEAD means the latest revision.
1152: * Returns SVNCommitInfo containing information on the new revision committed
1153: * (revision number, etc.)
1154: */
1155:
1156: return ourClientManager.getCopyClient().doCopy(srcURL,
1157: SVNRevision.HEAD, dstURL, isMove, commitMessage);
1158: }
1159:
1160: /*
1161: * Displays error information and exits.
1162: */
1163: private static void error(String message, Exception e) {
1164: System.err.println(message
1165: + (e != null ? ": " + e.getMessage() : ""));
1166: System.exit(1);
1167: }
1168:
1169: /*
1170: * This method does not relate to SVNKit API. Just a method which creates
1171: * local directories and files :)
1172: */
1173: private static final void createLocalDir(File aNewDir,
1174: File[] localFiles, String[] fileContents) {
1175: if (!aNewDir.mkdirs()) {
1176: error("failed to create a new directory '"
1177: + aNewDir.getAbsolutePath() + "'.", null);
1178: }
1179: for (int i = 0; i < localFiles.length; i++) {
1180: File aNewFile = localFiles[i];
1181: try {
1182: if (!aNewFile.createNewFile()) {
1183: error("failed to create a new file '"
1184: + aNewFile.getAbsolutePath() + "'.", null);
1185: }
1186: } catch (IOException ioe) {
1187: aNewFile.delete();
1188: error("error while creating a new file '"
1189: + aNewFile.getAbsolutePath() + "'", ioe);
1190: }
1191:
1192: String contents = null;
1193: if (i > fileContents.length - 1) {
1194: continue;
1195: }
1196: contents = fileContents[i];
1197:
1198: /*
1199: * writing a text into the file
1200: */
1201: FileOutputStream fos = null;
1202: try {
1203: fos = new FileOutputStream(aNewFile);
1204: fos.write(contents.getBytes());
1205: } catch (FileNotFoundException fnfe) {
1206: error("the file '" + aNewFile.getAbsolutePath()
1207: + "' is not found", fnfe);
1208: } catch (IOException ioe) {
1209: error("error while writing into the file '"
1210: + aNewFile.getAbsolutePath() + "'", ioe);
1211: } finally {
1212: if (fos != null) {
1213: try {
1214: fos.close();
1215: } catch (IOException ioe) {
1216: //
1217: }
1218: }
1219: }
1220: }
1221: }
1222: }
|