001: /*
002: * ====================================================================
003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
004: *
005: * This software is licensed as described in the file COPYING, which
006: * you should have received as part of this distribution. The terms
007: * are also available at http://svnkit.com/license.html
008: * If newer versions of this license are posted there, you may use a
009: * newer version instead, at your option.
010: * ====================================================================
011: */
012: package org.tmatesoft.svn.examples.repository;
013:
014: import java.io.ByteArrayInputStream;
015:
016: import org.tmatesoft.svn.core.SVNCommitInfo;
017: import org.tmatesoft.svn.core.SVNErrorCode;
018: import org.tmatesoft.svn.core.SVNErrorMessage;
019: import org.tmatesoft.svn.core.SVNException;
020: import org.tmatesoft.svn.core.SVNNodeKind;
021: import org.tmatesoft.svn.core.SVNURL;
022: import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
023: import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
024: import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
025: import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
026: import org.tmatesoft.svn.core.io.ISVNEditor;
027: import org.tmatesoft.svn.core.io.SVNRepository;
028: import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
029: import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
030: import org.tmatesoft.svn.core.wc.SVNWCUtil;
031:
032: /*
033: * This is an example of how to commit several types of changes to a repository:
034: * - a new directory with a file,
035: * - modification to anexisting file,
036: * - copying a directory into a branch,
037: * - deletion of the directory and its entries.
038: *
039: * Main aspects of performing a commit with the help of ISVNEditor:
040: * 0)initialize the library (this is done in setupLibrary() method);
041: *
042: * 1)create an SVNRepository driver for a particular repository location, that
043: * will be the root directory for committing - that is all paths that are being
044: * committed will be below that root;
045: *
046: * 2)provide user's authentication information - name/password, since committing
047: * generally requires authentication;
048: *
049: * 3)"ask" your SVNRepository for a commit editor:
050: *
051: * ISVNCommitEditor editor = SVNRepository.getCommitEditor();
052: *
053: * 4)"edit" a repository - perform a sequence of edit calls (for
054: * example, here you "say" to the server that you have added such-and-such a new
055: * directory at such-and-such a path as well as a new file). First of all,
056: * ISVNEditor.openRoot() is called to 'open' the root directory;
057: *
058: * 5)at last you close the editor with the ISVNEditor.closeEdit() method that
059: * fixes your modificaions in the repository finalizing the commit.
060: *
061: * For each commit a new ISVNEditor is required - that is after having been
062: * closed the editor can no longer be used!
063: *
064: * This example can be run for a locally installed Subversion repository via the
065: * svn:// protocol. This is how you can do it:
066: *
067: * 1)after you install the Subversion pack (available for download at
068: * http://subversion.tigris.org) you should create a new repository in a
069: * directory, like this (in a command line under a Windows OS):
070: *
071: * >svnadmin create X:\path\to\rep
072: *
073: * 2)after the repository is created you can add a new account: navigate to your
074: * repository root (X:\path\to\rep\), then move to \conf and open the file
075: * 'passwd'. In the file you'll see the section [users]. Uncomment it and add a
076: * new account below the section name, like:
077: *
078: * [users]
079: * userName = userPassword.
080: *
081: * In the program you may further use this account as user's credentials.
082: *
083: * 3)the next step is to launch the custom Subversion server (svnserve) in a
084: * background mode for the just created repository:
085: *
086: * >svnserve -d -r X:\path\to
087: *
088: * That's all. The repository is now available via svn://localhost/rep.
089: *
090: */
091: public class Commit {
092:
093: public static void main(String[] args) {
094: /*
095: * Initialize the library. It must be done before calling any
096: * method of the library.
097: */
098: setupLibrary();
099:
100: /*
101: * Run commit example and process error if any.
102: */
103: try {
104: commitExample();
105: } catch (SVNException e) {
106: SVNErrorMessage err = e.getErrorMessage();
107: /*
108: * Display all tree of error messages.
109: * Utility method SVNErrorMessage.getFullMessage() may be used instead of the loop.
110: */
111: while (err != null) {
112: System.err.println(err.getErrorCode().getCode() + " : "
113: + err.getMessage());
114: err = err.getChildErrorMessage();
115: }
116: System.exit(1);
117: }
118: System.exit(0);
119: }
120:
121: private static void commitExample() throws SVNException {
122: /*
123: * URL that points to repository.
124: */
125: SVNURL url = SVNURL.parseURIEncoded("svn://localhost/rep");
126: /*
127: * Credentials to use for authentication.
128: */
129: String userName = "foo";
130: String userPassword = "bar";
131:
132: /*
133: * Sample file contents.
134: */
135: byte[] contents = "This is a new file".getBytes();
136: byte[] modifiedContents = "This is the same file but modified a little."
137: .getBytes();
138:
139: /*
140: * Create an instance of SVNRepository class. This class is the main entry point
141: * for all "low-level" Subversion operations supported by Subversion protocol.
142: *
143: * These operations includes browsing, update and commit operations. See
144: * SVNRepository methods javadoc for more details.
145: */
146: SVNRepository repository = SVNRepositoryFactory.create(url);
147:
148: /*
149: * User's authentication information (name/password) is provided via an
150: * ISVNAuthenticationManager instance. SVNWCUtil creates a default
151: * authentication manager given user's name and password.
152: *
153: * Default authentication manager first attempts to use provided user name
154: * and password and then falls back to the credentials stored in the
155: * default Subversion credentials storage that is located in Subversion
156: * configuration area. If you'd like to use provided user name and password
157: * only you may use BasicAuthenticationManager class instead of default
158: * authentication manager:
159: *
160: * authManager = new BasicAuthenticationsManager(userName, userPassword);
161: *
162: * You may also skip this point - anonymous access will be used.
163: */
164: ISVNAuthenticationManager authManager = SVNWCUtil
165: .createDefaultAuthenticationManager(userName,
166: userPassword);
167: repository.setAuthenticationManager(authManager);
168:
169: /*
170: * Get type of the node located at URL we used to create SVNRepository.
171: *
172: * "" (empty string) is path relative to that URL,
173: * -1 is value that may be used to specify HEAD (latest) revision.
174: */
175: SVNNodeKind nodeKind = repository.checkPath("", -1);
176:
177: /*
178: * Checks up if the current path really corresponds to a directory. If
179: * it doesn't, the program exits. SVNNodeKind is that one who says what
180: * is located at a path in a revision.
181: */
182: if (nodeKind == SVNNodeKind.NONE) {
183: SVNErrorMessage err = SVNErrorMessage.create(
184: SVNErrorCode.UNKNOWN, "No entry at URL ''{0}''",
185: url);
186: throw new SVNException(err);
187: } else if (nodeKind == SVNNodeKind.FILE) {
188: SVNErrorMessage err = SVNErrorMessage
189: .create(
190: SVNErrorCode.UNKNOWN,
191: "Entry at URL ''{0}'' is a file while directory was expected",
192: url);
193: throw new SVNException(err);
194: }
195:
196: /*
197: * Get exact value of the latest (HEAD) revision.
198: */
199: long latestRevision = repository.getLatestRevision();
200: System.out
201: .println("Repository latest revision (before committing): "
202: + latestRevision);
203:
204: /*
205: * Gets an editor for committing the changes to the repository. NOTE:
206: * you must not invoke methods of the SVNRepository until you close the
207: * editor with the ISVNEditor.closeEdit() method.
208: *
209: * commitMessage will be applied as a log message of the commit.
210: *
211: * ISVNWorkspaceMediator instance will be used to store temporary files,
212: * when 'null' is passed, then default system temporary directory will be used to
213: * create temporary files.
214: */
215: ISVNEditor editor = repository.getCommitEditor(
216: "directory and file added", null);
217:
218: /*
219: * Add a directory and a file within that directory.
220: *
221: * SVNCommitInfo object contains basic information on the committed revision, i.e.
222: * revision number, author name, commit date and commit message.
223: */
224: SVNCommitInfo commitInfo = addDir(editor, "test",
225: "test/file.txt", contents);
226: System.out.println("The directory was added: " + commitInfo);
227:
228: /*
229: * Modify file added at the previous revision.
230: */
231: editor = repository.getCommitEditor("file contents changed",
232: null);
233: commitInfo = modifyFile(editor, "test", "test/file.txt",
234: contents, modifiedContents);
235: System.out.println("The file was changed: " + commitInfo);
236:
237: /*
238: * Copy recently added directory to another location. This operation
239: * will create new directory "test2" taking its properties and contents
240: * from the directory "test". Revisions history will be preserved.
241: *
242: * Copy is usually used to create tags or branches in repository or to
243: * rename files or directories.
244: */
245:
246: /*
247: * To make a copy of a repository entry, absolute path of that entry
248: * and revision from which to make a copy are needed.
249: *
250: * Absolute path is the path relative to repository root URL and
251: * such paths always start with '/'. Relative paths are relative
252: * to SVNRepository instance location.
253: *
254: * For more information see SVNRepository.getRepositoryRoot(...) and
255: * SVNRepository.getLocation() methods. Utility method getRepositoryPath(...)
256: * converts relative path to the absolute one.
257: */
258: String absoluteSrcPath = repository.getRepositoryPath("test");
259: long srcRevision = repository.getLatestRevision();
260:
261: editor = repository.getCommitEditor("directory copied", null);
262:
263: commitInfo = copyDir(editor, absoluteSrcPath, "test2",
264: srcRevision);
265: System.out.println("The directory was copied: " + commitInfo);
266:
267: /*
268: * Delete directory "test".
269: */
270: editor = repository.getCommitEditor("directory deleted", null);
271: commitInfo = deleteDir(editor, "test");
272: System.out.println("The directory was deleted: " + commitInfo);
273:
274: /*
275: * And directory "test2".
276: */
277: editor = repository.getCommitEditor("copied directory deleted",
278: null);
279: commitInfo = deleteDir(editor, "test2");
280: System.out.println("The copied directory was deleted: "
281: + commitInfo);
282:
283: latestRevision = repository.getLatestRevision();
284: System.out
285: .println("Repository latest revision (after committing): "
286: + latestRevision);
287: }
288:
289: /*
290: * This method performs commiting an addition of a directory containing a
291: * file.
292: */
293: private static SVNCommitInfo addDir(ISVNEditor editor,
294: String dirPath, String filePath, byte[] data)
295: throws SVNException {
296: /*
297: * Always called first. Opens the current root directory. It means all
298: * modifications will be applied to this directory until a next entry
299: * (located inside the root) is opened/added.
300: *
301: * -1 - revision is HEAD (actually, for a comit editor this number is
302: * irrelevant)
303: */
304: editor.openRoot(-1);
305: /*
306: * Adds a new directory (in this case - to the root directory for
307: * which the SVNRepository was created).
308: * Since this moment all changes will be applied to this new directory.
309: *
310: * dirPath is relative to the root directory.
311: *
312: * copyFromPath (the 2nd parameter) is set to null and copyFromRevision
313: * (the 3rd) parameter is set to -1 since the directory is not added
314: * with history (is not copied, in other words).
315: */
316: editor.addDir(dirPath, null, -1);
317: /*
318: * Adds a new file to the just added directory. The file path is also
319: * defined as relative to the root directory.
320: *
321: * copyFromPath (the 2nd parameter) is set to null and copyFromRevision
322: * (the 3rd parameter) is set to -1 since the file is not added with
323: * history.
324: */
325: editor.addFile(filePath, null, -1);
326: /*
327: * The next steps are directed to applying delta to the file (that is
328: * the full contents of the file in this case).
329: */
330: editor.applyTextDelta(filePath, null);
331: /*
332: * Use delta generator utility class to generate and send delta
333: *
334: * Note that you may use only 'target' data to generate delta when there is no
335: * access to the 'base' (previous) version of the file. However, using 'base'
336: * data will result in smaller network overhead.
337: *
338: * SVNDeltaGenerator will call editor.textDeltaChunk(...) method for each generated
339: * "diff window" and then editor.textDeltaEnd(...) in the end of delta transmission.
340: * Number of diff windows depends on the file size.
341: *
342: */
343: SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
344: String checksum = deltaGenerator.sendDelta(filePath,
345: new ByteArrayInputStream(data), editor, true);
346:
347: /*
348: * Closes the new added file.
349: */
350: editor.closeFile(filePath, checksum);
351: /*
352: * Closes the new added directory.
353: */
354: editor.closeDir();
355: /*
356: * Closes the root directory.
357: */
358: editor.closeDir();
359: /*
360: * This is the final point in all editor handling. Only now all that new
361: * information previously described with the editor's methods is sent to
362: * the server for committing. As a result the server sends the new
363: * commit information.
364: */
365: return editor.closeEdit();
366: }
367:
368: /*
369: * This method performs committing file modifications.
370: */
371: private static SVNCommitInfo modifyFile(ISVNEditor editor,
372: String dirPath, String filePath, byte[] oldData,
373: byte[] newData) throws SVNException {
374: /*
375: * Always called first. Opens the current root directory. It means all
376: * modifications will be applied to this directory until a next entry
377: * (located inside the root) is opened/added.
378: *
379: * -1 - revision is HEAD
380: */
381: editor.openRoot(-1);
382: /*
383: * Opens a next subdirectory (in this example program it's the directory
384: * added in the last commit). Since this moment all changes will be
385: * applied to this directory.
386: *
387: * dirPath is relative to the root directory.
388: * -1 - revision is HEAD
389: */
390: editor.openDir(dirPath, -1);
391: /*
392: * Opens the file added in the previous commit.
393: *
394: * filePath is also defined as a relative path to the root directory.
395: */
396: editor.openFile(filePath, -1);
397:
398: /*
399: * The next steps are directed to applying and writing the file delta.
400: */
401: editor.applyTextDelta(filePath, null);
402:
403: /*
404: * Use delta generator utility class to generate and send delta
405: *
406: * Note that you may use only 'target' data to generate delta when there is no
407: * access to the 'base' (previous) version of the file. However, here we've got 'base'
408: * data, what in case of larger files results in smaller network overhead.
409: *
410: * SVNDeltaGenerator will call editor.textDeltaChunk(...) method for each generated
411: * "diff window" and then editor.textDeltaEnd(...) in the end of delta transmission.
412: * Number of diff windows depends on the file size.
413: *
414: */
415: SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
416: String checksum = deltaGenerator.sendDelta(filePath,
417: new ByteArrayInputStream(oldData), 0,
418: new ByteArrayInputStream(newData), editor, true);
419:
420: /*
421: * Closes the file.
422: */
423: editor.closeFile(filePath, checksum);
424:
425: /*
426: * Closes the directory.
427: */
428: editor.closeDir();
429:
430: /*
431: * Closes the root directory.
432: */
433: editor.closeDir();
434:
435: /*
436: * This is the final point in all editor handling. Only now all that new
437: * information previously described with the editor's methods is sent to
438: * the server for committing. As a result the server sends the new
439: * commit information.
440: */
441: return editor.closeEdit();
442: }
443:
444: /*
445: * This method performs committing a deletion of a directory.
446: */
447: private static SVNCommitInfo deleteDir(ISVNEditor editor,
448: String dirPath) throws SVNException {
449: /*
450: * Always called first. Opens the current root directory. It means all
451: * modifications will be applied to this directory until a next entry
452: * (located inside the root) is opened/added.
453: *
454: * -1 - revision is HEAD
455: */
456: editor.openRoot(-1);
457: /*
458: * Deletes the subdirectory with all its contents.
459: *
460: * dirPath is relative to the root directory.
461: */
462: editor.deleteEntry(dirPath, -1);
463: /*
464: * Closes the root directory.
465: */
466: editor.closeDir();
467: /*
468: * This is the final point in all editor handling. Only now all that new
469: * information previously described with the editor's methods is sent to
470: * the server for committing. As a result the server sends the new
471: * commit information.
472: */
473: return editor.closeEdit();
474: }
475:
476: /*
477: * This method performs how a directory in the repository can be copied to
478: * branch.
479: */
480: private static SVNCommitInfo copyDir(ISVNEditor editor,
481: String srcDirPath, String dstDirPath, long revision)
482: throws SVNException {
483: /*
484: * Always called first. Opens the current root directory. It means all
485: * modifications will be applied to this directory until a next entry
486: * (located inside the root) is opened/added.
487: *
488: * -1 - revision is HEAD
489: */
490: editor.openRoot(-1);
491:
492: /*
493: * Adds a new directory that is a copy of the existing one.
494: *
495: * srcDirPath - the source directory path (relative to the root
496: * directory).
497: *
498: * dstDirPath - the destination directory path where the source will be
499: * copied to (relative to the root directory).
500: *
501: * revision - the number of the source directory revision.
502: */
503: editor.addDir(dstDirPath, srcDirPath, revision);
504: /*
505: * Closes the just added copy of the directory.
506: */
507: editor.closeDir();
508: /*
509: * Closes the root directory.
510: */
511: editor.closeDir();
512: /*
513: * This is the final point in all editor handling. Only now all that new
514: * information previously described with the editor's methods is sent to
515: * the server for committing. As a result the server sends the new
516: * commit information.
517: */
518: return editor.closeEdit();
519: }
520:
521: /*
522: * Initializes the library to work with a repository via
523: * different protocols.
524: */
525: private static void setupLibrary() {
526: /*
527: * For using over http:// and https://
528: */
529: DAVRepositoryFactory.setup();
530: /*
531: * For using over svn:// and svn+xxx://
532: */
533: SVNRepositoryFactoryImpl.setup();
534:
535: /*
536: * For using over file:///
537: */
538: FSRepositoryFactory.setup();
539: }
540: }
|