现在的位置: 首页 > 综合 > 正文

Using ISVNEditor in commit operations

2013年08月19日 ⁄ 综合 ⁄ 共 9164字 ⁄ 字号 评论关闭

Brief instructions how to commit changes using ISVNEditor:

  • Get a commit editor (ISVNCommitEditor) calling a getCommitEditor() method of your SVNRepository driver.
  • Prepare a commit transaction: traverse your tree of versioned directories/files making reports by calling methods of your commit editor.
  • Close the editor by calling its closeEdit() method. Having been closed the editor can no longer be used.

In preparing a commit transaction you may add, copy or delete directories/files, change file/directory properties, change file contents. Read the details below to get to know how to perform the listed steps on getting and using ISVNEditor.

SVNRepository - the provider of commit editors

First of all, your program creates an SVNRepository driver for a particular protocol to use for working with a Subversion repository. Let's suppose you would like to access a repository
via the custom svn protocol. In this case you must initialize the library for accessing the repository via svn://:

import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
...
    SVNRepositoryFactoryImpl.setup();

Then create an instance of an SVNRepository driver for the repository location that you want to be the ROOT directory ("path/to/rootDir")
for all commit processing:

...
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.SVNException;
...
    
    String url = "svn://localhost/path/to/repos/path/to/rootDir";
    SVNRepository repository = null;
    try {
        repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url));
    } catch (SVNException svne) {
        ...
    }

The next step (yet before getting a commit editor) - you should have an implementation of ISVNWorkspaceMediator. This mediator is used for temporary file delta storage allocations.
For example, you can implement a mediator that will store delta data in temporary files or, as an alternate, in a buffer, like this one:

...
import org.tmatesoft.svn.core.io.ISVNWorkspaceMediator;
...
public class CommitMediator implements ISVNWorkspaceMediator {
    private Map myTmpStorages = new HashMap();
    /*
     * This may be implemented to get properties from 
     * '.svn/wcprops'
     */
    public String getWorkspaceProperty(String path, String name)
            throws SVNException {
        return null;
    }

    /*
     * This may be implemented to set properties in 
     * '.svn/wcprops'
     */
    public void setWorkspaceProperty(String path, String name, String value)
            throws SVNException {
    }

    /*
     * Creates a temporary file delta  storage. id  will be  
     * used as the temporary storage identifier. Returns an  
     * OutputStream to write the delta data into the temporary 
     * storage.
     */
    public OutputStream createTemporaryLocation(String path, Object id)
            throws IOException {
        ByteArrayOutputStream tempStorageOS = new ByteArrayOutputStream();
        myTmpStorages.put(id, tempStorageOS);
        return tempStorageOS;
    }

    /*
     * Returns an InputStream of the temporary file delta 
     * storage identified by id to read the delta.
     */
    public InputStream getTemporaryLocation(Object id) throws IOException {
        return new ByteArrayInputStream(
                  ((ByteArrayOutputStream)myTmpStorages.get(id)).toByteArray());
    }

    /*
     * Gets the length of the delta that was written  
     * to the temporary storage identified by id.
     */
    public long getLength(Object id) throws IOException {
        ByteArrayOutputStream 
                   tempStorageOS = (ByteArrayOutputStream)myTmpStorages.get(id);
        if (tempStorageOS != null) {
            return tempStorageOS.size();
        }
        return 0;
    }

    /*
     * Deletes the temporary file delta storage identified 
     * by id.
     */
    public void deleteTemporaryLocation(Object id) {
        myTmpStorages.remove(id);
    }
}

Well, now when you've got a mediator you can ask you SVNRepository driver for a commit editor:

...
import org.tmatesoft.svn.core.io.ISVNEditor;
...

    String logMessage = "your commit log message";
    try {
        editor = 
            repository.getCommitEditor(logMessage, new CommitMediator());
    } catch (SVNException svne) {
        ...
    }

The above method receives only your log message and mediator and returns an editor. However when working with Subversion 1.2 and higher you should rather use another getCommitEditor() method,
which in addition receives lock-tokens presenting on locked paths and a boolean flag that dictates whether those paths should be unlocked or not after the commit succeeds:

    String logMessage = "your commit log message";
    
    Map locks; 

    ...

    /*
     * for each locked path under the commit 
     * root directory:
     */    
    String path;
    String lockToken;
    ...
    locks.put(path, lockToken);

    ...
    
    /*
     * If there're any locked paths and you wish 
     * them to be left locked after the commit,
     * set keepLocks to true;
     */
    boolean keepLocks = true;
    
    try {
        editor = 
            repository.getCommitEditor(logMessage, locks, keepLocks, 
            new CommitMediator());
    } catch (SVNException svne) {
        ...
    }

If there're no locked paths you can simply pass null instead of locks and
set keepLocks to false. It's just the same as using the first
version of the getCommitEditor() method.

Calling methods of ISVNEditor

Let's imagine we've got the following tree of versioned files and directories:

  /
  /dirA(root)/
             /dirB(added)/
                         /file1.txt(added)
             /dirC(normal)/
                          /dirG(normal)/
                                       /file2.txt(modified)
             /dirD(modified properties)/
             /file3.txt(deleted)

Suppose we are to commit '/dirA' recursively. On the above layout of the versioned tree you can see that 'dirA/dirB' with
its child entry'dirA/dirB/file1.txt' are scheduled for addition, 'dirA/dirC/dirG/file2.txt' has got local
edits, 'dirD' has got changes in properties and at last 'dirA/file3.txt' is scheduled for deletion (of course,
deleted files may have been already removed from the filesystem, although with SVNKit you are able only to schedule a file for deletion but not delete it from the filesystem). Let's commit changes that are found in '/dirA'.

1. Create an SVNRepository driver for '/dirA':

    String url = "svn://host/path/to/repos/path/to/dirA";
    ...
    repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url));

2. Then get a commit editor as described above.

    editor = 
           repository.getCommitEditor(logMessage, new CommitMediator());

3. Calls to methods of your commit editor will be accumulated and translated to commands that will be sent to the server. So, at first open the root (i.e. 'dirA'):

    /*
     * well, for a commit it doesn't matter what revision 
     * is passed here (let it be -1)
     */
    editor.openRoot(-1);

4. Adding 'dirA/dirB' with 'dirA/dirB/file1.txt':

    editor.addDir("dirB", null, -1);
    
    editor.addFile("dirB/file1.txt", null, -1);
    
    /*
     * sending file delta
     * baseChecksum is null - there's no any base file yet at all
     */
    editor.applyTextDelta("dirB/file1.txt", null);
	
    /*
     * Compute the size of the file delta
     */
    long deltaLength;
    ...
    
    /*
     * Create  a  new  diff  window  (provided  the  size  of the delta  - 
     * deltaLength) that will contain instructions of applying the delta  to
     * the file in the repository. A 'replacement' diff window will replace
     * the entire contents with new delta.
     */
     SVNDiffWindow diffWindow = SVNDiffWindowBuilder
            .createReplacementDiffWindow(deltaLength);
     
     /*
      * Get an Output Stream and write your delta into it.
      */
     OutputStream os = editor.textDeltaChunk("dirB/file1.txt", diffWindow);
     ...

     /*
      * When the delta data is written to the Output
      * Stream do not forget to close the stream.
      */
      try{
          os.close;
      }catch(IOException ioe){
          ...
      }

    /*
     * Finally closes the delta when all the bytes are already written.
     */
    editor.textDeltaEnd("dirB/file1.txt");

    /*
     * Closes 'dirB/file1.txt'.
     */
    editor.closeFile("dirB/file1.txt", null);

    /*
     * Closes 'dirB'.
     */
    editor.closeDir();

If the delta is too big and you can not send it as one portion for some reasons, you can divide it into several parts and repeat calls to those editor's delta-related methods for each separate part of the delta.

As for the files already presenting in the repository, you can use the SVNFileUtil class (from org.tmatesoft.svn.core.internal.wc) for computing an MD5 checksum of
the BASE file.

5. Modifying "dirC/dirG/file2.txt":

    editor.openDir("dirC", -1);

    editor.openDir("dirC/dirG", -1);

    /*
     * Provide the working copy revision of your file
     * to the server, so that it has an opportunity to
     * abort your editor in that case if the file is
     * out of date (need to be updated).
     */
    long wcRev;
    ...
    
    editor.openFile("dirC/dirG/file2.txt", wcRev);

    /*
     * Compute the MD5 checksum for the base file:
     * you may use computeChecksum() methods of the
     * org.tmatesoft.svn.core.internal.wc.SVNFileUtil class
     */
    String baseChecksum;
    ...
    
    editor.applyTextDelta("dirC/dirG/file2.txt", baseChecksum);

    long deltaLength;
    /*
     * write delta
     */
    
    ...

    String changedChecksum;

    /*
     * After we've got the delta written,
     * close 'dirC/dirG/file2.txt' providing a checksum
     * for the changed file.
     */
    editor.closeFile("dirC/dirG/file2.txt", changedChecksum);

    /*
     * Closes 'dirG'.
     */
    editor.closeDir();

    /*
     * Closes 'dirC'.
     */
    editor.closeDir();
    

All the rest is just similar to the previous examlpe for adding a file except one thing - compounding delta applying instructions.SVNAllDeltaGenerator is used for newly added files
as well as for binary ones (this class uses SVNDiffWindowBuilder). SVNSequenceDeltaGeneratoris used for
text files already being under version control.

...
import org.tmatesoft.svn.core.io.diff.SVNRAFileData;
import org.tmatesoft.svn.core.io.diff.SVNSequenceDeltaGenerator;
...    
   
    SVNSequenceDeltaGenerator deltaGenerator;
    ...
    
    /*
     * Base and Working 'dirC/dirG/file2.txt' files
     */
    File base = new File("path/to/file2.txt.base");
    
    File working = new File("path/to/file2.txt.working");

    SVNRAFileData baseFile = new SVNRAFileData(base, true);
    
    SVNRAFileData workingFile = new SVNRAFileData(working, true);
    
    SVNDiffWindow diffWindow = deltaGenerator.generateDiffWindow(
                   "dirC/dirG/file2.txt", editor, workingFile, baseFile);

6. Changing properties of 'dirA/dirD':

    editor.openDir("dirD", dirWorkingRev);

    String propName;

    String propValue;
    ...
    
    editor.changeDirProperty(propName, propValue);
    
    editor.closeDir();

7. Deleting 'dirA/file3.txt':

    editor.deleteEntry("file3.txt", fileWorkingRev);

8. Finishing the editor's reports (this will finalize the commit):

    /*
     * Close the root directory - 'dirA'
     */
    editor.closeDir();
     
    editor.closeEdit();

Yet some notes:

  • if you would like to commit a copy of a file/directory you should provide a copy ancestor and its revision to commit editor'saddDir()/addFile() methods.
  • if you catch an exception from an editor's method you should abort the previous reports calling the editor's abortEdit() method.

If you have any questions regarding SVNKit, would like to report a bug or contribute a patch, please write to support@svnkit.com

抱歉!评论已关闭.