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