Differences between revisions 1 and 4 (spanning 3 versions)
Revision 1 as of 2008-09-04 15:54:46
Size: 250
Editor: 194
Comment:
Revision 4 as of 2008-09-04 16:33:53
Size: 9005
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 to the [[DefaultSVNOptions]] used by our [[SVNDiffClient]].

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, false, SVNDepth.INFINITY);
  96 
  97             //now make some local changes to the A_copy tree 
  98             //change file contents of A_copy/B/lambda and A_copy/mu
  99             SamplesUtility.writeToFile(new File(wcRoot, "A_copy/B/lambda"), "New text in copied 'lambda'", true);
 100             SamplesUtility.writeToFile(new File(wcRoot, "A_copy/mu"), "New text in copied 'mu'", false);
 101 
 102             //now diff the base revision of the working copy against the repository
 103             SVNDiffClient diffClient = clientManager.getDiffClient();
 104 
 105             /*
 106              * Since we provided no custom ISVNOptions implementation to SVNClientManager, our 
 107              * manager uses DefaultSVNOptions, which is set to all SVN*Client classes which the 
 108              * manager produces. So, we can cast ISVNOptions to DefaultSVNOptions.
 109              */
 110             DefaultSVNOptions options = (DefaultSVNOptions) diffClient.getOptions();
 111             //This way we set a conflict handler which will automatically resolve conflicts for those 
 112             //cases that we would like
 113             options.setConflictHandler(new ConflictResolverHandler());
 114             
 115             /* do the same merge call, merge-tracking feature will merge only those revisions
 116              * which were not still merged.
 117              */ 
 118             SVNRevisionRange rangeToMerge = new SVNRevisionRange(SVNRevision.create(1), SVNRevision.HEAD);
 119             diffClient.doMerge(A_URL, SVNRevision.HEAD, Collections.singleton(rangeToMerge), 
 120                     new File(wcRoot, "A_copy"), SVNDepth.UNKNOWN, true, false, false, false);
 121             
 122         } catch (SVNException svne) {
 123             System.out.println(svne.getErrorMessage());
 124             System.exit(1);
 125         } catch (IOException ioe) {
 126             ioe.printStackTrace();
 127             System.exit(1);
 128         }
 129     }
 130 
 131     /**
 132      * Conflict resolver which always selects the local version of a file.
 133      * 
 134      * @version 1.2.0
 135      * @author  TMate Software Ltd.
 136      */
 137     private static class ConflictResolverHandler implements ISVNConflictHandler {
 138     
 139         public SVNConflictResult handleConflict(SVNConflictDescription conflictDescription) throws SVNException {
 140             SVNConflictReason reason = conflictDescription.getConflictReason();
 141             SVNMergeFileSet mergeFiles = conflictDescription.getMergeFiles();
 142             
 143             SVNConflictChoice choice = SVNConflictChoice.THEIRS_FULL;
 144             if (reason == SVNConflictReason.EDITED) {
 145                 //If the reason why conflict occurred is local edits, chose local version of the file
 146                 //Otherwise the repository version of the file will be chosen.
 147                 choice = SVNConflictChoice.MINE_FULL;
 148             }
 149             System.out.println("Automatically resolving conflict for " + mergeFiles.getWCFile() + 
 150                     ", choosing " + (choice == SVNConflictChoice.MINE_FULL ? "local file" : "repository file"));
 151             return new SVNConflictResult(choice, mergeFiles.getResultFile()); 
 152         }   
 153     
 154     }
 155 }

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 to the DefaultSVNOptions used by our SVNDiffClient.

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)