= Performing 'svn merge URL_TO_TRUNK BRANCH_WC' with SVNKit = This example demonstrates how you can merge changes from trunk to a branch several times without specifying revisions. What we are going to do here is the following: 0. Initialize SVNKit to work with file:/// protocol. 1. Create a repository. 2. Then populate it with a [[Greek Tree|greek tree]] not using file system directories and files. All this initializing work is done with the help of the utility class called [[SamplesUtility]]. 3. Make a copy of that tree: /A to /A_copy. 4. Checkout the entire repository tree to a working copy. 5. Make some changes to trunk (A). Commit those changes to the repository. 6. Then merge the changes from trunk (A) to the branch (A_copy). 7. Then again make some changes to trunk and commit them to the repository. 8. Again, merge the changes from trunk (A) to the branch (A_copy). This steps are reflected in code comments: {{{#!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.wc.SVNClientManager; import org.tmatesoft.svn.core.wc.SVNCommitClient; 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.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 Merge { /** * 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) { //0. 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 { //1. first create a repository SamplesUtility.createRepository(reposRoot); //2. fill it with data SVNCommitInfo info = SamplesUtility.createRepositoryTree(reposRoot); //print out new revision info System.out.println(info); SVNClientManager clientManager = SVNClientManager.newInstance(); SVNURL reposURL = SVNURL.fromFile(reposRoot); //3. copy A to A_copy in the 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); //4. checkout the entire repository tree SamplesUtility.checkOutWorkingCopy(reposURL, wcRoot); //5. now make some changes to the A tree and commit them SamplesUtility.writeToFile(new File(wcRoot, "iota"), "New text appended to 'iota'", 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); //6. now merge changes from trunk to the branch. SVNDiffClient diffClient = clientManager.getDiffClient(); SVNRevisionRange rangeToMerge = new SVNRevisionRange(SVNRevision.create(1), SVNRevision.HEAD); diffClient.doMerge(A_URL, SVNRevision.HEAD, Collections.singleton(rangeToMerge), new File(wcRoot, "A_copy"), SVNDepth.INFINITY, true, false, false, false); //7. now make some changes to the A tree again and commit them //change file contents of iota and A/D/gamma SamplesUtility.writeToFile(new File(wcRoot, "iota"), "New text2 appended to 'iota'", true); SamplesUtility.writeToFile(new File(wcRoot, "A/D/gamma"), "New text in 'gamma'", false); //remove A/C from version control wcClient.doDelete(new File(wcRoot, "A/C"), false, true, false); //commit local changes commitClient.doCommit(new File[] { wcRoot }, false, "committing changes again", null, null, false, false, SVNDepth.INFINITY); /* 8. do the same merge call, merge-tracking feature will merge only those revisions * which were not merged yet. */ diffClient.doMerge(A_URL, SVNRevision.HEAD, Collections.singleton(rangeToMerge), new File(wcRoot, "A_copy"), SVNDepth.INFINITY, true, false, false, false); } catch (SVNException svne) { System.out.println(svne.getErrorMessage()); System.exit(1); } catch (IOException ioe) { ioe.printStackTrace(); System.exit(1); } } } }}} Here are some comments on calling ''doMerge()'' in this example: * We use a pegged version of ''doMerge()'' which allows to merge differences between different revisions of the same file. * A_URL - URL of the merge source. * ''SVNRevision.HEAD'' - [[Peg revision|peg revision]] in which A_URL is valid for sure. * We use the entire revision range - from revisio 1 to HEAD not caring of how already merged revisions are skipped - merge-tracking feature will do that for us. * Then goes the merge target local path. * We use ''SVNDepth.INFINITY'' to recursively merge the whole target tree. * We want to merge only related (by history) objects, so the next parameter is ''true''. * We do not want our merge to delete any versioned files containing local edits or unversioned ones, so passing ''false''. * We do want a real merge to perform, not a just the final status of a potential merge, so passing ''false''. * We do wand a real merge to perform, not just to record mergeinfo without bringing real changes to A_copy. Each merge operation in this example is equivalent to the following command line client' command: {{{ svn merge file:///home/alex/workspace/tmp/exampleRepository/A /home/alex/workspace/exampleWC/A_copy }}} After the first merge, if you run 'svn status' in the working copy you should see this report: {{{ alex@UFO:~/workspace/tmp/exampleWC$ $JSVN_PATH stat M A_copy M A_copy/B M A_copy/mu }}} And 'svn pg svn:mergeinfo .' in A_copy gives: {{{ alex@UFO:~/workspace/tmp/exampleWC/A_copy$ $JSVN_PATH pg svn:mergeinfo . /A:2-3 }}} After the second merge the status report will look like this: {{{ alex@UFO:~/workspace/tmp/exampleWC$ $JSVN_PATH stat M A_copy D A_copy/C M A_copy/D/gamma }}} and propget report like this: {{{ alex@UFO:~/workspace/tmp/exampleWC/A_copy$ $JSVN_PATH pg svn:mergeinfo . /A:2-4 }}}