View Javadoc
1   /*
2    * Copyright (C) 2009, Google Inc.
3    * Copyright (C) 2008, Robin Rosenberg
4    * and other copyright owners as documented in the project's IP log.
5    *
6    * This program and the accompanying materials are made available
7    * under the terms of the Eclipse Distribution License v1.0 which
8    * accompanies this distribution, is reproduced below, and is
9    * available at http://www.eclipse.org/org/documents/edl-v10.php
10   *
11   * All rights reserved.
12   *
13   * Redistribution and use in source and binary forms, with or
14   * without modification, are permitted provided that the following
15   * conditions are met:
16   *
17   * - Redistributions of source code must retain the above copyright
18   *   notice, this list of conditions and the following disclaimer.
19   *
20   * - Redistributions in binary form must reproduce the above
21   *   copyright notice, this list of conditions and the following
22   *   disclaimer in the documentation and/or other materials provided
23   *   with the distribution.
24   *
25   * - Neither the name of the Eclipse Foundation, Inc. nor the
26   *   names of its contributors may be used to endorse or promote
27   *   products derived from this software without specific prior
28   *   written permission.
29   *
30   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
31   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
32   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
33   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
35   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
37   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
38   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
39   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
42   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43   */
44  
45  package org.eclipse.jgit.merge;
46  
47  import static org.junit.Assert.assertEquals;
48  import static org.junit.Assert.assertFalse;
49  import static org.junit.Assert.assertTrue;
50  
51  import java.io.IOException;
52  
53  import org.eclipse.jgit.dircache.DirCache;
54  import org.eclipse.jgit.dircache.DirCacheBuilder;
55  import org.eclipse.jgit.lib.CommitBuilder;
56  import org.eclipse.jgit.lib.FileMode;
57  import org.eclipse.jgit.lib.ObjectId;
58  import org.eclipse.jgit.lib.ObjectInserter;
59  import org.eclipse.jgit.lib.PersonIdent;
60  import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
61  import org.eclipse.jgit.treewalk.TreeWalk;
62  import org.junit.Test;
63  
64  public class SimpleMergeTest extends SampleDataRepositoryTestCase {
65  
66  	@Test
67  	public void testOurs() throws IOException {
68  		Merger ourMerger = MergeStrategy.OURS.newMerger(db);
69  		boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") });
70  		assertTrue(merge);
71  		assertEquals(db.resolve("a^{tree}"), ourMerger.getResultTreeId());
72  	}
73  
74  	@Test
75  	public void testOurs_noRepo() throws IOException {
76  		try (ObjectInserter ins = db.newObjectInserter()) {
77  			Merger ourMerger = MergeStrategy.OURS.newMerger(ins, db.getConfig());
78  			boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") });
79  			assertTrue(merge);
80  			assertEquals(db.resolve("a^{tree}"), ourMerger.getResultTreeId());
81  		}
82  	}
83  
84  	@Test
85  	public void testTheirs() throws IOException {
86  		Merger ourMerger = MergeStrategy.THEIRS.newMerger(db);
87  		boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") });
88  		assertTrue(merge);
89  		assertEquals(db.resolve("c^{tree}"), ourMerger.getResultTreeId());
90  	}
91  
92  	@Test
93  	public void testTheirs_noRepo() throws IOException {
94  		try (ObjectInserter ins = db.newObjectInserter()) {
95  			Merger ourMerger = MergeStrategy.THEIRS.newMerger(db);
96  			boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") });
97  			assertTrue(merge);
98  			assertEquals(db.resolve("c^{tree}"), ourMerger.getResultTreeId());
99  		}
100 	}
101 
102 	@Test
103 	public void testTrivialTwoWay() throws IOException {
104 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
105 		boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") });
106 		assertTrue(merge);
107 		assertEquals("02ba32d3649e510002c21651936b7077aa75ffa9",ourMerger.getResultTreeId().name());
108 	}
109 
110 	@Test
111 	public void testTrivialTwoWay_disjointhistories() throws IOException {
112 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
113 		boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c~4") });
114 		assertTrue(merge);
115 		assertEquals("86265c33b19b2be71bdd7b8cb95823f2743d03a8",ourMerger.getResultTreeId().name());
116 	}
117 
118 	@Test
119 	public void testTrivialTwoWay_ok() throws IOException {
120 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
121 		boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a^0^0^0"), db.resolve("a^0^0^1") });
122 		assertTrue(merge);
123 		assertEquals(db.resolve("a^0^0^{tree}"), ourMerger.getResultTreeId());
124 	}
125 
126 	@Test
127 	public void testTrivialTwoWay_noRepo() throws IOException {
128 		try (ObjectInserter ins = db.newObjectInserter()) {
129 			Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(ins, db.getConfig());
130 			boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a^0^0^0"), db.resolve("a^0^0^1") });
131 			assertTrue(merge);
132 			assertEquals(db.resolve("a^0^0^{tree}"), ourMerger.getResultTreeId());
133 		}
134 	}
135 
136 	@Test
137 	public void testTrivialTwoWay_conflict() throws IOException {
138 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
139 		boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("f"), db.resolve("g") });
140 		assertFalse(merge);
141 	}
142 
143 	@Test
144 	public void testTrivialTwoWay_validSubtreeSort() throws Exception {
145 		final DirCache treeB = db.readDirCache();
146 		final DirCache treeO = db.readDirCache();
147 		final DirCache treeT = db.readDirCache();
148 		{
149 			final DirCacheBuilder b = treeB.builder();
150 			final DirCacheBuilder o = treeO.builder();
151 			final DirCacheBuilder t = treeT.builder();
152 
153 			b.add(createEntry("libelf-po/a", FileMode.REGULAR_FILE));
154 			b.add(createEntry("libelf/c", FileMode.REGULAR_FILE));
155 
156 			o.add(createEntry("Makefile", FileMode.REGULAR_FILE));
157 			o.add(createEntry("libelf-po/a", FileMode.REGULAR_FILE));
158 			o.add(createEntry("libelf/c", FileMode.REGULAR_FILE));
159 
160 			t.add(createEntry("libelf-po/a", FileMode.REGULAR_FILE));
161 			t.add(createEntry("libelf/c", FileMode.REGULAR_FILE, "blah"));
162 
163 			b.finish();
164 			o.finish();
165 			t.finish();
166 		}
167 
168 		final ObjectInserter ow = db.newObjectInserter();
169 		final ObjectId b = commit(ow, treeB, new ObjectId[] {});
170 		final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
171 		final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
172 
173 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
174 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
175 		assertTrue(merge);
176 
177 		final TreeWalk tw = new TreeWalk(db);
178 		tw.setRecursive(true);
179 		tw.reset(ourMerger.getResultTreeId());
180 
181 		assertTrue(tw.next());
182 		assertEquals("Makefile", tw.getPathString());
183 		assertCorrectId(treeO, tw);
184 
185 		assertTrue(tw.next());
186 		assertEquals("libelf-po/a", tw.getPathString());
187 		assertCorrectId(treeO, tw);
188 
189 		assertTrue(tw.next());
190 		assertEquals("libelf/c", tw.getPathString());
191 		assertCorrectId(treeT, tw);
192 
193 		assertFalse(tw.next());
194 	}
195 
196 	@Test
197 	public void testTrivialTwoWay_concurrentSubtreeChange() throws Exception {
198 		final DirCache treeB = db.readDirCache();
199 		final DirCache treeO = db.readDirCache();
200 		final DirCache treeT = db.readDirCache();
201 		{
202 			final DirCacheBuilder b = treeB.builder();
203 			final DirCacheBuilder o = treeO.builder();
204 			final DirCacheBuilder t = treeT.builder();
205 
206 			b.add(createEntry("d/o", FileMode.REGULAR_FILE));
207 			b.add(createEntry("d/t", FileMode.REGULAR_FILE));
208 
209 			o.add(createEntry("d/o", FileMode.REGULAR_FILE, "o !"));
210 			o.add(createEntry("d/t", FileMode.REGULAR_FILE));
211 
212 			t.add(createEntry("d/o", FileMode.REGULAR_FILE));
213 			t.add(createEntry("d/t", FileMode.REGULAR_FILE, "t !"));
214 
215 			b.finish();
216 			o.finish();
217 			t.finish();
218 		}
219 
220 		final ObjectInserter ow = db.newObjectInserter();
221 		final ObjectId b = commit(ow, treeB, new ObjectId[] {});
222 		final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
223 		final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
224 
225 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
226 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
227 		assertTrue(merge);
228 
229 		final TreeWalk tw = new TreeWalk(db);
230 		tw.setRecursive(true);
231 		tw.reset(ourMerger.getResultTreeId());
232 
233 		assertTrue(tw.next());
234 		assertEquals("d/o", tw.getPathString());
235 		assertCorrectId(treeO, tw);
236 
237 		assertTrue(tw.next());
238 		assertEquals("d/t", tw.getPathString());
239 		assertCorrectId(treeT, tw);
240 
241 		assertFalse(tw.next());
242 	}
243 
244 	@Test
245 	public void testTrivialTwoWay_conflictSubtreeChange() throws Exception {
246 		final DirCache treeB = db.readDirCache();
247 		final DirCache treeO = db.readDirCache();
248 		final DirCache treeT = db.readDirCache();
249 		{
250 			final DirCacheBuilder b = treeB.builder();
251 			final DirCacheBuilder o = treeO.builder();
252 			final DirCacheBuilder t = treeT.builder();
253 
254 			b.add(createEntry("d/o", FileMode.REGULAR_FILE));
255 			b.add(createEntry("d/t", FileMode.REGULAR_FILE));
256 
257 			o.add(createEntry("d/o", FileMode.REGULAR_FILE));
258 			o.add(createEntry("d/t", FileMode.REGULAR_FILE, "o !"));
259 
260 			t.add(createEntry("d/o", FileMode.REGULAR_FILE, "t !"));
261 			t.add(createEntry("d/t", FileMode.REGULAR_FILE, "t !"));
262 
263 			b.finish();
264 			o.finish();
265 			t.finish();
266 		}
267 
268 		final ObjectInserter ow = db.newObjectInserter();
269 		final ObjectId b = commit(ow, treeB, new ObjectId[] {});
270 		final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
271 		final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
272 
273 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
274 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
275 		assertFalse(merge);
276 	}
277 
278 	@Test
279 	public void testTrivialTwoWay_leftDFconflict1() throws Exception {
280 		final DirCache treeB = db.readDirCache();
281 		final DirCache treeO = db.readDirCache();
282 		final DirCache treeT = db.readDirCache();
283 		{
284 			final DirCacheBuilder b = treeB.builder();
285 			final DirCacheBuilder o = treeO.builder();
286 			final DirCacheBuilder t = treeT.builder();
287 
288 			b.add(createEntry("d/o", FileMode.REGULAR_FILE));
289 			b.add(createEntry("d/t", FileMode.REGULAR_FILE));
290 
291 			o.add(createEntry("d", FileMode.REGULAR_FILE));
292 
293 			t.add(createEntry("d/o", FileMode.REGULAR_FILE));
294 			t.add(createEntry("d/t", FileMode.REGULAR_FILE, "t !"));
295 
296 			b.finish();
297 			o.finish();
298 			t.finish();
299 		}
300 
301 		final ObjectInserter ow = db.newObjectInserter();
302 		final ObjectId b = commit(ow, treeB, new ObjectId[] {});
303 		final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
304 		final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
305 
306 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
307 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
308 		assertFalse(merge);
309 	}
310 
311 	@Test
312 	public void testTrivialTwoWay_rightDFconflict1() throws Exception {
313 		final DirCache treeB = db.readDirCache();
314 		final DirCache treeO = db.readDirCache();
315 		final DirCache treeT = db.readDirCache();
316 		{
317 			final DirCacheBuilder b = treeB.builder();
318 			final DirCacheBuilder o = treeO.builder();
319 			final DirCacheBuilder t = treeT.builder();
320 
321 			b.add(createEntry("d/o", FileMode.REGULAR_FILE));
322 			b.add(createEntry("d/t", FileMode.REGULAR_FILE));
323 
324 			o.add(createEntry("d/o", FileMode.REGULAR_FILE));
325 			o.add(createEntry("d/t", FileMode.REGULAR_FILE, "o !"));
326 
327 			t.add(createEntry("d", FileMode.REGULAR_FILE));
328 
329 			b.finish();
330 			o.finish();
331 			t.finish();
332 		}
333 
334 		final ObjectInserter ow = db.newObjectInserter();
335 		final ObjectId b = commit(ow, treeB, new ObjectId[] {});
336 		final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
337 		final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
338 
339 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
340 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
341 		assertFalse(merge);
342 	}
343 
344 	@Test
345 	public void testTrivialTwoWay_leftDFconflict2() throws Exception {
346 		final DirCache treeB = db.readDirCache();
347 		final DirCache treeO = db.readDirCache();
348 		final DirCache treeT = db.readDirCache();
349 		{
350 			final DirCacheBuilder b = treeB.builder();
351 			final DirCacheBuilder o = treeO.builder();
352 			final DirCacheBuilder t = treeT.builder();
353 
354 			b.add(createEntry("d", FileMode.REGULAR_FILE));
355 
356 			o.add(createEntry("d", FileMode.REGULAR_FILE, "o !"));
357 
358 			t.add(createEntry("d/o", FileMode.REGULAR_FILE));
359 
360 			b.finish();
361 			o.finish();
362 			t.finish();
363 		}
364 
365 		final ObjectInserter ow = db.newObjectInserter();
366 		final ObjectId b = commit(ow, treeB, new ObjectId[] {});
367 		final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
368 		final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
369 
370 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
371 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
372 		assertFalse(merge);
373 	}
374 
375 	@Test
376 	public void testTrivialTwoWay_rightDFconflict2() throws Exception {
377 		final DirCache treeB = db.readDirCache();
378 		final DirCache treeO = db.readDirCache();
379 		final DirCache treeT = db.readDirCache();
380 		{
381 			final DirCacheBuilder b = treeB.builder();
382 			final DirCacheBuilder o = treeO.builder();
383 			final DirCacheBuilder t = treeT.builder();
384 
385 			b.add(createEntry("d", FileMode.REGULAR_FILE));
386 
387 			o.add(createEntry("d/o", FileMode.REGULAR_FILE));
388 
389 			t.add(createEntry("d", FileMode.REGULAR_FILE, "t !"));
390 
391 			b.finish();
392 			o.finish();
393 			t.finish();
394 		}
395 
396 		final ObjectInserter ow = db.newObjectInserter();
397 		final ObjectId b = commit(ow, treeB, new ObjectId[] {});
398 		final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
399 		final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
400 
401 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
402 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
403 		assertFalse(merge);
404 	}
405 
406 	private static void assertCorrectId(DirCache treeT, TreeWalk tw) {
407 		assertEquals(treeT.getEntry(tw.getPathString()).getObjectId(), tw
408 				.getObjectId(0));
409 	}
410 
411 	private static ObjectId commit(final ObjectInserter odi,
412 			final DirCache treeB,
413 			final ObjectId[] parentIds) throws Exception {
414 		final CommitBuilder c = new CommitBuilder();
415 		c.setTreeId(treeB.writeTree(odi));
416 		c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
417 		c.setCommitter(c.getAuthor());
418 		c.setParentIds(parentIds);
419 		c.setMessage("Tree " + c.getTreeId().name());
420 		ObjectId id = odi.insert(c);
421 		odi.flush();
422 		return id;
423 	}
424 }