View Javadoc
1   /*
2    * Copyright (C) 2011, 2013 Dariusz Luksza <dariusz@luksza.org>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  package org.eclipse.jgit.diff;
44  
45  import static org.eclipse.jgit.diff.DiffEntry.DEV_NULL;
46  import static org.eclipse.jgit.util.FileUtils.delete;
47  import static org.hamcrest.CoreMatchers.is;
48  import static org.hamcrest.CoreMatchers.notNullValue;
49  import static org.junit.Assert.assertEquals;
50  import static org.junit.Assert.assertFalse;
51  import static org.junit.Assert.assertThat;
52  import static org.junit.Assert.assertTrue;
53  
54  import java.io.File;
55  import java.util.List;
56  
57  import org.eclipse.jgit.api.Git;
58  import org.eclipse.jgit.diff.DiffEntry.ChangeType;
59  import org.eclipse.jgit.dircache.DirCache;
60  import org.eclipse.jgit.dircache.DirCacheEditor;
61  import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
62  import org.eclipse.jgit.dircache.DirCacheEntry;
63  import org.eclipse.jgit.junit.RepositoryTestCase;
64  import org.eclipse.jgit.lib.FileMode;
65  import org.eclipse.jgit.revwalk.RevCommit;
66  import org.eclipse.jgit.treewalk.EmptyTreeIterator;
67  import org.eclipse.jgit.treewalk.FileTreeIterator;
68  import org.eclipse.jgit.treewalk.TreeWalk;
69  import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
70  import org.eclipse.jgit.treewalk.filter.TreeFilter;
71  import org.eclipse.jgit.util.FileUtils;
72  import org.junit.Test;
73  
74  public class DiffEntryTest extends RepositoryTestCase {
75  
76  	@Test
77  	public void shouldListAddedFileInInitialCommit() throws Exception {
78  		// given
79  		writeTrashFile("a.txt", "content");
80  		try (Git git = new Git(db);
81  				TreeWalk walk = new TreeWalk(db)) {
82  			git.add().addFilepattern("a.txt").call();
83  			RevCommit c = git.commit().setMessage("initial commit").call();
84  
85  			// when
86  			walk.addTree(new EmptyTreeIterator());
87  			walk.addTree(c.getTree());
88  			List<DiffEntry> result = DiffEntry.scan(walk);
89  
90  			// then
91  			assertThat(result, notNullValue());
92  			assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
93  
94  			DiffEntry entry = result.get(0);
95  			assertThat(entry.getChangeType(), is(ChangeType.ADD));
96  			assertThat(entry.getNewPath(), is("a.txt"));
97  			assertThat(entry.getOldPath(), is(DEV_NULL));
98  		}
99  	}
100 
101 	@Test
102 	public void shouldListAddedFileBetweenTwoCommits() throws Exception {
103 		// given
104 		try (Git git = new Git(db);
105 				TreeWalk walk = new TreeWalk(db)) {
106 			RevCommit c1 = git.commit().setMessage("initial commit").call();
107 			writeTrashFile("a.txt", "content");
108 			git.add().addFilepattern("a.txt").call();
109 			RevCommit c2 = git.commit().setMessage("second commit").call();
110 
111 			// when
112 			walk.addTree(c1.getTree());
113 			walk.addTree(c2.getTree());
114 			List<DiffEntry> result = DiffEntry.scan(walk);
115 
116 			// then
117 			assertThat(result, notNullValue());
118 			assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
119 
120 			DiffEntry entry = result.get(0);
121 			assertThat(entry.getChangeType(), is(ChangeType.ADD));
122 			assertThat(entry.getNewPath(), is("a.txt"));
123 			assertThat(entry.getOldPath(), is(DEV_NULL));
124 		}
125 	}
126 
127 	@Test
128 	public void shouldListModificationBetweenTwoCommits() throws Exception {
129 		// given
130 		try (Git git = new Git(db);
131 				TreeWalk walk = new TreeWalk(db)) {
132 			File file = writeTrashFile("a.txt", "content");
133 			git.add().addFilepattern("a.txt").call();
134 			RevCommit c1 = git.commit().setMessage("initial commit").call();
135 			write(file, "new content");
136 			RevCommit c2 = git.commit().setAll(true).setMessage("second commit")
137 					.call();
138 
139 			// when
140 			walk.addTree(c1.getTree());
141 			walk.addTree(c2.getTree());
142 			List<DiffEntry> result = DiffEntry.scan(walk);
143 
144 			// then
145 			assertThat(result, notNullValue());
146 			assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
147 
148 			DiffEntry entry = result.get(0);
149 			assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
150 			assertThat(entry.getNewPath(), is("a.txt"));
151 		}
152 	}
153 
154 	@Test
155 	public void shouldListDeletionBetweenTwoCommits() throws Exception {
156 		// given
157 		try (Git git = new Git(db);
158 				TreeWalk walk = new TreeWalk(db)) {
159 			File file = writeTrashFile("a.txt", "content");
160 			git.add().addFilepattern("a.txt").call();
161 			RevCommit c1 = git.commit().setMessage("initial commit").call();
162 			delete(file);
163 			RevCommit c2 = git.commit().setAll(true).setMessage("delete a.txt")
164 					.call();
165 
166 			// when
167 			walk.addTree(c1.getTree());
168 			walk.addTree(c2.getTree());
169 			List<DiffEntry> result = DiffEntry.scan(walk);
170 
171 			// then
172 			assertThat(result, notNullValue());
173 			assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
174 
175 			DiffEntry entry = result.get(0);
176 			assertThat(entry.getOldPath(), is("a.txt"));
177 			assertThat(entry.getNewPath(), is(DEV_NULL));
178 			assertThat(entry.getChangeType(), is(ChangeType.DELETE));
179 		}
180 	}
181 
182 	@Test
183 	public void shouldListModificationInDirWithoutModifiedTrees()
184 			throws Exception {
185 		// given
186 		try (Git git = new Git(db);
187 				TreeWalk walk = new TreeWalk(db)) {
188 			File tree = new File(new File(db.getWorkTree(), "a"), "b");
189 			FileUtils.mkdirs(tree);
190 			File file = new File(tree, "c.txt");
191 			FileUtils.createNewFile(file);
192 			write(file, "content");
193 			git.add().addFilepattern("a").call();
194 			RevCommit c1 = git.commit().setMessage("initial commit").call();
195 			write(file, "new line");
196 			RevCommit c2 = git.commit().setAll(true).setMessage("second commit")
197 					.call();
198 
199 			// when
200 			walk.addTree(c1.getTree());
201 			walk.addTree(c2.getTree());
202 			walk.setRecursive(true);
203 			List<DiffEntry> result = DiffEntry.scan(walk);
204 
205 			// then
206 			assertThat(result, notNullValue());
207 			assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
208 
209 			DiffEntry entry = result.get(0);
210 			assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
211 			assertThat(entry.getNewPath(), is("a/b/c.txt"));
212 		}
213 	}
214 
215 	@Test
216 	public void shouldListModificationInDirWithModifiedTrees() throws Exception {
217 		// given
218 		try (Git git = new Git(db);
219 				TreeWalk walk = new TreeWalk(db)) {
220 			File tree = new File(new File(db.getWorkTree(), "a"), "b");
221 			FileUtils.mkdirs(tree);
222 			File file = new File(tree, "c.txt");
223 			FileUtils.createNewFile(file);
224 			write(file, "content");
225 			git.add().addFilepattern("a").call();
226 			RevCommit c1 = git.commit().setMessage("initial commit").call();
227 			write(file, "new line");
228 			RevCommit c2 = git.commit().setAll(true).setMessage("second commit")
229 					.call();
230 
231 			// when
232 			walk.addTree(c1.getTree());
233 			walk.addTree(c2.getTree());
234 			List<DiffEntry> result = DiffEntry.scan(walk, true);
235 
236 			// then
237 			assertThat(result, notNullValue());
238 			assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(3)));
239 
240 			DiffEntry entry = result.get(0);
241 			assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
242 			assertThat(entry.getNewPath(), is("a"));
243 
244 			entry = result.get(1);
245 			assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
246 			assertThat(entry.getNewPath(), is("a/b"));
247 
248 			entry = result.get(2);
249 			assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
250 			assertThat(entry.getNewPath(), is("a/b/c.txt"));
251 		}
252 	}
253 
254 	@Test
255 	public void shouldListChangesInWorkingTree() throws Exception {
256 		// given
257 		writeTrashFile("a.txt", "content");
258 		try (Git git = new Git(db);
259 				TreeWalk walk = new TreeWalk(db)) {
260 			git.add().addFilepattern("a.txt").call();
261 			RevCommit c = git.commit().setMessage("initial commit").call();
262 			writeTrashFile("b.txt", "new line");
263 
264 			// when
265 			walk.addTree(c.getTree());
266 			walk.addTree(new FileTreeIterator(db));
267 			List<DiffEntry> result = DiffEntry.scan(walk, true);
268 
269 			// then
270 			assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
271 			DiffEntry entry = result.get(0);
272 
273 			assertThat(entry.getChangeType(), is(ChangeType.ADD));
274 			assertThat(entry.getNewPath(), is("b.txt"));
275 		}
276 	}
277 
278 	@Test
279 	public void shouldMarkEntriesWhenGivenMarkTreeFilter() throws Exception {
280 		// given
281 		try (Git git = new Git(db);
282 				TreeWalk walk = new TreeWalk(db)) {
283 			RevCommit c1 = git.commit().setMessage("initial commit").call();
284 			FileUtils.mkdir(new File(db.getWorkTree(), "b"));
285 			writeTrashFile("a.txt", "a");
286 			writeTrashFile("b/1.txt", "b1");
287 			writeTrashFile("b/2.txt", "b2");
288 			writeTrashFile("c.txt", "c");
289 			git.add().addFilepattern("a.txt").addFilepattern("b")
290 					.addFilepattern("c.txt").call();
291 			RevCommit c2 = git.commit().setMessage("second commit").call();
292 			TreeFilter filterA = PathFilterGroup.createFromStrings("a.txt");
293 			TreeFilter filterB = PathFilterGroup.createFromStrings("b");
294 			TreeFilter filterB2 = PathFilterGroup.createFromStrings("b/2.txt");
295 
296 			// when
297 			walk.addTree(c1.getTree());
298 			walk.addTree(c2.getTree());
299 			List<DiffEntry> result = DiffEntry.scan(walk, true, new TreeFilter[] {
300 					filterA, filterB, filterB2 });
301 
302 			// then
303 			assertThat(result, notNullValue());
304 			assertEquals(5, result.size());
305 
306 			DiffEntry entryA = result.get(0);
307 			DiffEntry entryB = result.get(1);
308 			DiffEntry entryB1 = result.get(2);
309 			DiffEntry entryB2 = result.get(3);
310 			DiffEntry entryC = result.get(4);
311 
312 			assertThat(entryA.getNewPath(), is("a.txt"));
313 			assertTrue(entryA.isMarked(0));
314 			assertFalse(entryA.isMarked(1));
315 			assertFalse(entryA.isMarked(2));
316 			assertEquals(1, entryA.getTreeFilterMarks());
317 
318 			assertThat(entryB.getNewPath(), is("b"));
319 			assertFalse(entryB.isMarked(0));
320 			assertTrue(entryB.isMarked(1));
321 			assertTrue(entryB.isMarked(2));
322 			assertEquals(6, entryB.getTreeFilterMarks());
323 
324 			assertThat(entryB1.getNewPath(), is("b/1.txt"));
325 			assertFalse(entryB1.isMarked(0));
326 			assertTrue(entryB1.isMarked(1));
327 			assertFalse(entryB1.isMarked(2));
328 			assertEquals(2, entryB1.getTreeFilterMarks());
329 
330 			assertThat(entryB2.getNewPath(), is("b/2.txt"));
331 			assertFalse(entryB2.isMarked(0));
332 			assertTrue(entryB2.isMarked(1));
333 			assertTrue(entryB2.isMarked(2));
334 			assertEquals(6, entryB2.getTreeFilterMarks());
335 
336 			assertThat(entryC.getNewPath(), is("c.txt"));
337 			assertFalse(entryC.isMarked(0));
338 			assertFalse(entryC.isMarked(1));
339 			assertFalse(entryC.isMarked(2));
340 			assertEquals(0, entryC.getTreeFilterMarks());
341 		}
342 	}
343 
344 	@Test(expected = IllegalArgumentException.class)
345 	public void shouldThrowIAEWhenTreeWalkHasLessThanTwoTrees()
346 			throws Exception {
347 		// given - we don't need anything here
348 
349 		// when
350 		try (TreeWalk walk = new TreeWalk(db)) {
351 			walk.addTree(new EmptyTreeIterator());
352 			DiffEntry.scan(walk);
353 		}
354 	}
355 
356 	@Test(expected = IllegalArgumentException.class)
357 	public void shouldThrowIAEWhenTreeWalkHasMoreThanTwoTrees()
358 			throws Exception {
359 		// given - we don't need anything here
360 
361 		// when
362 		try (TreeWalk walk = new TreeWalk(db)) {
363 			walk.addTree(new EmptyTreeIterator());
364 			walk.addTree(new EmptyTreeIterator());
365 			walk.addTree(new EmptyTreeIterator());
366 			DiffEntry.scan(walk);
367 		}
368 	}
369 
370 	@Test(expected = IllegalArgumentException.class)
371 	public void shouldThrowIAEWhenScanShouldIncludeTreesAndWalkIsRecursive()
372 			throws Exception {
373 		// given - we don't need anything here
374 
375 		// when
376 		try (TreeWalk walk = new TreeWalk(db)) {
377 			walk.addTree(new EmptyTreeIterator());
378 			walk.addTree(new EmptyTreeIterator());
379 			walk.setRecursive(true);
380 			DiffEntry.scan(walk, true);
381 		}
382 	}
383 
384 	@Test
385 	public void shouldReportFileModeChange() throws Exception {
386 		writeTrashFile("a.txt", "content");
387 		try (Git git = new Git(db);
388 				TreeWalk walk = new TreeWalk(db)) {
389 			git.add().addFilepattern("a.txt").call();
390 			RevCommit c1 = git.commit().setMessage("initial commit").call();
391 			DirCache cache = db.lockDirCache();
392 			DirCacheEditor editor = cache.editor();
393 			walk.addTree(c1.getTree());
394 			walk.setRecursive(true);
395 			assertTrue(walk.next());
396 
397 			editor.add(new PathEdit("a.txt") {
398 				@Override
399 				public void apply(DirCacheEntry ent) {
400 					ent.setFileMode(FileMode.EXECUTABLE_FILE);
401 					ent.setObjectId(walk.getObjectId(0));
402 				}
403 			});
404 			assertTrue(editor.commit());
405 			RevCommit c2 = git.commit().setMessage("second commit").call();
406 			walk.reset();
407 			walk.addTree(c1.getTree());
408 			walk.addTree(c2.getTree());
409 			List<DiffEntry> diffs = DiffEntry.scan(walk, false);
410 			assertEquals(1, diffs.size());
411 			DiffEntry diff = diffs.get(0);
412 			assertEquals(ChangeType.MODIFY,diff.getChangeType());
413 			assertEquals(diff.getOldId(), diff.getNewId());
414 			assertEquals("a.txt", diff.getOldPath());
415 			assertEquals(diff.getOldPath(), diff.getNewPath());
416 			assertEquals(FileMode.EXECUTABLE_FILE, diff.getNewMode());
417 			assertEquals(FileMode.REGULAR_FILE, diff.getOldMode());
418 		}
419 	}
420 }