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
44 package org.eclipse.jgit.api;
45
46 import static org.eclipse.jgit.util.FileUtils.RECURSIVE;
47 import static org.junit.Assert.assertEquals;
48 import static org.junit.Assert.assertTrue;
49 import static org.junit.Assert.fail;
50
51 import java.io.File;
52 import java.io.FileInputStream;
53 import java.io.IOException;
54 import java.io.PrintWriter;
55 import java.util.Set;
56
57 import org.eclipse.jgit.api.errors.FilterFailedException;
58 import org.eclipse.jgit.api.errors.GitAPIException;
59 import org.eclipse.jgit.api.errors.NoFilepatternException;
60 import org.eclipse.jgit.attributes.FilterCommandRegistry;
61 import org.eclipse.jgit.dircache.DirCache;
62 import org.eclipse.jgit.dircache.DirCacheBuilder;
63 import org.eclipse.jgit.dircache.DirCacheEntry;
64 import org.eclipse.jgit.junit.JGitTestUtil;
65 import org.eclipse.jgit.junit.RepositoryTestCase;
66 import org.eclipse.jgit.lfs.BuiltinLFS;
67 import org.eclipse.jgit.lib.ConfigConstants;
68 import org.eclipse.jgit.lib.Constants;
69 import org.eclipse.jgit.lib.FileMode;
70 import org.eclipse.jgit.lib.ObjectId;
71 import org.eclipse.jgit.lib.ObjectInserter;
72 import org.eclipse.jgit.lib.Repository;
73 import org.eclipse.jgit.lib.StoredConfig;
74 import org.eclipse.jgit.revwalk.RevCommit;
75 import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
76 import org.eclipse.jgit.treewalk.TreeWalk;
77 import org.eclipse.jgit.treewalk.WorkingTreeOptions;
78 import org.eclipse.jgit.util.FS;
79 import org.eclipse.jgit.util.FileUtils;
80 import org.junit.Test;
81 import org.junit.experimental.theories.DataPoints;
82 import org.junit.experimental.theories.Theories;
83 import org.junit.experimental.theories.Theory;
84 import org.junit.runner.RunWith;
85
86 @RunWith(Theories.class)
87 public class AddCommandTest extends RepositoryTestCase {
88 @DataPoints
89 public static boolean[] sleepBeforeAddOptions = { true, false };
90
91
92 @Override
93 public void setUp() throws Exception {
94 BuiltinLFS.register();
95 super.setUp();
96 }
97
98 @Test
99 public void testAddNothing() throws GitAPIException {
100 try (Git git = new Git(db)) {
101 git.add().call();
102 fail("Expected IllegalArgumentException");
103 } catch (NoFilepatternException e) {
104
105 }
106
107 }
108
109 @Test
110 public void testAddNonExistingSingleFile() throws GitAPIException {
111 try (Git git = new Git(db)) {
112 DirCache dc = git.add().addFilepattern("a.txt").call();
113 assertEquals(0, dc.getEntryCount());
114 }
115 }
116
117 @Test
118 public void testAddExistingSingleFile() throws IOException, GitAPIException {
119 File file = new File(db.getWorkTree(), "a.txt");
120 FileUtils.createNewFile(file);
121 try (PrintWriter writer = new PrintWriter(file)) {
122 writer.print("content");
123 }
124
125 try (Git git = new Git(db)) {
126 git.add().addFilepattern("a.txt").call();
127
128 assertEquals(
129 "[a.txt, mode:100644, content:content]",
130 indexState(CONTENT));
131 }
132 }
133
134 @Test
135 public void testCleanFilter() throws IOException, GitAPIException {
136 writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
137 writeTrashFile("src/a.tmp", "foo");
138
139
140 writeTrashFile("src/a.txt", "foo\n");
141 File script = writeTempFile("sed s/o/e/g");
142
143 try (Git git = new Git(db)) {
144 StoredConfig config = git.getRepository().getConfig();
145 config.setString("filter", "tstFilter", "clean",
146 "sh " + slashify(script.getPath()));
147 config.save();
148
149 git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp")
150 .call();
151
152 assertEquals(
153 "[src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:fee\n]",
154 indexState(CONTENT));
155 }
156 }
157
158 @Theory
159 public void testBuiltinFilters(boolean sleepBeforeAdd)
160 throws IOException,
161 GitAPIException, InterruptedException {
162 writeTrashFile(".gitattributes", "*.txt filter=lfs");
163 writeTrashFile("src/a.tmp", "foo");
164
165
166 File script = writeTempFile("sed s/o/e/g");
167 File f = writeTrashFile("src/a.txt", "foo\n");
168
169 try (Git git = new Git(db)) {
170 if (!sleepBeforeAdd) {
171 fsTick(f);
172 }
173 git.add().addFilepattern(".gitattributes").call();
174 StoredConfig config = git.getRepository().getConfig();
175 config.setString("filter", "lfs", "clean",
176 "sh " + slashify(script.getPath()));
177 config.setString("filter", "lfs", "smudge",
178 "sh " + slashify(script.getPath()));
179 config.setBoolean("filter", "lfs", "useJGitBuiltin", true);
180 config.save();
181
182 if (!sleepBeforeAdd) {
183 fsTick(f);
184 }
185 git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp")
186 .addFilepattern(".gitattributes").call();
187
188 assertEquals(
189 "[.gitattributes, mode:100644, content:*.txt filter=lfs][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c\nsize 4\n]",
190 indexState(CONTENT));
191
192 RevCommit c1 = git.commit().setMessage("c1").call();
193 assertTrue(git.status().call().isClean());
194 f = writeTrashFile("src/a.txt", "foobar\n");
195 if (!sleepBeforeAdd) {
196 fsTick(f);
197 }
198 git.add().addFilepattern("src/a.txt").call();
199 git.commit().setMessage("c2").call();
200 assertTrue(git.status().call().isClean());
201 assertEquals(
202 "[.gitattributes, mode:100644, content:*.txt filter=lfs][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f\nsize 7\n]",
203 indexState(CONTENT));
204 assertEquals("foobar\n", read("src/a.txt"));
205 git.checkout().setName(c1.getName()).call();
206 assertEquals(
207 "[.gitattributes, mode:100644, content:*.txt filter=lfs][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c\nsize 4\n]",
208 indexState(CONTENT));
209 assertEquals(
210 "foo\n", read("src/a.txt"));
211 }
212 }
213
214 @Theory
215 public void testBuiltinCleanFilter(boolean sleepBeforeAdd)
216 throws IOException, GitAPIException, InterruptedException {
217 writeTrashFile(".gitattributes", "*.txt filter=lfs");
218 writeTrashFile("src/a.tmp", "foo");
219
220
221 File script = writeTempFile("sed s/o/e/g");
222 File f = writeTrashFile("src/a.txt", "foo\n");
223
224
225 FilterCommandRegistry.unregister(
226 org.eclipse.jgit.lib.Constants.BUILTIN_FILTER_PREFIX
227 + "lfs/smudge");
228
229 try (Git git = new Git(db)) {
230 if (!sleepBeforeAdd) {
231 fsTick(f);
232 }
233 git.add().addFilepattern(".gitattributes").call();
234 StoredConfig config = git.getRepository().getConfig();
235 config.setString("filter", "lfs", "clean",
236 "sh " + slashify(script.getPath()));
237 config.setString("filter", "lfs", "smudge",
238 "sh " + slashify(script.getPath()));
239 config.setBoolean("filter", "lfs", "useJGitBuiltin", true);
240 config.save();
241
242 if (!sleepBeforeAdd) {
243 fsTick(f);
244 }
245 git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp")
246 .addFilepattern(".gitattributes").call();
247
248 assertEquals(
249 "[.gitattributes, mode:100644, content:*.txt filter=lfs][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c\nsize 4\n]",
250 indexState(CONTENT));
251
252 RevCommit c1 = git.commit().setMessage("c1").call();
253 assertTrue(git.status().call().isClean());
254 f = writeTrashFile("src/a.txt", "foobar\n");
255 if (!sleepBeforeAdd) {
256 fsTick(f);
257 }
258 git.add().addFilepattern("src/a.txt").call();
259 git.commit().setMessage("c2").call();
260 assertTrue(git.status().call().isClean());
261 assertEquals(
262 "[.gitattributes, mode:100644, content:*.txt filter=lfs][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f\nsize 7\n]",
263 indexState(CONTENT));
264 assertEquals("foobar\n", read("src/a.txt"));
265 git.checkout().setName(c1.getName()).call();
266 assertEquals(
267 "[.gitattributes, mode:100644, content:*.txt filter=lfs][src/a.tmp, mode:100644, content:foo][src/a.txt, mode:100644, content:version https://git-lfs.github.com/spec/v1\noid sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c\nsize 4\n]",
268 indexState(CONTENT));
269
270
271
272
273
274 assertEquals(
275 "versien https://git-lfs.github.cem/spec/v1\neid sha256:b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c\nsize 4\n",
276 read("src/a.txt"));
277 }
278 }
279
280 @Test
281 public void testAttributesWithTreeWalkFilter()
282 throws IOException, GitAPIException {
283 writeTrashFile(".gitattributes", "*.txt filter=lfs");
284 writeTrashFile("src/a.tmp", "foo");
285 writeTrashFile("src/a.txt", "foo\n");
286 File script = writeTempFile("sed s/o/e/g");
287
288 try (Git git = new Git(db)) {
289 StoredConfig config = git.getRepository().getConfig();
290 config.setString("filter", "lfs", "clean",
291 "sh " + slashify(script.getPath()));
292 config.save();
293
294 git.add().addFilepattern(".gitattributes").call();
295 git.commit().setMessage("attr").call();
296 git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp")
297 .addFilepattern(".gitattributes").call();
298 git.commit().setMessage("c1").call();
299 assertTrue(git.status().call().isClean());
300 }
301 }
302
303 @Test
304 public void testAttributesConflictingMatch() throws Exception {
305 writeTrashFile(".gitattributes", "foo/** crlf=input\n*.jar binary");
306 writeTrashFile("foo/bar.jar", "\r\n");
307
308
309 try (Git git = new Git(db)) {
310 git.add().addFilepattern(".").call();
311 assertEquals(
312 "[.gitattributes, mode:100644, content:foo/** crlf=input\n*.jar binary]"
313 + "[foo/bar.jar, mode:100644, content:\r\n]",
314 indexState(CONTENT));
315 }
316 }
317
318 @Test
319 public void testCleanFilterEnvironment()
320 throws IOException, GitAPIException {
321 writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
322 writeTrashFile("src/a.txt", "foo");
323 File script = writeTempFile("echo $GIT_DIR; echo 1 >xyz");
324
325 try (Git git = new Git(db)) {
326 StoredConfig config = git.getRepository().getConfig();
327 config.setString("filter", "tstFilter", "clean",
328 "sh " + slashify(script.getPath()));
329 config.save();
330 git.add().addFilepattern("src/a.txt").call();
331
332 String gitDir = db.getDirectory().getAbsolutePath();
333 assertEquals("[src/a.txt, mode:100644, content:" + gitDir
334 + "\n]", indexState(CONTENT));
335 assertTrue(new File(db.getWorkTree(), "xyz").exists());
336 }
337 }
338
339 @Test
340 public void testMultipleCleanFilter() throws IOException, GitAPIException {
341 writeTrashFile(".gitattributes",
342 "*.txt filter=tstFilter\n*.tmp filter=tstFilter2");
343
344
345 writeTrashFile("src/a.tmp", "foo\n");
346 writeTrashFile("src/a.txt", "foo\n");
347 File script = writeTempFile("sed s/o/e/g");
348 File script2 = writeTempFile("sed s/f/x/g");
349
350 try (Git git = new Git(db)) {
351 StoredConfig config = git.getRepository().getConfig();
352 config.setString("filter", "tstFilter", "clean",
353 "sh " + slashify(script.getPath()));
354 config.setString("filter", "tstFilter2", "clean",
355 "sh " + slashify(script2.getPath()));
356 config.save();
357
358 git.add().addFilepattern("src/a.txt").addFilepattern("src/a.tmp")
359 .call();
360
361 assertEquals(
362 "[src/a.tmp, mode:100644, content:xoo\n][src/a.txt, mode:100644, content:fee\n]",
363 indexState(CONTENT));
364
365
366 }
367 }
368
369
370
371
372
373
374
375
376
377 @Test
378 public void testCommandInjection() throws IOException, GitAPIException {
379
380
381 writeTrashFile("; echo virus", "foo\n");
382 File script = writeTempFile("sed s/o/e/g");
383
384 try (Git git = new Git(db)) {
385 StoredConfig config = git.getRepository().getConfig();
386 config.setString("filter", "tstFilter", "clean",
387 "sh " + slashify(script.getPath()) + " %f");
388 writeTrashFile(".gitattributes", "* filter=tstFilter");
389
390 git.add().addFilepattern("; echo virus").call();
391
392
393 assertEquals("[; echo virus, mode:100644, content:fee\n]",
394 indexState(CONTENT));
395 }
396 }
397
398 @Test
399 public void testBadCleanFilter() throws IOException, GitAPIException {
400 writeTrashFile("a.txt", "foo");
401 File script = writeTempFile("sedfoo s/o/e/g");
402
403 try (Git git = new Git(db)) {
404 StoredConfig config = git.getRepository().getConfig();
405 config.setString("filter", "tstFilter", "clean",
406 "sh " + script.getPath());
407 config.save();
408 writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
409
410 try {
411 git.add().addFilepattern("a.txt").call();
412 fail("Didn't received the expected exception");
413 } catch (FilterFailedException e) {
414 assertEquals(127, e.getReturnCode());
415 }
416 }
417 }
418
419 @Test
420 public void testBadCleanFilter2() throws IOException, GitAPIException {
421 writeTrashFile("a.txt", "foo");
422 File script = writeTempFile("sed s/o/e/g");
423
424 try (Git git = new Git(db)) {
425 StoredConfig config = git.getRepository().getConfig();
426 config.setString("filter", "tstFilter", "clean",
427 "shfoo " + script.getPath());
428 config.save();
429 writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
430
431 try {
432 git.add().addFilepattern("a.txt").call();
433 fail("Didn't received the expected exception");
434 } catch (FilterFailedException e) {
435 assertEquals(127, e.getReturnCode());
436 }
437 }
438 }
439
440 @Test
441 public void testCleanFilterReturning12() throws IOException,
442 GitAPIException {
443 writeTrashFile("a.txt", "foo");
444 File script = writeTempFile("exit 12");
445
446 try (Git git = new Git(db)) {
447 StoredConfig config = git.getRepository().getConfig();
448 config.setString("filter", "tstFilter", "clean",
449 "sh " + slashify(script.getPath()));
450 config.save();
451 writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
452
453 try {
454 git.add().addFilepattern("a.txt").call();
455 fail("Didn't received the expected exception");
456 } catch (FilterFailedException e) {
457 assertEquals(12, e.getReturnCode());
458 }
459 }
460 }
461
462 @Test
463 public void testNotApplicableFilter() throws IOException, GitAPIException {
464 writeTrashFile("a.txt", "foo");
465 File script = writeTempFile("sed s/o/e/g");
466
467 try (Git git = new Git(db)) {
468 StoredConfig config = git.getRepository().getConfig();
469 config.setString("filter", "tstFilter", "something",
470 "sh " + script.getPath());
471 config.save();
472 writeTrashFile(".gitattributes", "*.txt filter=tstFilter");
473
474 git.add().addFilepattern("a.txt").call();
475
476 assertEquals("[a.txt, mode:100644, content:foo]",
477 indexState(CONTENT));
478 }
479 }
480
481 private File writeTempFile(String body) throws IOException {
482 File f = File.createTempFile("AddCommandTest_", "");
483 JGitTestUtil.write(f, body);
484 return f;
485 }
486
487 @Test
488 public void testAddExistingSingleSmallFileWithNewLine() throws IOException,
489 GitAPIException {
490 File file = new File(db.getWorkTree(), "a.txt");
491 FileUtils.createNewFile(file);
492 try (PrintWriter writer = new PrintWriter(file)) {
493 writer.print("row1\r\nrow2");
494 }
495
496 try (Git git = new Git(db)) {
497 db.getConfig().setString("core", null, "autocrlf", "false");
498 git.add().addFilepattern("a.txt").call();
499 assertEquals("[a.txt, mode:100644, content:row1\r\nrow2]",
500 indexState(CONTENT));
501 db.getConfig().setString("core", null, "autocrlf", "true");
502 git.add().addFilepattern("a.txt").call();
503 assertEquals("[a.txt, mode:100644, content:row1\nrow2]",
504 indexState(CONTENT));
505 db.getConfig().setString("core", null, "autocrlf", "input");
506 git.add().addFilepattern("a.txt").call();
507 assertEquals("[a.txt, mode:100644, content:row1\nrow2]",
508 indexState(CONTENT));
509 }
510 }
511
512 @Test
513 public void testAddExistingSingleMediumSizeFileWithNewLine()
514 throws IOException, GitAPIException {
515 File file = new File(db.getWorkTree(), "a.txt");
516 FileUtils.createNewFile(file);
517 StringBuilder data = new StringBuilder();
518 for (int i = 0; i < 1000; ++i) {
519 data.append("row1\r\nrow2");
520 }
521 String crData = data.toString();
522 try (PrintWriter writer = new PrintWriter(file)) {
523 writer.print(crData);
524 }
525 String lfData = data.toString().replaceAll("\r", "");
526 try (Git git = new Git(db)) {
527 db.getConfig().setString("core", null, "autocrlf", "false");
528 git.add().addFilepattern("a.txt").call();
529 assertEquals("[a.txt, mode:100644, content:" + data + "]",
530 indexState(CONTENT));
531 db.getConfig().setString("core", null, "autocrlf", "true");
532 git.add().addFilepattern("a.txt").call();
533 assertEquals("[a.txt, mode:100644, content:" + lfData + "]",
534 indexState(CONTENT));
535 db.getConfig().setString("core", null, "autocrlf", "input");
536 git.add().addFilepattern("a.txt").call();
537 assertEquals("[a.txt, mode:100644, content:" + lfData + "]",
538 indexState(CONTENT));
539 }
540 }
541
542 @Test
543 public void testAddExistingSingleBinaryFile() throws IOException,
544 GitAPIException {
545 File file = new File(db.getWorkTree(), "a.txt");
546 FileUtils.createNewFile(file);
547 try (PrintWriter writer = new PrintWriter(file)) {
548 writer.print("row1\r\nrow2\u0000");
549 }
550
551 try (Git git = new Git(db)) {
552 db.getConfig().setString("core", null, "autocrlf", "false");
553 git.add().addFilepattern("a.txt").call();
554 assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
555 indexState(CONTENT));
556 db.getConfig().setString("core", null, "autocrlf", "true");
557 git.add().addFilepattern("a.txt").call();
558 assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
559 indexState(CONTENT));
560 db.getConfig().setString("core", null, "autocrlf", "input");
561 git.add().addFilepattern("a.txt").call();
562 assertEquals("[a.txt, mode:100644, content:row1\r\nrow2\u0000]",
563 indexState(CONTENT));
564 }
565 }
566
567 @Test
568 public void testAddExistingSingleFileInSubDir() throws IOException,
569 GitAPIException {
570 FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
571 File file = new File(db.getWorkTree(), "sub/a.txt");
572 FileUtils.createNewFile(file);
573 try (PrintWriter writer = new PrintWriter(file)) {
574 writer.print("content");
575 }
576
577 try (Git git = new Git(db)) {
578 git.add().addFilepattern("sub/a.txt").call();
579
580 assertEquals(
581 "[sub/a.txt, mode:100644, content:content]",
582 indexState(CONTENT));
583 }
584 }
585
586 @Test
587 public void testAddExistingSingleFileTwice() throws IOException,
588 GitAPIException {
589 File file = new File(db.getWorkTree(), "a.txt");
590 FileUtils.createNewFile(file);
591 try (PrintWriter writer = new PrintWriter(file)) {
592 writer.print("content");
593 }
594
595 try (Git git = new Git(db)) {
596 DirCache dc = git.add().addFilepattern("a.txt").call();
597
598 dc.getEntry(0).getObjectId();
599
600 try (PrintWriter writer = new PrintWriter(file)) {
601 writer.print("other content");
602 }
603
604 dc = git.add().addFilepattern("a.txt").call();
605
606 assertEquals(
607 "[a.txt, mode:100644, content:other content]",
608 indexState(CONTENT));
609 }
610 }
611
612 @Test
613 public void testAddExistingSingleFileTwiceWithCommit() throws Exception {
614 File file = new File(db.getWorkTree(), "a.txt");
615 FileUtils.createNewFile(file);
616 try (PrintWriter writer = new PrintWriter(file)) {
617 writer.print("content");
618 }
619
620 try (Git git = new Git(db)) {
621 DirCache dc = git.add().addFilepattern("a.txt").call();
622
623 dc.getEntry(0).getObjectId();
624
625 git.commit().setMessage("commit a.txt").call();
626
627 try (PrintWriter writer = new PrintWriter(file)) {
628 writer.print("other content");
629 }
630
631 dc = git.add().addFilepattern("a.txt").call();
632
633 assertEquals(
634 "[a.txt, mode:100644, content:other content]",
635 indexState(CONTENT));
636 }
637 }
638
639 @Test
640 public void testAddRemovedFile() throws Exception {
641 File file = new File(db.getWorkTree(), "a.txt");
642 FileUtils.createNewFile(file);
643 try (PrintWriter writer = new PrintWriter(file)) {
644 writer.print("content");
645 }
646
647 try (Git git = new Git(db)) {
648 DirCache dc = git.add().addFilepattern("a.txt").call();
649
650 dc.getEntry(0).getObjectId();
651 FileUtils.delete(file);
652
653
654 dc = git.add().addFilepattern("a.txt").call();
655
656 assertEquals(
657 "[a.txt, mode:100644, content:content]",
658 indexState(CONTENT));
659 }
660 }
661
662 @Test
663 public void testAddRemovedCommittedFile() throws Exception {
664 File file = new File(db.getWorkTree(), "a.txt");
665 FileUtils.createNewFile(file);
666 try (PrintWriter writer = new PrintWriter(file)) {
667 writer.print("content");
668 }
669
670 try (Git git = new Git(db)) {
671 DirCache dc = git.add().addFilepattern("a.txt").call();
672
673 git.commit().setMessage("commit a.txt").call();
674
675 dc.getEntry(0).getObjectId();
676 FileUtils.delete(file);
677
678
679 dc = git.add().addFilepattern("a.txt").call();
680
681 assertEquals(
682 "[a.txt, mode:100644, content:content]",
683 indexState(CONTENT));
684 }
685 }
686
687 @Test
688 public void testAddWithConflicts() throws Exception {
689
690
691 File file = new File(db.getWorkTree(), "a.txt");
692 FileUtils.createNewFile(file);
693 try (PrintWriter writer = new PrintWriter(file)) {
694 writer.print("content");
695 }
696
697 File file2 = new File(db.getWorkTree(), "b.txt");
698 FileUtils.createNewFile(file2);
699 try (PrintWriter writer = new PrintWriter(file2)) {
700 writer.print("content b");
701 }
702
703 ObjectInserter newObjectInserter = db.newObjectInserter();
704 DirCache dc = db.lockDirCache();
705 DirCacheBuilder builder = dc.builder();
706
707 addEntryToBuilder("b.txt", file2, newObjectInserter, builder, 0);
708 addEntryToBuilder("a.txt", file, newObjectInserter, builder, 1);
709
710 try (PrintWriter writer = new PrintWriter(file)) {
711 writer.print("other content");
712 }
713 addEntryToBuilder("a.txt", file, newObjectInserter, builder, 3);
714
715 try (PrintWriter writer = new PrintWriter(file)) {
716 writer.print("our content");
717 }
718 addEntryToBuilder("a.txt", file, newObjectInserter, builder, 2)
719 .getObjectId();
720
721 builder.commit();
722
723 assertEquals(
724 "[a.txt, mode:100644, stage:1, content:content]" +
725 "[a.txt, mode:100644, stage:2, content:our content]" +
726 "[a.txt, mode:100644, stage:3, content:other content]" +
727 "[b.txt, mode:100644, content:content b]",
728 indexState(CONTENT));
729
730
731
732 try (Git git = new Git(db)) {
733 dc = git.add().addFilepattern("a.txt").call();
734
735 assertEquals(
736 "[a.txt, mode:100644, content:our content]" +
737 "[b.txt, mode:100644, content:content b]",
738 indexState(CONTENT));
739 }
740 }
741
742 @Test
743 public void testAddTwoFiles() throws Exception {
744 File file = new File(db.getWorkTree(), "a.txt");
745 FileUtils.createNewFile(file);
746 try (PrintWriter writer = new PrintWriter(file)) {
747 writer.print("content");
748 }
749
750 File file2 = new File(db.getWorkTree(), "b.txt");
751 FileUtils.createNewFile(file2);
752 try (PrintWriter writer = new PrintWriter(file2)) {
753 writer.print("content b");
754 }
755
756 try (Git git = new Git(db)) {
757 git.add().addFilepattern("a.txt").addFilepattern("b.txt").call();
758 assertEquals(
759 "[a.txt, mode:100644, content:content]" +
760 "[b.txt, mode:100644, content:content b]",
761 indexState(CONTENT));
762 }
763 }
764
765 @Test
766 public void testAddFolder() throws Exception {
767 FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
768 File file = new File(db.getWorkTree(), "sub/a.txt");
769 FileUtils.createNewFile(file);
770 try (PrintWriter writer = new PrintWriter(file)) {
771 writer.print("content");
772 }
773
774 File file2 = new File(db.getWorkTree(), "sub/b.txt");
775 FileUtils.createNewFile(file2);
776 try (PrintWriter writer = new PrintWriter(file2)) {
777 writer.print("content b");
778 }
779
780 try (Git git = new Git(db)) {
781 git.add().addFilepattern("sub").call();
782 assertEquals(
783 "[sub/a.txt, mode:100644, content:content]" +
784 "[sub/b.txt, mode:100644, content:content b]",
785 indexState(CONTENT));
786 }
787 }
788
789 @Test
790 public void testAddIgnoredFile() throws Exception {
791 FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
792 File file = new File(db.getWorkTree(), "sub/a.txt");
793 FileUtils.createNewFile(file);
794 try (PrintWriter writer = new PrintWriter(file)) {
795 writer.print("content");
796 }
797
798 File ignoreFile = new File(db.getWorkTree(), ".gitignore");
799 FileUtils.createNewFile(ignoreFile);
800 try (PrintWriter writer = new PrintWriter(ignoreFile)) {
801 writer.print("sub/b.txt");
802 }
803
804 File file2 = new File(db.getWorkTree(), "sub/b.txt");
805 FileUtils.createNewFile(file2);
806 try (PrintWriter writer = new PrintWriter(file2)) {
807 writer.print("content b");
808 }
809
810 try (Git git = new Git(db)) {
811 git.add().addFilepattern("sub").call();
812
813 assertEquals(
814 "[sub/a.txt, mode:100644, content:content]",
815 indexState(CONTENT));
816 }
817 }
818
819 @Test
820 public void testAddWholeRepo() throws Exception {
821 FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
822 File file = new File(db.getWorkTree(), "sub/a.txt");
823 FileUtils.createNewFile(file);
824 try (PrintWriter writer = new PrintWriter(file)) {
825 writer.print("content");
826 }
827
828 File file2 = new File(db.getWorkTree(), "sub/b.txt");
829 FileUtils.createNewFile(file2);
830 try (PrintWriter writer = new PrintWriter(file2)) {
831 writer.print("content b");
832 }
833
834 try (Git git = new Git(db)) {
835 git.add().addFilepattern(".").call();
836 assertEquals(
837 "[sub/a.txt, mode:100644, content:content]" +
838 "[sub/b.txt, mode:100644, content:content b]",
839 indexState(CONTENT));
840 }
841 }
842
843
844
845
846
847 @Test
848 public void testAddWithoutParameterUpdate() throws Exception {
849 FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
850 File file = new File(db.getWorkTree(), "sub/a.txt");
851 FileUtils.createNewFile(file);
852 try (PrintWriter writer = new PrintWriter(file)) {
853 writer.print("content");
854 }
855
856 File file2 = new File(db.getWorkTree(), "sub/b.txt");
857 FileUtils.createNewFile(file2);
858 try (PrintWriter writer = new PrintWriter(file2)) {
859 writer.print("content b");
860 }
861
862 try (Git git = new Git(db)) {
863 git.add().addFilepattern("sub").call();
864
865 assertEquals(
866 "[sub/a.txt, mode:100644, content:content]" +
867 "[sub/b.txt, mode:100644, content:content b]",
868 indexState(CONTENT));
869
870 git.commit().setMessage("commit").call();
871
872
873 File file3 = new File(db.getWorkTree(), "sub/c.txt");
874 FileUtils.createNewFile(file3);
875 try (PrintWriter writer = new PrintWriter(file3)) {
876 writer.print("content c");
877 }
878
879
880 try (PrintWriter writer = new PrintWriter(file)) {
881 writer.print("modified content");
882 }
883
884
885 FileUtils.delete(file2);
886
887 git.add().addFilepattern("sub").call();
888
889
890
891 assertEquals(
892 "[sub/a.txt, mode:100644, content:modified content]" +
893 "[sub/b.txt, mode:100644, content:content b]" +
894 "[sub/c.txt, mode:100644, content:content c]",
895 indexState(CONTENT));
896 }
897 }
898
899
900
901
902 @Test
903 public void testAddWithParameterUpdate() throws Exception {
904 FileUtils.mkdir(new File(db.getWorkTree(), "sub"));
905 File file = new File(db.getWorkTree(), "sub/a.txt");
906 FileUtils.createNewFile(file);
907 try (PrintWriter writer = new PrintWriter(file)) {
908 writer.print("content");
909 }
910
911 File file2 = new File(db.getWorkTree(), "sub/b.txt");
912 FileUtils.createNewFile(file2);
913 try (PrintWriter writer = new PrintWriter(file2)) {
914 writer.print("content b");
915 }
916
917 try (Git git = new Git(db)) {
918 git.add().addFilepattern("sub").call();
919
920 assertEquals(
921 "[sub/a.txt, mode:100644, content:content]" +
922 "[sub/b.txt, mode:100644, content:content b]",
923 indexState(CONTENT));
924
925 git.commit().setMessage("commit").call();
926
927
928 File file3 = new File(db.getWorkTree(), "sub/c.txt");
929 FileUtils.createNewFile(file3);
930 try (PrintWriter writer = new PrintWriter(file3)) {
931 writer.print("content c");
932 }
933
934
935 try (PrintWriter writer = new PrintWriter(file)) {
936 writer.print("modified content");
937 }
938
939 FileUtils.delete(file2);
940
941
942
943
944 git.add().addFilepattern("sub").setUpdate(true).call();
945
946 assertEquals(
947 "[sub/a.txt, mode:100644, content:modified content]",
948 indexState(CONTENT));
949 }
950 }
951
952 @Test
953 public void testAssumeUnchanged() throws Exception {
954 try (Git git = new Git(db)) {
955 String path = "a.txt";
956 writeTrashFile(path, "content");
957 git.add().addFilepattern(path).call();
958 String path2 = "b.txt";
959 writeTrashFile(path2, "content");
960 git.add().addFilepattern(path2).call();
961 git.commit().setMessage("commit").call();
962 assertEquals("[a.txt, mode:100644, content:"
963 + "content, assume-unchanged:false]"
964 + "[b.txt, mode:100644, content:content, "
965 + "assume-unchanged:false]", indexState(CONTENT
966 | ASSUME_UNCHANGED));
967 assumeUnchanged(path2);
968 assertEquals("[a.txt, mode:100644, content:content, "
969 + "assume-unchanged:false][b.txt, mode:100644, "
970 + "content:content, assume-unchanged:true]", indexState(CONTENT
971 | ASSUME_UNCHANGED));
972 writeTrashFile(path, "more content");
973 writeTrashFile(path2, "more content");
974
975 git.add().addFilepattern(".").call();
976
977 assertEquals("[a.txt, mode:100644, content:more content,"
978 + " assume-unchanged:false][b.txt, mode:100644,"
979 + " content:content, assume-unchanged:true]",
980 indexState(CONTENT
981 | ASSUME_UNCHANGED));
982 }
983 }
984
985 @Test
986 public void testReplaceFileWithDirectory()
987 throws IOException, NoFilepatternException, GitAPIException {
988 try (Git git = new Git(db)) {
989 writeTrashFile("df", "before replacement");
990 git.add().addFilepattern("df").call();
991 assertEquals("[df, mode:100644, content:before replacement]",
992 indexState(CONTENT));
993 FileUtils.delete(new File(db.getWorkTree(), "df"));
994 writeTrashFile("df/f", "after replacement");
995 git.add().addFilepattern("df").call();
996 assertEquals("[df/f, mode:100644, content:after replacement]",
997 indexState(CONTENT));
998 }
999 }
1000
1001 @Test
1002 public void testReplaceDirectoryWithFile()
1003 throws IOException, NoFilepatternException, GitAPIException {
1004 try (Git git = new Git(db)) {
1005 writeTrashFile("df/f", "before replacement");
1006 git.add().addFilepattern("df").call();
1007 assertEquals("[df/f, mode:100644, content:before replacement]",
1008 indexState(CONTENT));
1009 FileUtils.delete(new File(db.getWorkTree(), "df"), RECURSIVE);
1010 writeTrashFile("df", "after replacement");
1011 git.add().addFilepattern("df").call();
1012 assertEquals("[df, mode:100644, content:after replacement]",
1013 indexState(CONTENT));
1014 }
1015 }
1016
1017 @Test
1018 public void testReplaceFileByPartOfDirectory()
1019 throws IOException, NoFilepatternException, GitAPIException {
1020 try (Git git = new Git(db)) {
1021 writeTrashFile("src/main", "df", "before replacement");
1022 writeTrashFile("src/main", "z", "z");
1023 writeTrashFile("z", "z2");
1024 git.add().addFilepattern("src/main/df")
1025 .addFilepattern("src/main/z")
1026 .addFilepattern("z")
1027 .call();
1028 assertEquals(
1029 "[src/main/df, mode:100644, content:before replacement]" +
1030 "[src/main/z, mode:100644, content:z]" +
1031 "[z, mode:100644, content:z2]",
1032 indexState(CONTENT));
1033 FileUtils.delete(new File(db.getWorkTree(), "src/main/df"));
1034 writeTrashFile("src/main/df", "a", "after replacement");
1035 writeTrashFile("src/main/df", "b", "unrelated file");
1036 git.add().addFilepattern("src/main/df/a").call();
1037 assertEquals(
1038 "[src/main/df/a, mode:100644, content:after replacement]" +
1039 "[src/main/z, mode:100644, content:z]" +
1040 "[z, mode:100644, content:z2]",
1041 indexState(CONTENT));
1042 }
1043 }
1044
1045 @Test
1046 public void testReplaceDirectoryConflictsWithFile()
1047 throws IOException, NoFilepatternException, GitAPIException {
1048 DirCache dc = db.lockDirCache();
1049 try (ObjectInserter oi = db.newObjectInserter()) {
1050 DirCacheBuilder builder = dc.builder();
1051 File f = writeTrashFile("a", "df", "content");
1052 addEntryToBuilder("a", f, oi, builder, 1);
1053
1054 f = writeTrashFile("a", "df", "other content");
1055 addEntryToBuilder("a/df", f, oi, builder, 3);
1056
1057 f = writeTrashFile("a", "df", "our content");
1058 addEntryToBuilder("a/df", f, oi, builder, 2);
1059
1060 f = writeTrashFile("z", "z");
1061 addEntryToBuilder("z", f, oi, builder, 0);
1062 builder.commit();
1063 }
1064 assertEquals(
1065 "[a, mode:100644, stage:1, content:content]" +
1066 "[a/df, mode:100644, stage:2, content:our content]" +
1067 "[a/df, mode:100644, stage:3, content:other content]" +
1068 "[z, mode:100644, content:z]",
1069 indexState(CONTENT));
1070
1071 try (Git git = new Git(db)) {
1072 FileUtils.delete(new File(db.getWorkTree(), "a"), RECURSIVE);
1073 writeTrashFile("a", "merged");
1074 git.add().addFilepattern("a").call();
1075 assertEquals("[a, mode:100644, content:merged]" +
1076 "[z, mode:100644, content:z]",
1077 indexState(CONTENT));
1078 }
1079 }
1080
1081 @Test
1082 public void testExecutableRetention() throws Exception {
1083 StoredConfig config = db.getConfig();
1084 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
1085 ConfigConstants.CONFIG_KEY_FILEMODE, true);
1086 config.save();
1087
1088 FS executableFs = new FS() {
1089
1090 @Override
1091 public boolean supportsExecute() {
1092 return true;
1093 }
1094
1095 @Override
1096 public boolean setExecute(File f, boolean canExec) {
1097 return true;
1098 }
1099
1100 @Override
1101 public ProcessBuilder runInShell(String cmd, String[] args) {
1102 return null;
1103 }
1104
1105 @Override
1106 public boolean retryFailedLockFileCommit() {
1107 return false;
1108 }
1109
1110 @Override
1111 public FS newInstance() {
1112 return this;
1113 }
1114
1115 @Override
1116 protected File discoverGitExe() {
1117 return null;
1118 }
1119
1120 @Override
1121 public boolean canExecute(File f) {
1122 try {
1123 return read(f).startsWith("binary:");
1124 } catch (IOException e) {
1125 return false;
1126 }
1127 }
1128
1129 @Override
1130 public boolean isCaseSensitive() {
1131 return false;
1132 }
1133 };
1134
1135 Git git = Git.open(db.getDirectory(), executableFs);
1136 String path = "a.txt";
1137 String path2 = "a.sh";
1138 writeTrashFile(path, "content");
1139 writeTrashFile(path2, "binary: content");
1140 git.add().addFilepattern(path).addFilepattern(path2).call();
1141 RevCommit commit1 = git.commit().setMessage("commit").call();
1142 try (TreeWalk walk = new TreeWalk(db)) {
1143 walk.addTree(commit1.getTree());
1144 walk.next();
1145 assertEquals(path2, walk.getPathString());
1146 assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
1147 walk.next();
1148 assertEquals(path, walk.getPathString());
1149 assertEquals(FileMode.REGULAR_FILE, walk.getFileMode(0));
1150 }
1151
1152 config = db.getConfig();
1153 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
1154 ConfigConstants.CONFIG_KEY_FILEMODE, false);
1155 config.save();
1156
1157 Git git2 = Git.open(db.getDirectory(), executableFs);
1158 writeTrashFile(path2, "content2");
1159 writeTrashFile(path, "binary: content2");
1160 git2.add().addFilepattern(path).addFilepattern(path2).call();
1161 RevCommit commit2 = git2.commit().setMessage("commit2").call();
1162 try (TreeWalk walk = new TreeWalk(db)) {
1163 walk.addTree(commit2.getTree());
1164 walk.next();
1165 assertEquals(path2, walk.getPathString());
1166 assertEquals(FileMode.EXECUTABLE_FILE, walk.getFileMode(0));
1167 walk.next();
1168 assertEquals(path, walk.getPathString());
1169 assertEquals(FileMode.REGULAR_FILE, walk.getFileMode(0));
1170 }
1171 }
1172
1173 @Test
1174 public void testAddGitlink() throws Exception {
1175 createNestedRepo("git-link-dir");
1176 try (Git git = new Git(db)) {
1177 git.add().addFilepattern("git-link-dir").call();
1178
1179 assertEquals(
1180 "[git-link-dir, mode:160000]",
1181 indexState(0));
1182 Set<String> untrackedFiles = git.status().call().getUntracked();
1183 assert (untrackedFiles.isEmpty());
1184 }
1185
1186 }
1187
1188 @Test
1189 public void testAddSubrepoWithDirNoGitlinks() throws Exception {
1190 createNestedRepo("nested-repo");
1191
1192
1193 StoredConfig config = db.getConfig();
1194 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
1195 ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, true);
1196 config.save();
1197
1198 assert (db.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks());
1199
1200 try (Git git = new Git(db)) {
1201 git.add().addFilepattern("nested-repo").call();
1202
1203 assertEquals(
1204 "[nested-repo/README1.md, mode:100644]" +
1205 "[nested-repo/README2.md, mode:100644]",
1206 indexState(0));
1207 }
1208
1209
1210
1211
1212 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
1213 ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, false);
1214 config.save();
1215
1216 writeTrashFile("nested-repo", "README3.md", "content");
1217
1218 try (Git git = new Git(db)) {
1219 git.add().addFilepattern("nested-repo").call();
1220
1221 assertEquals(
1222 "[nested-repo/README1.md, mode:100644]" +
1223 "[nested-repo/README2.md, mode:100644]" +
1224 "[nested-repo/README3.md, mode:100644]",
1225 indexState(0));
1226 }
1227 }
1228
1229 @Test
1230 public void testAddGitlinkDoesNotChange() throws Exception {
1231 createNestedRepo("nested-repo");
1232
1233 try (Git git = new Git(db)) {
1234 git.add().addFilepattern("nested-repo").call();
1235
1236 assertEquals(
1237 "[nested-repo, mode:160000]",
1238 indexState(0));
1239 }
1240
1241
1242 StoredConfig config = db.getConfig();
1243 config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
1244 ConfigConstants.CONFIG_KEY_DIRNOGITLINKS, true);
1245 config.save();
1246
1247 assert (db.getConfig().get(WorkingTreeOptions.KEY).isDirNoGitLinks());
1248
1249 try (Git git = new Git(db)) {
1250 git.add().addFilepattern("nested-repo").call();
1251
1252 assertEquals(
1253 "[nested-repo/README1.md, mode:100644][nested-repo/README2.md, mode:100644]",
1254 indexState(0));
1255 }
1256 }
1257
1258 private static DirCacheEntry addEntryToBuilder(String path, File file,
1259 ObjectInserter newObjectInserter, DirCacheBuilder builder, int stage)
1260 throws IOException {
1261 ObjectId id;
1262 try (FileInputStream inputStream = new FileInputStream(file)) {
1263 id = newObjectInserter.insert(
1264 Constants.OBJ_BLOB, file.length(), inputStream);
1265 }
1266 DirCacheEntry entry = new DirCacheEntry(path, stage);
1267 entry.setObjectId(id);
1268 entry.setFileMode(FileMode.REGULAR_FILE);
1269 entry.setLastModified(file.lastModified());
1270 entry.setLength((int) file.length());
1271
1272 builder.add(entry);
1273 return entry;
1274 }
1275
1276 private void assumeUnchanged(String path) throws IOException {
1277 final DirCache dirc = db.lockDirCache();
1278 final DirCacheEntry ent = dirc.getEntry(path);
1279 if (ent != null)
1280 ent.setAssumeValid(true);
1281 dirc.write();
1282 if (!dirc.commit())
1283 throw new IOException("could not commit");
1284 }
1285
1286 private void createNestedRepo(String path) throws IOException {
1287 File gitLinkDir = new File(db.getWorkTree(), path);
1288 FileUtils.mkdir(gitLinkDir);
1289
1290 FileRepositoryBuilder nestedBuilder = new FileRepositoryBuilder();
1291 nestedBuilder.setWorkTree(gitLinkDir);
1292
1293 Repository nestedRepo = nestedBuilder.build();
1294 nestedRepo.create();
1295
1296 writeTrashFile(path, "README1.md", "content");
1297 writeTrashFile(path, "README2.md", "content");
1298
1299
1300 try (Git git = new Git(nestedRepo)) {
1301 git.add().addFilepattern(".").call();
1302 git.commit().setMessage("subrepo commit").call();
1303 } catch (GitAPIException e) {
1304 throw new RuntimeException(e);
1305 }
1306 }
1307 }