1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.junit;
12
13 import static java.nio.charset.StandardCharsets.UTF_8;
14 import static org.junit.Assert.assertEquals;
15 import static org.junit.Assert.fail;
16
17 import java.io.BufferedOutputStream;
18 import java.io.File;
19 import java.io.FileOutputStream;
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.security.MessageDigest;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.Date;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.TimeZone;
31
32 import org.eclipse.jgit.api.Git;
33 import org.eclipse.jgit.dircache.DirCache;
34 import org.eclipse.jgit.dircache.DirCacheBuilder;
35 import org.eclipse.jgit.dircache.DirCacheEditor;
36 import org.eclipse.jgit.dircache.DirCacheEditor.DeletePath;
37 import org.eclipse.jgit.dircache.DirCacheEditor.DeleteTree;
38 import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
39 import org.eclipse.jgit.dircache.DirCacheEntry;
40 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
41 import org.eclipse.jgit.errors.MissingObjectException;
42 import org.eclipse.jgit.errors.ObjectWritingException;
43 import org.eclipse.jgit.internal.storage.file.FileRepository;
44 import org.eclipse.jgit.internal.storage.file.LockFile;
45 import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
46 import org.eclipse.jgit.internal.storage.file.PackFile;
47 import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
48 import org.eclipse.jgit.internal.storage.pack.PackWriter;
49 import org.eclipse.jgit.lib.AnyObjectId;
50 import org.eclipse.jgit.lib.Constants;
51 import org.eclipse.jgit.lib.FileMode;
52 import org.eclipse.jgit.lib.NullProgressMonitor;
53 import org.eclipse.jgit.lib.ObjectChecker;
54 import org.eclipse.jgit.lib.ObjectId;
55 import org.eclipse.jgit.lib.ObjectInserter;
56 import org.eclipse.jgit.lib.PersonIdent;
57 import org.eclipse.jgit.lib.Ref;
58 import org.eclipse.jgit.lib.RefUpdate;
59 import org.eclipse.jgit.lib.RefWriter;
60 import org.eclipse.jgit.lib.Repository;
61 import org.eclipse.jgit.lib.TagBuilder;
62 import org.eclipse.jgit.merge.MergeStrategy;
63 import org.eclipse.jgit.merge.ThreeWayMerger;
64 import org.eclipse.jgit.revwalk.ObjectWalk;
65 import org.eclipse.jgit.revwalk.RevBlob;
66 import org.eclipse.jgit.revwalk.RevCommit;
67 import org.eclipse.jgit.revwalk.RevObject;
68 import org.eclipse.jgit.revwalk.RevTag;
69 import org.eclipse.jgit.revwalk.RevTree;
70 import org.eclipse.jgit.revwalk.RevWalk;
71 import org.eclipse.jgit.treewalk.TreeWalk;
72 import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
73 import org.eclipse.jgit.util.ChangeIdUtil;
74 import org.eclipse.jgit.util.FileUtils;
75
76
77
78
79
80
81
82 public class TestRepository<R extends Repository> implements AutoCloseable {
83
84
85 public static final String AUTHOR = "J. Author";
86
87
88 public static final String AUTHOR_EMAIL = "jauthor@example.com";
89
90
91 public static final String COMMITTER = "J. Committer";
92
93
94 public static final String COMMITTER_EMAIL = "jcommitter@example.com";
95
96 private final PersonIdent defaultAuthor;
97
98 private final PersonIdent defaultCommitter;
99
100 private final R db;
101
102 private final Git git;
103
104 private final RevWalk pool;
105
106 private final ObjectInserter inserter;
107
108 private final MockSystemReader mockSystemReader;
109
110
111
112
113
114
115
116
117 public TestRepository(R db) throws IOException {
118 this(db, new RevWalk(db), new MockSystemReader());
119 }
120
121
122
123
124
125
126
127
128
129
130 public TestRepository(R db, RevWalk rw) throws IOException {
131 this(db, rw, new MockSystemReader());
132 }
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147 public TestRepository(R db, RevWalk rw, MockSystemReader reader)
148 throws IOException {
149 this.db = db;
150 this.git = Git.wrap(db);
151 this.pool = rw;
152 this.inserter = db.newObjectInserter();
153 this.mockSystemReader = reader;
154 long now = mockSystemReader.getCurrentTime();
155 int tz = mockSystemReader.getTimezone(now);
156 defaultAuthor = new PersonIdent(AUTHOR, AUTHOR_EMAIL, now, tz);
157 defaultCommitter = new PersonIdent(COMMITTER, COMMITTER_EMAIL, now, tz);
158 }
159
160
161
162
163
164
165 public R getRepository() {
166 return db;
167 }
168
169
170
171
172
173
174 public RevWalk getRevWalk() {
175 return pool;
176 }
177
178
179
180
181
182
183
184
185 public Git git() {
186 return git;
187 }
188
189
190
191
192
193
194
195 public Date getDate() {
196 return new Date(mockSystemReader.getCurrentTime());
197 }
198
199
200
201
202
203
204 public TimeZone getTimeZone() {
205 return mockSystemReader.getTimeZone();
206 }
207
208
209
210
211
212
213
214 public void tick(int secDelta) {
215 mockSystemReader.tick(secDelta);
216 }
217
218
219
220
221
222
223
224 public void setAuthorAndCommitter(org.eclipse.jgit.lib.CommitBuilder c) {
225 c.setAuthor(new PersonIdent(defaultAuthor, getDate()));
226 c.setCommitter(new PersonIdent(defaultCommitter, getDate()));
227 }
228
229
230
231
232
233
234
235
236
237 public RevBlob blob(String content) throws Exception {
238 return blob(content.getBytes(UTF_8));
239 }
240
241
242
243
244
245
246
247
248
249 public RevBlob blob(byte[] content) throws Exception {
250 ObjectId id;
251 try (ObjectInserter ins = inserter) {
252 id = ins.insert(Constants.OBJ_BLOB, content);
253 ins.flush();
254 }
255 return (RevBlob) pool.parseAny(id);
256 }
257
258
259
260
261
262
263
264
265
266
267
268 public DirCacheEntry file(String path, RevBlob blob)
269 throws Exception {
270 final DirCacheEntry e = new DirCacheEntry(path);
271 e.setFileMode(FileMode.REGULAR_FILE);
272 e.setObjectId(blob);
273 return e;
274 }
275
276
277
278
279
280
281
282
283
284
285 public RevTree tree(DirCacheEntry... entries) throws Exception {
286 final DirCache dc = DirCache.newInCore();
287 final DirCacheBuilder b = dc.builder();
288 for (DirCacheEntry e : entries) {
289 b.add(e);
290 }
291 b.finish();
292 ObjectId root;
293 try (ObjectInserter ins = inserter) {
294 root = dc.writeTree(ins);
295 ins.flush();
296 }
297 return pool.parseTree(root);
298 }
299
300
301
302
303
304
305
306
307
308
309
310 public RevObject get(RevTree tree, String path)
311 throws Exception {
312 try (TreeWalk tw = new TreeWalk(pool.getObjectReader())) {
313 tw.setFilter(PathFilterGroup.createFromStrings(Collections
314 .singleton(path)));
315 tw.reset(tree);
316 while (tw.next()) {
317 if (tw.isSubtree() && !path.equals(tw.getPathString())) {
318 tw.enterSubtree();
319 continue;
320 }
321 final ObjectId entid = tw.getObjectId(0);
322 final FileMode entmode = tw.getFileMode(0);
323 return pool.lookupAny(entid, entmode.getObjectType());
324 }
325 }
326 fail("Can't find " + path + " in tree " + tree.name());
327 return null;
328 }
329
330
331
332
333
334
335
336
337
338
339
340
341 public ObjectId unparsedCommit(ObjectId... parents) throws Exception {
342 return unparsedCommit(1, tree(), parents);
343 }
344
345
346
347
348
349
350
351
352
353
354
355
356 public RevCommit commit(RevCommit... parents) throws Exception {
357 return commit(1, tree(), parents);
358 }
359
360
361
362
363
364
365
366
367
368
369
370
371
372 public RevCommit commit(RevTree tree, RevCommit... parents)
373 throws Exception {
374 return commit(1, tree, parents);
375 }
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390 public RevCommit commit(int secDelta, RevCommit... parents)
391 throws Exception {
392 return commit(secDelta, tree(), parents);
393 }
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411 public RevCommit commit(final int secDelta, final RevTree tree,
412 final RevCommit... parents) throws Exception {
413 ObjectId id = unparsedCommit(secDelta, tree, parents);
414 return pool.parseCommit(id);
415 }
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433 public ObjectId unparsedCommit(final int secDelta, final RevTree tree,
434 final ObjectId... parents) throws Exception {
435 tick(secDelta);
436
437 final org.eclipse.jgit.lib.CommitBuilder c;
438
439 c = new org.eclipse.jgit.lib.CommitBuilder();
440 c.setTreeId(tree);
441 c.setParentIds(parents);
442 c.setAuthor(new PersonIdent(defaultAuthor, getDate()));
443 c.setCommitter(new PersonIdent(defaultCommitter, getDate()));
444 c.setMessage("");
445 ObjectId id;
446 try (ObjectInserter ins = inserter) {
447 id = ins.insert(c);
448 ins.flush();
449 }
450 return id;
451 }
452
453
454
455
456
457
458 public CommitBuilder commit() {
459 return new CommitBuilder();
460 }
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478 public RevTag tag(String name, RevObject dst) throws Exception {
479 final TagBuilder t = new TagBuilder();
480 t.setObjectId(dst);
481 t.setTag(name);
482 t.setTagger(new PersonIdent(defaultCommitter, getDate()));
483 t.setMessage("");
484 ObjectId id;
485 try (ObjectInserter ins = inserter) {
486 id = ins.insert(t);
487 ins.flush();
488 }
489 return pool.parseTag(id);
490 }
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506 public RevCommit update(String ref, CommitBuilder to) throws Exception {
507 return update(ref, to.create());
508 }
509
510
511
512
513
514
515
516
517
518
519
520
521
522 public CommitBuilder amendRef(String ref) throws Exception {
523 String name = normalizeRef(ref);
524 Ref r = db.exactRef(name);
525 if (r == null)
526 throw new IOException("Not a ref: " + ref);
527 return amend(pool.parseCommit(r.getObjectId()), branch(name).commit());
528 }
529
530
531
532
533
534
535
536
537
538 public CommitBuilder amend(AnyObjectId id) throws Exception {
539 return amend(pool.parseCommit(id), commit());
540 }
541
542 private CommitBuilder amend(RevCommit old, CommitBuilder b) throws Exception {
543 pool.parseBody(old);
544 b.author(old.getAuthorIdent());
545 b.committer(old.getCommitterIdent());
546 b.message(old.getFullMessage());
547
548
549 b.updateCommitterTime = true;
550
551
552 b.noParents();
553 for (int i = 0; i < old.getParentCount(); i++)
554 b.parent(old.getParent(i));
555
556
557
558 b.tree.clear();
559 try (TreeWalk tw = new TreeWalk(db)) {
560 tw.reset(old.getTree());
561 tw.setRecursive(true);
562 while (tw.next()) {
563 b.edit(new PathEdit(tw.getPathString()) {
564 @Override
565 public void apply(DirCacheEntry ent) {
566 ent.setFileMode(tw.getFileMode(0));
567 ent.setObjectId(tw.getObjectId(0));
568 }
569 });
570 }
571 }
572
573 return b;
574 }
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592 public <T extends AnyObjectId> T update(String ref, T obj) throws Exception {
593 ref = normalizeRef(ref);
594 RefUpdate u = db.updateRef(ref);
595 u.setNewObjectId(obj);
596 switch (u.forceUpdate()) {
597 case FAST_FORWARD:
598 case FORCED:
599 case NEW:
600 case NO_CHANGE:
601 updateServerInfo();
602 return obj;
603
604 default:
605 throw new IOException("Cannot write " + ref + " " + u.getResult());
606 }
607 }
608
609
610
611
612
613
614
615
616
617
618 public void delete(String ref) throws Exception {
619 ref = normalizeRef(ref);
620 RefUpdate u = db.updateRef(ref);
621 u.setForceUpdate(true);
622 switch (u.delete()) {
623 case FAST_FORWARD:
624 case FORCED:
625 case NEW:
626 case NO_CHANGE:
627 updateServerInfo();
628 return;
629
630 default:
631 throw new IOException("Cannot delete " + ref + " " + u.getResult());
632 }
633 }
634
635 private static String normalizeRef(String ref) {
636 if (Constants.HEAD.equals(ref)) {
637
638 } else if ("FETCH_HEAD".equals(ref)) {
639
640 } else if ("MERGE_HEAD".equals(ref)) {
641
642 } else if (ref.startsWith(Constants.R_REFS)) {
643
644 } else
645 ref = Constants.R_HEADS + ref;
646 return ref;
647 }
648
649
650
651
652
653
654
655
656
657 public void reset(AnyObjectId id) throws Exception {
658 RefUpdate ru = db.updateRef(Constants.HEAD, true);
659 ru.setNewObjectId(id);
660 RefUpdate.Result result = ru.forceUpdate();
661 switch (result) {
662 case FAST_FORWARD:
663 case FORCED:
664 case NEW:
665 case NO_CHANGE:
666 break;
667 default:
668 throw new IOException(String.format(
669 "Checkout \"%s\" failed: %s", id.name(), result));
670 }
671 }
672
673
674
675
676
677
678
679
680
681
682
683
684 public void reset(String name) throws Exception {
685 RefUpdate.Result result;
686 ObjectId id = db.resolve(name);
687 if (id == null)
688 throw new IOException("Not a revision: " + name);
689 RefUpdate ru = db.updateRef(Constants.HEAD, false);
690 ru.setNewObjectId(id);
691 result = ru.forceUpdate();
692 switch (result) {
693 case FAST_FORWARD:
694 case FORCED:
695 case NEW:
696 case NO_CHANGE:
697 break;
698 default:
699 throw new IOException(String.format(
700 "Checkout \"%s\" failed: %s", name, result));
701 }
702 }
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717 public RevCommit cherryPick(AnyObjectId id) throws Exception {
718 RevCommit commit = pool.parseCommit(id);
719 pool.parseBody(commit);
720 if (commit.getParentCount() != 1)
721 throw new IOException(String.format(
722 "Expected 1 parent for %s, found: %s",
723 id.name(), Arrays.asList(commit.getParents())));
724 RevCommit parent = commit.getParent(0);
725 pool.parseHeaders(parent);
726
727 Ref headRef = db.exactRef(Constants.HEAD);
728 if (headRef == null)
729 throw new IOException("Missing HEAD");
730 RevCommit head = pool.parseCommit(headRef.getObjectId());
731
732 ThreeWayMerger merger = MergeStrategy.RECURSIVE.newMerger(db, true);
733 merger.setBase(parent.getTree());
734 if (merger.merge(head, commit)) {
735 if (AnyObjectId.isEqual(head.getTree(), merger.getResultTreeId()))
736 return null;
737 tick(1);
738 org.eclipse.jgit.lib.CommitBuilder b =
739 new org.eclipse.jgit.lib.CommitBuilder();
740 b.setParentId(head);
741 b.setTreeId(merger.getResultTreeId());
742 b.setAuthor(commit.getAuthorIdent());
743 b.setCommitter(new PersonIdent(defaultCommitter, getDate()));
744 b.setMessage(commit.getFullMessage());
745 ObjectId result;
746 try (ObjectInserter ins = inserter) {
747 result = ins.insert(b);
748 ins.flush();
749 }
750 update(Constants.HEAD, result);
751 return pool.parseCommit(result);
752 }
753 throw new IOException("Merge conflict");
754 }
755
756
757
758
759
760
761 public void updateServerInfo() throws Exception {
762 if (db instanceof FileRepository) {
763 final FileRepository fr = (FileRepository) db;
764 RefWriter rw = new RefWriter(fr.getRefDatabase().getRefs()) {
765 @Override
766 protected void writeFile(String name, byte[] bin)
767 throws IOException {
768 File path = new File(fr.getDirectory(), name);
769 TestRepository.this.writeFile(path, bin);
770 }
771 };
772 rw.writePackedRefs();
773 rw.writeInfoRefs();
774
775 final StringBuilder w = new StringBuilder();
776 for (PackFile p : fr.getObjectDatabase().getPacks()) {
777 w.append("P ");
778 w.append(p.getPackFile().getName());
779 w.append('\n');
780 }
781 writeFile(new File(new File(fr.getObjectDatabase().getDirectory(),
782 "info"), "packs"), Constants.encodeASCII(w.toString()));
783 }
784 }
785
786
787
788
789
790
791
792
793
794
795
796
797
798 public <T extends RevObject> T parseBody(T object) throws Exception {
799 pool.parseBody(object);
800 return object;
801 }
802
803
804
805
806
807
808
809
810
811
812 public BranchBuilder branch(String ref) {
813 if (Constants.HEAD.equals(ref)) {
814
815 } else if (ref.startsWith(Constants.R_REFS)) {
816
817 } else
818 ref = Constants.R_HEADS + ref;
819 return new BranchBuilder(ref);
820 }
821
822
823
824
825
826
827
828
829
830
831
832
833 public ObjectId lightweightTag(String name, ObjectId obj) throws Exception {
834 if (!name.startsWith(Constants.R_TAGS))
835 name = Constants.R_TAGS + name;
836 return update(name, obj);
837 }
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852 public void fsck(RevObject... tips) throws MissingObjectException,
853 IncorrectObjectTypeException, IOException {
854 try (ObjectWalk ow = new ObjectWalk(db)) {
855 if (tips.length != 0) {
856 for (RevObject o : tips)
857 ow.markStart(ow.parseAny(o));
858 } else {
859 for (Ref r : db.getRefDatabase().getRefs())
860 ow.markStart(ow.parseAny(r.getObjectId()));
861 }
862
863 ObjectChecker oc = new ObjectChecker();
864 for (;;) {
865 final RevCommit o = ow.next();
866 if (o == null)
867 break;
868
869 final byte[] bin = db.open(o, o.getType()).getCachedBytes();
870 oc.checkCommit(o, bin);
871 assertHash(o, bin);
872 }
873
874 for (;;) {
875 final RevObject o = ow.nextObject();
876 if (o == null)
877 break;
878
879 final byte[] bin = db.open(o, o.getType()).getCachedBytes();
880 oc.check(o, o.getType(), bin);
881 assertHash(o, bin);
882 }
883 }
884 }
885
886 private static void assertHash(RevObject id, byte[] bin) {
887 MessageDigest md = Constants.newMessageDigest();
888 md.update(Constants.encodedTypeString(id.getType()));
889 md.update((byte) ' ');
890 md.update(Constants.encodeASCII(bin.length));
891 md.update((byte) 0);
892 md.update(bin);
893 assertEquals(id, ObjectId.fromRaw(md.digest()));
894 }
895
896
897
898
899
900
901
902
903
904 public void packAndPrune() throws Exception {
905 if (db.getObjectDatabase() instanceof ObjectDirectory) {
906 ObjectDirectory odb = (ObjectDirectory) db.getObjectDatabase();
907 NullProgressMonitor m = NullProgressMonitor.INSTANCE;
908
909 final File pack, idx;
910 try (PackWriter pw = new PackWriter(db)) {
911 Set<ObjectId> all = new HashSet<>();
912 for (Ref r : db.getRefDatabase().getRefs())
913 all.add(r.getObjectId());
914 pw.preparePack(m, all, PackWriter.NONE);
915
916 final ObjectId name = pw.computeName();
917
918 pack = nameFor(odb, name, ".pack");
919 try (OutputStream out =
920 new BufferedOutputStream(new FileOutputStream(pack))) {
921 pw.writePack(m, m, out);
922 }
923 pack.setReadOnly();
924
925 idx = nameFor(odb, name, ".idx");
926 try (OutputStream out =
927 new BufferedOutputStream(new FileOutputStream(idx))) {
928 pw.writeIndex(out);
929 }
930 idx.setReadOnly();
931 }
932
933 odb.openPack(pack);
934 updateServerInfo();
935 prunePacked(odb);
936 }
937 }
938
939
940
941
942
943
944
945
946
947 @Override
948 public void close() {
949 try {
950 inserter.close();
951 } finally {
952 db.close();
953 }
954 }
955
956 private static void prunePacked(ObjectDirectory odb) throws IOException {
957 for (PackFile p : odb.getPacks()) {
958 for (MutableEntry e : p)
959 FileUtils.delete(odb.fileFor(e.toObjectId()));
960 }
961 }
962
963 private static File nameFor(ObjectDirectory odb, ObjectId name, String t) {
964 File packdir = odb.getPackDirectory();
965 return new File(packdir, "pack-" + name.name() + t);
966 }
967
968 private void writeFile(File p, byte[] bin) throws IOException,
969 ObjectWritingException {
970 final LockFile lck = new LockFile(p);
971 if (!lck.lock())
972 throw new ObjectWritingException("Can't write " + p);
973 try {
974 lck.write(bin);
975 } catch (IOException ioe) {
976 throw new ObjectWritingException("Can't write " + p, ioe);
977 }
978 if (!lck.commit())
979 throw new ObjectWritingException("Can't write " + p);
980 }
981
982
983 public class BranchBuilder {
984 private final String ref;
985
986 BranchBuilder(String ref) {
987 this.ref = ref;
988 }
989
990
991
992
993
994
995
996
997
998 public CommitBuilder commit() throws Exception {
999 return new CommitBuilder(this);
1000 }
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010 public RevCommit update(CommitBuilder to) throws Exception {
1011 return update(to.create());
1012 }
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022 public RevCommit update(RevCommit to) throws Exception {
1023 return TestRepository.this.update(ref, to);
1024 }
1025
1026
1027
1028
1029
1030
1031 public void delete() throws Exception {
1032 TestRepository.this.delete(ref);
1033 }
1034 }
1035
1036
1037 public class CommitBuilder {
1038 private final BranchBuilder branch;
1039
1040 private final DirCache tree = DirCache.newInCore();
1041
1042 private ObjectId topLevelTree;
1043
1044 private final List<RevCommit> parents = new ArrayList<>(2);
1045
1046 private int tick = 1;
1047
1048 private String message = "";
1049
1050 private RevCommit self;
1051
1052 private PersonIdent author;
1053 private PersonIdent committer;
1054
1055 private String changeId;
1056
1057 private boolean updateCommitterTime;
1058
1059 CommitBuilder() {
1060 branch = null;
1061 }
1062
1063 CommitBuilder(BranchBuilder b) throws Exception {
1064 branch = b;
1065
1066 Ref ref = db.exactRef(branch.ref);
1067 if (ref != null && ref.getObjectId() != null)
1068 parent(pool.parseCommit(ref.getObjectId()));
1069 }
1070
1071 CommitBuilder(CommitBuilder prior) throws Exception {
1072 branch = prior.branch;
1073
1074 DirCacheBuilder b = tree.builder();
1075 for (int i = 0; i < prior.tree.getEntryCount(); i++)
1076 b.add(prior.tree.getEntry(i));
1077 b.finish();
1078
1079 parents.add(prior.create());
1080 }
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090 public CommitBuilder parent(RevCommit p) throws Exception {
1091 if (parents.isEmpty()) {
1092 DirCacheBuilder b = tree.builder();
1093 parseBody(p);
1094 b.addTree(new byte[0], DirCacheEntry.STAGE_0, pool
1095 .getObjectReader(), p.getTree());
1096 b.finish();
1097 }
1098 parents.add(p);
1099 return this;
1100 }
1101
1102
1103
1104
1105
1106
1107 public List<RevCommit> parents() {
1108 return Collections.unmodifiableList(parents);
1109 }
1110
1111
1112
1113
1114
1115
1116 public CommitBuilder noParents() {
1117 parents.clear();
1118 return this;
1119 }
1120
1121
1122
1123
1124
1125
1126 public CommitBuilder noFiles() {
1127 tree.clear();
1128 return this;
1129 }
1130
1131
1132
1133
1134
1135
1136
1137
1138 public CommitBuilder setTopLevelTree(ObjectId treeId) {
1139 topLevelTree = treeId;
1140 return this;
1141 }
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153 public CommitBuilder add(String path, String content) throws Exception {
1154 return add(path, blob(content));
1155 }
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167 public CommitBuilder add(String path, RevBlob id)
1168 throws Exception {
1169 return edit(new PathEdit(path) {
1170 @Override
1171 public void apply(DirCacheEntry ent) {
1172 ent.setFileMode(FileMode.REGULAR_FILE);
1173 ent.setObjectId(id);
1174 }
1175 });
1176 }
1177
1178
1179
1180
1181
1182
1183
1184
1185 public CommitBuilder edit(PathEdit edit) {
1186 DirCacheEditor e = tree.editor();
1187 e.add(edit);
1188 e.finish();
1189 return this;
1190 }
1191
1192
1193
1194
1195
1196
1197
1198
1199 public CommitBuilder rm(String path) {
1200 DirCacheEditor e = tree.editor();
1201 e.add(new DeletePath(path));
1202 e.add(new DeleteTree(path));
1203 e.finish();
1204 return this;
1205 }
1206
1207
1208
1209
1210
1211
1212
1213
1214 public CommitBuilder message(String m) {
1215 message = m;
1216 return this;
1217 }
1218
1219
1220
1221
1222
1223
1224 public String message() {
1225 return message;
1226 }
1227
1228
1229
1230
1231
1232
1233
1234
1235 public CommitBuilder tick(int secs) {
1236 tick = secs;
1237 return this;
1238 }
1239
1240
1241
1242
1243
1244
1245
1246
1247 public CommitBuilder ident(PersonIdent ident) {
1248 author = ident;
1249 committer = ident;
1250 return this;
1251 }
1252
1253
1254
1255
1256
1257
1258
1259
1260 public CommitBuilder author(PersonIdent a) {
1261 author = a;
1262 return this;
1263 }
1264
1265
1266
1267
1268
1269
1270 public PersonIdent author() {
1271 return author;
1272 }
1273
1274
1275
1276
1277
1278
1279
1280
1281 public CommitBuilder committer(PersonIdent c) {
1282 committer = c;
1283 return this;
1284 }
1285
1286
1287
1288
1289
1290
1291 public PersonIdent committer() {
1292 return committer;
1293 }
1294
1295
1296
1297
1298
1299
1300 public CommitBuilder insertChangeId() {
1301 changeId = "";
1302 return this;
1303 }
1304
1305
1306
1307
1308
1309
1310
1311
1312 public CommitBuilder insertChangeId(String c) {
1313
1314 ObjectId.fromString(c);
1315 changeId = c;
1316 return this;
1317 }
1318
1319
1320
1321
1322
1323
1324
1325
1326 public RevCommit create() throws Exception {
1327 if (self == null) {
1328 TestRepository.this.tick(tick);
1329
1330 final org.eclipse.jgit.lib.CommitBuilder c;
1331
1332 c = new org.eclipse.jgit.lib.CommitBuilder();
1333 c.setParentIds(parents);
1334 setAuthorAndCommitter(c);
1335 if (author != null)
1336 c.setAuthor(author);
1337 if (committer != null) {
1338 if (updateCommitterTime)
1339 committer = new PersonIdent(committer, getDate());
1340 c.setCommitter(committer);
1341 }
1342
1343 ObjectId commitId;
1344 try (ObjectInserter ins = inserter) {
1345 if (topLevelTree != null)
1346 c.setTreeId(topLevelTree);
1347 else
1348 c.setTreeId(tree.writeTree(ins));
1349 insertChangeId(c);
1350 c.setMessage(message);
1351 commitId = ins.insert(c);
1352 ins.flush();
1353 }
1354 self = pool.parseCommit(commitId);
1355
1356 if (branch != null)
1357 branch.update(self);
1358 }
1359 return self;
1360 }
1361
1362 private void insertChangeId(org.eclipse.jgit.lib.CommitBuilder c) {
1363 if (changeId == null)
1364 return;
1365 int idx = ChangeIdUtil.indexOfChangeId(message, "\n");
1366 if (idx >= 0)
1367 return;
1368
1369 ObjectId firstParentId = null;
1370 if (!parents.isEmpty())
1371 firstParentId = parents.get(0);
1372
1373 ObjectId cid;
1374 if (changeId.isEmpty())
1375 cid = ChangeIdUtil.computeChangeId(c.getTreeId(), firstParentId,
1376 c.getAuthor(), c.getCommitter(), message);
1377 else
1378 cid = ObjectId.fromString(changeId);
1379 message = ChangeIdUtil.insertId(message, cid);
1380 if (cid != null)
1381 message = message.replaceAll("\nChange-Id: I"
1382 + ObjectId.zeroId().getName() + "\n", "\nChange-Id: I"
1383 + cid.getName() + "\n");
1384 }
1385
1386
1387
1388
1389
1390
1391
1392 public CommitBuilder child() throws Exception {
1393 return new CommitBuilder(this);
1394 }
1395 }
1396 }