1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.api;
11
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertNotEquals;
14 import static org.junit.Assert.assertNotNull;
15 import static org.junit.Assert.assertNull;
16 import static org.junit.Assert.assertSame;
17 import static org.junit.Assert.assertTrue;
18 import static org.junit.Assert.fail;
19 import static org.junit.Assume.assumeTrue;
20
21 import java.io.File;
22 import java.util.Date;
23 import java.util.List;
24 import java.util.TimeZone;
25 import java.util.concurrent.atomic.AtomicInteger;
26
27 import org.eclipse.jgit.api.CherryPickResult.CherryPickStatus;
28 import org.eclipse.jgit.api.errors.CanceledException;
29 import org.eclipse.jgit.api.errors.EmptyCommitException;
30 import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
31 import org.eclipse.jgit.diff.DiffEntry;
32 import org.eclipse.jgit.dircache.DirCache;
33 import org.eclipse.jgit.dircache.DirCacheBuilder;
34 import org.eclipse.jgit.dircache.DirCacheEntry;
35 import org.eclipse.jgit.junit.RepositoryTestCase;
36 import org.eclipse.jgit.junit.time.TimeUtil;
37 import org.eclipse.jgit.lib.CommitBuilder;
38 import org.eclipse.jgit.lib.ConfigConstants;
39 import org.eclipse.jgit.lib.Constants;
40 import org.eclipse.jgit.lib.FileMode;
41 import org.eclipse.jgit.lib.GpgSigner;
42 import org.eclipse.jgit.lib.ObjectId;
43 import org.eclipse.jgit.lib.PersonIdent;
44 import org.eclipse.jgit.lib.RefUpdate;
45 import org.eclipse.jgit.lib.RefUpdate.Result;
46 import org.eclipse.jgit.lib.ReflogEntry;
47 import org.eclipse.jgit.lib.Repository;
48 import org.eclipse.jgit.lib.StoredConfig;
49 import org.eclipse.jgit.lib.CommitConfig.CleanupMode;
50 import org.eclipse.jgit.revwalk.RevCommit;
51 import org.eclipse.jgit.storage.file.FileBasedConfig;
52 import org.eclipse.jgit.submodule.SubmoduleWalk;
53 import org.eclipse.jgit.transport.CredentialsProvider;
54 import org.eclipse.jgit.treewalk.TreeWalk;
55 import org.eclipse.jgit.treewalk.filter.TreeFilter;
56 import org.eclipse.jgit.util.FS;
57 import org.junit.Ignore;
58 import org.junit.Test;
59
60
61
62
63 public class CommitCommandTest extends RepositoryTestCase {
64
65 @Test
66 public void testExecutableRetention() throws Exception {
67 StoredConfig config = db.getConfig();
68 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
69 ConfigConstants.CONFIG_KEY_FILEMODE, true);
70 config.save();
71
72 FS executableFs = new FS() {
73
74 @Override
75 public boolean supportsExecute() {
76 return true;
77 }
78
79 @Override
80 public boolean setExecute(File f, boolean canExec) {
81 return true;
82 }
83
84 @Override
85 public ProcessBuilder runInShell(String cmd, String[] args) {
86 return null;
87 }
88
89 @Override
90 public boolean retryFailedLockFileCommit() {
91 return false;
92 }
93
94 @Override
95 public FS newInstance() {
96 return this;
97 }
98
99 @Override
100 protected File discoverGitExe() {
101 return null;
102 }
103
104 @Override
105 public boolean canExecute(File f) {
106 return true;
107 }
108
109 @Override
110 public boolean isCaseSensitive() {
111 return true;
112 }
113 };
114
115 Git git = Git.open(db.getDirectory(), executableFs);
116 String path = "a.txt";
117 writeTrashFile(path, "content");
118 git.add().addFilepattern(path).call();
119 RevCommit commit1 = git.commit().setMessage("commit").call();
120 try (TreeWalk walk = TreeWalk.forPath(db, path, commit1.getTree())) {
121 assertNotNull(walk);
122 assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
123 }
124
125 FS nonExecutableFs = new FS() {
126
127 @Override
128 public boolean supportsExecute() {
129 return false;
130 }
131
132 @Override
133 public boolean setExecute(File f, boolean canExec) {
134 return false;
135 }
136
137 @Override
138 public ProcessBuilder runInShell(String cmd, String[] args) {
139 return null;
140 }
141
142 @Override
143 public boolean retryFailedLockFileCommit() {
144 return false;
145 }
146
147 @Override
148 public FS newInstance() {
149 return this;
150 }
151
152 @Override
153 protected File discoverGitExe() {
154 return null;
155 }
156
157 @Override
158 public boolean canExecute(File f) {
159 return false;
160 }
161
162 @Override
163 public boolean isCaseSensitive() {
164 return true;
165 }
166 };
167
168 config = db.getConfig();
169 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
170 ConfigConstants.CONFIG_KEY_FILEMODE, false);
171 config.save();
172
173 Git git2 = Git.open(db.getDirectory(), nonExecutableFs);
174 writeTrashFile(path, "content2");
175 RevCommit commit2 = git2.commit().setOnly(path).setMessage("commit2")
176 .call();
177 try (TreeWalk walk = TreeWalk.forPath(db, path, commit2.getTree())) {
178 assertNotNull(walk);
179 assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
180 }
181 }
182
183 @Test
184 public void commitNewSubmodule() throws Exception {
185 try (Git git = new Git(db)) {
186 writeTrashFile("file.txt", "content");
187 git.add().addFilepattern("file.txt").call();
188 RevCommit commit = git.commit().setMessage("create file").call();
189
190 SubmoduleAddCommand command = new SubmoduleAddCommand(db);
191 String path = "sub";
192 command.setPath(path);
193 String uri = db.getDirectory().toURI().toString();
194 command.setURI(uri);
195 Repository repo = command.call();
196 assertNotNull(repo);
197 addRepoToClose(repo);
198
199 try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
200 assertTrue(generator.next());
201 assertEquals(path, generator.getPath());
202 assertEquals(commit, generator.getObjectId());
203 assertEquals(uri, generator.getModulesUrl());
204 assertEquals(path, generator.getModulesPath());
205 assertEquals(uri, generator.getConfigUrl());
206 try (Repository subModRepo = generator.getRepository()) {
207 assertNotNull(subModRepo);
208 }
209 }
210 assertEquals(commit, repo.resolve(Constants.HEAD));
211
212 RevCommit submoduleCommit = git.commit().setMessage("submodule add")
213 .setOnly(path).call();
214 assertNotNull(submoduleCommit);
215 try (TreeWalk walk = new TreeWalk(db)) {
216 walk.addTree(commit.getTree());
217 walk.addTree(submoduleCommit.getTree());
218 walk.setFilter(TreeFilter.ANY_DIFF);
219 List<DiffEntry> diffs = DiffEntry.scan(walk);
220 assertEquals(1, diffs.size());
221 DiffEntry subDiff = diffs.get(0);
222 assertEquals(FileMode.MISSING, subDiff.getOldMode());
223 assertEquals(FileMode.GITLINK, subDiff.getNewMode());
224 assertEquals(ObjectId.zeroId(), subDiff.getOldId().toObjectId());
225 assertEquals(commit, subDiff.getNewId().toObjectId());
226 assertEquals(path, subDiff.getNewPath());
227 }
228 }
229 }
230
231 @Test
232 public void commitSubmoduleUpdate() throws Exception {
233 try (Git git = new Git(db)) {
234 writeTrashFile("file.txt", "content");
235 git.add().addFilepattern("file.txt").call();
236 RevCommit commit = git.commit().setMessage("create file").call();
237 writeTrashFile("file.txt", "content2");
238 git.add().addFilepattern("file.txt").call();
239 RevCommit commit2 = git.commit().setMessage("edit file").call();
240
241 SubmoduleAddCommand command = new SubmoduleAddCommand(db);
242 String path = "sub";
243 command.setPath(path);
244 String uri = db.getDirectory().toURI().toString();
245 command.setURI(uri);
246 Repository repo = command.call();
247 assertNotNull(repo);
248 addRepoToClose(repo);
249
250 try (SubmoduleWalk generator = SubmoduleWalk.forIndex(db)) {
251 assertTrue(generator.next());
252 assertEquals(path, generator.getPath());
253 assertEquals(commit2, generator.getObjectId());
254 assertEquals(uri, generator.getModulesUrl());
255 assertEquals(path, generator.getModulesPath());
256 assertEquals(uri, generator.getConfigUrl());
257 try (Repository subModRepo = generator.getRepository()) {
258 assertNotNull(subModRepo);
259 }
260 }
261 assertEquals(commit2, repo.resolve(Constants.HEAD));
262
263 RevCommit submoduleAddCommit = git.commit().setMessage("submodule add")
264 .setOnly(path).call();
265 assertNotNull(submoduleAddCommit);
266
267 RefUpdate update = repo.updateRef(Constants.HEAD);
268 update.setNewObjectId(commit);
269 assertEquals(Result.FORCED, update.forceUpdate());
270
271 RevCommit submoduleEditCommit = git.commit()
272 .setMessage("submodule add").setOnly(path).call();
273 assertNotNull(submoduleEditCommit);
274 try (TreeWalk walk = new TreeWalk(db)) {
275 walk.addTree(submoduleAddCommit.getTree());
276 walk.addTree(submoduleEditCommit.getTree());
277 walk.setFilter(TreeFilter.ANY_DIFF);
278 List<DiffEntry> diffs = DiffEntry.scan(walk);
279 assertEquals(1, diffs.size());
280 DiffEntry subDiff = diffs.get(0);
281 assertEquals(FileMode.GITLINK, subDiff.getOldMode());
282 assertEquals(FileMode.GITLINK, subDiff.getNewMode());
283 assertEquals(commit2, subDiff.getOldId().toObjectId());
284 assertEquals(commit, subDiff.getNewId().toObjectId());
285 assertEquals(path, subDiff.getNewPath());
286 assertEquals(path, subDiff.getOldPath());
287 }
288 }
289 }
290
291 @Ignore("very flaky when run with Hudson")
292 @Test
293 public void commitUpdatesSmudgedEntries() throws Exception {
294 try (Git git = new Git(db)) {
295 File file1 = writeTrashFile("file1.txt", "content1");
296 TimeUtil.setLastModifiedWithOffset(file1.toPath(), -5000L);
297 File file2 = writeTrashFile("file2.txt", "content2");
298 TimeUtil.setLastModifiedWithOffset(file2.toPath(), -5000L);
299 File file3 = writeTrashFile("file3.txt", "content3");
300 TimeUtil.setLastModifiedWithOffset(file3.toPath(), -5000L);
301
302 assertNotNull(git.add().addFilepattern("file1.txt")
303 .addFilepattern("file2.txt").addFilepattern("file3.txt").call());
304 RevCommit commit = git.commit().setMessage("add files").call();
305 assertNotNull(commit);
306
307 DirCache cache = DirCache.read(db.getIndexFile(), db.getFS());
308 int file1Size = cache.getEntry("file1.txt").getLength();
309 int file2Size = cache.getEntry("file2.txt").getLength();
310 int file3Size = cache.getEntry("file3.txt").getLength();
311 ObjectId file2Id = cache.getEntry("file2.txt").getObjectId();
312 ObjectId file3Id = cache.getEntry("file3.txt").getObjectId();
313 assertTrue(file1Size > 0);
314 assertTrue(file2Size > 0);
315 assertTrue(file3Size > 0);
316
317
318 cache = DirCache.lock(db.getIndexFile(), db.getFS());
319 cache.getEntry("file1.txt").setLength(0);
320 cache.getEntry("file2.txt").setLength(0);
321 cache.getEntry("file3.txt").setLength(0);
322 cache.write();
323 assertTrue(cache.commit());
324
325
326 cache = DirCache.read(db.getIndexFile(), db.getFS());
327 assertEquals(0, cache.getEntry("file1.txt").getLength());
328 assertEquals(0, cache.getEntry("file2.txt").getLength());
329 assertEquals(0, cache.getEntry("file3.txt").getLength());
330
331 TimeUtil.setLastModifiedWithOffset(db.getIndexFile().toPath(),
332 -5000L);
333
334 write(file1, "content4");
335
336 TimeUtil.setLastModifiedWithOffset(file1.toPath(), 2500L);
337 assertNotNull(git.commit().setMessage("edit file").setOnly("file1.txt")
338 .call());
339
340 cache = db.readDirCache();
341 assertEquals(file1Size, cache.getEntry("file1.txt").getLength());
342 assertEquals(file2Size, cache.getEntry("file2.txt").getLength());
343 assertEquals(file3Size, cache.getEntry("file3.txt").getLength());
344 assertEquals(file2Id, cache.getEntry("file2.txt").getObjectId());
345 assertEquals(file3Id, cache.getEntry("file3.txt").getObjectId());
346 }
347 }
348
349 @Ignore("very flaky when run with Hudson")
350 @Test
351 public void commitIgnoresSmudgedEntryWithDifferentId() throws Exception {
352 try (Git git = new Git(db)) {
353 File file1 = writeTrashFile("file1.txt", "content1");
354 TimeUtil.setLastModifiedWithOffset(file1.toPath(), -5000L);
355 File file2 = writeTrashFile("file2.txt", "content2");
356 TimeUtil.setLastModifiedWithOffset(file2.toPath(), -5000L);
357
358 assertNotNull(git.add().addFilepattern("file1.txt")
359 .addFilepattern("file2.txt").call());
360 RevCommit commit = git.commit().setMessage("add files").call();
361 assertNotNull(commit);
362
363 DirCache cache = DirCache.read(db.getIndexFile(), db.getFS());
364 int file1Size = cache.getEntry("file1.txt").getLength();
365 int file2Size = cache.getEntry("file2.txt").getLength();
366 assertTrue(file1Size > 0);
367 assertTrue(file2Size > 0);
368
369 writeTrashFile("file2.txt", "content3");
370 assertNotNull(git.add().addFilepattern("file2.txt").call());
371 writeTrashFile("file2.txt", "content4");
372
373
374 cache = DirCache.lock(db.getIndexFile(), db.getFS());
375 cache.getEntry("file1.txt").setLength(0);
376 cache.getEntry("file2.txt").setLength(0);
377 cache.write();
378 assertTrue(cache.commit());
379
380
381 cache = db.readDirCache();
382 assertEquals(0, cache.getEntry("file1.txt").getLength());
383 assertEquals(0, cache.getEntry("file2.txt").getLength());
384
385 TimeUtil.setLastModifiedWithOffset(db.getIndexFile().toPath(),
386 -5000L);
387
388 write(file1, "content5");
389 TimeUtil.setLastModifiedWithOffset(file1.toPath(), 1000L);
390
391 assertNotNull(git.commit().setMessage("edit file").setOnly("file1.txt")
392 .call());
393
394 cache = db.readDirCache();
395 assertEquals(file1Size, cache.getEntry("file1.txt").getLength());
396 assertEquals(0, cache.getEntry("file2.txt").getLength());
397 }
398 }
399
400 @Test
401 public void commitAfterSquashMerge() throws Exception {
402 try (Git git = new Git(db)) {
403 writeTrashFile("file1", "file1");
404 git.add().addFilepattern("file1").call();
405 RevCommit first = git.commit().setMessage("initial commit").call();
406
407 assertTrue(new File(db.getWorkTree(), "file1").exists());
408 createBranch(first, "refs/heads/branch1");
409 checkoutBranch("refs/heads/branch1");
410
411 writeTrashFile("file2", "file2");
412 git.add().addFilepattern("file2").call();
413 git.commit().setMessage("second commit").call();
414 assertTrue(new File(db.getWorkTree(), "file2").exists());
415
416 checkoutBranch("refs/heads/master");
417
418 MergeResult result = git.merge()
419 .include(db.exactRef("refs/heads/branch1"))
420 .setSquash(true)
421 .call();
422
423 assertTrue(new File(db.getWorkTree(), "file1").exists());
424 assertTrue(new File(db.getWorkTree(), "file2").exists());
425 assertEquals(MergeResult.MergeStatus.FAST_FORWARD_SQUASHED,
426 result.getMergeStatus());
427
428
429 RevCommit squashedCommit = git.commit().call();
430
431 assertEquals(1, squashedCommit.getParentCount());
432 assertNull(db.readSquashCommitMsg());
433 assertEquals("commit: Squashed commit of the following:", db
434 .getReflogReader(Constants.HEAD).getLastEntry().getComment());
435 assertEquals("commit: Squashed commit of the following:", db
436 .getReflogReader(db.getBranch()).getLastEntry().getComment());
437 }
438 }
439
440 @Test
441 public void testReflogs() throws Exception {
442 try (Git git = new Git(db)) {
443 writeTrashFile("f", "1");
444 git.add().addFilepattern("f").call();
445 git.commit().setMessage("c1").call();
446 writeTrashFile("f", "2");
447 git.commit().setMessage("c2").setAll(true).setReflogComment(null)
448 .call();
449 writeTrashFile("f", "3");
450 git.commit().setMessage("c3").setAll(true)
451 .setReflogComment("testRl").call();
452
453 db.getReflogReader(Constants.HEAD).getReverseEntries();
454
455 assertEquals("testRl;commit (initial): c1;", reflogComments(
456 db.getReflogReader(Constants.HEAD).getReverseEntries()));
457 assertEquals("testRl;commit (initial): c1;", reflogComments(
458 db.getReflogReader(db.getBranch()).getReverseEntries()));
459 }
460 }
461
462 private static String reflogComments(List<ReflogEntry> entries) {
463 StringBuilder b = new StringBuilder();
464 for (ReflogEntry e : entries) {
465 b.append(e.getComment()).append(";");
466 }
467 return b.toString();
468 }
469
470 @Test(expected = WrongRepositoryStateException.class)
471 public void commitAmendOnInitialShouldFail() throws Exception {
472 try (Git git = new Git(db)) {
473 git.commit().setAmend(true).setMessage("initial commit").call();
474 }
475 }
476
477 @Test
478 public void commitAmendWithoutAuthorShouldSetOriginalAuthorAndAuthorTime()
479 throws Exception {
480 try (Git git = new Git(db)) {
481 writeTrashFile("file1", "file1");
482 git.add().addFilepattern("file1").call();
483
484 final String authorName = "First Author";
485 final String authorEmail = "author@example.org";
486 final Date authorDate = new Date(1349621117000L);
487 PersonIdent firstAuthor = new PersonIdent(authorName, authorEmail,
488 authorDate, TimeZone.getTimeZone("UTC"));
489 git.commit().setMessage("initial commit").setAuthor(firstAuthor).call();
490
491 RevCommit amended = git.commit().setAmend(true)
492 .setMessage("amend commit").call();
493
494 PersonIdent amendedAuthor = amended.getAuthorIdent();
495 assertEquals(authorName, amendedAuthor.getName());
496 assertEquals(authorEmail, amendedAuthor.getEmailAddress());
497 assertEquals(authorDate.getTime(), amendedAuthor.getWhen().getTime());
498 }
499 }
500
501 @Test
502 public void commitAmendWithAuthorShouldUseIt() throws Exception {
503 try (Git git = new Git(db)) {
504 writeTrashFile("file1", "file1");
505 git.add().addFilepattern("file1").call();
506 git.commit().setMessage("initial commit").call();
507
508 RevCommit amended = git.commit().setAmend(true)
509 .setAuthor("New Author", "newauthor@example.org")
510 .setMessage("amend commit").call();
511
512 PersonIdent amendedAuthor = amended.getAuthorIdent();
513 assertEquals("New Author", amendedAuthor.getName());
514 assertEquals("newauthor@example.org", amendedAuthor.getEmailAddress());
515 }
516 }
517
518 @Test
519 public void commitMessageVerbatim() throws Exception {
520 try (Git git = new Git(db)) {
521 writeTrashFile("file1", "file1");
522 git.add().addFilepattern("file1").call();
523 RevCommit committed = git.commit().setMessage("#initial commit")
524 .call();
525
526 assertEquals("#initial commit", committed.getFullMessage());
527 }
528 }
529
530 @Test
531 public void commitMessageStrip() throws Exception {
532 try (Git git = new Git(db)) {
533 writeTrashFile("file1", "file1");
534 git.add().addFilepattern("file1").call();
535 RevCommit committed = git.commit().setMessage(
536 "#Comment\ninitial commit\t\n\n commit body \n \t#another comment")
537 .setCleanupMode(CleanupMode.STRIP).call();
538
539 assertEquals("initial commit\n\n commit body",
540 committed.getFullMessage());
541 }
542 }
543
544 @Test
545 public void commitMessageDefault() throws Exception {
546 try (Git git = new Git(db)) {
547 writeTrashFile("file1", "file1");
548 git.add().addFilepattern("file1").call();
549 RevCommit committed = git.commit().setMessage(
550 "#Comment\ninitial commit\t\n\n commit body \n\n\n \t#another comment ")
551 .setCleanupMode(CleanupMode.DEFAULT).call();
552
553 assertEquals("initial commit\n\n commit body",
554 committed.getFullMessage());
555 }
556 }
557
558 @Test
559 public void commitMessageDefaultWhitespace() throws Exception {
560 try (Git git = new Git(db)) {
561 writeTrashFile("file1", "file1");
562 git.add().addFilepattern("file1").call();
563 RevCommit committed = git.commit().setMessage(
564 "#Comment\ninitial commit\t\n\n commit body \n\n\n \t#another comment ")
565 .setCleanupMode(CleanupMode.DEFAULT).setDefaultClean(false)
566 .call();
567
568 assertEquals(
569 "#Comment\ninitial commit\n\n commit body\n\n \t#another comment",
570 committed.getFullMessage());
571 }
572 }
573
574 @Test
575 public void commitEmptyCommits() throws Exception {
576 try (Git git = new Git(db)) {
577
578 writeTrashFile("file1", "file1");
579 git.add().addFilepattern("file1").call();
580 RevCommit initial = git.commit().setMessage("initial commit")
581 .call();
582
583 RevCommit emptyFollowUp = git.commit()
584 .setAuthor("New Author", "newauthor@example.org")
585 .setMessage("no change").call();
586
587 assertNotEquals(initial.getId(), emptyFollowUp.getId());
588 assertEquals(initial.getTree().getId(),
589 emptyFollowUp.getTree().getId());
590
591 try {
592 git.commit().setAuthor("New Author", "newauthor@example.org")
593 .setMessage("again no change").setAllowEmpty(false)
594 .call();
595 fail("Didn't get the expected EmptyCommitException");
596 } catch (EmptyCommitException e) {
597
598 }
599
600
601 git.commit().setAuthor("New Author", "newauthor@example.org")
602 .setMessage("again no change").setOnly("file1")
603 .setAllowEmpty(true).call();
604 }
605 }
606
607 @Test
608 public void commitOnlyShouldCommitUnmergedPathAndNotAffectOthers()
609 throws Exception {
610 DirCache index = db.lockDirCache();
611 DirCacheBuilder builder = index.builder();
612 addUnmergedEntry("unmerged1", builder);
613 addUnmergedEntry("unmerged2", builder);
614 DirCacheEntry other = new DirCacheEntry("other");
615 other.setFileMode(FileMode.REGULAR_FILE);
616 builder.add(other);
617 builder.commit();
618
619 writeTrashFile("unmerged1", "unmerged1 data");
620 writeTrashFile("unmerged2", "unmerged2 data");
621 writeTrashFile("other", "other data");
622
623 assertEquals("[other, mode:100644]"
624 + "[unmerged1, mode:100644, stage:1]"
625 + "[unmerged1, mode:100644, stage:2]"
626 + "[unmerged1, mode:100644, stage:3]"
627 + "[unmerged2, mode:100644, stage:1]"
628 + "[unmerged2, mode:100644, stage:2]"
629 + "[unmerged2, mode:100644, stage:3]",
630 indexState(0));
631
632 try (Git git = new Git(db)) {
633 RevCommit commit = git.commit().setOnly("unmerged1")
634 .setMessage("Only one file").call();
635
636 assertEquals("[other, mode:100644]" + "[unmerged1, mode:100644]"
637 + "[unmerged2, mode:100644, stage:1]"
638 + "[unmerged2, mode:100644, stage:2]"
639 + "[unmerged2, mode:100644, stage:3]",
640 indexState(0));
641
642 try (TreeWalk walk = TreeWalk.forPath(db, "unmerged1", commit.getTree())) {
643 assertEquals(FileMode.REGULAR_FILE, walk.getFileMode(0));
644 }
645 }
646 }
647
648 @Test
649 public void commitOnlyShouldHandleIgnored() throws Exception {
650 try (Git git = new Git(db)) {
651 writeTrashFile("subdir/foo", "Hello World");
652 writeTrashFile("subdir/bar", "Hello World");
653 writeTrashFile(".gitignore", "bar");
654 git.add().addFilepattern("subdir").call();
655 git.commit().setOnly("subdir").setMessage("first commit").call();
656 assertEquals("[subdir/foo, mode:100644, content:Hello World]",
657 indexState(CONTENT));
658 }
659 }
660
661 private void nonNormalizedIndexTest(boolean executable) throws Exception {
662 String mode = executable ? "100755" : "100644";
663 try (Git git = new Git(db)) {
664
665 FileBasedConfig config = db.getConfig();
666 config.setString("core", null, "autocrlf", "false");
667 config.save();
668 File testFile = writeTrashFile("file.txt", "line 1\r\nline 2\r\n");
669 if (executable) {
670 FS.DETECTED.setExecute(testFile, true);
671 }
672 git.add().addFilepattern("file.txt").call();
673 git.commit().setMessage("Initial").call();
674 assertEquals(
675 "[file.txt, mode:" + mode
676 + ", content:line 1\r\nline 2\r\n]",
677 indexState(CONTENT));
678 config.setString("core", null, "autocrlf", "true");
679 config.save();
680 writeTrashFile("file.txt", "line 1\r\nline 1.5\r\nline 2\r\n");
681 testFile = writeTrashFile("file2.txt", "new\r\nfile\r\n");
682 if (executable) {
683 FS.DETECTED.setExecute(testFile, true);
684 }
685 git.add().addFilepattern("file.txt").addFilepattern("file2.txt")
686 .call();
687 git.commit().setMessage("Second").call();
688 assertEquals(
689 "[file.txt, mode:" + mode
690 + ", content:line 1\r\nline 1.5\r\nline 2\r\n]"
691 + "[file2.txt, mode:" + mode
692 + ", content:new\nfile\n]",
693 indexState(CONTENT));
694 writeTrashFile("file2.txt", "new\r\nfile\r\ncontent\r\n");
695 git.add().addFilepattern("file2.txt").call();
696 git.commit().setMessage("Third").call();
697 assertEquals(
698 "[file.txt, mode:" + mode
699 + ", content:line 1\r\nline 1.5\r\nline 2\r\n]"
700 + "[file2.txt, mode:" + mode
701 + ", content:new\nfile\ncontent\n]",
702 indexState(CONTENT));
703 }
704 }
705
706 @Test
707 public void commitWithAutoCrlfAndNonNormalizedIndex() throws Exception {
708 nonNormalizedIndexTest(false);
709 }
710
711 @Test
712 public void commitExecutableWithAutoCrlfAndNonNormalizedIndex()
713 throws Exception {
714 assumeTrue(FS.DETECTED.supportsExecute());
715 nonNormalizedIndexTest(true);
716 }
717
718 @Test
719 public void testDeletionConflictWithAutoCrlf() throws Exception {
720 try (Git git = new Git(db)) {
721
722 FileBasedConfig config = db.getConfig();
723 config.setString("core", null, "autocrlf", "false");
724 config.save();
725 File file = writeTrashFile("file.txt", "foo\r\n");
726 git.add().addFilepattern("file.txt").call();
727 git.commit().setMessage("Initial").call();
728
729 git.checkout().setCreateBranch(true).setName("side").call();
730 assertTrue(file.delete());
731 git.rm().addFilepattern("file.txt").call();
732 git.commit().setMessage("Side").call();
733
734 config.setString("core", null, "autocrlf", "true");
735 config.save();
736
737 git.checkout().setName("master").call();
738 writeTrashFile("file.txt", "foob\r\n");
739 git.add().addFilepattern("file.txt").call();
740 assertEquals("[file.txt, mode:100644, content:foob\r\n]",
741 indexState(CONTENT));
742 writeTrashFile("g", "file2.txt", "anything");
743 git.add().addFilepattern("g/file2.txt");
744 RevCommit master = git.commit().setMessage("Second").call();
745
746 git.checkout().setName("side").call();
747
748 CherryPickResult pick = git.cherryPick().include(master).call();
749 assertEquals("Expected a cherry-pick conflict",
750 CherryPickStatus.CONFLICTING, pick.getStatus());
751
752 git.add().addFilepattern("g/file2.txt").call();
753
754 writeTrashFile("file.txt", "foob\r\n");
755 git.add().addFilepattern("file.txt").call();
756 git.commit().setMessage("Cherry").call();
757
758
759 assertEquals(
760 "[file.txt, mode:100644, content:foob\n]"
761 + "[g/file2.txt, mode:100644, content:anything]",
762 indexState(CONTENT));
763 }
764 }
765
766 private void testConflictWithAutoCrlf(String baseLf, String lf)
767 throws Exception {
768 try (Git git = new Git(db)) {
769
770 FileBasedConfig config = db.getConfig();
771 config.setString("core", null, "autocrlf", "false");
772 config.save();
773 writeTrashFile("file.txt", "foo" + baseLf);
774 git.add().addFilepattern("file.txt").call();
775 git.commit().setMessage("Initial").call();
776
777 git.checkout().setCreateBranch(true).setName("side").call();
778 writeTrashFile("file.txt", "bar\r\n");
779 git.add().addFilepattern("file.txt").call();
780 RevCommit side = git.commit().setMessage("Side").call();
781
782 git.checkout().setName("master");
783 writeTrashFile("file.txt", "foob" + lf);
784 git.add().addFilepattern("file.txt").call();
785 git.commit().setMessage("Second").call();
786
787 config.setString("core", null, "autocrlf", "true");
788 config.save();
789
790 CherryPickResult pick = git.cherryPick().include(side).call();
791 assertEquals("Expected a cherry-pick conflict",
792 CherryPickStatus.CONFLICTING, pick.getStatus());
793 writeTrashFile("file.txt", "foobar\r\n");
794 git.add().addFilepattern("file.txt").call();
795 git.commit().setMessage("Second").call();
796 assertEquals("[file.txt, mode:100644, content:foobar" + lf + "]",
797 indexState(CONTENT));
798 }
799 }
800
801 @Test
802 public void commitConflictWithAutoCrlfBaseCrLfOursLf() throws Exception {
803 testConflictWithAutoCrlf("\r\n", "\n");
804 }
805
806 @Test
807 public void commitConflictWithAutoCrlfBaseLfOursLf() throws Exception {
808 testConflictWithAutoCrlf("\n", "\n");
809 }
810
811 @Test
812 public void commitConflictWithAutoCrlfBasCrLfOursCrLf() throws Exception {
813 testConflictWithAutoCrlf("\r\n", "\r\n");
814 }
815
816 @Test
817 public void commitConflictWithAutoCrlfBaseLfOursCrLf() throws Exception {
818 testConflictWithAutoCrlf("\n", "\r\n");
819 }
820
821 private static void addUnmergedEntry(String file, DirCacheBuilder builder) {
822 DirCacheEntry stage1 = new DirCacheEntry(file, DirCacheEntry.STAGE_1);
823 DirCacheEntry stage2 = new DirCacheEntry(file, DirCacheEntry.STAGE_2);
824 DirCacheEntry stage3 = new DirCacheEntry(file, DirCacheEntry.STAGE_3);
825 stage1.setFileMode(FileMode.REGULAR_FILE);
826 stage2.setFileMode(FileMode.REGULAR_FILE);
827 stage3.setFileMode(FileMode.REGULAR_FILE);
828 builder.add(stage1);
829 builder.add(stage2);
830 builder.add(stage3);
831 }
832
833 @Test
834 public void callSignerWithProperSigningKey() throws Exception {
835 try (Git git = new Git(db)) {
836 writeTrashFile("file1", "file1");
837 git.add().addFilepattern("file1").call();
838
839 String[] signingKey = new String[1];
840 PersonIdent[] signingCommitters = new PersonIdent[1];
841 AtomicInteger callCount = new AtomicInteger();
842 GpgSigner.setDefault(new GpgSigner() {
843 @Override
844 public void sign(CommitBuilder commit, String gpgSigningKey,
845 PersonIdent signingCommitter, CredentialsProvider credentialsProvider) {
846 signingKey[0] = gpgSigningKey;
847 signingCommitters[0] = signingCommitter;
848 callCount.incrementAndGet();
849 }
850
851 @Override
852 public boolean canLocateSigningKey(String gpgSigningKey,
853 PersonIdent signingCommitter,
854 CredentialsProvider credentialsProvider)
855 throws CanceledException {
856 return false;
857 }
858 });
859
860
861
862 git.commit().setCommitter(committer).setSign(Boolean.TRUE)
863 .setMessage("initial commit")
864 .call();
865 assertNull(signingKey[0]);
866 assertEquals(1, callCount.get());
867 assertSame(committer, signingCommitters[0]);
868
869 writeTrashFile("file2", "file2");
870 git.add().addFilepattern("file2").call();
871
872
873 String expectedConfigSigningKey = "config-" + System.nanoTime();
874 StoredConfig config = git.getRepository().getConfig();
875 config.setString("user", null, "signingKey",
876 expectedConfigSigningKey);
877 config.save();
878
879 git.commit().setCommitter(committer).setSign(Boolean.TRUE)
880 .setMessage("initial commit")
881 .call();
882 assertEquals(expectedConfigSigningKey, signingKey[0]);
883 assertEquals(2, callCount.get());
884 assertSame(committer, signingCommitters[0]);
885
886 writeTrashFile("file3", "file3");
887 git.add().addFilepattern("file3").call();
888
889
890 String expectedSigningKey = "my-" + System.nanoTime();
891 git.commit().setCommitter(committer).setSign(Boolean.TRUE)
892 .setSigningKey(expectedSigningKey)
893 .setMessage("initial commit").call();
894 assertEquals(expectedSigningKey, signingKey[0]);
895 assertEquals(3, callCount.get());
896 assertSame(committer, signingCommitters[0]);
897 }
898 }
899
900 @Test
901 public void callSignerOnlyWhenSigning() throws Exception {
902 try (Git git = new Git(db)) {
903 writeTrashFile("file1", "file1");
904 git.add().addFilepattern("file1").call();
905
906 AtomicInteger callCount = new AtomicInteger();
907 GpgSigner.setDefault(new GpgSigner() {
908 @Override
909 public void sign(CommitBuilder commit, String gpgSigningKey,
910 PersonIdent signingCommitter, CredentialsProvider credentialsProvider) {
911 callCount.incrementAndGet();
912 }
913
914 @Override
915 public boolean canLocateSigningKey(String gpgSigningKey,
916 PersonIdent signingCommitter,
917 CredentialsProvider credentialsProvider)
918 throws CanceledException {
919 return false;
920 }
921 });
922
923
924
925 git.commit().setMessage("initial commit").call();
926 assertEquals(0, callCount.get());
927
928 writeTrashFile("file2", "file2");
929 git.add().addFilepattern("file2").call();
930
931
932 git.commit().setSign(Boolean.TRUE).setMessage("commit").call();
933 assertEquals(1, callCount.get());
934
935 writeTrashFile("file3", "file3");
936 git.add().addFilepattern("file3").call();
937
938
939 StoredConfig config = git.getRepository().getConfig();
940 config.setBoolean("commit", null, "gpgSign", true);
941 config.save();
942
943 git.commit().setMessage("commit").call();
944 assertEquals(2, callCount.get());
945
946 writeTrashFile("file4", "file4");
947 git.add().addFilepattern("file4").call();
948
949
950 git.commit().setSign(Boolean.FALSE).setMessage("commit").call();
951 assertEquals(2, callCount.get());
952 }
953 }
954 }