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.api;
44
45 import static org.junit.Assert.assertEquals;
46 import static org.junit.Assert.assertNotNull;
47 import static org.junit.Assert.assertTrue;
48
49 import java.io.File;
50
51 import org.eclipse.jgit.api.MergeCommand.FastForwardMode;
52 import org.eclipse.jgit.api.ResetCommand.ResetType;
53 import org.eclipse.jgit.blame.BlameResult;
54 import org.eclipse.jgit.diff.RawTextComparator;
55 import org.eclipse.jgit.junit.RepositoryTestCase;
56 import org.eclipse.jgit.lib.ConfigConstants;
57 import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
58 import org.eclipse.jgit.revwalk.RevCommit;
59 import org.eclipse.jgit.storage.file.FileBasedConfig;
60 import org.junit.Test;
61
62
63
64
65 public class BlameCommandTest extends RepositoryTestCase {
66
67 private static String join(String... lines) {
68 StringBuilder joined = new StringBuilder();
69 for (String line : lines)
70 joined.append(line).append('\n');
71 return joined.toString();
72 }
73
74 @Test
75 public void testSingleRevision() throws Exception {
76 try (Git git = new Git(db)) {
77 String[] content = new String[] { "first", "second", "third" };
78
79 writeTrashFile("file.txt", join(content));
80 git.add().addFilepattern("file.txt").call();
81 RevCommit commit = git.commit().setMessage("create file").call();
82
83 BlameCommand command = new BlameCommand(db);
84 command.setFilePath("file.txt");
85 BlameResult lines = command.call();
86 assertNotNull(lines);
87 assertEquals(3, lines.getResultContents().size());
88
89 for (int i = 0; i < 3; i++) {
90 assertEquals(commit, lines.getSourceCommit(i));
91 assertEquals(i, lines.getSourceLine(i));
92 }
93 }
94 }
95
96 @Test
97 public void testTwoRevisions() throws Exception {
98 try (Git git = new Git(db)) {
99 String[] content1 = new String[] { "first", "second" };
100 writeTrashFile("file.txt", join(content1));
101 git.add().addFilepattern("file.txt").call();
102 RevCommit commit1 = git.commit().setMessage("create file").call();
103
104 String[] content2 = new String[] { "first", "second", "third" };
105 writeTrashFile("file.txt", join(content2));
106 git.add().addFilepattern("file.txt").call();
107 RevCommit commit2 = git.commit().setMessage("create file").call();
108
109 BlameCommand command = new BlameCommand(db);
110 command.setFilePath("file.txt");
111 BlameResult lines = command.call();
112 assertEquals(3, lines.getResultContents().size());
113
114 assertEquals(commit1, lines.getSourceCommit(0));
115 assertEquals(0, lines.getSourceLine(0));
116
117 assertEquals(commit1, lines.getSourceCommit(1));
118 assertEquals(1, lines.getSourceLine(1));
119
120 assertEquals(commit2, lines.getSourceCommit(2));
121 assertEquals(2, lines.getSourceLine(2));
122 }
123 }
124
125 @Test
126 public void testRename() throws Exception {
127 testRename("file1.txt", "file2.txt");
128 }
129
130 @Test
131 public void testRenameInSubDir() throws Exception {
132 testRename("subdir/file1.txt", "subdir/file2.txt");
133 }
134
135 @Test
136 public void testMoveToOtherDir() throws Exception {
137 testRename("subdir/file1.txt", "otherdir/file1.txt");
138 }
139
140 private void testRename(String sourcePath, String destPath)
141 throws Exception {
142 try (Git git = new Git(db)) {
143 String[] content1 = new String[] { "a", "b", "c" };
144 writeTrashFile(sourcePath, join(content1));
145 git.add().addFilepattern(sourcePath).call();
146 RevCommit commit1 = git.commit().setMessage("create file").call();
147
148 writeTrashFile(destPath, join(content1));
149 git.add().addFilepattern(destPath).call();
150 git.rm().addFilepattern(sourcePath).call();
151 git.commit().setMessage("moving file").call();
152
153 String[] content2 = new String[] { "a", "b", "c2" };
154 writeTrashFile(destPath, join(content2));
155 git.add().addFilepattern(destPath).call();
156 RevCommit commit3 = git.commit().setMessage("editing file").call();
157
158 BlameCommand command = new BlameCommand(db);
159 command.setFollowFileRenames(true);
160 command.setFilePath(destPath);
161 BlameResult lines = command.call();
162
163 assertEquals(commit1, lines.getSourceCommit(0));
164 assertEquals(0, lines.getSourceLine(0));
165 assertEquals(sourcePath, lines.getSourcePath(0));
166
167 assertEquals(commit1, lines.getSourceCommit(1));
168 assertEquals(1, lines.getSourceLine(1));
169 assertEquals(sourcePath, lines.getSourcePath(1));
170
171 assertEquals(commit3, lines.getSourceCommit(2));
172 assertEquals(2, lines.getSourceLine(2));
173 assertEquals(destPath, lines.getSourcePath(2));
174 }
175 }
176
177 @Test
178 public void testTwoRenames() throws Exception {
179 try (Git git = new Git(db)) {
180
181 String[] content1 = new String[] { "a" };
182 writeTrashFile("file.txt", join(content1));
183 git.add().addFilepattern("file.txt").call();
184 RevCommit commit1 = git.commit().setMessage("create file").call();
185
186
187 writeTrashFile("file1.txt", join(content1));
188 git.add().addFilepattern("file1.txt").call();
189 git.rm().addFilepattern("file.txt").call();
190 git.commit().setMessage("moving file").call();
191
192
193 String[] content2 = new String[] { "a", "b" };
194 writeTrashFile("file1.txt", join(content2));
195 git.add().addFilepattern("file1.txt").call();
196 RevCommit commit3 = git.commit().setMessage("editing file").call();
197
198
199 writeTrashFile("file2.txt", join(content2));
200 git.add().addFilepattern("file2.txt").call();
201 git.rm().addFilepattern("file1.txt").call();
202 git.commit().setMessage("moving file again").call();
203
204 BlameCommand command = new BlameCommand(db);
205 command.setFollowFileRenames(true);
206 command.setFilePath("file2.txt");
207 BlameResult lines = command.call();
208
209 assertEquals(commit1, lines.getSourceCommit(0));
210 assertEquals(0, lines.getSourceLine(0));
211 assertEquals("file.txt", lines.getSourcePath(0));
212
213 assertEquals(commit3, lines.getSourceCommit(1));
214 assertEquals(1, lines.getSourceLine(1));
215 assertEquals("file1.txt", lines.getSourcePath(1));
216 }
217 }
218
219 @Test
220 public void testDeleteTrailingLines() throws Exception {
221 try (Git git = new Git(db)) {
222 String[] content1 = new String[] { "a", "b", "c", "d" };
223 String[] content2 = new String[] { "a", "b" };
224
225 writeTrashFile("file.txt", join(content2));
226 git.add().addFilepattern("file.txt").call();
227 RevCommit commit1 = git.commit().setMessage("create file").call();
228
229 writeTrashFile("file.txt", join(content1));
230 git.add().addFilepattern("file.txt").call();
231 git.commit().setMessage("edit file").call();
232
233 writeTrashFile("file.txt", join(content2));
234 git.add().addFilepattern("file.txt").call();
235 git.commit().setMessage("edit file").call();
236
237 BlameCommand command = new BlameCommand(db);
238
239 command.setFilePath("file.txt");
240 BlameResult lines = command.call();
241 assertEquals(content2.length, lines.getResultContents().size());
242
243 assertEquals(commit1, lines.getSourceCommit(0));
244 assertEquals(commit1, lines.getSourceCommit(1));
245
246 assertEquals(0, lines.getSourceLine(0));
247 assertEquals(1, lines.getSourceLine(1));
248 }
249 }
250
251 @Test
252 public void testDeleteMiddleLines() throws Exception {
253 try (Git git = new Git(db)) {
254 String[] content1 = new String[] { "a", "b", "c", "d", "e" };
255 String[] content2 = new String[] { "a", "c", "e" };
256
257 writeTrashFile("file.txt", join(content2));
258 git.add().addFilepattern("file.txt").call();
259 RevCommit commit1 = git.commit().setMessage("edit file").call();
260
261 writeTrashFile("file.txt", join(content1));
262 git.add().addFilepattern("file.txt").call();
263 git.commit().setMessage("edit file").call();
264
265 writeTrashFile("file.txt", join(content2));
266 git.add().addFilepattern("file.txt").call();
267 git.commit().setMessage("edit file").call();
268
269 BlameCommand command = new BlameCommand(db);
270
271 command.setFilePath("file.txt");
272 BlameResult lines = command.call();
273 assertEquals(content2.length, lines.getResultContents().size());
274
275 assertEquals(commit1, lines.getSourceCommit(0));
276 assertEquals(0, lines.getSourceLine(0));
277
278 assertEquals(commit1, lines.getSourceCommit(1));
279 assertEquals(1, lines.getSourceLine(1));
280
281 assertEquals(commit1, lines.getSourceCommit(2));
282 assertEquals(2, lines.getSourceLine(2));
283 }
284 }
285
286 @Test
287 public void testEditAllLines() throws Exception {
288 try (Git git = new Git(db)) {
289 String[] content1 = new String[] { "a", "1" };
290 String[] content2 = new String[] { "b", "2" };
291
292 writeTrashFile("file.txt", join(content1));
293 git.add().addFilepattern("file.txt").call();
294 git.commit().setMessage("edit file").call();
295
296 writeTrashFile("file.txt", join(content2));
297 git.add().addFilepattern("file.txt").call();
298 RevCommit commit2 = git.commit().setMessage("create file").call();
299
300 BlameCommand command = new BlameCommand(db);
301
302 command.setFilePath("file.txt");
303 BlameResult lines = command.call();
304 assertEquals(content2.length, lines.getResultContents().size());
305 assertEquals(commit2, lines.getSourceCommit(0));
306 assertEquals(commit2, lines.getSourceCommit(1));
307 }
308 }
309
310 @Test
311 public void testMiddleClearAllLines() throws Exception {
312 try (Git git = new Git(db)) {
313 String[] content1 = new String[] { "a", "b", "c" };
314
315 writeTrashFile("file.txt", join(content1));
316 git.add().addFilepattern("file.txt").call();
317 git.commit().setMessage("edit file").call();
318
319 writeTrashFile("file.txt", "");
320 git.add().addFilepattern("file.txt").call();
321 git.commit().setMessage("create file").call();
322
323 writeTrashFile("file.txt", join(content1));
324 git.add().addFilepattern("file.txt").call();
325 RevCommit commit3 = git.commit().setMessage("edit file").call();
326
327 BlameCommand command = new BlameCommand(db);
328
329 command.setFilePath("file.txt");
330 BlameResult lines = command.call();
331 assertEquals(content1.length, lines.getResultContents().size());
332 assertEquals(commit3, lines.getSourceCommit(0));
333 assertEquals(commit3, lines.getSourceCommit(1));
334 assertEquals(commit3, lines.getSourceCommit(2));
335 }
336 }
337
338 @Test
339 public void testCoreAutoCrlf1() throws Exception {
340 testCoreAutoCrlf(AutoCRLF.INPUT, AutoCRLF.FALSE);
341 }
342
343 @Test
344 public void testCoreAutoCrlf2() throws Exception {
345 testCoreAutoCrlf(AutoCRLF.FALSE, AutoCRLF.FALSE);
346 }
347
348 @Test
349 public void testCoreAutoCrlf3() throws Exception {
350 testCoreAutoCrlf(AutoCRLF.INPUT, AutoCRLF.INPUT);
351 }
352
353 @Test
354 public void testCoreAutoCrlf4() throws Exception {
355 testCoreAutoCrlf(AutoCRLF.FALSE, AutoCRLF.INPUT);
356 }
357
358 @Test
359 public void testCoreAutoCrlf5() throws Exception {
360 testCoreAutoCrlf(AutoCRLF.INPUT, AutoCRLF.TRUE);
361 }
362
363 private void testCoreAutoCrlf(AutoCRLF modeForCommitting,
364 AutoCRLF modeForReset) throws Exception {
365 try (Git git = new Git(db)) {
366 FileBasedConfig config = db.getConfig();
367 config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
368 ConfigConstants.CONFIG_KEY_AUTOCRLF, modeForCommitting);
369 config.save();
370
371 String joinedCrlf = "a\r\nb\r\nc\r\n";
372 File trashFile = writeTrashFile("file.txt", joinedCrlf);
373 git.add().addFilepattern("file.txt").call();
374 RevCommit commit = git.commit().setMessage("create file").call();
375
376
377 trashFile.delete();
378 config.setEnum(ConfigConstants.CONFIG_CORE_SECTION, null,
379 ConfigConstants.CONFIG_KEY_AUTOCRLF, modeForReset);
380 config.save();
381 git.reset().setMode(ResetType.HARD).call();
382
383 BlameCommand command = new BlameCommand(db);
384 command.setFilePath("file.txt");
385 BlameResult lines = command.call();
386
387 assertEquals(3, lines.getResultContents().size());
388 assertEquals(commit, lines.getSourceCommit(0));
389 assertEquals(commit, lines.getSourceCommit(1));
390 assertEquals(commit, lines.getSourceCommit(2));
391 }
392 }
393
394 @Test
395 public void testConflictingMerge1() throws Exception {
396 try (Git git = new Git(db)) {
397 RevCommit base = commitFile("file.txt", join("0", "1", "2", "3", "4"),
398 "master");
399
400 git.checkout().setName("side").setCreateBranch(true)
401 .setStartPoint(base).call();
402 RevCommit side = commitFile("file.txt",
403 join("0", "1 side", "2", "3 on side", "4"), "side");
404
405 commitFile("file.txt", join("0", "1", "2"), "master");
406
407 checkoutBranch("refs/heads/master");
408 git.merge().include(side).call();
409
410
411
412 RevCommit merge = commitFile("file.txt",
413 join("0", "1 side", "2", "3 resolved", "4"), "master");
414
415 BlameCommand command = new BlameCommand(db);
416 command.setFilePath("file.txt");
417 BlameResult lines = command.call();
418
419 assertEquals(5, lines.getResultContents().size());
420 assertEquals(base, lines.getSourceCommit(0));
421 assertEquals(side, lines.getSourceCommit(1));
422 assertEquals(base, lines.getSourceCommit(2));
423 assertEquals(merge, lines.getSourceCommit(3));
424 assertEquals(base, lines.getSourceCommit(4));
425 }
426 }
427
428
429
430 @Test
431 public void testConflictingMerge2() throws Exception {
432 try (Git git = new Git(db)) {
433 RevCommit base = commitFile("file.txt", join("0", "1", "2", "3", "4"),
434 "master");
435
436 commitFile("file.txt", join("0", "1", "2"), "master");
437
438 git.checkout().setName("side").setCreateBranch(true)
439 .setStartPoint(base).call();
440 RevCommit side = commitFile("file.txt",
441 join("0", "1 side", "2", "3 on side", "4"), "side");
442
443 checkoutBranch("refs/heads/master");
444 git.merge().include(side).call();
445
446
447
448 RevCommit merge = commitFile("file.txt",
449 join("0", "1 side", "2", "3 resolved", "4"), "master");
450
451 BlameCommand command = new BlameCommand(db);
452 command.setFilePath("file.txt");
453 BlameResult lines = command.call();
454
455 assertEquals(5, lines.getResultContents().size());
456 assertEquals(base, lines.getSourceCommit(0));
457 assertEquals(side, lines.getSourceCommit(1));
458 assertEquals(base, lines.getSourceCommit(2));
459 assertEquals(merge, lines.getSourceCommit(3));
460 assertEquals(base, lines.getSourceCommit(4));
461 }
462 }
463
464 @Test
465 public void testWhitespaceMerge() throws Exception {
466 try (Git git = new Git(db)) {
467 RevCommit base = commitFile("file.txt", join("0", "1", "2"), "master");
468 RevCommit side = commitFile("file.txt", join("0", "1", " 2 side "),
469 "side");
470
471 checkoutBranch("refs/heads/master");
472 git.merge().setFastForward(FastForwardMode.NO_FF).include(side).call();
473
474
475
476 writeTrashFile("file.txt", join("0", "1", "2 side"));
477 RevCommit merge = git.commit().setAll(true).setMessage("merge")
478 .setAmend(true)
479 .call();
480
481 BlameCommand command = new BlameCommand(db);
482 command.setFilePath("file.txt")
483 .setTextComparator(RawTextComparator.WS_IGNORE_ALL)
484 .setStartCommit(merge.getId());
485 BlameResult lines = command.call();
486
487 assertEquals(3, lines.getResultContents().size());
488 assertEquals(base, lines.getSourceCommit(0));
489 assertEquals(base, lines.getSourceCommit(1));
490 assertEquals(side, lines.getSourceCommit(2));
491 }
492 }
493
494 @Test
495 public void testBlameWithNulByteInHistory() throws Exception {
496 try (Git git = new Git(db)) {
497 String[] content1 = { "First line", "Another line" };
498 writeTrashFile("file.txt", join(content1));
499 git.add().addFilepattern("file.txt").call();
500 RevCommit c1 = git.commit().setMessage("create file").call();
501
502 String[] content2 = { "First line", "Second line with NUL >\000<",
503 "Another line" };
504 assertTrue("Content should contain a NUL byte",
505 content2[1].indexOf(0) > 0);
506 writeTrashFile("file.txt", join(content2));
507 git.add().addFilepattern("file.txt").call();
508 git.commit().setMessage("add line with NUL").call();
509
510 String[] content3 = { "First line", "Second line with NUL >\000<",
511 "Third line" };
512 writeTrashFile("file.txt", join(content3));
513 git.add().addFilepattern("file.txt").call();
514 RevCommit c3 = git.commit().setMessage("change third line").call();
515
516 String[] content4 = { "First line", "Second line with NUL >\\000<",
517 "Third line" };
518 assertTrue("Content should not contain a NUL byte",
519 content4[1].indexOf(0) < 0);
520 writeTrashFile("file.txt", join(content4));
521 git.add().addFilepattern("file.txt").call();
522 RevCommit c4 = git.commit().setMessage("fix NUL line").call();
523
524 BlameResult lines = git.blame().setFilePath("file.txt").call();
525 assertEquals(3, lines.getResultContents().size());
526 assertEquals(c1, lines.getSourceCommit(0));
527 assertEquals(c4, lines.getSourceCommit(1));
528 assertEquals(c3, lines.getSourceCommit(2));
529 }
530 }
531
532 @Test
533 public void testBlameWithNulByteInTopRevision() throws Exception {
534 try (Git git = new Git(db)) {
535 String[] content1 = { "First line", "Another line" };
536 writeTrashFile("file.txt", join(content1));
537 git.add().addFilepattern("file.txt").call();
538 RevCommit c1 = git.commit().setMessage("create file").call();
539
540 String[] content2 = { "First line", "Second line with NUL >\000<",
541 "Another line" };
542 assertTrue("Content should contain a NUL byte",
543 content2[1].indexOf(0) > 0);
544 writeTrashFile("file.txt", join(content2));
545 git.add().addFilepattern("file.txt").call();
546 RevCommit c2 = git.commit().setMessage("add line with NUL").call();
547
548 String[] content3 = { "First line", "Second line with NUL >\000<",
549 "Third line" };
550 writeTrashFile("file.txt", join(content3));
551 git.add().addFilepattern("file.txt").call();
552 RevCommit c3 = git.commit().setMessage("change third line").call();
553
554 BlameResult lines = git.blame().setFilePath("file.txt").call();
555 assertEquals(3, lines.getResultContents().size());
556 assertEquals(c1, lines.getSourceCommit(0));
557 assertEquals(c2, lines.getSourceCommit(1));
558 assertEquals(c3, lines.getSourceCommit(2));
559 }
560 }
561
562 }