1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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.internal.storage.file.FileRepository;
63 import org.eclipse.jgit.dircache.DirCacheEntry;
64 import org.eclipse.jgit.junit.JGitTestUtil;
65 import org.eclipse.jgit.junit.RepositoryTestCase;
66 import org.eclipse.jgit.lib.FileMode;
67 import org.eclipse.jgit.lib.Repository;
68 import org.eclipse.jgit.revwalk.RevCommit;
69 import org.eclipse.jgit.treewalk.EmptyTreeIterator;
70 import org.eclipse.jgit.treewalk.FileTreeIterator;
71 import org.eclipse.jgit.treewalk.TreeWalk;
72 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
73 import org.eclipse.jgit.treewalk.filter.TreeFilter;
74 import org.eclipse.jgit.util.FileUtils;
75 import org.junit.Test;
76
77 public class DiffEntryTest extends RepositoryTestCase {
78
79 @Test
80 public void shouldListAddedFileInInitialCommit() throws Exception {
81
82 writeTrashFile("a.txt", "content");
83 try (Git git = new Git(db);
84 TreeWalk walk = new TreeWalk(db)) {
85 git.add().addFilepattern("a.txt").call();
86 RevCommit c = git.commit().setMessage("initial commit").call();
87
88
89 walk.addTree(new EmptyTreeIterator());
90 walk.addTree(c.getTree());
91 List<DiffEntry> result = DiffEntry.scan(walk);
92
93
94 assertThat(result, notNullValue());
95 assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
96
97 DiffEntry entry = result.get(0);
98 assertThat(entry.getChangeType(), is(ChangeType.ADD));
99 assertThat(entry.getNewPath(), is("a.txt"));
100 assertThat(entry.getOldPath(), is(DEV_NULL));
101 }
102 }
103
104 @Test
105 public void shouldListAddedFileBetweenTwoCommits() throws Exception {
106
107 try (Git git = new Git(db);
108 TreeWalk walk = new TreeWalk(db)) {
109 RevCommit c1 = git.commit().setMessage("initial commit").call();
110 writeTrashFile("a.txt", "content");
111 git.add().addFilepattern("a.txt").call();
112 RevCommit c2 = git.commit().setMessage("second commit").call();
113
114
115 walk.addTree(c1.getTree());
116 walk.addTree(c2.getTree());
117 List<DiffEntry> result = DiffEntry.scan(walk);
118
119
120 assertThat(result, notNullValue());
121 assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
122
123 DiffEntry entry = result.get(0);
124 assertThat(entry.getChangeType(), is(ChangeType.ADD));
125 assertThat(entry.getNewPath(), is("a.txt"));
126 assertThat(entry.getOldPath(), is(DEV_NULL));
127 }
128 }
129
130 @Test
131 public void shouldListModificationBetweenTwoCommits() throws Exception {
132
133 try (Git git = new Git(db);
134 TreeWalk walk = new TreeWalk(db)) {
135 File file = writeTrashFile("a.txt", "content");
136 git.add().addFilepattern("a.txt").call();
137 RevCommit c1 = git.commit().setMessage("initial commit").call();
138 write(file, "new content");
139 RevCommit c2 = git.commit().setAll(true).setMessage("second commit")
140 .call();
141
142
143 walk.addTree(c1.getTree());
144 walk.addTree(c2.getTree());
145 List<DiffEntry> result = DiffEntry.scan(walk);
146
147
148 assertThat(result, notNullValue());
149 assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
150
151 DiffEntry entry = result.get(0);
152 assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
153 assertThat(entry.getNewPath(), is("a.txt"));
154 }
155 }
156
157 @Test
158 public void shouldListDeletionBetweenTwoCommits() throws Exception {
159
160 try (Git git = new Git(db);
161 TreeWalk walk = new TreeWalk(db)) {
162 File file = writeTrashFile("a.txt", "content");
163 git.add().addFilepattern("a.txt").call();
164 RevCommit c1 = git.commit().setMessage("initial commit").call();
165 delete(file);
166 RevCommit c2 = git.commit().setAll(true).setMessage("delete a.txt")
167 .call();
168
169
170 walk.addTree(c1.getTree());
171 walk.addTree(c2.getTree());
172 List<DiffEntry> result = DiffEntry.scan(walk);
173
174
175 assertThat(result, notNullValue());
176 assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
177
178 DiffEntry entry = result.get(0);
179 assertThat(entry.getOldPath(), is("a.txt"));
180 assertThat(entry.getNewPath(), is(DEV_NULL));
181 assertThat(entry.getChangeType(), is(ChangeType.DELETE));
182 }
183 }
184
185 @Test
186 public void shouldListModificationInDirWithoutModifiedTrees()
187 throws Exception {
188
189 try (Git git = new Git(db);
190 TreeWalk walk = new TreeWalk(db)) {
191 File tree = new File(new File(db.getWorkTree(), "a"), "b");
192 FileUtils.mkdirs(tree);
193 File file = new File(tree, "c.txt");
194 FileUtils.createNewFile(file);
195 write(file, "content");
196 git.add().addFilepattern("a").call();
197 RevCommit c1 = git.commit().setMessage("initial commit").call();
198 write(file, "new line");
199 RevCommit c2 = git.commit().setAll(true).setMessage("second commit")
200 .call();
201
202
203 walk.addTree(c1.getTree());
204 walk.addTree(c2.getTree());
205 walk.setRecursive(true);
206 List<DiffEntry> result = DiffEntry.scan(walk);
207
208
209 assertThat(result, notNullValue());
210 assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
211
212 DiffEntry entry = result.get(0);
213 assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
214 assertThat(entry.getNewPath(), is("a/b/c.txt"));
215 }
216 }
217
218 @Test
219 public void shouldListModificationInDirWithModifiedTrees() throws Exception {
220
221 try (Git git = new Git(db);
222 TreeWalk walk = new TreeWalk(db)) {
223 File tree = new File(new File(db.getWorkTree(), "a"), "b");
224 FileUtils.mkdirs(tree);
225 File file = new File(tree, "c.txt");
226 FileUtils.createNewFile(file);
227 write(file, "content");
228 git.add().addFilepattern("a").call();
229 RevCommit c1 = git.commit().setMessage("initial commit").call();
230 write(file, "new line");
231 RevCommit c2 = git.commit().setAll(true).setMessage("second commit")
232 .call();
233
234
235 walk.addTree(c1.getTree());
236 walk.addTree(c2.getTree());
237 List<DiffEntry> result = DiffEntry.scan(walk, true);
238
239
240 assertThat(result, notNullValue());
241 assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(3)));
242
243 DiffEntry entry = result.get(0);
244 assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
245 assertThat(entry.getNewPath(), is("a"));
246
247 entry = result.get(1);
248 assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
249 assertThat(entry.getNewPath(), is("a/b"));
250
251 entry = result.get(2);
252 assertThat(entry.getChangeType(), is(ChangeType.MODIFY));
253 assertThat(entry.getNewPath(), is("a/b/c.txt"));
254 }
255 }
256
257 @Test
258 public void shouldListChangesInWorkingTree() throws Exception {
259
260 writeTrashFile("a.txt", "content");
261 try (Git git = new Git(db);
262 TreeWalk walk = new TreeWalk(db)) {
263 git.add().addFilepattern("a.txt").call();
264 RevCommit c = git.commit().setMessage("initial commit").call();
265 writeTrashFile("b.txt", "new line");
266
267
268 walk.addTree(c.getTree());
269 walk.addTree(new FileTreeIterator(db));
270 List<DiffEntry> result = DiffEntry.scan(walk, true);
271
272
273 assertThat(Integer.valueOf(result.size()), is(Integer.valueOf(1)));
274 DiffEntry entry = result.get(0);
275
276 assertThat(entry.getChangeType(), is(ChangeType.ADD));
277 assertThat(entry.getNewPath(), is("b.txt"));
278 }
279 }
280
281 @Test
282 public void shouldMarkEntriesWhenGivenMarkTreeFilter() throws Exception {
283
284 try (Git git = new Git(db);
285 TreeWalk walk = new TreeWalk(db)) {
286 RevCommit c1 = git.commit().setMessage("initial commit").call();
287 FileUtils.mkdir(new File(db.getWorkTree(), "b"));
288 writeTrashFile("a.txt", "a");
289 writeTrashFile("b/1.txt", "b1");
290 writeTrashFile("b/2.txt", "b2");
291 writeTrashFile("c.txt", "c");
292 git.add().addFilepattern("a.txt").addFilepattern("b")
293 .addFilepattern("c.txt").call();
294 RevCommit c2 = git.commit().setMessage("second commit").call();
295 TreeFilter filterA = PathFilterGroup.createFromStrings("a.txt");
296 TreeFilter filterB = PathFilterGroup.createFromStrings("b");
297 TreeFilter filterB2 = PathFilterGroup.createFromStrings("b/2.txt");
298
299
300 walk.addTree(c1.getTree());
301 walk.addTree(c2.getTree());
302 List<DiffEntry> result = DiffEntry.scan(walk, true, new TreeFilter[] {
303 filterA, filterB, filterB2 });
304
305
306 assertThat(result, notNullValue());
307 assertEquals(5, result.size());
308
309 DiffEntry entryA = result.get(0);
310 DiffEntry entryB = result.get(1);
311 DiffEntry entryB1 = result.get(2);
312 DiffEntry entryB2 = result.get(3);
313 DiffEntry entryC = result.get(4);
314
315 assertThat(entryA.getNewPath(), is("a.txt"));
316 assertTrue(entryA.isMarked(0));
317 assertFalse(entryA.isMarked(1));
318 assertFalse(entryA.isMarked(2));
319 assertEquals(1, entryA.getTreeFilterMarks());
320
321 assertThat(entryB.getNewPath(), is("b"));
322 assertFalse(entryB.isMarked(0));
323 assertTrue(entryB.isMarked(1));
324 assertTrue(entryB.isMarked(2));
325 assertEquals(6, entryB.getTreeFilterMarks());
326
327 assertThat(entryB1.getNewPath(), is("b/1.txt"));
328 assertFalse(entryB1.isMarked(0));
329 assertTrue(entryB1.isMarked(1));
330 assertFalse(entryB1.isMarked(2));
331 assertEquals(2, entryB1.getTreeFilterMarks());
332
333 assertThat(entryB2.getNewPath(), is("b/2.txt"));
334 assertFalse(entryB2.isMarked(0));
335 assertTrue(entryB2.isMarked(1));
336 assertTrue(entryB2.isMarked(2));
337 assertEquals(6, entryB2.getTreeFilterMarks());
338
339 assertThat(entryC.getNewPath(), is("c.txt"));
340 assertFalse(entryC.isMarked(0));
341 assertFalse(entryC.isMarked(1));
342 assertFalse(entryC.isMarked(2));
343 assertEquals(0, entryC.getTreeFilterMarks());
344 }
345 }
346
347 @Test(expected = IllegalArgumentException.class)
348 public void shouldThrowIAEWhenTreeWalkHasLessThanTwoTrees()
349 throws Exception {
350
351
352
353 try (TreeWalk walk = new TreeWalk(db)) {
354 walk.addTree(new EmptyTreeIterator());
355 DiffEntry.scan(walk);
356 }
357 }
358
359 @Test(expected = IllegalArgumentException.class)
360 public void shouldThrowIAEWhenTreeWalkHasMoreThanTwoTrees()
361 throws Exception {
362
363
364
365 try (TreeWalk walk = new TreeWalk(db)) {
366 walk.addTree(new EmptyTreeIterator());
367 walk.addTree(new EmptyTreeIterator());
368 walk.addTree(new EmptyTreeIterator());
369 DiffEntry.scan(walk);
370 }
371 }
372
373 @Test(expected = IllegalArgumentException.class)
374 public void shouldThrowIAEWhenScanShouldIncludeTreesAndWalkIsRecursive()
375 throws Exception {
376
377
378
379 try (TreeWalk walk = new TreeWalk(db)) {
380 walk.addTree(new EmptyTreeIterator());
381 walk.addTree(new EmptyTreeIterator());
382 walk.setRecursive(true);
383 DiffEntry.scan(walk, true);
384 }
385 }
386
387 @Test
388 public void shouldReportFileModeChange() throws Exception {
389 writeTrashFile("a.txt", "content");
390 try (Git git = new Git(db);
391 TreeWalk walk = new TreeWalk(db)) {
392 git.add().addFilepattern("a.txt").call();
393 RevCommit c1 = git.commit().setMessage("initial commit").call();
394 DirCache cache = db.lockDirCache();
395 DirCacheEditor editor = cache.editor();
396 walk.addTree(c1.getTree());
397 walk.setRecursive(true);
398 assertTrue(walk.next());
399
400 editor.add(new PathEdit("a.txt") {
401 @Override
402 public void apply(DirCacheEntry ent) {
403 ent.setFileMode(FileMode.EXECUTABLE_FILE);
404 ent.setObjectId(walk.getObjectId(0));
405 }
406 });
407 assertTrue(editor.commit());
408 RevCommit c2 = git.commit().setMessage("second commit").call();
409 walk.reset();
410 walk.addTree(c1.getTree());
411 walk.addTree(c2.getTree());
412 List<DiffEntry> diffs = DiffEntry.scan(walk, false);
413 assertEquals(1, diffs.size());
414 DiffEntry diff = diffs.get(0);
415 assertEquals(ChangeType.MODIFY,diff.getChangeType());
416 assertEquals(diff.getOldId(), diff.getNewId());
417 assertEquals("a.txt", diff.getOldPath());
418 assertEquals(diff.getOldPath(), diff.getNewPath());
419 assertEquals(FileMode.EXECUTABLE_FILE, diff.getNewMode());
420 assertEquals(FileMode.REGULAR_FILE, diff.getOldMode());
421 }
422 }
423
424 @Test
425 public void shouldReportSubmoduleReplacedByFileMove() throws Exception {
426
427 FileRepository submoduleStandalone = createWorkRepository();
428 JGitTestUtil.writeTrashFile(submoduleStandalone, "fileInSubmodule",
429 "submodule");
430 Git submoduleStandaloneGit = Git.wrap(submoduleStandalone);
431 submoduleStandaloneGit.add().addFilepattern("fileInSubmodule").call();
432 submoduleStandaloneGit.commit().setMessage("add file to submodule")
433 .call();
434
435 Repository submodule_db = Git.wrap(db).submoduleAdd()
436 .setPath("modules/submodule")
437 .setURI(submoduleStandalone.getDirectory().toURI().toString())
438 .call();
439 File submodule_trash = submodule_db.getWorkTree();
440 addRepoToClose(submodule_db);
441 writeTrashFile("fileInRoot", "root");
442 Git rootGit = Git.wrap(db);
443 rootGit.add().addFilepattern("fileInRoot").call();
444 rootGit.commit().setMessage("add submodule and root file").call();
445
446 writeTrashFile("fileInRoot", "changed");
447 rootGit.add().addFilepattern("fileInRoot").call();
448 RevCommit firstCommit = rootGit.commit().setMessage("change root file")
449 .call();
450
451 rootGit.rm().setCached(true).addFilepattern("modules/submodule").call();
452 recursiveDelete(submodule_trash);
453 JGitTestUtil.deleteTrashFile(db, "fileInRoot");
454
455 writeTrashFile("modules/submodule/fileInRoot", "changed");
456 rootGit.rm().addFilepattern("fileInRoot").addFilepattern("modules/")
457 .call();
458 rootGit.add().addFilepattern("modules/").call();
459 RevCommit secondCommit = rootGit.commit()
460 .setMessage("remove submodule and move root file")
461 .call();
462
463
464 try (TreeWalk walk = new TreeWalk(db)) {
465 walk.addTree(firstCommit.getTree());
466 walk.addTree(secondCommit.getTree());
467 walk.setRecursive(true);
468 List<DiffEntry> diffs = DiffEntry.scan(walk);
469 assertEquals(3, diffs.size());
470 DiffEntry e = diffs.get(0);
471 assertEquals(DiffEntry.ChangeType.DELETE, e.getChangeType());
472 assertEquals("fileInRoot", e.getOldPath());
473 e = diffs.get(1);
474 assertEquals(DiffEntry.ChangeType.DELETE, e.getChangeType());
475 assertEquals("modules/submodule", e.getOldPath());
476 assertEquals(FileMode.GITLINK, e.getOldMode());
477 e = diffs.get(2);
478 assertEquals(DiffEntry.ChangeType.ADD, e.getChangeType());
479 assertEquals("modules/submodule/fileInRoot", e.getNewPath());
480 }
481
482 }
483 }