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