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.assertNull;
48 import static org.junit.Assert.assertTrue;
49 import static org.junit.Assert.assertNotEquals;
50 import static org.junit.Assert.fail;
51
52 import java.io.File;
53 import java.util.Date;
54 import java.util.List;
55 import java.util.TimeZone;
56
57 import org.eclipse.jgit.api.errors.EmtpyCommitException;
58 import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
59 import org.eclipse.jgit.diff.DiffEntry;
60 import org.eclipse.jgit.dircache.DirCache;
61 import org.eclipse.jgit.dircache.DirCacheBuilder;
62 import org.eclipse.jgit.dircache.DirCacheEntry;
63 import org.eclipse.jgit.junit.RepositoryTestCase;
64 import org.eclipse.jgit.lib.ConfigConstants;
65 import org.eclipse.jgit.lib.Constants;
66 import org.eclipse.jgit.lib.FileMode;
67 import org.eclipse.jgit.lib.ObjectId;
68 import org.eclipse.jgit.lib.PersonIdent;
69 import org.eclipse.jgit.lib.RefUpdate;
70 import org.eclipse.jgit.lib.RefUpdate.Result;
71 import org.eclipse.jgit.lib.Repository;
72 import org.eclipse.jgit.lib.StoredConfig;
73 import org.eclipse.jgit.revwalk.RevCommit;
74 import org.eclipse.jgit.submodule.SubmoduleWalk;
75 import org.eclipse.jgit.treewalk.TreeWalk;
76 import org.eclipse.jgit.treewalk.filter.TreeFilter;
77 import org.eclipse.jgit.util.FS;
78 import org.junit.Test;
79
80
81
82
83 public class CommitCommandTest extends RepositoryTestCase {
84
85 @Test
86 public void testExecutableRetention() throws Exception {
87 StoredConfig config = db.getConfig();
88 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
89 ConfigConstants.CONFIG_KEY_FILEMODE, true);
90 config.save();
91
92 FS executableFs = new FS() {
93
94 public boolean supportsExecute() {
95 return true;
96 }
97
98 public boolean setExecute(File f, boolean canExec) {
99 return true;
100 }
101
102 public ProcessBuilder runInShell(String cmd, String[] args) {
103 return null;
104 }
105
106 public boolean retryFailedLockFileCommit() {
107 return false;
108 }
109
110 public FS newInstance() {
111 return this;
112 }
113
114 protected File discoverGitExe() {
115 return null;
116 }
117
118 public boolean canExecute(File f) {
119 return true;
120 }
121
122 @Override
123 public boolean isCaseSensitive() {
124 return true;
125 }
126 };
127
128 Git git = Git.open(db.getDirectory(), executableFs);
129 String path = "a.txt";
130 writeTrashFile(path, "content");
131 git.add().addFilepattern(path).call();
132 RevCommit commit1 = git.commit().setMessage("commit").call();
133 TreeWalk walk = TreeWalk.forPath(db, path, commit1.getTree());
134 assertNotNull(walk);
135 assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
136
137 FS nonExecutableFs = new FS() {
138
139 public boolean supportsExecute() {
140 return false;
141 }
142
143 public boolean setExecute(File f, boolean canExec) {
144 return false;
145 }
146
147 public ProcessBuilder runInShell(String cmd, String[] args) {
148 return null;
149 }
150
151 public boolean retryFailedLockFileCommit() {
152 return false;
153 }
154
155 public FS newInstance() {
156 return this;
157 }
158
159 protected File discoverGitExe() {
160 return null;
161 }
162
163 public boolean canExecute(File f) {
164 return false;
165 }
166
167 @Override
168 public boolean isCaseSensitive() {
169 return true;
170 }
171 };
172
173 config = db.getConfig();
174 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
175 ConfigConstants.CONFIG_KEY_FILEMODE, false);
176 config.save();
177
178 Git git2 = Git.open(db.getDirectory(), nonExecutableFs);
179 writeTrashFile(path, "content2");
180 RevCommit commit2 = git2.commit().setOnly(path).setMessage("commit2")
181 .call();
182 walk = TreeWalk.forPath(db, path, commit2.getTree());
183 assertNotNull(walk);
184 assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
185 }
186
187 @Test
188 public void commitNewSubmodule() throws Exception {
189 try (Git git = new Git(db)) {
190 writeTrashFile("file.txt", "content");
191 git.add().addFilepattern("file.txt").call();
192 RevCommit commit = git.commit().setMessage("create file").call();
193
194 SubmoduleAddCommand command = new SubmoduleAddCommand(db);
195 String path = "sub";
196 command.setPath(path);
197 String uri = db.getDirectory().toURI().toString();
198 command.setURI(uri);
199 Repository repo = command.call();
200 assertNotNull(repo);
201 addRepoToClose(repo);
202
203 SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
204 assertTrue(generator.next());
205 assertEquals(path, generator.getPath());
206 assertEquals(commit, generator.getObjectId());
207 assertEquals(uri, generator.getModulesUrl());
208 assertEquals(path, generator.getModulesPath());
209 assertEquals(uri, generator.getConfigUrl());
210 Repository subModRepo = generator.getRepository();
211 assertNotNull(subModRepo);
212 subModRepo.close();
213 assertEquals(commit, repo.resolve(Constants.HEAD));
214
215 RevCommit submoduleCommit = git.commit().setMessage("submodule add")
216 .setOnly(path).call();
217 assertNotNull(submoduleCommit);
218 try (TreeWalk walk = new TreeWalk(db)) {
219 walk.addTree(commit.getTree());
220 walk.addTree(submoduleCommit.getTree());
221 walk.setFilter(TreeFilter.ANY_DIFF);
222 List<DiffEntry> diffs = DiffEntry.scan(walk);
223 assertEquals(1, diffs.size());
224 DiffEntry subDiff = diffs.get(0);
225 assertEquals(FileMode.MISSING, subDiff.getOldMode());
226 assertEquals(FileMode.GITLINK, subDiff.getNewMode());
227 assertEquals(ObjectId.zeroId(), subDiff.getOldId().toObjectId());
228 assertEquals(commit, subDiff.getNewId().toObjectId());
229 assertEquals(path, subDiff.getNewPath());
230 }
231 }
232 }
233
234 @Test
235 public void commitSubmoduleUpdate() throws Exception {
236 try (Git git = new Git(db)) {
237 writeTrashFile("file.txt", "content");
238 git.add().addFilepattern("file.txt").call();
239 RevCommit commit = git.commit().setMessage("create file").call();
240 writeTrashFile("file.txt", "content2");
241 git.add().addFilepattern("file.txt").call();
242 RevCommit commit2 = git.commit().setMessage("edit file").call();
243
244 SubmoduleAddCommand command = new SubmoduleAddCommand(db);
245 String path = "sub";
246 command.setPath(path);
247 String uri = db.getDirectory().toURI().toString();
248 command.setURI(uri);
249 Repository repo = command.call();
250 assertNotNull(repo);
251 addRepoToClose(repo);
252
253 SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
254 assertTrue(generator.next());
255 assertEquals(path, generator.getPath());
256 assertEquals(commit2, generator.getObjectId());
257 assertEquals(uri, generator.getModulesUrl());
258 assertEquals(path, generator.getModulesPath());
259 assertEquals(uri, generator.getConfigUrl());
260 Repository subModRepo = generator.getRepository();
261 assertNotNull(subModRepo);
262 subModRepo.close();
263 assertEquals(commit2, repo.resolve(Constants.HEAD));
264
265 RevCommit submoduleAddCommit = git.commit().setMessage("submodule add")
266 .setOnly(path).call();
267 assertNotNull(submoduleAddCommit);
268
269 RefUpdate update = repo.updateRef(Constants.HEAD);
270 update.setNewObjectId(commit);
271 assertEquals(Result.FORCED, update.forceUpdate());
272
273 RevCommit submoduleEditCommit = git.commit()
274 .setMessage("submodule add").setOnly(path).call();
275 assertNotNull(submoduleEditCommit);
276 try (TreeWalk walk = new TreeWalk(db)) {
277 walk.addTree(submoduleAddCommit.getTree());
278 walk.addTree(submoduleEditCommit.getTree());
279 walk.setFilter(TreeFilter.ANY_DIFF);
280 List<DiffEntry> diffs = DiffEntry.scan(walk);
281 assertEquals(1, diffs.size());
282 DiffEntry subDiff = diffs.get(0);
283 assertEquals(FileMode.GITLINK, subDiff.getOldMode());
284 assertEquals(FileMode.GITLINK, subDiff.getNewMode());
285 assertEquals(commit2, subDiff.getOldId().toObjectId());
286 assertEquals(commit, subDiff.getNewId().toObjectId());
287 assertEquals(path, subDiff.getNewPath());
288 assertEquals(path, subDiff.getOldPath());
289 }
290 }
291 }
292
293 @Test
294 public void commitUpdatesSmudgedEntries() throws Exception {
295 try (Git git = new Git(db)) {
296 File file1 = writeTrashFile("file1.txt", "content1");
297 assertTrue(file1.setLastModified(file1.lastModified() - 5000));
298 File file2 = writeTrashFile("file2.txt", "content2");
299 assertTrue(file2.setLastModified(file2.lastModified() - 5000));
300 File file3 = writeTrashFile("file3.txt", "content3");
301 assertTrue(file3.setLastModified(file3.lastModified() - 5000));
302
303 assertNotNull(git.add().addFilepattern("file1.txt")
304 .addFilepattern("file2.txt").addFilepattern("file3.txt").call());
305 RevCommit commit = git.commit().setMessage("add files").call();
306 assertNotNull(commit);
307
308 DirCache cache = DirCache.read(db.getIndexFile(), db.getFS());
309 int file1Size = cache.getEntry("file1.txt").getLength();
310 int file2Size = cache.getEntry("file2.txt").getLength();
311 int file3Size = cache.getEntry("file3.txt").getLength();
312 ObjectId file2Id = cache.getEntry("file2.txt").getObjectId();
313 ObjectId file3Id = cache.getEntry("file3.txt").getObjectId();
314 assertTrue(file1Size > 0);
315 assertTrue(file2Size > 0);
316 assertTrue(file3Size > 0);
317
318
319 cache = DirCache.lock(db.getIndexFile(), db.getFS());
320 cache.getEntry("file1.txt").setLength(0);
321 cache.getEntry("file2.txt").setLength(0);
322 cache.getEntry("file3.txt").setLength(0);
323 cache.write();
324 assertTrue(cache.commit());
325
326
327 cache = DirCache.read(db.getIndexFile(), db.getFS());
328 assertEquals(0, cache.getEntry("file1.txt").getLength());
329 assertEquals(0, cache.getEntry("file2.txt").getLength());
330 assertEquals(0, cache.getEntry("file3.txt").getLength());
331
332 long indexTime = db.getIndexFile().lastModified();
333 db.getIndexFile().setLastModified(indexTime - 5000);
334
335 write(file1, "content4");
336 assertTrue(file1.setLastModified(file1.lastModified() + 2500));
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 @Test
350 public void commitIgnoresSmudgedEntryWithDifferentId() throws Exception {
351 try (Git git = new Git(db)) {
352 File file1 = writeTrashFile("file1.txt", "content1");
353 assertTrue(file1.setLastModified(file1.lastModified() - 5000));
354 File file2 = writeTrashFile("file2.txt", "content2");
355 assertTrue(file2.setLastModified(file2.lastModified() - 5000));
356
357 assertNotNull(git.add().addFilepattern("file1.txt")
358 .addFilepattern("file2.txt").call());
359 RevCommit commit = git.commit().setMessage("add files").call();
360 assertNotNull(commit);
361
362 DirCache cache = DirCache.read(db.getIndexFile(), db.getFS());
363 int file1Size = cache.getEntry("file1.txt").getLength();
364 int file2Size = cache.getEntry("file2.txt").getLength();
365 assertTrue(file1Size > 0);
366 assertTrue(file2Size > 0);
367
368 writeTrashFile("file2.txt", "content3");
369 assertNotNull(git.add().addFilepattern("file2.txt").call());
370 writeTrashFile("file2.txt", "content4");
371
372
373 cache = DirCache.lock(db.getIndexFile(), db.getFS());
374 cache.getEntry("file1.txt").setLength(0);
375 cache.getEntry("file2.txt").setLength(0);
376 cache.write();
377 assertTrue(cache.commit());
378
379
380 cache = db.readDirCache();
381 assertEquals(0, cache.getEntry("file1.txt").getLength());
382 assertEquals(0, cache.getEntry("file2.txt").getLength());
383
384 long indexTime = db.getIndexFile().lastModified();
385 db.getIndexFile().setLastModified(indexTime - 5000);
386
387 write(file1, "content5");
388 assertTrue(file1.setLastModified(file1.lastModified() + 1000));
389
390 assertNotNull(git.commit().setMessage("edit file").setOnly("file1.txt")
391 .call());
392
393 cache = db.readDirCache();
394 assertEquals(file1Size, cache.getEntry("file1.txt").getLength());
395 assertEquals(0, cache.getEntry("file2.txt").getLength());
396 }
397 }
398
399 @Test
400 public void commitAfterSquashMerge() throws Exception {
401 try (Git git = new Git(db)) {
402 writeTrashFile("file1", "file1");
403 git.add().addFilepattern("file1").call();
404 RevCommit first = git.commit().setMessage("initial commit").call();
405
406 assertTrue(new File(db.getWorkTree(), "file1").exists());
407 createBranch(first, "refs/heads/branch1");
408 checkoutBranch("refs/heads/branch1");
409
410 writeTrashFile("file2", "file2");
411 git.add().addFilepattern("file2").call();
412 git.commit().setMessage("second commit").call();
413 assertTrue(new File(db.getWorkTree(), "file2").exists());
414
415 checkoutBranch("refs/heads/master");
416
417 MergeResult result = git.merge()
418 .include(db.exactRef("refs/heads/branch1"))
419 .setSquash(true)
420 .call();
421
422 assertTrue(new File(db.getWorkTree(), "file1").exists());
423 assertTrue(new File(db.getWorkTree(), "file2").exists());
424 assertEquals(MergeResult.MergeStatus.FAST_FORWARD_SQUASHED,
425 result.getMergeStatus());
426
427
428 RevCommit squashedCommit = git.commit().call();
429
430 assertEquals(1, squashedCommit.getParentCount());
431 assertNull(db.readSquashCommitMsg());
432 assertEquals("commit: Squashed commit of the following:", db
433 .getReflogReader(Constants.HEAD).getLastEntry().getComment());
434 assertEquals("commit: Squashed commit of the following:", db
435 .getReflogReader(db.getBranch()).getLastEntry().getComment());
436 }
437 }
438
439 @Test(expected = WrongRepositoryStateException.class)
440 public void commitAmendOnInitialShouldFail() throws Exception {
441 try (Git git = new Git(db)) {
442 git.commit().setAmend(true).setMessage("initial commit").call();
443 }
444 }
445
446 @Test
447 public void commitAmendWithoutAuthorShouldSetOriginalAuthorAndAuthorTime()
448 throws Exception {
449 try (Git git = new Git(db)) {
450 writeTrashFile("file1", "file1");
451 git.add().addFilepattern("file1").call();
452
453 final String authorName = "First Author";
454 final String authorEmail = "author@example.org";
455 final Date authorDate = new Date(1349621117000L);
456 PersonIdent firstAuthor = new PersonIdent(authorName, authorEmail,
457 authorDate, TimeZone.getTimeZone("UTC"));
458 git.commit().setMessage("initial commit").setAuthor(firstAuthor).call();
459
460 RevCommit amended = git.commit().setAmend(true)
461 .setMessage("amend commit").call();
462
463 PersonIdent amendedAuthor = amended.getAuthorIdent();
464 assertEquals(authorName, amendedAuthor.getName());
465 assertEquals(authorEmail, amendedAuthor.getEmailAddress());
466 assertEquals(authorDate.getTime(), amendedAuthor.getWhen().getTime());
467 }
468 }
469
470 @Test
471 public void commitAmendWithAuthorShouldUseIt() throws Exception {
472 try (Git git = new Git(db)) {
473 writeTrashFile("file1", "file1");
474 git.add().addFilepattern("file1").call();
475 git.commit().setMessage("initial commit").call();
476
477 RevCommit amended = git.commit().setAmend(true)
478 .setAuthor("New Author", "newauthor@example.org")
479 .setMessage("amend commit").call();
480
481 PersonIdent amendedAuthor = amended.getAuthorIdent();
482 assertEquals("New Author", amendedAuthor.getName());
483 assertEquals("newauthor@example.org", amendedAuthor.getEmailAddress());
484 }
485 }
486
487 @Test
488 public void commitEmptyCommits() throws Exception {
489 try (Git git = new Git(db)) {
490
491 writeTrashFile("file1", "file1");
492 git.add().addFilepattern("file1").call();
493 RevCommit initial = git.commit().setMessage("initial commit")
494 .call();
495
496 RevCommit emptyFollowUp = git.commit()
497 .setAuthor("New Author", "newauthor@example.org")
498 .setMessage("no change").call();
499
500 assertNotEquals(initial.getId(), emptyFollowUp.getId());
501 assertEquals(initial.getTree().getId(),
502 emptyFollowUp.getTree().getId());
503
504 try {
505 git.commit().setAuthor("New Author", "newauthor@example.org")
506 .setMessage("again no change").setAllowEmpty(false)
507 .call();
508 fail("Didn't get the expected EmtpyCommitException");
509 } catch (EmtpyCommitException e) {
510
511 }
512 }
513 }
514
515 @Test
516 public void commitOnlyShouldCommitUnmergedPathAndNotAffectOthers()
517 throws Exception {
518 DirCache index = db.lockDirCache();
519 DirCacheBuilder builder = index.builder();
520 addUnmergedEntry("unmerged1", builder);
521 addUnmergedEntry("unmerged2", builder);
522 DirCacheEntry other = new DirCacheEntry("other");
523 other.setFileMode(FileMode.REGULAR_FILE);
524 builder.add(other);
525 builder.commit();
526
527 writeTrashFile("unmerged1", "unmerged1 data");
528 writeTrashFile("unmerged2", "unmerged2 data");
529 writeTrashFile("other", "other data");
530
531 assertEquals("[other, mode:100644]"
532 + "[unmerged1, mode:100644, stage:1]"
533 + "[unmerged1, mode:100644, stage:2]"
534 + "[unmerged1, mode:100644, stage:3]"
535 + "[unmerged2, mode:100644, stage:1]"
536 + "[unmerged2, mode:100644, stage:2]"
537 + "[unmerged2, mode:100644, stage:3]",
538 indexState(0));
539
540 try (Git git = new Git(db)) {
541 RevCommit commit = git.commit().setOnly("unmerged1")
542 .setMessage("Only one file").call();
543
544 assertEquals("[other, mode:100644]" + "[unmerged1, mode:100644]"
545 + "[unmerged2, mode:100644, stage:1]"
546 + "[unmerged2, mode:100644, stage:2]"
547 + "[unmerged2, mode:100644, stage:3]",
548 indexState(0));
549
550 try (TreeWalk walk = TreeWalk.forPath(db, "unmerged1", commit.getTree())) {
551 assertEquals(FileMode.REGULAR_FILE, walk.getFileMode(0));
552 }
553 }
554 }
555
556 @Test
557 public void commitOnlyShouldHandleIgnored() throws Exception {
558 try (Git git = new Git(db)) {
559 writeTrashFile("subdir/foo", "Hello World");
560 writeTrashFile("subdir/bar", "Hello World");
561 writeTrashFile(".gitignore", "bar");
562 git.add().addFilepattern("subdir").call();
563 git.commit().setOnly("subdir").setMessage("first commit").call();
564 }
565 }
566
567 private static void addUnmergedEntry(String file, DirCacheBuilder builder) {
568 DirCacheEntry stage1 = new DirCacheEntry(file, DirCacheEntry.STAGE_1);
569 DirCacheEntry stage2 = new DirCacheEntry(file, DirCacheEntry.STAGE_2);
570 DirCacheEntry stage3 = new DirCacheEntry(file, DirCacheEntry.STAGE_3);
571 stage1.setFileMode(FileMode.REGULAR_FILE);
572 stage2.setFileMode(FileMode.REGULAR_FILE);
573 stage3.setFileMode(FileMode.REGULAR_FILE);
574 builder.add(stage1);
575 builder.add(stage2);
576 builder.add(stage3);
577 }
578 }