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 testTheirs() throws IOException {
76  		Merger ourMerger = MergeStrategy.THEIRS.newMerger(db);
77  		boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") });
78  		assertTrue(merge);
79  		assertEquals(db.resolve("c^{tree}"), ourMerger.getResultTreeId());
80  	}
81  
82  	@Test
83  	public void testTrivialTwoWay() throws IOException {
84  		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
85  		boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c") });
86  		assertTrue(merge);
87  		assertEquals("02ba32d3649e510002c21651936b7077aa75ffa9",ourMerger.getResultTreeId().name());
88  	}
89  
90  	@Test
91  	public void testTrivialTwoWay_disjointhistories() throws IOException {
92  		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
93  		boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a"), db.resolve("c~4") });
94  		assertTrue(merge);
95  		assertEquals("86265c33b19b2be71bdd7b8cb95823f2743d03a8",ourMerger.getResultTreeId().name());
96  	}
97  
98  	@Test
99  	public void testTrivialTwoWay_ok() throws IOException {
100 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
101 		boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("a^0^0^0"), db.resolve("a^0^0^1") });
102 		assertTrue(merge);
103 		assertEquals(db.resolve("a^0^0^{tree}"), ourMerger.getResultTreeId());
104 	}
105 
106 	@Test
107 	public void testTrivialTwoWay_conflict() throws IOException {
108 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
109 		boolean merge = ourMerger.merge(new ObjectId[] { db.resolve("f"), db.resolve("g") });
110 		assertFalse(merge);
111 	}
112 
113 	@Test
114 	public void testTrivialTwoWay_validSubtreeSort() throws Exception {
115 		final DirCache treeB = db.readDirCache();
116 		final DirCache treeO = db.readDirCache();
117 		final DirCache treeT = db.readDirCache();
118 		{
119 			final DirCacheBuilder b = treeB.builder();
120 			final DirCacheBuilder o = treeO.builder();
121 			final DirCacheBuilder t = treeT.builder();
122 
123 			b.add(createEntry("libelf-po/a", FileMode.REGULAR_FILE));
124 			b.add(createEntry("libelf/c", FileMode.REGULAR_FILE));
125 
126 			o.add(createEntry("Makefile", FileMode.REGULAR_FILE));
127 			o.add(createEntry("libelf-po/a", FileMode.REGULAR_FILE));
128 			o.add(createEntry("libelf/c", FileMode.REGULAR_FILE));
129 
130 			t.add(createEntry("libelf-po/a", FileMode.REGULAR_FILE));
131 			t.add(createEntry("libelf/c", FileMode.REGULAR_FILE, "blah"));
132 
133 			b.finish();
134 			o.finish();
135 			t.finish();
136 		}
137 
138 		final ObjectInserter ow = db.newObjectInserter();
139 		final ObjectId b = commit(ow, treeB, new ObjectId[] {});
140 		final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
141 		final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
142 
143 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
144 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
145 		assertTrue(merge);
146 
147 		final TreeWalk tw = new TreeWalk(db);
148 		tw.setRecursive(true);
149 		tw.reset(ourMerger.getResultTreeId());
150 
151 		assertTrue(tw.next());
152 		assertEquals("Makefile", tw.getPathString());
153 		assertCorrectId(treeO, tw);
154 
155 		assertTrue(tw.next());
156 		assertEquals("libelf-po/a", tw.getPathString());
157 		assertCorrectId(treeO, tw);
158 
159 		assertTrue(tw.next());
160 		assertEquals("libelf/c", tw.getPathString());
161 		assertCorrectId(treeT, tw);
162 
163 		assertFalse(tw.next());
164 	}
165 
166 	@Test
167 	public void testTrivialTwoWay_concurrentSubtreeChange() throws Exception {
168 		final DirCache treeB = db.readDirCache();
169 		final DirCache treeO = db.readDirCache();
170 		final DirCache treeT = db.readDirCache();
171 		{
172 			final DirCacheBuilder b = treeB.builder();
173 			final DirCacheBuilder o = treeO.builder();
174 			final DirCacheBuilder t = treeT.builder();
175 
176 			b.add(createEntry("d/o", FileMode.REGULAR_FILE));
177 			b.add(createEntry("d/t", FileMode.REGULAR_FILE));
178 
179 			o.add(createEntry("d/o", FileMode.REGULAR_FILE, "o !"));
180 			o.add(createEntry("d/t", FileMode.REGULAR_FILE));
181 
182 			t.add(createEntry("d/o", FileMode.REGULAR_FILE));
183 			t.add(createEntry("d/t", FileMode.REGULAR_FILE, "t !"));
184 
185 			b.finish();
186 			o.finish();
187 			t.finish();
188 		}
189 
190 		final ObjectInserter ow = db.newObjectInserter();
191 		final ObjectId b = commit(ow, treeB, new ObjectId[] {});
192 		final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
193 		final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
194 
195 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
196 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
197 		assertTrue(merge);
198 
199 		final TreeWalk tw = new TreeWalk(db);
200 		tw.setRecursive(true);
201 		tw.reset(ourMerger.getResultTreeId());
202 
203 		assertTrue(tw.next());
204 		assertEquals("d/o", tw.getPathString());
205 		assertCorrectId(treeO, tw);
206 
207 		assertTrue(tw.next());
208 		assertEquals("d/t", tw.getPathString());
209 		assertCorrectId(treeT, tw);
210 
211 		assertFalse(tw.next());
212 	}
213 
214 	@Test
215 	public void testTrivialTwoWay_conflictSubtreeChange() throws Exception {
216 		final DirCache treeB = db.readDirCache();
217 		final DirCache treeO = db.readDirCache();
218 		final DirCache treeT = db.readDirCache();
219 		{
220 			final DirCacheBuilder b = treeB.builder();
221 			final DirCacheBuilder o = treeO.builder();
222 			final DirCacheBuilder t = treeT.builder();
223 
224 			b.add(createEntry("d/o", FileMode.REGULAR_FILE));
225 			b.add(createEntry("d/t", FileMode.REGULAR_FILE));
226 
227 			o.add(createEntry("d/o", FileMode.REGULAR_FILE));
228 			o.add(createEntry("d/t", FileMode.REGULAR_FILE, "o !"));
229 
230 			t.add(createEntry("d/o", FileMode.REGULAR_FILE, "t !"));
231 			t.add(createEntry("d/t", FileMode.REGULAR_FILE, "t !"));
232 
233 			b.finish();
234 			o.finish();
235 			t.finish();
236 		}
237 
238 		final ObjectInserter ow = db.newObjectInserter();
239 		final ObjectId b = commit(ow, treeB, new ObjectId[] {});
240 		final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
241 		final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
242 
243 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
244 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
245 		assertFalse(merge);
246 	}
247 
248 	@Test
249 	public void testTrivialTwoWay_leftDFconflict1() throws Exception {
250 		final DirCache treeB = db.readDirCache();
251 		final DirCache treeO = db.readDirCache();
252 		final DirCache treeT = db.readDirCache();
253 		{
254 			final DirCacheBuilder b = treeB.builder();
255 			final DirCacheBuilder o = treeO.builder();
256 			final DirCacheBuilder t = treeT.builder();
257 
258 			b.add(createEntry("d/o", FileMode.REGULAR_FILE));
259 			b.add(createEntry("d/t", FileMode.REGULAR_FILE));
260 
261 			o.add(createEntry("d", FileMode.REGULAR_FILE));
262 
263 			t.add(createEntry("d/o", FileMode.REGULAR_FILE));
264 			t.add(createEntry("d/t", FileMode.REGULAR_FILE, "t !"));
265 
266 			b.finish();
267 			o.finish();
268 			t.finish();
269 		}
270 
271 		final ObjectInserter ow = db.newObjectInserter();
272 		final ObjectId b = commit(ow, treeB, new ObjectId[] {});
273 		final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
274 		final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
275 
276 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
277 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
278 		assertFalse(merge);
279 	}
280 
281 	@Test
282 	public void testTrivialTwoWay_rightDFconflict1() throws Exception {
283 		final DirCache treeB = db.readDirCache();
284 		final DirCache treeO = db.readDirCache();
285 		final DirCache treeT = db.readDirCache();
286 		{
287 			final DirCacheBuilder b = treeB.builder();
288 			final DirCacheBuilder o = treeO.builder();
289 			final DirCacheBuilder t = treeT.builder();
290 
291 			b.add(createEntry("d/o", FileMode.REGULAR_FILE));
292 			b.add(createEntry("d/t", FileMode.REGULAR_FILE));
293 
294 			o.add(createEntry("d/o", FileMode.REGULAR_FILE));
295 			o.add(createEntry("d/t", FileMode.REGULAR_FILE, "o !"));
296 
297 			t.add(createEntry("d", FileMode.REGULAR_FILE));
298 
299 			b.finish();
300 			o.finish();
301 			t.finish();
302 		}
303 
304 		final ObjectInserter ow = db.newObjectInserter();
305 		final ObjectId b = commit(ow, treeB, new ObjectId[] {});
306 		final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
307 		final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
308 
309 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
310 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
311 		assertFalse(merge);
312 	}
313 
314 	@Test
315 	public void testTrivialTwoWay_leftDFconflict2() throws Exception {
316 		final DirCache treeB = db.readDirCache();
317 		final DirCache treeO = db.readDirCache();
318 		final DirCache treeT = db.readDirCache();
319 		{
320 			final DirCacheBuilder b = treeB.builder();
321 			final DirCacheBuilder o = treeO.builder();
322 			final DirCacheBuilder t = treeT.builder();
323 
324 			b.add(createEntry("d", FileMode.REGULAR_FILE));
325 
326 			o.add(createEntry("d", FileMode.REGULAR_FILE, "o !"));
327 
328 			t.add(createEntry("d/o", FileMode.REGULAR_FILE));
329 
330 			b.finish();
331 			o.finish();
332 			t.finish();
333 		}
334 
335 		final ObjectInserter ow = db.newObjectInserter();
336 		final ObjectId b = commit(ow, treeB, new ObjectId[] {});
337 		final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
338 		final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
339 
340 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
341 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
342 		assertFalse(merge);
343 	}
344 
345 	@Test
346 	public void testTrivialTwoWay_rightDFconflict2() throws Exception {
347 		final DirCache treeB = db.readDirCache();
348 		final DirCache treeO = db.readDirCache();
349 		final DirCache treeT = db.readDirCache();
350 		{
351 			final DirCacheBuilder b = treeB.builder();
352 			final DirCacheBuilder o = treeO.builder();
353 			final DirCacheBuilder t = treeT.builder();
354 
355 			b.add(createEntry("d", FileMode.REGULAR_FILE));
356 
357 			o.add(createEntry("d/o", FileMode.REGULAR_FILE));
358 
359 			t.add(createEntry("d", FileMode.REGULAR_FILE, "t !"));
360 
361 			b.finish();
362 			o.finish();
363 			t.finish();
364 		}
365 
366 		final ObjectInserter ow = db.newObjectInserter();
367 		final ObjectId b = commit(ow, treeB, new ObjectId[] {});
368 		final ObjectId o = commit(ow, treeO, new ObjectId[] { b });
369 		final ObjectId t = commit(ow, treeT, new ObjectId[] { b });
370 
371 		Merger ourMerger = MergeStrategy.SIMPLE_TWO_WAY_IN_CORE.newMerger(db);
372 		boolean merge = ourMerger.merge(new ObjectId[] { o, t });
373 		assertFalse(merge);
374 	}
375 
376 	private static void assertCorrectId(final DirCache treeT, final TreeWalk tw) {
377 		assertEquals(treeT.getEntry(tw.getPathString()).getObjectId(), tw
378 				.getObjectId(0));
379 	}
380 
381 	private static ObjectId commit(final ObjectInserter odi,
382 			final DirCache treeB,
383 			final ObjectId[] parentIds) throws Exception {
384 		final CommitBuilder c = new CommitBuilder();
385 		c.setTreeId(treeB.writeTree(odi));
386 		c.setAuthor(new PersonIdent("A U Thor", "a.u.thor", 1L, 0));
387 		c.setCommitter(c.getAuthor());
388 		c.setParentIds(parentIds);
389 		c.setMessage("Tree " + c.getTreeId().name());
390 		ObjectId id = odi.insert(c);
391 		odi.flush();
392 		return id;
393 	}
394 }