Managing A Working Copy
Finally we came to discussing the SVNKit high-level API - the one for manipulating Working Copies. All Working Copy operations are logically organized in different SVN*Client classes. At the same time there's SVNClientManager class which composes all SVN*Client classes and, thus, simplifies work on creating and keeping various client classes:
So, you may instantiate different SVN*Client classes separately or keep a single SVNClientManager for the same purposes. In fact, SVNClientManager instantiates each SVN*Client object at the first time you request it, but not earlier.
Methods of each SVN*Client class are similar to commands of the Subversion command line client. We will show several simple operations you can perform upon Working Copies using SVNKit. We write a class WorkingCopy which utilizes an SVNClientManager object:
Then go several demonstration functions that will be used in the main program. The following one creates a new directory immediately in a repository:
This one imports a local directory to a repository:
This one recursively commits Working Copy modifications to a repository:
This one checks out a Working Copy given a repository url:
1 private static long checkout( SVNURL url , SVNRevision revision , File destPath , boolean isRecursive ) throws SVNException {
2
3 SVNUpdateClient updateClient = ourClientManager.getUpdateClient( );
4 /*
5 * sets externals not to be ignored during the checkout
6 */
7 updateClient.setIgnoreExternals( false );
8 /*
9 * returns the number of the revision at which the working copy is
10 */
11 return updateClient.doCheckout( url , destPath , revision , revision , isRecursive );
12 }
This one updates a Working Copy to a particular revision:
1 private static long update( File wcPath , SVNRevision updateToRevision , boolean isRecursive ) throws SVNException {
2
3 SVNUpdateClient updateClient = ourClientManager.getUpdateClient( );
4 /*
5 * sets externals not to be ignored during the update
6 */
7 updateClient.setIgnoreExternals( false );
8 /*
9 * returns the number of the revision wcPath was updated to
10 */
11 return updateClient.doUpdate( wcPath , updateToRevision , isRecursive );
12 }
This one switches a Working Copy to another url:
1 private static long switchToURL( File wcPath , SVNURL url , SVNRevision updateToRevision , boolean isRecursive ) throws SVNException {
2 SVNUpdateClient updateClient = ourClientManager.getUpdateClient( );
3 /*
4 * sets externals not to be ignored during the switch
5 */
6 updateClient.setIgnoreExternals( false );
7 /*
8 * returns the number of the revision wcPath was updated to
9 */
10 return updateClient.doSwitch( wcPath , url , updateToRevision , isRecursive );
11 }
This one recursively adds an existing local item under version control (schedules for addition):
This one locks a versioned item:
This one deletes a versioned item from version control (schedules for deletion):
This function copies or moves one location to another one within the same repository:
We also need a function which will report us status (including remote status) of our Working Copy. The high-level API introduces several types of handlers to procees information on the fly. For our purposes we need to implement two handler interfaces so that we could perform status operations on our Working Copy: ISVNStatusHandler and ISVNEventHandler. This handler will receive status information about Working Copy items as a status client traverses the Working Copy tree:
1 ...
2 public class StatusHandler implements ISVNStatusHandler, ISVNEventHandler {
3 private boolean myIsRemote;
4
5 public StatusHandler( boolean isRemote ) {
6 myIsRemote = isRemote;
7 }
8
9 public void handleStatus( SVNStatus status ) {
10 /*
11 * Gets the status of file/directory/symbolic link text contents.
12 * It is SVNStatusType who contains information on the state of an
13 * item.
14 */
15 SVNStatusType contentsStatus = status.getContentsStatus( );
16
17 String pathChangeType = " ";
18
19 boolean isAddedWithHistory = status.isCopied( );
20 if ( contentsStatus == SVNStatusType.STATUS_MODIFIED ) {
21 /*
22 * The contents of the file have been Modified.
23 */
24 pathChangeType = "M";
25 } else if ( contentsStatus == SVNStatusType.STATUS_CONFLICTED ) {
26 /*
27 * The item is in a state of Conflict.
28 */
29 pathChangeType = "C";
30 } else if ( contentsStatus == SVNStatusType.STATUS_DELETED ) {
31 /*
32 * The item has been scheduled for Deletion from the repository.
33 */
34 pathChangeType = "D";
35 } else if ( contentsStatus == SVNStatusType.STATUS_ADDED ) {
36 /*
37 * The item has been scheduled for Addition to the repository.
38 */
39 pathChangeType = "A";
40 } else if ( contentsStatus == SVNStatusType.STATUS_UNVERSIONED ) {
41 /*
42 * The item is not under version control.
43 */
44 pathChangeType = "?";
45 } else if ( contentsStatus == SVNStatusType.STATUS_EXTERNAL ) {
46 /*
47 * The item is unversioned, but is used by an eXternals definition.
48 */
49 pathChangeType = "X";
50 } else if ( contentsStatus == SVNStatusType.STATUS_IGNORED ) {
51 /*
52 * The item is Ignored.
53 */
54 pathChangeType = "I";
55 } else if ( contentsStatus == SVNStatusType.STATUS_MISSING || contentsStatus == SVNStatusType.STATUS_INCOMPLETE ) {
56 /*
57 * The file, directory or symbolic link item is under version
58 * control but is missing or somehow incomplete.
59 */
60 pathChangeType = "!";
61 } else if ( contentsStatus == SVNStatusType.STATUS_OBSTRUCTED ) {
62 /*
63 * The item is in the repository as one kind of object,
64 * but what's actually in the user's working
65 * copy is some other kind.
66 */
67 pathChangeType = "~";
68 } else if ( contentsStatus == SVNStatusType.STATUS_REPLACED ) {
69 /*
70 * The item was Replaced in the user's working copy; that is,
71 * the item was deleted, and a new item with the same name
72 * was added (within a single revision).
73 */
74 pathChangeType = "R";
75 } else if ( contentsStatus == SVNStatusType.STATUS_NONE || contentsStatus == SVNStatusType.STATUS_NORMAL ) {
76 /*
77 * The item was not modified (normal).
78 */
79 pathChangeType = " ";
80 }
81
82 /*
83 * If SVNStatusClient.doStatus(..) is invoked with remote = true the
84 * following code finds out whether the current item has been changed
85 * in the repository
86 */
87 String remoteChangeType = " ";
88
89 if ( status.getRemotePropertiesStatus( ) != SVNStatusType.STATUS_NONE || status.getRemoteContentsStatus( ) != SVNStatusType.STATUS_NONE ) {
90 /*
91 * the local item is out of date
92 */
93 remoteChangeType = "*";
94 }
95
96 /*
97 * Now getting the status of properties of an item. SVNStatusType also
98 * contains information on the properties state.
99 */
100 SVNStatusType propertiesStatus = status.getPropertiesStatus( );
101
102 /*
103 * Default - properties are normal (unmodified).
104 */
105 String propertiesChangeType = " ";
106 if ( propertiesStatus == SVNStatusType.STATUS_MODIFIED ) {
107 /*
108 * Properties were modified.
109 */
110 propertiesChangeType = "M";
111 } else if ( propertiesStatus == SVNStatusType.STATUS_CONFLICTED ) {
112 /*
113 * Properties are in conflict with the repository.
114 */
115 propertiesChangeType = "C";
116 }
117
118 /*
119 * Whether the item was locked in the .svn working area (for example,
120 * during a commit or maybe the previous operation was interrupted, in
121 * this case the lock needs to be cleaned up).
122 */
123 boolean isLocked = status.isLocked( );
124 /*
125 * Whether the item is switched to a different URL (branch).
126 */
127 boolean isSwitched = status.isSwitched( );
128 /*
129 * If the item is a file it may be locked.
130 */
131 SVNLock localLock = status.getLocalLock( );
132 /*
133 * If doStatus() was run with remote = true and the item is a file,
134 * checks whether a remote lock presents.
135 */
136 SVNLock remoteLock = status.getRemoteLock( );
137 String lockLabel = " ";
138
139 if ( localLock != null ) {
140 /*
141 * at first suppose the file is locKed
142 */
143 lockLabel = "K";
144 if ( remoteLock != null ) {
145 /*
146 * if the lock-token of the local lock differs from the lock-
147 * token of the remote lock - the lock was sTolen!
148 */
149 if ( !remoteLock.getID( ).equals( localLock.getID( ) ) ) {
150 lockLabel = "T";
151 }
152 } else {
153 if ( myIsRemote ) {
154 /*
155 * the local lock presents but there's no lock in the
156 * repository - the lock was Broken. This is true only if
157 * doStatus() was invoked with remote=true.
158 */
159 lockLabel = "B";
160 }
161 }
162 } else if ( remoteLock != null ) {
163 /*
164 * the file is not locally locked but locked in the repository -
165 * the lock token is in some Other working copy.
166 */
167 lockLabel = "O";
168 }
169
170 /*
171 * Obtains the working revision number of the item.
172 */
173 long workingRevision = status.getRevision( ).getNumber( );
174 /*
175 * Obtains the number of the revision when the item was last changed.
176 */
177 long lastChangedRevision = status.getCommittedRevision( ).getNumber( );
178 String offset = " ";
179 String[] offsets = new String[3];
180 offsets[0] = offset.substring( 0 , 6 - String.valueOf( workingRevision ).length( ) );
181 offsets[1] = offset.substring( 0 , 6 - String.valueOf( lastChangedRevision ).length( ) );
182 offsets[2] = offset.substring( 0 , offset.length( ) - ( status.getAuthor( ) != null ? status.getAuthor( ).length( ) : 1 ) );
183 /*
184 * status is shown in the manner of the native Subversion command line
185 * client's command "svn status"
186 */
187 System.out.println( pathChangeType
188 + propertiesChangeType
189 + ( isLocked ? "L" : " " )
190 + ( isAddedWithHistory ? "+" : " " )
191 + ( isSwitched ? "S" : " " )
192 + lockLabel
193 + " "
194 + remoteChangeType
195 + " "
196 + workingRevision
197 + offsets[0]
198 + ( lastChangedRevision >= 0 ? String.valueOf( lastChangedRevision ) : "?" ) + offsets[1]
199 + ( status.getAuthor( ) != null ? status.getAuthor( ) : "?" )
200 + offsets[2] + status.getFile( ).getPath( ) );
201 }
202
203 public void handleEvent( SVNEvent event , double progress ) {
204 SVNEventAction action = event.getAction( );
205 /*
206 * Print out the revision against which the status was performed. This
207 * event is dispatched when the SVNStatusClient.doStatus() was invoked
208 * with the flag remote set to true - that is for a local status it
209 * won't be dispatched.
210 */
211 if ( action == SVNEventAction.STATUS_COMPLETED ) {
212 System.out.println( "Status against revision: " + event.getRevision( ) );
213 }
214
215 }
216
217 public void checkCancelled( ) throws SVNCancelException {
218 }
219 }
We use this handler in a status function:
1 private static void showStatus( File wcPath , boolean isRecursive , boolean isRemote , boolean isReportAll ,
2 boolean isIncludeIgnored , boolean isCollectParentExternals ) throws SVNException {
3
4 ourClientManager.getStatusClient( ).doStatus( wcPath , isRecursive , isRemote , isReportAll ,
5 isIncludeIgnored , isCollectParentExternals ,
6 new StatusHandler( isRemote ) );
7 }
For getting info on Working Copy items (like svn info command) we implement an info handler:
1 ...
2 public class InfoHandler implements ISVNInfoHandler {
3
4 public void handleInfo( SVNInfo info ) {
5 System.out.println( "-----------------INFO-----------------" );
6 System.out.println( "Local Path: " + info.getFile( ).getPath( ) );
7 System.out.println( "URL: " + info.getURL( ) );
8
9 if ( info.isRemote( ) && info.getRepositoryRootURL( ) != null ) {
10 System.out.println( "Repository Root URL: " + info.getRepositoryRootURL( ) );
11 }
12
13 if ( info.getRepositoryUUID( ) != null ) {
14 System.out.println( "Repository UUID: " + info.getRepositoryUUID( ) );
15 }
16
17 System.out.println( "Revision: " + info.getRevision( ).getNumber( ) );
18 System.out.println( "Node Kind: " + info.getKind( ).toString( ) );
19
20 if ( !info.isRemote( ) ){
21 System.out.println( "Schedule: " + ( info.getSchedule( ) != null ? info.getSchedule( ) : "normal" ) );
22 }
23
24 System.out.println( "Last Changed Author: " + info.getAuthor( ) );
25 System.out.println( "Last Changed Revision: " + info.getCommittedRevision( ).getNumber( ) );
26 System.out.println( "Last Changed Date: " + info.getCommittedDate( ) );
27
28 if ( info.getPropTime( ) != null ) {
29 System.out.println( "Properties Last Updated: " + info.getPropTime( ) );
30 }
31
32 if ( info.getKind( ) == SVNNodeKind.FILE && info.getChecksum( ) != null ) {
33 if ( info.getTextTime( ) != null ) {
34 System.out.println( "Text Last Updated: " + info.getTextTime( ) );
35 }
36 System.out.println( "Checksum: " + info.getChecksum( ) );
37 }
38
39 if ( info.getLock( ) != null ) {
40 if ( info.getLock( ).getID( ) != null ) {
41 System.out.println( "Lock Token: " + info.getLock( ).getID( ) );
42 }
43
44 System.out.println( "Lock Owner: " + info.getLock( ).getOwner( ) );
45 System.out.println( "Lock Created: " + info.getLock( ).getCreationDate( ) );
46
47 if ( info.getLock( ).getExpirationDate( ) != null ) {
48 System.out.println( "Lock Expires: " + info.getLock( ).getExpirationDate( ) );
49 }
50
51 if ( info.getLock( ).getComment( ) != null ) {
52 System.out.println( "Lock Comment: " + info.getLock( ).getComment( ) );
53 }
54 }
55 }
56 }
Which we then use in our info function:
We also use several hooks (event handlers) to display progress information in a console while an operation is running. We implement handlers for the following operations: update, commit, several local operations (add, delete, lock, etc.):
1 ...
2 public class UpdateEventHandler implements ISVNEventHandler {
3
4 public void handleEvent( SVNEvent event , double progress ) {
5 /*
6 * Gets the current action. An action is represented by SVNEventAction.
7 * In case of an update an action can be determined via comparing
8 * SVNEvent.getAction() and SVNEventAction.UPDATE_-like constants.
9 */
10 SVNEventAction action = event.getAction( );
11 String pathChangeType = " ";
12 if ( action == SVNEventAction.UPDATE_ADD ) {
13 /*
14 * the item was added
15 */
16 pathChangeType = "A";
17 } else if ( action == SVNEventAction.UPDATE_DELETE ) {
18 /*
19 * the item was deleted
20 */
21 pathChangeType = "D";
22 } else if ( action == SVNEventAction.UPDATE_UPDATE ) {
23 /*
24 * Find out in details what state the item is (after having been
25 * updated).
26 *
27 * Gets the status of file/directory item contents. It is
28 * SVNStatusType who contains information on the state of an item.
29 */
30 SVNStatusType contentsStatus = event.getContentsStatus( );
31 if ( contentsStatus == SVNStatusType.CHANGED ) {
32 /*
33 * the item was modified in the repository (got the changes
34 * from the repository
35 */
36 pathChangeType = "U";
37 } else if ( contentsStatus == SVNStatusType.CONFLICTED ) {
38 /*
39 * The file item is in a state of Conflict. That is, changes
40 * received from the repository during an update, overlap with
41 * local changes the user has in his working copy.
42 */
43 pathChangeType = "C";
44 } else if ( contentsStatus == SVNStatusType.MERGED ) {
45 /*
46 * The file item was merGed (those changes that came from the
47 * repository did not overlap local changes and were merged
48 * into the file).
49 */
50 pathChangeType = "G";
51 }
52 } else if ( action == SVNEventAction.UPDATE_EXTERNAL ) {
53 /*for externals definitions*/
54 System.out.println( "Fetching external item into '" + event.getFile( ).getAbsolutePath( ) + "'" );
55 System.out.println( "External at revision " + event.getRevision( ) );
56 return;
57 } else if ( action == SVNEventAction.UPDATE_COMPLETED ) {
58 /*
59 * Working copy update is completed. Prints out the revision.
60 */
61 System.out.println( "At revision " + event.getRevision( ) );
62 return;
63 } else if ( action == SVNEventAction.ADD ) {
64 System.out.println( "A " + event.getPath( ) );
65 return;
66 } else if ( action == SVNEventAction.DELETE ) {
67 System.out.println( "D " + event.getPath( ) );
68 return;
69 } else if ( action == SVNEventAction.LOCKED ) {
70 System.out.println( "L " + event.getPath( ) );
71 return;
72 } else if ( action == SVNEventAction.LOCK_FAILED ) {
73 System.out.println( "failed to lock " + event.getPath( ) );
74 return;
75 }
76
77 /*
78 * Status of properties of an item. SVNStatusType also
79 * contains information on the properties state.
80 */
81 SVNStatusType propertiesStatus = event.getPropertiesStatus( );
82 String propertiesChangeType = " ";
83 if ( propertiesStatus == SVNStatusType.CHANGED ) {
84 /*
85 * Properties were updated.
86 */
87 propertiesChangeType = "U";
88 } else if ( propertiesStatus == SVNStatusType.CONFLICTED ) {
89 /*
90 * Properties are in conflict with the repository.
91 */
92 propertiesChangeType = "C";
93 } else if ( propertiesStatus == SVNStatusType.MERGED ) {
94 /*
95 * Properties that came from the repository were merged with the
96 * local ones.
97 */
98 propertiesChangeType = "G";
99 }
100
101 /*
102 * Gets the status of the lock.
103 */
104 String lockLabel = " ";
105 SVNStatusType lockType = event.getLockStatus();
106
107 if ( lockType == SVNStatusType.LOCK_UNLOCKED ) {
108 /*
109 * The lock is broken by someone.
110 */
111 lockLabel = "B";
112 }
113
114 System.out.println( pathChangeType + propertiesChangeType + lockLabel + " " + event.getPath( ) );
115 }
116
117 public void checkCancelled( ) throws SVNCancelException {
118 }
119 }
120
121 ...
122
123 public class CommitEventHandler implements ISVNEventHandler {
124
125 public void handleEvent( SVNEvent event , double progress ) {
126 SVNEventAction action = event.getAction( );
127 if ( action == SVNEventAction.COMMIT_MODIFIED ) {
128 System.out.println( "Sending " + event.getPath( ) );
129 } else if ( action == SVNEventAction.COMMIT_DELETED ) {
130 System.out.println( "Deleting " + event.getPath( ) );
131 } else if ( action == SVNEventAction.COMMIT_REPLACED ) {
132 System.out.println( "Replacing " + event.getPath( ) );
133 } else if ( action == SVNEventAction.COMMIT_DELTA_SENT ) {
134 System.out.println( "Transmitting file data...." );
135 } else if ( action == SVNEventAction.COMMIT_ADDED ) {
136 /*
137 * Gets the MIME-type of the item.
138 */
139 String mimeType = event.getMimeType( );
140 if ( SVNProperty.isBinaryMimeType( mimeType ) ) {
141 /*
142 * If the item is a binary file
143 */
144 System.out.println( "Adding (bin) " + event.getPath( ) );
145 } else {
146 System.out.println( "Adding " + event.getPath( ) );
147 }
148 }
149
150 }
151
152 public void checkCancelled( ) throws SVNCancelException {
153 }
154 }
155
156 ...
157
158 public class WCEventHandler implements ISVNEventHandler {
159
160 public void handleEvent( SVNEvent event , double progress ) {
161 SVNEventAction action = event.getAction( );
162
163 if ( action == SVNEventAction.ADD ) {
164 /*
165 * The item is scheduled for addition.
166 */
167 System.out.println( "A " + event.getPath( ) );
168 return;
169 }else if ( action == SVNEventAction.COPY ){
170 /*
171 * The item is scheduled for addition with history (copied, in
172 * other words).
173 */
174 System.out.println( "A + " + event.getPath( ) );
175 return;
176 }else if ( action == SVNEventAction.DELETE ) {
177 /*
178 * The item is scheduled for deletion.
179 */
180 System.out.println( "D " + event.getPath( ) );
181 return;
182 } else if ( action == SVNEventAction.LOCKED ){
183 /*
184 * The item is locked.
185 */
186 System.out.println( "L " + event.getPath( ) );
187 return;
188 } else if ( action == SVNEventAction.LOCK_FAILED ) {
189 /*
190 * Locking operation failed.
191 */
192 System.out.println( "failed to lock " + event.getPath( ) );
193 return;
194 }
195 }
196
197 public void checkCancelled( ) throws SVNCancelException {
198 }
199 }
There are also two auxiliary functions: one for handling errors
and one for creating some local dummy file/directory trees:
1 private static final void createLocalDir( File aNewDir , File[] localFiles , String[] fileContents ) {
2 if ( !aNewDir.mkdirs( ) ) {
3 error( "failed to create a new directory '" + aNewDir.getAbsolutePath( ) + "'.", null );
4 }
5
6 for( int i = 0; i < localFiles.length; i++ ) {
7 File aNewFile = localFiles[i];
8 try {
9 if ( !aNewFile.createNewFile( ) ) {
10 error( "failed to create a new file '" + aNewFile.getAbsolutePath( ) + "'." , null );
11 }
12 } catch ( IOException ioe ) {
13 aNewFile.delete( );
14 error( "error while creating a new file '" + aNewFile.getAbsolutePath( ) + "'" , ioe );
15 }
16
17 String contents = null;
18 if ( i > fileContents.length - 1 ) {
19 continue;
20 }
21 contents = fileContents[i];
22
23 /*
24 * writing a text into the file
25 */
26 FileOutputStream fos = null;
27 try {
28 fos = new FileOutputStream( aNewFile );
29 fos.write( contents.getBytes( ) );
30 } catch ( FileNotFoundException fnfe ) {
31 error( "the file '" + aNewFile.getAbsolutePath( ) + "' is not found" , fnfe );
32 } catch ( IOException ioe ) {
33 error( "error while writing into the file '" + aNewFile.getAbsolutePath( ) + "'" , ioe );
34 } finally {
35 if ( fos != null ) {
36 try {
37 fos.close( );
38 } catch ( IOException ioe ) {
39 //
40 }
41 }
42 }
43 }
44 }
And now, step by step, we demonstrate using all these functions in a main program. Firts of all, we need some initializations to be performed:
1 ...
2 public class WorkingCopy {
3
4 private static SVNClientManager ourClientManager;
5 private static ISVNEventHandler myCommitEventHandler;
6 private static ISVNEventHandler myUpdateEventHandler;
7 private static ISVNEventHandler myWCEventHandler;
8
9 public static void main( String[] args ) throws SVNException {
10 FSRepositoryFactory.setup( );
11
12 SVNURL repositoryURL = null;
13 try {
14 repositoryURL = SVNURL.parseURIEncoded( "file://localhost/testRep" );
15 } catch ( SVNException e ) {
16 //
17 }
18
19 String myWorkingCopyPath = "/MyWorkingCopy";
20
21 String importDir = "/importDir";
22 String importFile = importDir + "/importFile.txt";
23 String importFileText = "This unversioned file is imported into a repository";
24
25 String newDir = "/newDir";
26 String newFile = newDir + "/newFile.txt";
27 String fileText = "This is a new file added to the working copy";
28
29 /*
30 * That's where a new directory will be created
31 */
32 SVNURL url = repositoryURL.appendPath( "MyRepos" , false );
33 /*
34 * That's where '/MyRepos' will be copied to (branched)
35 */
36 SVNURL copyURL = repositoryURL.appendPath( "MyReposCopy" , false );
37
38 /*
39 * That's where a local directory will be imported into.
40 * Note that it's not necessary that the '/importDir' directory must already
41 * exist - it will created if necessary.
42 */
43 SVNURL importToURL = url.appendPath( importDir , false );
44
45 /*
46 * Creating custom handlers that will process events
47 */
48 myCommitEventHandler = new CommitEventHandler( );
49
50 myUpdateEventHandler = new UpdateEventHandler( );
51
52 myWCEventHandler = new WCEventHandler( );
53
54 /*
55 * Creates a default run-time configuration options driver. Default options
56 * created in this way use the Subversion run-time configuration area (for
57 * instance, on a Windows platform it can be found in the '%APPDATA%\Subversion'
58 * directory).
59 *
60 * readonly = true - not to save any configuration changes that can be done
61 * during the program run to a config file (config settings will only
62 * be read to initialize; to enable changes the readonly flag should be set
63 * to false).
64 *
65 * SVNWCUtil is a utility class that creates a default options driver.
66 */
67 ISVNOptions options = SVNWCUtil.createDefaultOptions( true );
68
69 ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager( );
70
71 /*
72 * Creates an instance of SVNClientManager providing a default auth
73 * manager and an options driver
74 */
75 ourClientManager = SVNClientManager.newInstance( options , authManager );
76
77 /*
78 * Registers a commit event handler
79 */
80 ourClientManager.getCommitClient( ).setEventHandler( myCommitEventHandler );
81
82 /*
83 * Registers an update event handler
84 */
85 ourClientManager.getUpdateClient( ).setEventHandler( myUpdateEventHandler );
86
87 /*
88 * Registers a WC event handler
89 */
90 ourClientManager.getWCClient( ).setEventHandler( myWCEventHandler );
91 ...
Then (listing main actions):
1.First we make a directory in our repository
2. Then we import a local dummy directory into it
3. Then we
checkout MyRepos,
- locally add a new directory into the Working Copy and
- commit it to the repository
4. Then we copy MyRepos to a new location - MyReposCopy
5. Then we
switch the Working Copy to MyReposCopy,
locally delete newDir and
- commit it to the repository
The same steps (marked with numbers to the left) in code:
1 ...
2 long committedRevision = -1;
3 System.out.println( "Making a new directory at '" + url + "'..." );
4 try {
5 /*
6 * creates a new version comtrolled directory in a repository and
7 * displays what revision the repository was committed to
8 */
9 1 committedRevision = makeDirectory(url, "making a new directory at '" + url + "'").getNewRevision();
10 } catch( SVNException svne ) {
11 error( "error while making a new directory at '" + url + "'" , svne );
12 }
13 System.out.println( "Committed to revision " + committedRevision );
14 System.out.println( );
15
16 File anImportDir = new File( importDir );
17 File anImportFile = new File( anImportDir , SVNPathUtil.tail( importFile ) );
18 /*
19 * creates a new local directory - './importDir' and a new file -
20 * './importDir/importFile.txt' that will be imported into the repository
21 * into the '/MyRepos/importDir' directory
22 */
23 createLocalDir( anImportDir , new File[] { anImportFile } , new String[] { importFileText } );
24
25 System.out.println( "Importing a new directory into '" + importToURL + "'..." );
26 try {
27 /*
28 * recursively imports an unversioned directory into a repository
29 * and displays what revision the repository was committed to
30 */
31 boolean isRecursive = true;
32 2 committedRevision = importDirectory( anImportDir , importToURL , "importing a new directory '" + anImportDir.getAbsolutePath( ) + "'" , isRecursive ).getNewRevision( );
33 } catch( SVNException svne ) {
34 error( "error while importing a new directory '" + anImportDir.getAbsolutePath( ) + "' into '" + importToURL + "'" , svne );
35 }
36 System.out.println( "Committed to revision " + committedRevision );
37 System.out.println( );
38
39 /*
40 * creates a local directory where the working copy will be checked out into
41 */
42 File wcDir = new File( myWorkingCopyPath );
43 if ( wcDir.exists() ) {
44 error ( "the destination directory '"
45 + wcDir.getAbsolutePath( ) + "' already exists!" , null );
46 }
47 wcDir.mkdirs( );
48
49 System.out.println( "Checking out a working copy from '" + url + "'..." );
50 try {
51 /*
52 * recursively checks out a working copy from url into wcDir.
53 * SVNRevision.HEAD means the latest revision to be checked out.
54 */
55 3.1 checkout( url , SVNRevision.HEAD , wcDir , true );
56 } catch ( SVNException svne ) {
57 error( "error while checking out a working copy for the location '"
58 + url + "'" , svne );
59 }
60 System.out.println( );
61
62 /*
63 * recursively displays info for wcDir at the current working revision
64 * in the manner of 'svn info -R' command
65 */
66 try {
67 showInfo( wcDir , SVNRevision.WORKING , true );
68 } catch ( SVNException svne ) {
69 error( "error while recursively getting info for the working copy at'"
70 + wcDir.getAbsolutePath( ) + "'" , svne );
71 }
72 System.out.println( );
73
74 File aNewDir = new File( wcDir , newDir );
75 File aNewFile = new File( aNewDir , SVNPathUtil.tail( newFile ) );
76 /*
77 * creates a new local directory - 'wcDir/newDir' and a new file -
78 * '/MyWorkspace/newDir/newFile.txt'
79 */
80 createLocalDir( aNewDir , new File[] { aNewFile } , new String[] { fileText } );
81
82 System.out.println( "Recursively scheduling a new directory '" + aNewDir.getAbsolutePath( ) + "' for addition..." );
83 try {
84 /*
85 * recursively schedules aNewDir for addition
86 */
87 3.2 addEntry( aNewDir );
88 } catch ( SVNException svne ) {
89 error( "error while recursively adding the directory '"
90 + aNewDir.getAbsolutePath( ) + "'" , svne );
91 }
92 System.out.println( );
93
94 boolean isRecursive = true;
95 boolean isRemote = true;
96 boolean isReportAll = false;
97 boolean isIncludeIgnored = true;
98 boolean isCollectParentExternals = false;
99 System.out.println( "Status for '" + wcDir.getAbsolutePath( ) + "':" );
100 try {
101 /*
102 * gets and shows status information for the WC directory.
103 * status will be recursive on wcDir, will also cover the repository,
104 * won't cover unmodified entries, will disregard 'svn:ignore' property
105 * ignores (if any), will ignore externals definitions.
106 */
107 showStatus( wcDir , isRecursive , isRemote , isReportAll ,
108 isIncludeIgnored , isCollectParentExternals );
109 } catch ( SVNException svne ) {
110 error( "error while recursively performing status for '"
111 + wcDir.getAbsolutePath( ) + "'" , svne );
112 }
113 System.out.println( );
114
115 System.out.println( "Updating '" + wcDir.getAbsolutePath( ) + "'..." );
116 try {
117 /*
118 * recursively updates wcDir to the latest revision (SVNRevision.HEAD)
119 */
120 update( wcDir , SVNRevision.HEAD , true );
121 } catch ( SVNException svne ) {
122 error( "error while recursively updating the working copy at '"
123 + wcDir.getAbsolutePath( ) + "'" , svne );
124 }
125 System.out.println( "" );
126
127 System.out.println( "Committing changes for '" + wcDir.getAbsolutePath( ) + "'..." );
128 try {
129 /*
130 * commits changes in wcDir to the repository with not leaving items
131 * locked (if any) after the commit succeeds; this will add aNewDir &
132 * aNewFile to the repository.
133 */
134 3.3 committedRevision = commit( wcDir , false , "'/newDir' with '/newDir/newFile.txt' were added" ).getNewRevision( );
135 } catch ( SVNException svne ) {
136 error( "error while committing changes to the working copy at '"
137 + wcDir.getAbsolutePath( )
138 + "'" , svne );
139 }
140 System.out.println( "Committed to revision " + committedRevision );
141 System.out.println( );
142
143 System.out
144 .println( "Locking (with stealing if the entry is already locked) '"
145 + aNewFile.getAbsolutePath( ) + "'." );
146 try {
147 /*
148 * locks aNewFile with stealing (if it has been already locked by someone
149 * else), providing a lock comment
150 */
151 lock( aNewFile , true , "locking '/newDir/newFile.txt'" );
152 } catch ( SVNException svne ) {
153 error( "error while locking the working copy file '"
154 + aNewFile.getAbsolutePath( ) + "'" , svne );
155 }
156 System.out.println( );
157
158 System.out.println( "Status for '" + wcDir.getAbsolutePath( ) + "':" );
159 try {
160 /*
161 * displays status once again to see the file is really locked
162 */
163 showStatus( wcDir , isRecursive , isRemote , isReportAll ,
164 isIncludeIgnored , isCollectParentExternals );
165 } catch ( SVNException svne ) {
166 error( "error while recursively performing status for '"
167 + wcDir.getAbsolutePath( ) + "'" , svne );
168 }
169 System.out.println( );
170
171 System.out.println( "Copying '" + url + "' to '" + copyURL + "'..." );
172 try {
173 /*
174 * makes a branch of url at copyURL - that is URL->URL copying
175 * with history
176 */
177 4 committedRevision = copy( url , copyURL , false ,
178 "remotely copying '" + url + "' to '" + copyURL + "'" )
179 .getNewRevision( );
180 } catch ( SVNException svne ) {
181 error( "error while copying '" + url + "' to '"
182 + copyURL + "'" , svne );
183 }
184 /*
185 * displays what revision the repository was committed to
186 */
187 System.out.println( "Committed to revision " + committedRevision );
188 System.out.println( );
189
190 System.out.println( "Switching '" + wcDir.getAbsolutePath() + "' to '"
191 + copyURL + "'..." );
192 try {
193 /*
194 * recursively switches wcDir to copyURL in the latest revision
195 * (SVNRevision.HEAD)
196 */
197 5.1 switchToURL( wcDir , copyURL , SVNRevision.HEAD , true );
198 } catch ( SVNException svne ) {
199 error( "error while switching '"
200 + wcDir.getAbsolutePath( ) + "' to '" + copyURL + "'" , svne );
201 }
202 System.out.println( );
203
204 /*
205 * recursively displays info for the working copy once again to see
206 * it was really switched to a new URL
207 */
208 try {
209 showInfo( wcDir , SVNRevision.WORKING , true );
210 } catch ( SVNException svne ) {
211 error( "error while recursively getting info for the working copy at'"
212 + wcDir.getAbsolutePath( ) + "'" , svne );
213 }
214 System.out.println( );
215
216 System.out.println( "Scheduling '" + aNewDir.getAbsolutePath( ) + "' for deletion ..." );
217 try {
218 /*
219 * schedules aNewDir for deletion (with forcing)
220 */
221 5.2 delete( aNewDir , true );
222 } catch ( SVNException svne ) {
223 error( "error while schediling '"
224 + wcDir.getAbsolutePath( ) + "' for deletion" , svne );
225 }
226 System.out.println( );
227
228 System.out.println( "Status for '" + wcDir.getAbsolutePath( ) + "':" );
229 try {
230 /*
231 * recursively displays status once more to see whether aNewDir
232 * was really scheduled for deletion
233 */
234 showStatus( wcDir , isRecursive , isRemote , isReportAll ,
235 isIncludeIgnored , isCollectParentExternals );
236 } catch ( SVNException svne ) {
237 error( "error while recursively performing status for '"
238 + wcDir.getAbsolutePath( ) + "'" , svne );
239 }
240 System.out.println( );
241
242 System.out.println( "Committing changes for '" + wcDir.getAbsolutePath( ) + "'..." );
243 try {
244 /*
245 * lastly commits changes in wcDir to the repository; all items that
246 * were locked by the user (if any) will be unlocked after the commit
247 * succeeds; this commit will remove aNewDir from the repository.
248 */
249 5.3 committedRevision = commit(
250 wcDir ,
251 false ,
252 "deleting '" + aNewDir.getAbsolutePath( )
253 + "' from the filesystem as well as from the repository" ).getNewRevision( );
254 } catch ( SVNException svne ) {
255 error( "error while committing changes to the working copy '"
256 + wcDir.getAbsolutePath( )
257 + "'" , svne );
258 }
259 System.out.println( "Committed to revision " + committedRevision );
260 System.exit( 0 );
261 }
And if the program runs successfully you'll see in your console the following output:
Making a new directory at 'file:///G:/testRep/MyRepos'... Committed to revision 5 Importing a new directory into 'file:///G:/testRep/MyRepos/importDir'... Adding importFile.txt Committed to revision 6 Checking out a working copy from 'file:///G:/testRep/MyRepos'... A importDir AU importDir/importFile.txt At revision 6 -----------------INFO----------------- Local Path: G:\MyWorkingCopy URL: file:///G:/testRep/MyRepos Repository UUID: bcf16199-7f9e-be47-a3e5-e4d194b5d0ae Revision: 6 Node Kind: dir Schedule: normal Last Changed Author: userName Last Changed Revision: 6 Last Changed Date: Fri Jul 07 16:19:37 NOVST 2006 -----------------INFO----------------- Local Path: G:\MyWorkingCopy\importDir URL: file:///G:/testRep/MyRepos/importDir Repository UUID: bcf16199-7f9e-be47-a3e5-e4d194b5d0ae Revision: 6 Node Kind: dir Schedule: normal Last Changed Author: userName Last Changed Revision: 6 Last Changed Date: Fri Jul 07 16:19:37 NOVST 2006 -----------------INFO----------------- Local Path: G:\MyWorkingCopy\importDir\importFile.txt URL: file:///G:/testRep/MyRepos/importDir/importFile.txt Repository UUID: bcf16199-7f9e-be47-a3e5-e4d194b5d0ae Revision: 6 Node Kind: file Schedule: normal Last Changed Author: userName Last Changed Revision: 6 Last Changed Date: Fri Jul 07 16:19:37 NOVST 2006 Properties Last Updated: Fri Jul 07 16:19:38 NOVST 2006 Text Last Updated: Fri Jul 07 16:19:37 NOVST 2006 Checksum: 75e9e68e21ae4453f318424738aef57e Recursively scheduling a new directory 'G:\MyWorkingCopy\newDir' for addition... A newDir A newDir/newFile.txt Status for 'G:\MyWorkingCopy': A 0 ? ? G:\MyWorkingCopy\newDir\newFile.txt A 0 ? ? G:\MyWorkingCopy\newDir Updating 'G:\MyWorkingCopy'... At revision 6 Committing changes for 'G:\MyWorkingCopy'... Adding newDir Adding newDir/newFile.txt Transmitting file data.... Committed to revision 7 Locking (with stealing if the entry is already locked) 'G:\MyWorkingCopy\newDir\newFile.txt'. L newFile.txt Status for 'G:\MyWorkingCopy': K 7 7 userName G:\MyWorkingCopy\newDir\newFile.txt Copying 'file:///G:/testRep/MyRepos' to 'file:///G:/testRep/MyReposCopy'... Committed to revision 8 Switching 'G:\MyWorkingCopy' to 'file:///G:/testRep/MyReposCopy'... B newDir/newFile.txt At revision 8 -----------------INFO----------------- Local Path: G:\MyWorkingCopy URL: file:///G:/testRep/MyReposCopy Repository UUID: bcf16199-7f9e-be47-a3e5-e4d194b5d0ae Revision: 8 Node Kind: dir Schedule: normal Last Changed Author: userName Last Changed Revision: 8 Last Changed Date: Fri Jul 07 16:19:42 NOVST 2006 -----------------INFO----------------- Local Path: G:\MyWorkingCopy\importDir URL: file:///G:/testRep/MyReposCopy/importDir Repository UUID: bcf16199-7f9e-be47-a3e5-e4d194b5d0ae Revision: 8 Node Kind: dir Schedule: normal Last Changed Author: userName Last Changed Revision: 6 Last Changed Date: Fri Jul 07 16:19:37 NOVST 2006 -----------------INFO----------------- Local Path: G:\MyWorkingCopy\importDir\importFile.txt URL: file:///G:/testRep/MyReposCopy/importDir/importFile.txt Repository UUID: bcf16199-7f9e-be47-a3e5-e4d194b5d0ae Revision: 8 Node Kind: file Schedule: normal Last Changed Author: userName Last Changed Revision: 6 Last Changed Date: Fri Jul 07 16:19:37 NOVST 2006 Properties Last Updated: Fri Jul 07 16:19:38 NOVST 2006 Text Last Updated: Fri Jul 07 16:19:37 NOVST 2006 Checksum: 75e9e68e21ae4453f318424738aef57e -----------------INFO----------------- Local Path: G:\MyWorkingCopy\newDir URL: file:///G:/testRep/MyReposCopy/newDir Repository UUID: bcf16199-7f9e-be47-a3e5-e4d194b5d0ae Revision: 8 Node Kind: dir Schedule: normal Last Changed Author: userName Last Changed Revision: 7 Last Changed Date: Fri Jul 07 16:19:41 NOVST 2006 -----------------INFO----------------- Local Path: G:\MyWorkingCopy\newDir\newFile.txt URL: file:///G:/testRep/MyReposCopy/newDir/newFile.txt Repository UUID: bcf16199-7f9e-be47-a3e5-e4d194b5d0ae Revision: 8 Node Kind: file Schedule: normal Last Changed Author: userName Last Changed Revision: 7 Last Changed Date: Fri Jul 07 16:19:41 NOVST 2006 Properties Last Updated: Fri Jul 07 16:19:40 NOVST 2006 Text Last Updated: Fri Jul 07 16:19:40 NOVST 2006 Checksum: 023b67e9660b2faabaf84b10ba32c6cf Scheduling 'G:\MyWorkingCopy\newDir' for deletion ... D newDir/newFile.txt D newDir Status for 'G:\MyWorkingCopy': D 8 7 userName G:\MyWorkingCopy\newDir\newFile.txt D 8 7 userName G:\MyWorkingCopy\newDir Committing changes for 'G:\MyWorkingCopy'... Deleting newDir Committed to revision 9
Download the full sources of this example program: