Differences between revisions 1 and 6 (spanning 5 versions)
Revision 1 as of 2008-09-04 15:54:46
Size: 250
Editor: 194
Comment:
Revision 6 as of 2008-09-04 16:38:53
Size: 9322
Editor: 194
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
This example is similar to [[Merging from trunk to a branch]] except for the second merge to the branch working copy produces a conflict. In this example we demonstrate how = Performing 'svn merge URL_TO_TRUNK BRANCH_WC' and handling conflicts with SVNKit =
This example is similar to [[Merging from trunk to a branch]] except for that the merge to the branch working copy produces a conflict since we make some changes to the same files in the branch working copy. In this example we demonstrate how you can handle conflicts programmatically using [[ISVNConflictHandler]].
Line 3: Line 4:
you can handle conflicts programmatically using [[ISVNConflictHandler]]. {{{#!java
/*
 * ====================================================================
 * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at http://svnkit.com/license.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 * ====================================================================
 */
package org.tmatesoft.svn.examples.wc;

import java.io.File;
import java.io.IOException;
import java.util.Collections;

import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNPropertyValue;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.wc.ISVNConflictHandler;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNCommitClient;
import org.tmatesoft.svn.core.wc.SVNConflictChoice;
import org.tmatesoft.svn.core.wc.SVNConflictDescription;
import org.tmatesoft.svn.core.wc.SVNConflictReason;
import org.tmatesoft.svn.core.wc.SVNConflictResult;
import org.tmatesoft.svn.core.wc.SVNCopyClient;
import org.tmatesoft.svn.core.wc.SVNCopySource;
import org.tmatesoft.svn.core.wc.SVNDiffClient;
import org.tmatesoft.svn.core.wc.SVNMergeFileSet;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNRevisionRange;
import org.tmatesoft.svn.core.wc.SVNWCClient;


/**
 * @version 1.2.0
 * @author TMate Software Ltd.
 */
public class ConflictedMerge {
    /**
     * Pass the absolute path of the base directory where all example data will be created in
     * arg[0]. The sample will create:
     *
     * - arg[0]/exampleRepository - repository with some test data
     * - arg[0]/exampleWC - working copy checked out from exampleRepository
     */
    public static void main (String[] args) {
        //initialize SVNKit to work through file:/// protocol
        SamplesUtility.initializeFSFSprotocol();
        
        File baseDirectory = new File(args[0]);
        File reposRoot = new File(baseDirectory, "exampleRepository");
        File wcRoot = new File(baseDirectory, "exampleWC");
        
        try {
            //first create a repository and fill it with data
            SamplesUtility.createRepository(reposRoot);
            SVNCommitInfo info = SamplesUtility.createRepositoryTree(reposRoot);
            //print out new revision info
            System.out.println(info);

            SVNClientManager clientManager = SVNClientManager.newInstance();
            
            SVNURL reposURL = SVNURL.fromFile(reposRoot);

            //copy A to A_copy in repository (url-to-url copy)
            SVNCopyClient copyClient = clientManager.getCopyClient();
            SVNURL A_URL = reposURL.appendPath("A", true);
            SVNURL copyTargetURL = reposURL.appendPath("A_copy", true);
            SVNCopySource copySource = new SVNCopySource(SVNRevision.UNDEFINED, SVNRevision.HEAD, A_URL);
            info = copyClient.doCopy(new SVNCopySource[] { copySource }, copyTargetURL, false, false, true,
                    "copy A to A_copy", null);
            //print out new revision info
            System.out.println(info);
            
            //checkout the entire repository tree
            SamplesUtility.checkOutWorkingCopy(reposURL, wcRoot);

            
            //now make some changes to the A tree
            SamplesUtility.writeToFile(new File(wcRoot, "A/B/lambda"), "New text appended to 'lambda'", true);
            SamplesUtility.writeToFile(new File(wcRoot, "A/mu"), "New text in 'mu'", false);
            
            SVNWCClient wcClient = SVNClientManager.newInstance().getWCClient();
            wcClient.doSetProperty(new File(wcRoot, "A/B"), "spam", SVNPropertyValue.create("egg"), false,
                    SVNDepth.EMPTY, null, null);

            //commit local changes
            SVNCommitClient commitClient = clientManager.getCommitClient();
            commitClient.doCommit(new File[] { wcRoot }, false, "committing changes", null, null, false,
                                  false, SVNDepth.INFINITY);

            //now make some local changes to the A_copy tree
            //change file contents of A_copy/B/lambda and A_copy/mu
            SamplesUtility.writeToFile(new File(wcRoot, "A_copy/B/lambda"), "New text in copied 'lambda'", true);
            SamplesUtility.writeToFile(new File(wcRoot, "A_copy/mu"), "New text in copied 'mu'", false);

            //now diff the base revision of the working copy against the repository
            SVNDiffClient diffClient = clientManager.getDiffClient();

            /*
             * Since we provided no custom ISVNOptions implementation to SVNClientManager, our
             * manager uses DefaultSVNOptions, which is set to all SVN*Client classes which the
             * manager produces. So, we can cast ISVNOptions to DefaultSVNOptions.
             */
            DefaultSVNOptions options = (DefaultSVNOptions) diffClient.getOptions();
            //This way we set a conflict handler which will automatically resolve conflicts for those
            //cases that we would like
            options.setConflictHandler(new ConflictResolverHandler());
            
            /* do the same merge call, merge-tracking feature will merge only those revisions
             * which were not still merged.
             */
            SVNRevisionRange rangeToMerge = new SVNRevisionRange(SVNRevision.create(1), SVNRevision.HEAD);
            diffClient.doMerge(A_URL, SVNRevision.HEAD, Collections.singleton(rangeToMerge),
                    new File(wcRoot, "A_copy"), SVNDepth.UNKNOWN, true, false, false, false);
            
        } catch (SVNException svne) {
            System.out.println(svne.getErrorMessage());
            System.exit(1);
        } catch (IOException ioe) {
            ioe.printStackTrace();
            System.exit(1);
        }
    }

    /**
     * Conflict resolver which always selects the local version of a file.
     *
     * @version 1.2.0
     * @author TMate Software Ltd.
     */
    private static class ConflictResolverHandler implements ISVNConflictHandler {
    
        public SVNConflictResult handleConflict(SVNConflictDescription conflictDescription) throws SVNException {
            SVNConflictReason reason = conflictDescription.getConflictReason();
            SVNMergeFileSet mergeFiles = conflictDescription.getMergeFiles();
            
            SVNConflictChoice choice = SVNConflictChoice.THEIRS_FULL;
            if (reason == SVNConflictReason.EDITED) {
                //If the reason why conflict occurred is local edits, chose local version of the file
                //Otherwise the repository version of the file will be chosen.
                choice = SVNConflictChoice.MINE_FULL;
            }
            System.out.println("Automatically resolving conflict for " + mergeFiles.getWCFile() +
                    ", choosing " + (choice == SVNConflictChoice.MINE_FULL ? "local file" : "repository file"));
            return new SVNConflictResult(choice, mergeFiles.getResultFile());
        }
    
    }
}
}}}

== Remarks on the example ==
When no custom run-time options object ([[ISVNOptions]]) is set to the [[SVNDiffClient]] (in our case by [[SVNClientManager]]), [[DefaultSVNOptions]] will be used. This default implementation of run-time options provides a default implementation of [[ISVNMerger]] - [[DefaultSVNMerger]] - merge driver which is used to merge files and properties. This [[DefaultSVNMerger]] can use the caller's implementation of [[ISVNConflictHandler]] for automatic conflict resolution. A conflict handler is provided through [[DefaultSVNOptions]], so we set our custom conflict handler - ''ConflictResolverHandler'' - to the [[DefaultSVNOptions]] used by our [[SVNDiffClient]]. Our handler
instructs the merge driver to choose a local working copy file in case of a conflict given that the conflict itself is caused by our local edits to the file. In all other
cases we just say:"Just take what the repository is giving us".

When you run the program you will see an output similar to this one:
{{{
r1 by 'alex' at Thu Sep 04 18:05:00 CEST 2008
r2 by 'alex' at Thu Sep 04 18:05:00 CEST 2008
Automatically resolving conflict for /home/alex/workspace/tmp/exampleWC/A_copy/B/lambda, choosing local file
Automatically resolving conflict for /home/alex/workspace/tmp/exampleWC/A_copy/mu, choosing local file
}}}

Performing 'svn merge URL_TO_TRUNK BRANCH_WC' and handling conflicts with SVNKit

This example is similar to Merging from trunk to a branch except for that the merge to the branch working copy produces a conflict since we make some changes to the same files in the branch working copy. In this example we demonstrate how you can handle conflicts programmatically using ISVNConflictHandler.

   1 /*
   2  * ====================================================================
   3  * Copyright (c) 2004-2008 TMate Software Ltd.  All rights reserved.
   4  *
   5  * This software is licensed as described in the file COPYING, which
   6  * you should have received as part of this distribution.  The terms
   7  * are also available at http://svnkit.com/license.html.
   8  * If newer versions of this license are posted there, you may use a
   9  * newer version instead, at your option.
  10  * ====================================================================
  11  */
  12 package org.tmatesoft.svn.examples.wc;
  13 
  14 import java.io.File;
  15 import java.io.IOException;
  16 import java.util.Collections;
  17 
  18 import org.tmatesoft.svn.core.SVNCommitInfo;
  19 import org.tmatesoft.svn.core.SVNDepth;
  20 import org.tmatesoft.svn.core.SVNException;
  21 import org.tmatesoft.svn.core.SVNPropertyValue;
  22 import org.tmatesoft.svn.core.SVNURL;
  23 import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
  24 import org.tmatesoft.svn.core.wc.ISVNConflictHandler;
  25 import org.tmatesoft.svn.core.wc.SVNClientManager;
  26 import org.tmatesoft.svn.core.wc.SVNCommitClient;
  27 import org.tmatesoft.svn.core.wc.SVNConflictChoice;
  28 import org.tmatesoft.svn.core.wc.SVNConflictDescription;
  29 import org.tmatesoft.svn.core.wc.SVNConflictReason;
  30 import org.tmatesoft.svn.core.wc.SVNConflictResult;
  31 import org.tmatesoft.svn.core.wc.SVNCopyClient;
  32 import org.tmatesoft.svn.core.wc.SVNCopySource;
  33 import org.tmatesoft.svn.core.wc.SVNDiffClient;
  34 import org.tmatesoft.svn.core.wc.SVNMergeFileSet;
  35 import org.tmatesoft.svn.core.wc.SVNRevision;
  36 import org.tmatesoft.svn.core.wc.SVNRevisionRange;
  37 import org.tmatesoft.svn.core.wc.SVNWCClient;
  38 
  39 
  40 /**
  41  * @version 1.2.0
  42  * @author  TMate Software Ltd.
  43  */
  44 public class ConflictedMerge {
  45     /**
  46      * Pass the absolute path of the base directory where all example data will be created in 
  47      * arg[0]. The sample will create:
  48      *  
  49      *  - arg[0]/exampleRepository - repository with some test data
  50      *  - arg[0]/exampleWC         - working copy checked out from exampleRepository
  51      */
  52     public static void main (String[] args) {
  53         //initialize SVNKit to work through file:/// protocol
  54         SamplesUtility.initializeFSFSprotocol();
  55         
  56         File baseDirectory = new File(args[0]);
  57         File reposRoot = new File(baseDirectory, "exampleRepository");
  58         File wcRoot = new File(baseDirectory, "exampleWC");
  59         
  60         try {
  61             //first create a repository and fill it with data
  62             SamplesUtility.createRepository(reposRoot);
  63             SVNCommitInfo info = SamplesUtility.createRepositoryTree(reposRoot);
  64             //print out new revision info
  65             System.out.println(info);
  66 
  67             SVNClientManager clientManager = SVNClientManager.newInstance();
  68             
  69             SVNURL reposURL = SVNURL.fromFile(reposRoot);
  70 
  71             //copy A to A_copy in repository (url-to-url copy)
  72             SVNCopyClient copyClient = clientManager.getCopyClient();
  73             SVNURL A_URL = reposURL.appendPath("A", true);
  74             SVNURL copyTargetURL = reposURL.appendPath("A_copy", true);
  75             SVNCopySource copySource = new SVNCopySource(SVNRevision.UNDEFINED, SVNRevision.HEAD, A_URL); 
  76             info = copyClient.doCopy(new SVNCopySource[] { copySource }, copyTargetURL, false, false, true, 
  77                     "copy A to A_copy", null);
  78             //print out new revision info
  79             System.out.println(info);
  80             
  81             //checkout the entire repository tree
  82             SamplesUtility.checkOutWorkingCopy(reposURL, wcRoot);
  83 
  84             
  85             //now make some changes to the A tree
  86             SamplesUtility.writeToFile(new File(wcRoot, "A/B/lambda"), "New text appended to 'lambda'", true);
  87             SamplesUtility.writeToFile(new File(wcRoot, "A/mu"), "New text in 'mu'", false);
  88             
  89             SVNWCClient wcClient = SVNClientManager.newInstance().getWCClient();
  90             wcClient.doSetProperty(new File(wcRoot, "A/B"), "spam", SVNPropertyValue.create("egg"), false, 
  91                     SVNDepth.EMPTY, null, null);
  92 
  93             //commit local changes
  94             SVNCommitClient commitClient = clientManager.getCommitClient();
  95             commitClient.doCommit(new File[] { wcRoot }, false, "committing changes", null, null, false, 
  96                                   false, SVNDepth.INFINITY);
  97 
  98             //now make some local changes to the A_copy tree 
  99             //change file contents of A_copy/B/lambda and A_copy/mu
 100             SamplesUtility.writeToFile(new File(wcRoot, "A_copy/B/lambda"), "New text in copied 'lambda'", true);
 101             SamplesUtility.writeToFile(new File(wcRoot, "A_copy/mu"), "New text in copied 'mu'", false);
 102 
 103             //now diff the base revision of the working copy against the repository
 104             SVNDiffClient diffClient = clientManager.getDiffClient();
 105 
 106             /*
 107              * Since we provided no custom ISVNOptions implementation to SVNClientManager, our 
 108              * manager uses DefaultSVNOptions, which is set to all SVN*Client classes which the 
 109              * manager produces. So, we can cast ISVNOptions to DefaultSVNOptions.
 110              */
 111             DefaultSVNOptions options = (DefaultSVNOptions) diffClient.getOptions();
 112             //This way we set a conflict handler which will automatically resolve conflicts for those 
 113             //cases that we would like
 114             options.setConflictHandler(new ConflictResolverHandler());
 115             
 116             /* do the same merge call, merge-tracking feature will merge only those revisions
 117              * which were not still merged.
 118              */ 
 119             SVNRevisionRange rangeToMerge = new SVNRevisionRange(SVNRevision.create(1), SVNRevision.HEAD);
 120             diffClient.doMerge(A_URL, SVNRevision.HEAD, Collections.singleton(rangeToMerge), 
 121                     new File(wcRoot, "A_copy"), SVNDepth.UNKNOWN, true, false, false, false);
 122             
 123         } catch (SVNException svne) {
 124             System.out.println(svne.getErrorMessage());
 125             System.exit(1);
 126         } catch (IOException ioe) {
 127             ioe.printStackTrace();
 128             System.exit(1);
 129         }
 130     }
 131 
 132     /**
 133      * Conflict resolver which always selects the local version of a file.
 134      * 
 135      * @version 1.2.0
 136      * @author  TMate Software Ltd.
 137      */
 138     private static class ConflictResolverHandler implements ISVNConflictHandler {
 139     
 140         public SVNConflictResult handleConflict(SVNConflictDescription conflictDescription) throws SVNException {
 141             SVNConflictReason reason = conflictDescription.getConflictReason();
 142             SVNMergeFileSet mergeFiles = conflictDescription.getMergeFiles();
 143             
 144             SVNConflictChoice choice = SVNConflictChoice.THEIRS_FULL;
 145             if (reason == SVNConflictReason.EDITED) {
 146                 //If the reason why conflict occurred is local edits, chose local version of the file
 147                 //Otherwise the repository version of the file will be chosen.
 148                 choice = SVNConflictChoice.MINE_FULL;
 149             }
 150             System.out.println("Automatically resolving conflict for " + mergeFiles.getWCFile() + 
 151                     ", choosing " + (choice == SVNConflictChoice.MINE_FULL ? "local file" : "repository file"));
 152             return new SVNConflictResult(choice, mergeFiles.getResultFile()); 
 153         }   
 154     
 155     }
 156 }

Remarks on the example

When no custom run-time options object (ISVNOptions) is set to the SVNDiffClient (in our case by SVNClientManager), DefaultSVNOptions will be used. This default implementation of run-time options provides a default implementation of ISVNMerger - DefaultSVNMerger - merge driver which is used to merge files and properties. This DefaultSVNMerger can use the caller's implementation of ISVNConflictHandler for automatic conflict resolution. A conflict handler is provided through DefaultSVNOptions, so we set our custom conflict handler - ConflictResolverHandler - to the DefaultSVNOptions used by our SVNDiffClient. Our handler instructs the merge driver to choose a local working copy file in case of a conflict given that the conflict itself is caused by our local edits to the file. In all other cases we just say:"Just take what the repository is giving us".

When you run the program you will see an output similar to this one:

r1 by 'alex' at Thu Sep 04 18:05:00 CEST 2008
r2 by 'alex' at Thu Sep 04 18:05:00 CEST 2008
Automatically resolving conflict for /home/alex/workspace/tmp/exampleWC/A_copy/B/lambda, choosing local file
Automatically resolving conflict for /home/alex/workspace/tmp/exampleWC/A_copy/mu, choosing local file

Merging from trunk to a branch with conflicts (last edited 2008-09-04 16:40:51 by 194)