1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.diff;
12
13 import static org.junit.Assert.assertEquals;
14 import static org.junit.Assert.assertNotNull;
15 import static org.junit.Assert.assertNull;
16 import static org.junit.Assert.assertTrue;
17
18 import java.io.BufferedOutputStream;
19 import java.io.ByteArrayOutputStream;
20 import java.io.File;
21
22 import org.eclipse.jgit.api.Git;
23 import org.eclipse.jgit.api.Status;
24 import org.eclipse.jgit.diff.DiffEntry.ChangeType;
25 import org.eclipse.jgit.dircache.DirCacheIterator;
26 import org.eclipse.jgit.junit.RepositoryTestCase;
27 import org.eclipse.jgit.junit.TestRepository;
28 import org.eclipse.jgit.lib.AnyObjectId;
29 import org.eclipse.jgit.lib.ConfigConstants;
30 import org.eclipse.jgit.lib.FileMode;
31 import org.eclipse.jgit.lib.ObjectId;
32 import org.eclipse.jgit.lib.Repository;
33 import org.eclipse.jgit.patch.FileHeader;
34 import org.eclipse.jgit.patch.HunkHeader;
35 import org.eclipse.jgit.revwalk.RevCommit;
36 import org.eclipse.jgit.storage.file.FileBasedConfig;
37 import org.eclipse.jgit.treewalk.FileTreeIterator;
38 import org.eclipse.jgit.treewalk.filter.PathFilter;
39 import org.eclipse.jgit.util.FileUtils;
40 import org.eclipse.jgit.util.RawParseUtils;
41 import org.eclipse.jgit.util.io.DisabledOutputStream;
42 import org.junit.After;
43 import org.junit.Before;
44 import org.junit.Test;
45
46 public class DiffFormatterTest extends RepositoryTestCase {
47 private static final String DIFF = "diff --git ";
48
49 private static final String REGULAR_FILE = "100644";
50
51 private static final String GITLINK = "160000";
52
53 private static final String PATH_A = "src/a";
54
55 private static final String PATH_B = "src/b";
56
57 private DiffFormatter df;
58
59 private TestRepository<Repository> testDb;
60
61 @Override
62 @Before
63 public void setUp() throws Exception {
64 super.setUp();
65 testDb = new TestRepository<>(db);
66 df = new DiffFormatter(DisabledOutputStream.INSTANCE);
67 df.setRepository(db);
68 df.setAbbreviationLength(8);
69 }
70
71 @Override
72 @After
73 public void tearDown() throws Exception {
74 if (df != null) {
75 df.close();
76 }
77 super.tearDown();
78 }
79
80 @Test
81 public void testDefaultRenameDetectorSettings() throws Exception {
82 RenameDetector rd = df.getRenameDetector();
83 assertNull(rd);
84 df.setDetectRenames(true);
85 rd = df.getRenameDetector();
86 assertNotNull(rd);
87 assertEquals(400, rd.getRenameLimit());
88 assertEquals(60, rd.getRenameScore());
89 }
90
91 @Test
92 public void testCreateFileHeader_Add() throws Exception {
93 ObjectId adId = blob("a\nd\n");
94 DiffEntry ent = DiffEntry.add("FOO", adId);
95 FileHeader fh = df.toFileHeader(ent);
96
97 String diffHeader = "diff --git a/FOO b/FOO\n"
98 + "new file mode " + REGULAR_FILE + "\n"
99 + "index "
100 + ObjectId.zeroId().abbreviate(8).name()
101 + ".."
102 + adId.abbreviate(8).name() + "\n"
103 + "--- /dev/null\n"
104 + "+++ b/FOO\n";
105 assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer()));
106
107 assertEquals(0, fh.getStartOffset());
108 assertEquals(fh.getBuffer().length, fh.getEndOffset());
109 assertEquals(FileHeader.PatchType.UNIFIED, fh.getPatchType());
110
111 assertEquals(1, fh.getHunks().size());
112
113 HunkHeader hh = fh.getHunks().get(0);
114 assertEquals(1, hh.toEditList().size());
115
116 EditList el = hh.toEditList();
117 assertEquals(1, el.size());
118
119 Edit e = el.get(0);
120 assertEquals(0, e.getBeginA());
121 assertEquals(0, e.getEndA());
122 assertEquals(0, e.getBeginB());
123 assertEquals(2, e.getEndB());
124 assertEquals(Edit.Type.INSERT, e.getType());
125 }
126
127 @Test
128 public void testCreateFileHeader_Delete() throws Exception {
129 ObjectId adId = blob("a\nd\n");
130 DiffEntry ent = DiffEntry.delete("FOO", adId);
131 FileHeader fh = df.toFileHeader(ent);
132
133 String diffHeader = "diff --git a/FOO b/FOO\n"
134 + "deleted file mode " + REGULAR_FILE + "\n"
135 + "index "
136 + adId.abbreviate(8).name()
137 + ".."
138 + ObjectId.zeroId().abbreviate(8).name() + "\n"
139 + "--- a/FOO\n"
140 + "+++ /dev/null\n";
141 assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer()));
142
143 assertEquals(0, fh.getStartOffset());
144 assertEquals(fh.getBuffer().length, fh.getEndOffset());
145 assertEquals(FileHeader.PatchType.UNIFIED, fh.getPatchType());
146
147 assertEquals(1, fh.getHunks().size());
148
149 HunkHeader hh = fh.getHunks().get(0);
150 assertEquals(1, hh.toEditList().size());
151
152 EditList el = hh.toEditList();
153 assertEquals(1, el.size());
154
155 Edit e = el.get(0);
156 assertEquals(0, e.getBeginA());
157 assertEquals(2, e.getEndA());
158 assertEquals(0, e.getBeginB());
159 assertEquals(0, e.getEndB());
160 assertEquals(Edit.Type.DELETE, e.getType());
161 }
162
163 @Test
164 public void testCreateFileHeader_Modify() throws Exception {
165 ObjectId adId = blob("a\nd\n");
166 ObjectId abcdId = blob("a\nb\nc\nd\n");
167
168 String diffHeader = makeDiffHeader(PATH_A, PATH_A, adId, abcdId);
169
170 DiffEntry ad = DiffEntry.delete(PATH_A, adId);
171 DiffEntry abcd = DiffEntry.add(PATH_A, abcdId);
172
173 DiffEntry mod = DiffEntry.pair(ChangeType.MODIFY, ad, abcd, 0);
174
175 FileHeader fh = df.toFileHeader(mod);
176
177 assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer()));
178 assertEquals(0, fh.getStartOffset());
179 assertEquals(fh.getBuffer().length, fh.getEndOffset());
180 assertEquals(FileHeader.PatchType.UNIFIED, fh.getPatchType());
181
182 assertEquals(1, fh.getHunks().size());
183
184 HunkHeader hh = fh.getHunks().get(0);
185 assertEquals(1, hh.toEditList().size());
186
187 EditList el = hh.toEditList();
188 assertEquals(1, el.size());
189
190 Edit e = el.get(0);
191 assertEquals(1, e.getBeginA());
192 assertEquals(1, e.getEndA());
193 assertEquals(1, e.getBeginB());
194 assertEquals(3, e.getEndB());
195 assertEquals(Edit.Type.INSERT, e.getType());
196 }
197
198 @Test
199 public void testCreateFileHeader_Binary() throws Exception {
200 ObjectId adId = blob("a\nd\n");
201 ObjectId binId = blob("a\nb\nc\n\0\0\0\0d\n");
202
203 String diffHeader = makeDiffHeader(PATH_A, PATH_B, adId, binId)
204 + "Binary files differ\n";
205
206 DiffEntry ad = DiffEntry.delete(PATH_A, adId);
207 DiffEntry abcd = DiffEntry.add(PATH_B, binId);
208
209 DiffEntry mod = DiffEntry.pair(ChangeType.MODIFY, ad, abcd, 0);
210
211 FileHeader fh = df.toFileHeader(mod);
212
213 assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer()));
214 assertEquals(FileHeader.PatchType.BINARY, fh.getPatchType());
215
216 assertEquals(1, fh.getHunks().size());
217
218 HunkHeader hh = fh.getHunks().get(0);
219 assertEquals(0, hh.toEditList().size());
220 }
221
222 @Test
223 public void testCreateFileHeader_GitLink() throws Exception {
224 ObjectId aId = blob("a\n");
225 ObjectId bId = blob("b\n");
226
227 String diffHeader = makeDiffHeaderModeChange(PATH_A, PATH_A, aId, bId,
228 GITLINK, REGULAR_FILE);
229
230 DiffEntry ad = DiffEntry.delete(PATH_A, aId);
231 ad.oldMode = FileMode.GITLINK;
232 DiffEntry abcd = DiffEntry.add(PATH_A, bId);
233
234 DiffEntry mod = DiffEntry.pair(ChangeType.MODIFY, ad, abcd, 0);
235
236 FileHeader fh = df.toFileHeader(mod);
237
238 assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer()));
239
240 assertEquals(1, fh.getHunks().size());
241
242 HunkHeader hh = fh.getHunks().get(0);
243 assertEquals(1, hh.toEditList().size());
244 }
245
246 @Test
247 public void testCreateFileHeader_AddGitLink() throws Exception {
248 ObjectId adId = blob("a\nd\n");
249 DiffEntry ent = DiffEntry.add("FOO", adId);
250 ent.newMode = FileMode.GITLINK;
251 FileHeader fh = df.toFileHeader(ent);
252
253 String diffHeader = "diff --git a/FOO b/FOO\n"
254 + "new file mode " + GITLINK + "\n"
255 + "index "
256 + ObjectId.zeroId().abbreviate(8).name()
257 + ".."
258 + adId.abbreviate(8).name() + "\n"
259 + "--- /dev/null\n"
260 + "+++ b/FOO\n";
261 assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer()));
262
263 assertEquals(1, fh.getHunks().size());
264 HunkHeader hh = fh.getHunks().get(0);
265
266 EditList el = hh.toEditList();
267 assertEquals(1, el.size());
268
269 Edit e = el.get(0);
270 assertEquals(0, e.getBeginA());
271 assertEquals(0, e.getEndA());
272 assertEquals(0, e.getBeginB());
273 assertEquals(1, e.getEndB());
274 assertEquals(Edit.Type.INSERT, e.getType());
275 }
276
277 @Test
278 public void testCreateFileHeader_DeleteGitLink() throws Exception {
279 ObjectId adId = blob("a\nd\n");
280 DiffEntry ent = DiffEntry.delete("FOO", adId);
281 ent.oldMode = FileMode.GITLINK;
282 FileHeader fh = df.toFileHeader(ent);
283
284 String diffHeader = "diff --git a/FOO b/FOO\n"
285 + "deleted file mode " + GITLINK + "\n"
286 + "index "
287 + adId.abbreviate(8).name()
288 + ".."
289 + ObjectId.zeroId().abbreviate(8).name() + "\n"
290 + "--- a/FOO\n"
291 + "+++ /dev/null\n";
292 assertEquals(diffHeader, RawParseUtils.decode(fh.getBuffer()));
293
294 assertEquals(1, fh.getHunks().size());
295 HunkHeader hh = fh.getHunks().get(0);
296
297 EditList el = hh.toEditList();
298 assertEquals(1, el.size());
299
300 Edit e = el.get(0);
301 assertEquals(0, e.getBeginA());
302 assertEquals(1, e.getEndA());
303 assertEquals(0, e.getBeginB());
304 assertEquals(0, e.getEndB());
305 assertEquals(Edit.Type.DELETE, e.getType());
306 }
307
308 @Test
309 public void testCreateFileHeaderWithoutIndexLine() throws Exception {
310 DiffEntry m = DiffEntry.modify(PATH_A);
311 m.oldMode = FileMode.REGULAR_FILE;
312 m.newMode = FileMode.EXECUTABLE_FILE;
313
314 FileHeader fh = df.toFileHeader(m);
315 String expected = DIFF + "a/src/a b/src/a\n" +
316 "old mode 100644\n" +
317 "new mode 100755\n";
318 assertEquals(expected, fh.getScriptText());
319 }
320
321 @Test
322 public void testCreateFileHeaderForRenameWithoutContentChange() throws Exception {
323 DiffEntry a = DiffEntry.delete(PATH_A, ObjectId.zeroId());
324 DiffEntry b = DiffEntry.add(PATH_B, ObjectId.zeroId());
325 DiffEntry m = DiffEntry.pair(ChangeType.RENAME, a, b, 100);
326 m.oldId = null;
327 m.newId = null;
328
329 FileHeader fh = df.toFileHeader(m);
330 String expected = DIFF + "a/src/a b/src/b\n" +
331 "similarity index 100%\n" +
332 "rename from src/a\n" +
333 "rename to src/b\n";
334 assertEquals(expected, fh.getScriptText());
335 }
336
337 @Test
338 public void testCreateFileHeaderForRenameModeChange()
339 throws Exception {
340 DiffEntry a = DiffEntry.delete(PATH_A, ObjectId.zeroId());
341 DiffEntry b = DiffEntry.add(PATH_B, ObjectId.zeroId());
342 b.oldMode = FileMode.REGULAR_FILE;
343 b.newMode = FileMode.EXECUTABLE_FILE;
344 DiffEntry m = DiffEntry.pair(ChangeType.RENAME, a, b, 100);
345 m.oldId = null;
346 m.newId = null;
347
348 FileHeader fh = df.toFileHeader(m);
349
350 String expected = DIFF + "a/src/a b/src/b\n" +
351 "old mode 100644\n" +
352 "new mode 100755\n" +
353 "similarity index 100%\n" +
354 "rename from src/a\n" +
355 "rename to src/b\n";
356
357 assertEquals(expected, fh.getScriptText());
358 }
359
360 @Test
361 public void testDiff() throws Exception {
362 write(new File(db.getDirectory().getParent(), "test.txt"), "test");
363 File folder = new File(db.getDirectory().getParent(), "folder");
364 FileUtils.mkdir(folder);
365 write(new File(folder, "folder.txt"), "folder");
366 try (Git git = new Git(db);
367 ByteArrayOutputStream os = new ByteArrayOutputStream();
368 DiffFormatter dfmt = new DiffFormatter(new BufferedOutputStream(os))) {
369 git.add().addFilepattern(".").call();
370 git.commit().setMessage("Initial commit").call();
371 write(new File(folder, "folder.txt"), "folder change");
372 dfmt.setRepository(db);
373 dfmt.setPathFilter(PathFilter.create("folder"));
374 DirCacheIterator oldTree = new DirCacheIterator(db.readDirCache());
375 FileTreeIterator newTree = new FileTreeIterator(db);
376
377 dfmt.format(oldTree, newTree);
378 dfmt.flush();
379
380 String actual = os.toString("UTF-8");
381 String expected =
382 "diff --git a/folder/folder.txt b/folder/folder.txt\n"
383 + "index 0119635..95c4c65 100644\n"
384 + "--- a/folder/folder.txt\n" + "+++ b/folder/folder.txt\n"
385 + "@@ -1 +1 @@\n" + "-folder\n"
386 + "\\ No newline at end of file\n" + "+folder change\n"
387 + "\\ No newline at end of file\n";
388
389 assertEquals(expected, actual);
390 }
391 }
392
393 @Test
394 public void testDiffRootNullToTree() throws Exception {
395 write(new File(db.getDirectory().getParent(), "test.txt"), "test");
396 File folder = new File(db.getDirectory().getParent(), "folder");
397 FileUtils.mkdir(folder);
398 write(new File(folder, "folder.txt"), "folder");
399 try (Git git = new Git(db);
400 ByteArrayOutputStream os = new ByteArrayOutputStream();
401 DiffFormatter dfmt = new DiffFormatter(new BufferedOutputStream(os))) {
402 git.add().addFilepattern(".").call();
403 RevCommit commit = git.commit().setMessage("Initial commit").call();
404 write(new File(folder, "folder.txt"), "folder change");
405
406 dfmt.setRepository(db);
407 dfmt.setPathFilter(PathFilter.create("folder"));
408 dfmt.format(null, commit.getTree().getId());
409 dfmt.flush();
410
411 String actual = os.toString("UTF-8");
412 String expected = "diff --git a/folder/folder.txt b/folder/folder.txt\n"
413 + "new file mode 100644\n"
414 + "index 0000000..0119635\n"
415 + "--- /dev/null\n"
416 + "+++ b/folder/folder.txt\n"
417 + "@@ -0,0 +1 @@\n"
418 + "+folder\n"
419 + "\\ No newline at end of file\n";
420
421 assertEquals(expected, actual);
422 }
423 }
424
425 @Test
426 public void testDiffRootTreeToNull() throws Exception {
427 write(new File(db.getDirectory().getParent(), "test.txt"), "test");
428 File folder = new File(db.getDirectory().getParent(), "folder");
429 FileUtils.mkdir(folder);
430 write(new File(folder, "folder.txt"), "folder");
431 try (Git git = new Git(db);
432 ByteArrayOutputStream os = new ByteArrayOutputStream();
433 DiffFormatter dfmt = new DiffFormatter(new BufferedOutputStream(os));) {
434 git.add().addFilepattern(".").call();
435 RevCommit commit = git.commit().setMessage("Initial commit").call();
436 write(new File(folder, "folder.txt"), "folder change");
437
438 dfmt.setRepository(db);
439 dfmt.setPathFilter(PathFilter.create("folder"));
440 dfmt.format(commit.getTree().getId(), null);
441 dfmt.flush();
442
443 String actual = os.toString("UTF-8");
444 String expected = "diff --git a/folder/folder.txt b/folder/folder.txt\n"
445 + "deleted file mode 100644\n"
446 + "index 0119635..0000000\n"
447 + "--- a/folder/folder.txt\n"
448 + "+++ /dev/null\n"
449 + "@@ -1 +0,0 @@\n"
450 + "-folder\n"
451 + "\\ No newline at end of file\n";
452
453 assertEquals(expected, actual);
454 }
455 }
456
457 @Test
458 public void testDiffNullToNull() throws Exception {
459 try (ByteArrayOutputStream os = new ByteArrayOutputStream();
460 DiffFormatter dfmt = new DiffFormatter(new BufferedOutputStream(os))) {
461 dfmt.setRepository(db);
462 dfmt.format((AnyObjectId) null, null);
463 dfmt.flush();
464
465 String actual = os.toString("UTF-8");
466 String expected = "";
467
468 assertEquals(expected, actual);
469 }
470 }
471
472 @Test
473 public void testTrackedFileInIgnoredFolderUnchanged()
474 throws Exception {
475 commitFile("empty/empty/foo", "", "master");
476 commitFile(".gitignore", "empty/*", "master");
477 try (Git git = new Git(db)) {
478 Status status = git.status().call();
479 assertTrue(status.isClean());
480 }
481 try (ByteArrayOutputStream os = new ByteArrayOutputStream();
482 DiffFormatter dfmt = new DiffFormatter(os)) {
483 dfmt.setRepository(db);
484 dfmt.format(new DirCacheIterator(db.readDirCache()),
485 new FileTreeIterator(db));
486 dfmt.flush();
487
488 String actual = os.toString("UTF-8");
489
490 assertEquals("", actual);
491 }
492 }
493
494 @Test
495 public void testTrackedFileInIgnoredFolderChanged()
496 throws Exception {
497 String expectedDiff = "diff --git a/empty/empty/foo b/empty/empty/foo\n"
498 + "index e69de29..5ea2ed4 100644\n"
499 + "--- a/empty/empty/foo\n"
500 + "+++ b/empty/empty/foo\n"
501 + "@@ -0,0 +1 @@\n"
502 + "+changed\n";
503
504 commitFile("empty/empty/foo", "", "master");
505 commitFile(".gitignore", "empty/*", "master");
506 try (Git git = new Git(db)) {
507 Status status = git.status().call();
508 assertTrue(status.isClean());
509 }
510 try (ByteArrayOutputStream os = new ByteArrayOutputStream();
511 DiffFormatter dfmt = new DiffFormatter(os)) {
512 writeTrashFile("empty/empty/foo", "changed\n");
513 dfmt.setRepository(db);
514 dfmt.format(new DirCacheIterator(db.readDirCache()),
515 new FileTreeIterator(db));
516 dfmt.flush();
517
518 String actual = os.toString("UTF-8");
519
520 assertEquals(expectedDiff, actual);
521 }
522 }
523
524 @Test
525 public void testDiffAutoCrlfSmallFile() throws Exception {
526 String content = "01234\r\n01234\r\n01234\r\n";
527 String expectedDiff = "diff --git a/test.txt b/test.txt\n"
528 + "index fe25983..a44a032 100644\n"
529 + "--- a/test.txt\n"
530 + "+++ b/test.txt\n"
531 + "@@ -1,3 +1,4 @@\n"
532 + " 01234\n"
533 + "+ABCD\n"
534 + " 01234\n"
535 + " 01234\n";
536 doAutoCrLfTest(content, expectedDiff);
537 }
538
539 @Test
540 public void testDiffAutoCrlfMediumFile() throws Exception {
541 String content = mediumCrLfString();
542 String expectedDiff = "diff --git a/test.txt b/test.txt\n"
543 + "index 215c502..c10f08c 100644\n"
544 + "--- a/test.txt\n"
545 + "+++ b/test.txt\n"
546 + "@@ -1,4 +1,5 @@\n"
547 + " 01234567\n"
548 + "+ABCD\n"
549 + " 01234567\n"
550 + " 01234567\n"
551 + " 01234567\n";
552 doAutoCrLfTest(content, expectedDiff);
553 }
554
555 @Test
556 public void testDiffAutoCrlfLargeFile() throws Exception {
557 String content = largeCrLfString();
558 String expectedDiff = "diff --git a/test.txt b/test.txt\n"
559 + "index 7014942..c0487a7 100644\n"
560 + "--- a/test.txt\n"
561 + "+++ b/test.txt\n"
562 + "@@ -1,4 +1,5 @@\n"
563 + " 012345678901234567890123456789012345678901234567\n"
564 + "+ABCD\n"
565 + " 012345678901234567890123456789012345678901234567\n"
566 + " 012345678901234567890123456789012345678901234567\n"
567 + " 012345678901234567890123456789012345678901234567\n";
568 doAutoCrLfTest(content, expectedDiff);
569 }
570
571 private void doAutoCrLfTest(String content, String expectedDiff)
572 throws Exception {
573 FileBasedConfig config = db.getConfig();
574 config.setString(ConfigConstants.CONFIG_CORE_SECTION, null,
575 ConfigConstants.CONFIG_KEY_AUTOCRLF, "true");
576 config.save();
577 commitFile("test.txt", content, "master");
578
579 int i = content.indexOf('\n');
580 content = content.substring(0, i + 1) + "ABCD\r\n"
581 + content.substring(i + 1);
582 writeTrashFile("test.txt", content);
583
584 try (ByteArrayOutputStream os = new ByteArrayOutputStream();
585 DiffFormatter dfmt = new DiffFormatter(
586 new BufferedOutputStream(os))) {
587 dfmt.setRepository(db);
588 dfmt.format(new DirCacheIterator(db.readDirCache()),
589 new FileTreeIterator(db));
590 dfmt.flush();
591
592 String actual = os.toString("UTF-8");
593
594 assertEquals(expectedDiff, actual);
595 }
596 }
597
598 private static String largeCrLfString() {
599 String line = "012345678901234567890123456789012345678901234567\r\n";
600 StringBuilder builder = new StringBuilder(
601 2 * RawText.FIRST_FEW_BYTES);
602 while (builder.length() < 2 * RawText.FIRST_FEW_BYTES) {
603 builder.append(line);
604 }
605 return builder.toString();
606 }
607
608 private static String mediumCrLfString() {
609
610
611 String line = "01234567\r\n";
612 StringBuilder builder = new StringBuilder(
613 RawText.FIRST_FEW_BYTES + line.length());
614 while (builder.length() <= RawText.FIRST_FEW_BYTES) {
615 builder.append(line);
616 }
617 return builder.toString();
618 }
619
620 private static String makeDiffHeader(String pathA, String pathB,
621 ObjectId aId,
622 ObjectId bId) {
623 String a = aId.abbreviate(8).name();
624 String b = bId.abbreviate(8).name();
625 return DIFF + "a/" + pathA + " " + "b/" + pathB + "\n" +
626 "index " + a + ".." + b + " " + REGULAR_FILE + "\n" +
627 "--- a/" + pathA + "\n" +
628 "+++ b/" + pathB + "\n";
629 }
630
631 private static String makeDiffHeaderModeChange(String pathA, String pathB,
632 ObjectId aId, ObjectId bId, String modeA, String modeB) {
633 String a = aId.abbreviate(8).name();
634 String b = bId.abbreviate(8).name();
635 return DIFF + "a/" + pathA + " " + "b/" + pathB + "\n" +
636 "old mode " + modeA + "\n" +
637 "new mode " + modeB + "\n" +
638 "index " + a + ".." + b + "\n" +
639 "--- a/" + pathA + "\n" +
640 "+++ b/" + pathB + "\n";
641 }
642
643 private ObjectId blob(String content) throws Exception {
644 return testDb.blob(content).copy();
645 }
646 }