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:
Initialize SVNKit to work with file:/// protocol.
- Create a repository.
Then populate it with a greek tree not using file system directories and files. All this initializing work is done with the help of the utility class called SamplesUtility.
- Make a copy of that tree: /A to /A_copy.
- Checkout the entire repository tree to a working copy.
- Make some changes to trunk (A). Commit those changes to the repository.
- Then merge the changes from trunk (A) to the branch (A_copy).
- Then again make some changes to trunk and commit them to the repository.
- Again, merge the changes from trunk (A) to the branch (A_copy).
This steps are reflected in code comments:
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.wc.SVNClientManager;
24 import org.tmatesoft.svn.core.wc.SVNCommitClient;
25 import org.tmatesoft.svn.core.wc.SVNCopyClient;
26 import org.tmatesoft.svn.core.wc.SVNCopySource;
27 import org.tmatesoft.svn.core.wc.SVNDiffClient;
28 import org.tmatesoft.svn.core.wc.SVNRevision;
29 import org.tmatesoft.svn.core.wc.SVNRevisionRange;
30 import org.tmatesoft.svn.core.wc.SVNWCClient;
31
32
33 /**
34 * @version 1.2.0
35 * @author TMate Software Ltd.
36 */
37 public class Merge {
38
39 /**
40 * Pass the absolute path of the base directory where all example data will be created in
41 * arg[0]. The sample will create:
42 *
43 * - arg[0]/exampleRepository - repository with some test data
44 * - arg[0]/exampleWC - working copy checked out from exampleRepository
45 */
46 public static void main (String[] args) {
47 //0. initialize SVNKit to work through file:/// protocol
48 SamplesUtility.initializeFSFSprotocol();
49
50 File baseDirectory = new File(args[0]);
51 File reposRoot = new File(baseDirectory, "exampleRepository");
52 File wcRoot = new File(baseDirectory, "exampleWC");
53
54 try {
55 //1. first create a repository
56 SamplesUtility.createRepository(reposRoot);
57 //2. fill it with data
58 SVNCommitInfo info = SamplesUtility.createRepositoryTree(reposRoot);
59 //print out new revision info
60 System.out.println(info);
61
62 SVNClientManager clientManager = SVNClientManager.newInstance();
63
64 SVNURL reposURL = SVNURL.fromFile(reposRoot);
65
66 //3. copy A to A_copy in the repository (url-to-url copy)
67 SVNCopyClient copyClient = clientManager.getCopyClient();
68 SVNURL A_URL = reposURL.appendPath("A", true);
69 SVNURL copyTargetURL = reposURL.appendPath("A_copy", true);
70 SVNCopySource copySource = new SVNCopySource(SVNRevision.UNDEFINED, SVNRevision.HEAD, A_URL);
71 info = copyClient.doCopy(new SVNCopySource[] { copySource }, copyTargetURL, false, false, true,
72 "copy A to A_copy", null);
73 //print out new revision info
74 System.out.println(info);
75
76 //4. checkout the entire repository tree
77 SamplesUtility.checkOutWorkingCopy(reposURL, wcRoot);
78
79
80 //5. now make some changes to the A tree and commit them
81 SamplesUtility.writeToFile(new File(wcRoot, "iota"), "New text appended to 'iota'", true);
82 SamplesUtility.writeToFile(new File(wcRoot, "A/mu"), "New text in 'mu'", false);
83
84 SVNWCClient wcClient = SVNClientManager.newInstance().getWCClient();
85 wcClient.doSetProperty(new File(wcRoot, "A/B"), "spam", SVNPropertyValue.create("egg"), false,
86 SVNDepth.EMPTY, null, null);
87
88 //commit local changes
89 SVNCommitClient commitClient = clientManager.getCommitClient();
90 commitClient.doCommit(new File[] { wcRoot }, false, "committing changes", null, null, false,
91 false, SVNDepth.INFINITY);
92
93 //6. now merge changes from trunk to the branch.
94 SVNDiffClient diffClient = clientManager.getDiffClient();
95 SVNRevisionRange rangeToMerge = new SVNRevisionRange(SVNRevision.create(1), SVNRevision.HEAD);
96
97 diffClient.doMerge(A_URL, SVNRevision.HEAD, Collections.singleton(rangeToMerge),
98 new File(wcRoot, "A_copy"), SVNDepth.INFINITY, true, false, false, false);
99
100
101 //7. now make some changes to the A tree again and commit them
102 //change file contents of iota and A/D/gamma
103 SamplesUtility.writeToFile(new File(wcRoot, "iota"), "New text2 appended to 'iota'", true);
104 SamplesUtility.writeToFile(new File(wcRoot, "A/D/gamma"), "New text in 'gamma'", false);
105 //remove A/C from version control
106 wcClient.doDelete(new File(wcRoot, "A/C"), false, true, false);
107
108 //commit local changes
109 commitClient.doCommit(new File[] { wcRoot }, false, "committing changes again", null, null,
110 false, false, SVNDepth.INFINITY);
111
112 /* 8. do the same merge call, merge-tracking feature will merge only those revisions
113 * which were not merged yet.
114 */
115 diffClient.doMerge(A_URL, SVNRevision.HEAD, Collections.singleton(rangeToMerge),
116 new File(wcRoot, "A_copy"), SVNDepth.INFINITY, true, false, false, false);
117
118 } catch (SVNException svne) {
119 System.out.println(svne.getErrorMessage());
120 System.exit(1);
121 } catch (IOException ioe) {
122 ioe.printStackTrace();
123 System.exit(1);
124 }
125 }
126 }
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 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