2 * Copyright (C) 2008, Robin Rosenberg
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 package org
.spearce
.jgit
.merge
;
39 import java
.io
.ByteArrayInputStream
;
40 import java
.io
.IOException
;
42 import org
.spearce
.jgit
.dircache
.DirCache
;
43 import org
.spearce
.jgit
.dircache
.DirCacheBuilder
;
44 import org
.spearce
.jgit
.dircache
.DirCacheEntry
;
45 import org
.spearce
.jgit
.lib
.Commit
;
46 import org
.spearce
.jgit
.lib
.Constants
;
47 import org
.spearce
.jgit
.lib
.FileMode
;
48 import org
.spearce
.jgit
.lib
.ObjectId
;
49 import org
.spearce
.jgit
.lib
.ObjectWriter
;
50 import org
.spearce
.jgit
.lib
.PersonIdent
;
51 import org
.spearce
.jgit
.lib
.RepositoryTestCase
;
52 import org
.spearce
.jgit
.treewalk
.TreeWalk
;
54 public class SimpleMergeTest
extends RepositoryTestCase
{
56 public void testOurs() throws IOException
{
57 Merger ourMerger
= MergeStrategy
.OURS
.newMerger(db
);
58 boolean merge
= ourMerger
.merge(new ObjectId
[] { db
.resolve("a"), db
.resolve("c") });
60 assertEquals(db
.mapTree("a").getId(), ourMerger
.getResultTreeId());
63 public void testTheirs() throws IOException
{
64 Merger ourMerger
= MergeStrategy
.THEIRS
.newMerger(db
);
65 boolean merge
= ourMerger
.merge(new ObjectId
[] { db
.resolve("a"), db
.resolve("c") });
67 assertEquals(db
.mapTree("c").getId(), ourMerger
.getResultTreeId());
70 public void testTrivialTwoWay() throws IOException
{
71 Merger ourMerger
= MergeStrategy
.SIMPLE_TWO_WAY_IN_CORE
.newMerger(db
);
72 boolean merge
= ourMerger
.merge(new ObjectId
[] { db
.resolve("a"), db
.resolve("c") });
74 assertEquals("02ba32d3649e510002c21651936b7077aa75ffa9",ourMerger
.getResultTreeId().name());
77 public void testTrivialTwoWay_disjointhistories() throws IOException
{
78 Merger ourMerger
= MergeStrategy
.SIMPLE_TWO_WAY_IN_CORE
.newMerger(db
);
79 boolean merge
= ourMerger
.merge(new ObjectId
[] { db
.resolve("a"), db
.resolve("c~4") });
81 assertEquals("86265c33b19b2be71bdd7b8cb95823f2743d03a8",ourMerger
.getResultTreeId().name());
84 public void testTrivialTwoWay_ok() throws IOException
{
85 Merger ourMerger
= MergeStrategy
.SIMPLE_TWO_WAY_IN_CORE
.newMerger(db
);
86 boolean merge
= ourMerger
.merge(new ObjectId
[] { db
.resolve("a^0^0^0"), db
.resolve("a^0^0^1") });
88 assertEquals(db
.mapTree("a^0^0").getId(), ourMerger
.getResultTreeId());
91 public void testTrivialTwoWay_conflict() throws IOException
{
92 Merger ourMerger
= MergeStrategy
.SIMPLE_TWO_WAY_IN_CORE
.newMerger(db
);
93 boolean merge
= ourMerger
.merge(new ObjectId
[] { db
.resolve("f"), db
.resolve("g") });
97 public void testTrivialTwoWay_validSubtreeSort() throws Exception
{
98 final DirCache treeB
= DirCache
.read(db
);
99 final DirCache treeO
= DirCache
.read(db
);
100 final DirCache treeT
= DirCache
.read(db
);
102 final DirCacheBuilder b
= treeB
.builder();
103 final DirCacheBuilder o
= treeO
.builder();
104 final DirCacheBuilder t
= treeT
.builder();
106 b
.add(makeEntry("libelf-po/a", FileMode
.REGULAR_FILE
));
107 b
.add(makeEntry("libelf/c", FileMode
.REGULAR_FILE
));
109 o
.add(makeEntry("Makefile", FileMode
.REGULAR_FILE
));
110 o
.add(makeEntry("libelf-po/a", FileMode
.REGULAR_FILE
));
111 o
.add(makeEntry("libelf/c", FileMode
.REGULAR_FILE
));
113 t
.add(makeEntry("libelf-po/a", FileMode
.REGULAR_FILE
));
114 t
.add(makeEntry("libelf/c", FileMode
.REGULAR_FILE
, "blah"));
121 final ObjectWriter ow
= new ObjectWriter(db
);
122 final ObjectId b
= commit(ow
, treeB
, new ObjectId
[] {});
123 final ObjectId o
= commit(ow
, treeO
, new ObjectId
[] { b
});
124 final ObjectId t
= commit(ow
, treeT
, new ObjectId
[] { b
});
126 Merger ourMerger
= MergeStrategy
.SIMPLE_TWO_WAY_IN_CORE
.newMerger(db
);
127 boolean merge
= ourMerger
.merge(new ObjectId
[] { o
, t
});
130 final TreeWalk tw
= new TreeWalk(db
);
131 tw
.setRecursive(true);
132 tw
.reset(ourMerger
.getResultTreeId());
134 assertTrue(tw
.next());
135 assertEquals("Makefile", tw
.getPathString());
136 assertCorrectId(treeO
, tw
);
138 assertTrue(tw
.next());
139 assertEquals("libelf-po/a", tw
.getPathString());
140 assertCorrectId(treeO
, tw
);
142 assertTrue(tw
.next());
143 assertEquals("libelf/c", tw
.getPathString());
144 assertCorrectId(treeT
, tw
);
146 assertFalse(tw
.next());
149 public void testTrivialTwoWay_concurrentSubtreeChange() throws Exception
{
150 final DirCache treeB
= DirCache
.read(db
);
151 final DirCache treeO
= DirCache
.read(db
);
152 final DirCache treeT
= DirCache
.read(db
);
154 final DirCacheBuilder b
= treeB
.builder();
155 final DirCacheBuilder o
= treeO
.builder();
156 final DirCacheBuilder t
= treeT
.builder();
158 b
.add(makeEntry("d/o", FileMode
.REGULAR_FILE
));
159 b
.add(makeEntry("d/t", FileMode
.REGULAR_FILE
));
161 o
.add(makeEntry("d/o", FileMode
.REGULAR_FILE
, "o !"));
162 o
.add(makeEntry("d/t", FileMode
.REGULAR_FILE
));
164 t
.add(makeEntry("d/o", FileMode
.REGULAR_FILE
));
165 t
.add(makeEntry("d/t", FileMode
.REGULAR_FILE
, "t !"));
172 final ObjectWriter ow
= new ObjectWriter(db
);
173 final ObjectId b
= commit(ow
, treeB
, new ObjectId
[] {});
174 final ObjectId o
= commit(ow
, treeO
, new ObjectId
[] { b
});
175 final ObjectId t
= commit(ow
, treeT
, new ObjectId
[] { b
});
177 Merger ourMerger
= MergeStrategy
.SIMPLE_TWO_WAY_IN_CORE
.newMerger(db
);
178 boolean merge
= ourMerger
.merge(new ObjectId
[] { o
, t
});
181 final TreeWalk tw
= new TreeWalk(db
);
182 tw
.setRecursive(true);
183 tw
.reset(ourMerger
.getResultTreeId());
185 assertTrue(tw
.next());
186 assertEquals("d/o", tw
.getPathString());
187 assertCorrectId(treeO
, tw
);
189 assertTrue(tw
.next());
190 assertEquals("d/t", tw
.getPathString());
191 assertCorrectId(treeT
, tw
);
193 assertFalse(tw
.next());
196 public void testTrivialTwoWay_conflictSubtreeChange() throws Exception
{
197 final DirCache treeB
= DirCache
.read(db
);
198 final DirCache treeO
= DirCache
.read(db
);
199 final DirCache treeT
= DirCache
.read(db
);
201 final DirCacheBuilder b
= treeB
.builder();
202 final DirCacheBuilder o
= treeO
.builder();
203 final DirCacheBuilder t
= treeT
.builder();
205 b
.add(makeEntry("d/o", FileMode
.REGULAR_FILE
));
206 b
.add(makeEntry("d/t", FileMode
.REGULAR_FILE
));
208 o
.add(makeEntry("d/o", FileMode
.REGULAR_FILE
));
209 o
.add(makeEntry("d/t", FileMode
.REGULAR_FILE
, "o !"));
211 t
.add(makeEntry("d/o", FileMode
.REGULAR_FILE
, "t !"));
212 t
.add(makeEntry("d/t", FileMode
.REGULAR_FILE
, "t !"));
219 final ObjectWriter ow
= new ObjectWriter(db
);
220 final ObjectId b
= commit(ow
, treeB
, new ObjectId
[] {});
221 final ObjectId o
= commit(ow
, treeO
, new ObjectId
[] { b
});
222 final ObjectId t
= commit(ow
, treeT
, new ObjectId
[] { b
});
224 Merger ourMerger
= MergeStrategy
.SIMPLE_TWO_WAY_IN_CORE
.newMerger(db
);
225 boolean merge
= ourMerger
.merge(new ObjectId
[] { o
, t
});
229 public void testTrivialTwoWay_leftDFconflict1() throws Exception
{
230 final DirCache treeB
= DirCache
.read(db
);
231 final DirCache treeO
= DirCache
.read(db
);
232 final DirCache treeT
= DirCache
.read(db
);
234 final DirCacheBuilder b
= treeB
.builder();
235 final DirCacheBuilder o
= treeO
.builder();
236 final DirCacheBuilder t
= treeT
.builder();
238 b
.add(makeEntry("d/o", FileMode
.REGULAR_FILE
));
239 b
.add(makeEntry("d/t", FileMode
.REGULAR_FILE
));
241 o
.add(makeEntry("d", FileMode
.REGULAR_FILE
));
243 t
.add(makeEntry("d/o", FileMode
.REGULAR_FILE
));
244 t
.add(makeEntry("d/t", FileMode
.REGULAR_FILE
, "t !"));
251 final ObjectWriter ow
= new ObjectWriter(db
);
252 final ObjectId b
= commit(ow
, treeB
, new ObjectId
[] {});
253 final ObjectId o
= commit(ow
, treeO
, new ObjectId
[] { b
});
254 final ObjectId t
= commit(ow
, treeT
, new ObjectId
[] { b
});
256 Merger ourMerger
= MergeStrategy
.SIMPLE_TWO_WAY_IN_CORE
.newMerger(db
);
257 boolean merge
= ourMerger
.merge(new ObjectId
[] { o
, t
});
261 public void testTrivialTwoWay_rightDFconflict1() throws Exception
{
262 final DirCache treeB
= DirCache
.read(db
);
263 final DirCache treeO
= DirCache
.read(db
);
264 final DirCache treeT
= DirCache
.read(db
);
266 final DirCacheBuilder b
= treeB
.builder();
267 final DirCacheBuilder o
= treeO
.builder();
268 final DirCacheBuilder t
= treeT
.builder();
270 b
.add(makeEntry("d/o", FileMode
.REGULAR_FILE
));
271 b
.add(makeEntry("d/t", FileMode
.REGULAR_FILE
));
273 o
.add(makeEntry("d/o", FileMode
.REGULAR_FILE
));
274 o
.add(makeEntry("d/t", FileMode
.REGULAR_FILE
, "o !"));
276 t
.add(makeEntry("d", FileMode
.REGULAR_FILE
));
283 final ObjectWriter ow
= new ObjectWriter(db
);
284 final ObjectId b
= commit(ow
, treeB
, new ObjectId
[] {});
285 final ObjectId o
= commit(ow
, treeO
, new ObjectId
[] { b
});
286 final ObjectId t
= commit(ow
, treeT
, new ObjectId
[] { b
});
288 Merger ourMerger
= MergeStrategy
.SIMPLE_TWO_WAY_IN_CORE
.newMerger(db
);
289 boolean merge
= ourMerger
.merge(new ObjectId
[] { o
, t
});
293 public void testTrivialTwoWay_leftDFconflict2() throws Exception
{
294 final DirCache treeB
= DirCache
.read(db
);
295 final DirCache treeO
= DirCache
.read(db
);
296 final DirCache treeT
= DirCache
.read(db
);
298 final DirCacheBuilder b
= treeB
.builder();
299 final DirCacheBuilder o
= treeO
.builder();
300 final DirCacheBuilder t
= treeT
.builder();
302 b
.add(makeEntry("d", FileMode
.REGULAR_FILE
));
304 o
.add(makeEntry("d", FileMode
.REGULAR_FILE
, "o !"));
306 t
.add(makeEntry("d/o", FileMode
.REGULAR_FILE
));
313 final ObjectWriter ow
= new ObjectWriter(db
);
314 final ObjectId b
= commit(ow
, treeB
, new ObjectId
[] {});
315 final ObjectId o
= commit(ow
, treeO
, new ObjectId
[] { b
});
316 final ObjectId t
= commit(ow
, treeT
, new ObjectId
[] { b
});
318 Merger ourMerger
= MergeStrategy
.SIMPLE_TWO_WAY_IN_CORE
.newMerger(db
);
319 boolean merge
= ourMerger
.merge(new ObjectId
[] { o
, t
});
323 public void testTrivialTwoWay_rightDFconflict2() throws Exception
{
324 final DirCache treeB
= DirCache
.read(db
);
325 final DirCache treeO
= DirCache
.read(db
);
326 final DirCache treeT
= DirCache
.read(db
);
328 final DirCacheBuilder b
= treeB
.builder();
329 final DirCacheBuilder o
= treeO
.builder();
330 final DirCacheBuilder t
= treeT
.builder();
332 b
.add(makeEntry("d", FileMode
.REGULAR_FILE
));
334 o
.add(makeEntry("d/o", FileMode
.REGULAR_FILE
));
336 t
.add(makeEntry("d", FileMode
.REGULAR_FILE
, "t !"));
343 final ObjectWriter ow
= new ObjectWriter(db
);
344 final ObjectId b
= commit(ow
, treeB
, new ObjectId
[] {});
345 final ObjectId o
= commit(ow
, treeO
, new ObjectId
[] { b
});
346 final ObjectId t
= commit(ow
, treeT
, new ObjectId
[] { b
});
348 Merger ourMerger
= MergeStrategy
.SIMPLE_TWO_WAY_IN_CORE
.newMerger(db
);
349 boolean merge
= ourMerger
.merge(new ObjectId
[] { o
, t
});
353 private void assertCorrectId(final DirCache treeT
, final TreeWalk tw
) {
354 assertEquals(treeT
.getEntry(tw
.getPathString()).getObjectId(), tw
358 private ObjectId
commit(final ObjectWriter ow
, final DirCache treeB
,
359 final ObjectId
[] parentIds
) throws Exception
{
360 final Commit c
= new Commit(db
);
361 c
.setTreeId(treeB
.writeTree(ow
));
362 c
.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
363 c
.setCommitter(c
.getAuthor());
364 c
.setParentIds(parentIds
);
365 c
.setMessage("Tree " + c
.getTreeId().name());
366 return ow
.writeCommit(c
);
369 private DirCacheEntry
makeEntry(final String path
, final FileMode mode
)
371 return makeEntry(path
, mode
, path
);
374 private DirCacheEntry
makeEntry(final String path
, final FileMode mode
,
375 final String content
) throws Exception
{
376 final DirCacheEntry ent
= new DirCacheEntry(path
);
377 ent
.setFileMode(mode
);
378 final byte[] contentBytes
= Constants
.encode(content
);
379 ent
.setObjectId(new ObjectWriter(db
).computeBlobSha1(
380 contentBytes
.length
, new ByteArrayInputStream(contentBytes
)));