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
44 package org.eclipse.jgit.diff;
45
46 import static org.junit.Assert.assertEquals;
47
48 import java.io.ByteArrayOutputStream;
49 import java.io.File;
50
51 import org.eclipse.jgit.api.Git;
52 import org.eclipse.jgit.diff.DiffEntry.ChangeType;
53 import org.eclipse.jgit.dircache.DirCacheIterator;
54 import org.eclipse.jgit.junit.RepositoryTestCase;
55 import org.eclipse.jgit.junit.TestRepository;
56 import org.eclipse.jgit.lib.AnyObjectId;
57 import org.eclipse.jgit.lib.FileMode;
58 import org.eclipse.jgit.lib.ObjectId;
59 import org.eclipse.jgit.lib.Repository;
60 import org.eclipse.jgit.patch.FileHeader;
61 import org.eclipse.jgit.patch.HunkHeader;
62 import org.eclipse.jgit.revwalk.RevCommit;
63 import org.eclipse.jgit.treewalk.FileTreeIterator;
64 import org.eclipse.jgit.treewalk.filter.PathFilter;
65 import org.eclipse.jgit.util.FileUtils;
66 import org.eclipse.jgit.util.RawParseUtils;
67 import org.eclipse.jgit.util.io.DisabledOutputStream;
68 import org.eclipse.jgit.util.io.SafeBufferedOutputStream;
69 import org.junit.After;
70 import org.junit.Before;
71 import org.junit.Test;
72
73 public class DiffFormatterTest extends RepositoryTestCase {
74 private static final String DIFF = "diff --git ";
75
76 private static final String REGULAR_FILE = "100644";
77
78 private static final String GITLINK = "160000";
79
80 private static final String PATH_A = "src/a";
81
82 private static final String PATH_B = "src/b";
83
84 private DiffFormatter df;
85
86 private TestRepository<Repository> testDb;
87
88 @Override
89 @Before
90 public void setUp() throws Exception {
91 super.setUp();
92 testDb = new TestRepository<Repository>(db);
93 df = new DiffFormatter(DisabledOutputStream.INSTANCE);
94 df.setRepository(db);
95 df.setAbbreviationLength(8);
96 }
97
98 @Override
99 @After
100 public void tearDown() throws Exception {
101 if (df != null) {
102 df.close();
103 }
104 super.tearDown();
105 }
106
107 @Test
108 public void testCreateFileHeader_Add() throws Exception {
109 ObjectId adId = blob("a\nd\n");
110 DiffEntry ent = DiffEntry.add("FOO", adId);
111 FileHeader fh = df.toFileHeader(ent);
112
113 String diffHeader = "diff --git a/FOO b/FOO\n"
114 + "new file mode " + REGULAR_FILE + "\n"
115 + "index "
116 + ObjectId.zeroId().abbreviate(8).name()
117 + ".."
118 + adId.abbreviate(8).name() + "\n"
119 + "--- /dev/null\n"
120 + "+++ b/FOO\n";
121 assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer()));
122
123 assertEquals(0, fh.getStartOffset());
124 assertEquals(fh.getBuffer().length, fh.getEndOffset());
125 assertEquals(FileHeader.PatchType.UNIFIED, fh.getPatchType());
126
127 assertEquals(1, fh.getHunks().size());
128
129 HunkHeader hh = fh.getHunks().get(0);
130 assertEquals(1, hh.toEditList().size());
131
132 EditList el = hh.toEditList();
133 assertEquals(1, el.size());
134
135 Edit e = el.get(0);
136 assertEquals(0, e.getBeginA());
137 assertEquals(0, e.getEndA());
138 assertEquals(0, e.getBeginB());
139 assertEquals(2, e.getEndB());
140 assertEquals(Edit.Type.INSERT, e.getType());
141 }
142
143 @Test
144 public void testCreateFileHeader_Delete() throws Exception {
145 ObjectId adId = blob("a\nd\n");
146 DiffEntry ent = DiffEntry.delete("FOO", adId);
147 FileHeader fh = df.toFileHeader(ent);
148
149 String diffHeader = "diff --git a/FOO b/FOO\n"
150 + "deleted file mode " + REGULAR_FILE + "\n"
151 + "index "
152 + adId.abbreviate(8).name()
153 + ".."
154 + ObjectId.zeroId().abbreviate(8).name() + "\n"
155 + "--- a/FOO\n"
156 + "+++ /dev/null\n";
157 assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer()));
158
159 assertEquals(0, fh.getStartOffset());
160 assertEquals(fh.getBuffer().length, fh.getEndOffset());
161 assertEquals(FileHeader.PatchType.UNIFIED, fh.getPatchType());
162
163 assertEquals(1, fh.getHunks().size());
164
165 HunkHeader hh = fh.getHunks().get(0);
166 assertEquals(1, hh.toEditList().size());
167
168 EditList el = hh.toEditList();
169 assertEquals(1, el.size());
170
171 Edit e = el.get(0);
172 assertEquals(0, e.getBeginA());
173 assertEquals(2, e.getEndA());
174 assertEquals(0, e.getBeginB());
175 assertEquals(0, e.getEndB());
176 assertEquals(Edit.Type.DELETE, e.getType());
177 }
178
179 @Test
180 public void testCreateFileHeader_Modify() throws Exception {
181 ObjectId adId = blob("a\nd\n");
182 ObjectId abcdId = blob("a\nb\nc\nd\n");
183
184 String diffHeader = makeDiffHeader(PATH_A, PATH_A, adId, abcdId);
185
186 DiffEntry ad = DiffEntry.delete(PATH_A, adId);
187 DiffEntry abcd = DiffEntry.add(PATH_A, abcdId);
188
189 DiffEntry mod = DiffEntry.pair(ChangeType.MODIFY, ad, abcd, 0);
190
191 FileHeader fh = df.toFileHeader(mod);
192
193 assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer()));
194 assertEquals(0, fh.getStartOffset());
195 assertEquals(fh.getBuffer().length, fh.getEndOffset());
196 assertEquals(FileHeader.PatchType.UNIFIED, fh.getPatchType());
197
198 assertEquals(1, fh.getHunks().size());
199
200 HunkHeader hh = fh.getHunks().get(0);
201 assertEquals(1, hh.toEditList().size());
202
203 EditList el = hh.toEditList();
204 assertEquals(1, el.size());
205
206 Edit e = el.get(0);
207 assertEquals(1, e.getBeginA());
208 assertEquals(1, e.getEndA());
209 assertEquals(1, e.getBeginB());
210 assertEquals(3, e.getEndB());
211 assertEquals(Edit.Type.INSERT, e.getType());
212 }
213
214 @Test
215 public void testCreateFileHeader_Binary() throws Exception {
216 ObjectId adId = blob("a\nd\n");
217 ObjectId binId = blob("a\nb\nc\n\0\0\0\0d\n");
218
219 String diffHeader = makeDiffHeader(PATH_A, PATH_B, adId, binId)
220 + "Binary files differ\n";
221
222 DiffEntry ad = DiffEntry.delete(PATH_A, adId);
223 DiffEntry abcd = DiffEntry.add(PATH_B, binId);
224
225 DiffEntry mod = DiffEntry.pair(ChangeType.MODIFY, ad, abcd, 0);
226
227 FileHeader fh = df.toFileHeader(mod);
228
229 assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer()));
230 assertEquals(FileHeader.PatchType.BINARY, fh.getPatchType());
231
232 assertEquals(1, fh.getHunks().size());
233
234 HunkHeader hh = fh.getHunks().get(0);
235 assertEquals(0, hh.toEditList().size());
236 }
237
238 @Test
239 public void testCreateFileHeader_GitLink() throws Exception {
240 ObjectId aId = blob("a\n");
241 ObjectId bId = blob("b\n");
242
243 String diffHeader = makeDiffHeaderModeChange(PATH_A, PATH_A, aId, bId,
244 GITLINK, REGULAR_FILE)
245 + "-Subproject commit " + aId.name() + "\n";
246
247 DiffEntry ad = DiffEntry.delete(PATH_A, aId);
248 ad.oldMode = FileMode.GITLINK;
249 DiffEntry abcd = DiffEntry.add(PATH_A, bId);
250
251 DiffEntry mod = DiffEntry.pair(ChangeType.MODIFY, ad, abcd, 0);
252
253 FileHeader fh = df.toFileHeader(mod);
254
255 assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer()));
256
257 assertEquals(1, fh.getHunks().size());
258
259 HunkHeader hh = fh.getHunks().get(0);
260 assertEquals(0, hh.toEditList().size());
261 }
262
263 @Test
264 public void testCreateFileHeaderWithoutIndexLine() throws Exception {
265 DiffEntry m = DiffEntry.modify(PATH_A);
266 m.oldMode = FileMode.REGULAR_FILE;
267 m.newMode = FileMode.EXECUTABLE_FILE;
268
269 FileHeader fh = df.toFileHeader(m);
270 String expected = DIFF + "a/src/a b/src/a\n" +
271 "old mode 100644\n" +
272 "new mode 100755\n";
273 assertEquals(expected, fh.getScriptText());
274 }
275
276 @Test
277 public void testCreateFileHeaderForRenameWithoutContentChange() throws Exception {
278 DiffEntry a = DiffEntry.delete(PATH_A, ObjectId.zeroId());
279 DiffEntry b = DiffEntry.add(PATH_B, ObjectId.zeroId());
280 DiffEntry m = DiffEntry.pair(ChangeType.RENAME, a, b, 100);
281 m.oldId = null;
282 m.newId = null;
283
284 FileHeader fh = df.toFileHeader(m);
285 String expected = DIFF + "a/src/a b/src/b\n" +
286 "similarity index 100%\n" +
287 "rename from src/a\n" +
288 "rename to src/b\n";
289 assertEquals(expected, fh.getScriptText());
290 }
291
292 @Test
293 public void testCreateFileHeaderForRenameModeChange()
294 throws Exception {
295 DiffEntry a = DiffEntry.delete(PATH_A, ObjectId.zeroId());
296 DiffEntry b = DiffEntry.add(PATH_B, ObjectId.zeroId());
297 b.oldMode = FileMode.REGULAR_FILE;
298 b.newMode = FileMode.EXECUTABLE_FILE;
299 DiffEntry m = DiffEntry.pair(ChangeType.RENAME, a, b, 100);
300 m.oldId = null;
301 m.newId = null;
302
303 FileHeader fh = df.toFileHeader(m);
304
305 String expected = DIFF + "a/src/a b/src/b\n" +
306 "old mode 100644\n" +
307 "new mode 100755\n" +
308 "similarity index 100%\n" +
309 "rename from src/a\n" +
310 "rename to src/b\n";
311
312 assertEquals(expected, fh.getScriptText());
313 }
314
315 @Test
316 public void testDiff() throws Exception {
317 write(new File(db.getDirectory().getParent(), "test.txt"), "test");
318 File folder = new File(db.getDirectory().getParent(), "folder");
319 FileUtils.mkdir(folder);
320 write(new File(folder, "folder.txt"), "folder");
321 Git git = new Git(db);
322 git.add().addFilepattern(".").call();
323 git.commit().setMessage("Initial commit").call();
324 write(new File(folder, "folder.txt"), "folder change");
325
326 ByteArrayOutputStream os = new ByteArrayOutputStream();
327 DiffFormatter dfmt = new DiffFormatter(new SafeBufferedOutputStream(os));
328 dfmt.setRepository(db);
329 dfmt.setPathFilter(PathFilter.create("folder"));
330 DirCacheIterator oldTree = new DirCacheIterator(db.readDirCache());
331 FileTreeIterator newTree = new FileTreeIterator(db);
332 dfmt.format(oldTree, newTree);
333 dfmt.flush();
334
335 String actual = os.toString("UTF-8");
336 String expected =
337 "diff --git a/folder/folder.txt b/folder/folder.txt\n"
338 + "index 0119635..95c4c65 100644\n"
339 + "--- a/folder/folder.txt\n" + "+++ b/folder/folder.txt\n"
340 + "@@ -1 +1 @@\n" + "-folder\n"
341 + "\\ No newline at end of file\n" + "+folder change\n"
342 + "\\ No newline at end of file\n";
343
344 assertEquals(expected, actual);
345 }
346
347 @Test
348 public void testDiffRootNullToTree() throws Exception {
349 write(new File(db.getDirectory().getParent(), "test.txt"), "test");
350 File folder = new File(db.getDirectory().getParent(), "folder");
351 FileUtils.mkdir(folder);
352 write(new File(folder, "folder.txt"), "folder");
353 Git git = new Git(db);
354 git.add().addFilepattern(".").call();
355 RevCommit commit = git.commit().setMessage("Initial commit").call();
356 write(new File(folder, "folder.txt"), "folder change");
357
358 ByteArrayOutputStream os = new ByteArrayOutputStream();
359 DiffFormatter dfmt = new DiffFormatter(new SafeBufferedOutputStream(os));
360 dfmt.setRepository(db);
361 dfmt.setPathFilter(PathFilter.create("folder"));
362 dfmt.format(null, commit.getTree().getId());
363 dfmt.flush();
364
365 String actual = os.toString("UTF-8");
366 String expected = "diff --git a/folder/folder.txt b/folder/folder.txt\n"
367 + "new file mode 100644\n"
368 + "index 0000000..0119635\n"
369 + "--- /dev/null\n"
370 + "+++ b/folder/folder.txt\n"
371 + "@@ -0,0 +1 @@\n"
372 + "+folder\n"
373 + "\\ No newline at end of file\n";
374
375 assertEquals(expected, actual);
376 }
377
378 @Test
379 public void testDiffRootTreeToNull() throws Exception {
380 write(new File(db.getDirectory().getParent(), "test.txt"), "test");
381 File folder = new File(db.getDirectory().getParent(), "folder");
382 FileUtils.mkdir(folder);
383 write(new File(folder, "folder.txt"), "folder");
384 Git git = new Git(db);
385 git.add().addFilepattern(".").call();
386 RevCommit commit = git.commit().setMessage("Initial commit").call();
387 write(new File(folder, "folder.txt"), "folder change");
388
389 ByteArrayOutputStream os = new ByteArrayOutputStream();
390 DiffFormatter dfmt = new DiffFormatter(new SafeBufferedOutputStream(os));
391 dfmt.setRepository(db);
392 dfmt.setPathFilter(PathFilter.create("folder"));
393 dfmt.format(commit.getTree().getId(), null);
394 dfmt.flush();
395
396 String actual = os.toString("UTF-8");
397 String expected = "diff --git a/folder/folder.txt b/folder/folder.txt\n"
398 + "deleted file mode 100644\n"
399 + "index 0119635..0000000\n"
400 + "--- a/folder/folder.txt\n"
401 + "+++ /dev/null\n"
402 + "@@ -1 +0,0 @@\n"
403 + "-folder\n"
404 + "\\ No newline at end of file\n";
405
406 assertEquals(expected, actual);
407 }
408
409 @Test
410 public void testDiffNullToNull() throws Exception {
411 ByteArrayOutputStream os = new ByteArrayOutputStream();
412 DiffFormatter dfmt = new DiffFormatter(new SafeBufferedOutputStream(os));
413 dfmt.setRepository(db);
414 dfmt.format((AnyObjectId) null, null);
415 dfmt.flush();
416
417 String actual = os.toString("UTF-8");
418 String expected = "";
419
420 assertEquals(expected, actual);
421 }
422
423 private static String makeDiffHeader(String pathA, String pathB,
424 ObjectId aId,
425 ObjectId bId) {
426 String a = aId.abbreviate(8).name();
427 String b = bId.abbreviate(8).name();
428 return DIFF + "a/" + pathA + " " + "b/" + pathB + "\n" +
429 "index " + a + ".." + b + " " + REGULAR_FILE + "\n" +
430 "--- a/" + pathA + "\n" +
431 "+++ b/" + pathB + "\n";
432 }
433
434 private static String makeDiffHeaderModeChange(String pathA, String pathB,
435 ObjectId aId, ObjectId bId, String modeA, String modeB) {
436 String a = aId.abbreviate(8).name();
437 String b = bId.abbreviate(8).name();
438 return DIFF + "a/" + pathA + " " + "b/" + pathB + "\n" +
439 "old mode " + modeA + "\n" +
440 "new mode " + modeB + "\n" +
441 "index " + a + ".." + b + "\n" +
442 "--- a/" + pathA + "\n" +
443 "+++ b/" + pathB + "\n";
444 }
445
446 private ObjectId blob(String content) throws Exception {
447 return testDb.blob(content).copy();
448 }
449 }