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