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 java.nio.charset.StandardCharsets.UTF_8;
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(UTF_8));
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 (RevBlob) pool.parseAny(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 }
324 b.finish();
325 ObjectId root;
326 try (ObjectInserter ins = inserter) {
327 root = dc.writeTree(ins);
328 ins.flush();
329 }
330 return pool.parseTree(root);
331 }
332
333
334
335
336
337
338
339
340
341
342
343 public RevObject get(RevTree tree, String path)
344 throws Exception {
345 try (TreeWalk tw = new TreeWalk(pool.getObjectReader())) {
346 tw.setFilter(PathFilterGroup.createFromStrings(Collections
347 .singleton(path)));
348 tw.reset(tree);
349 while (tw.next()) {
350 if (tw.isSubtree() && !path.equals(tw.getPathString())) {
351 tw.enterSubtree();
352 continue;
353 }
354 final ObjectId entid = tw.getObjectId(0);
355 final FileMode entmode = tw.getFileMode(0);
356 return pool.lookupAny(entid, entmode.getObjectType());
357 }
358 }
359 fail("Can't find " + path + " in tree " + tree.name());
360 return null;
361 }
362
363
364
365
366
367
368
369
370
371
372
373
374 public RevCommit commit(RevCommit... parents) throws Exception {
375 return commit(1, tree(), parents);
376 }
377
378
379
380
381
382
383
384
385
386
387
388
389
390 public RevCommit commit(RevTree tree, RevCommit... parents)
391 throws Exception {
392 return commit(1, tree, parents);
393 }
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408 public RevCommit commit(int secDelta, RevCommit... parents)
409 throws Exception {
410 return commit(secDelta, tree(), parents);
411 }
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429 public RevCommit commit(final int secDelta, final RevTree tree,
430 final RevCommit... parents) throws Exception {
431 tick(secDelta);
432
433 final org.eclipse.jgit.lib.CommitBuilder c;
434
435 c = new org.eclipse.jgit.lib.CommitBuilder();
436 c.setTreeId(tree);
437 c.setParentIds(parents);
438 c.setAuthor(new PersonIdent(defaultAuthor, getDate()));
439 c.setCommitter(new PersonIdent(defaultCommitter, getDate()));
440 c.setMessage("");
441 ObjectId id;
442 try (ObjectInserter ins = inserter) {
443 id = ins.insert(c);
444 ins.flush();
445 }
446 return pool.parseCommit(id);
447 }
448
449
450
451
452
453
454 public CommitBuilder commit() {
455 return new CommitBuilder();
456 }
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474 public RevTag tag(String name, RevObject dst) throws Exception {
475 final TagBuilder t = new TagBuilder();
476 t.setObjectId(dst);
477 t.setTag(name);
478 t.setTagger(new PersonIdent(defaultCommitter, getDate()));
479 t.setMessage("");
480 ObjectId id;
481 try (ObjectInserter ins = inserter) {
482 id = ins.insert(t);
483 ins.flush();
484 }
485 return pool.parseTag(id);
486 }
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502 public RevCommit update(String ref, CommitBuilder to) throws Exception {
503 return update(ref, to.create());
504 }
505
506
507
508
509
510
511
512
513
514
515
516
517
518 public CommitBuilder amendRef(String ref) throws Exception {
519 String name = normalizeRef(ref);
520 Ref r = db.exactRef(name);
521 if (r == null)
522 throw new IOException("Not a ref: " + ref);
523 return amend(pool.parseCommit(r.getObjectId()), branch(name).commit());
524 }
525
526
527
528
529
530
531
532
533
534 public CommitBuilder amend(AnyObjectId id) throws Exception {
535 return amend(pool.parseCommit(id), commit());
536 }
537
538 private CommitBuilder amend(RevCommit old, CommitBuilder b) throws Exception {
539 pool.parseBody(old);
540 b.author(old.getAuthorIdent());
541 b.committer(old.getCommitterIdent());
542 b.message(old.getFullMessage());
543
544
545 b.updateCommitterTime = true;
546
547
548 b.noParents();
549 for (int i = 0; i < old.getParentCount(); i++)
550 b.parent(old.getParent(i));
551
552
553
554 b.tree.clear();
555 try (TreeWalk tw = new TreeWalk(db)) {
556 tw.reset(old.getTree());
557 tw.setRecursive(true);
558 while (tw.next()) {
559 b.edit(new PathEdit(tw.getPathString()) {
560 @Override
561 public void apply(DirCacheEntry ent) {
562 ent.setFileMode(tw.getFileMode(0));
563 ent.setObjectId(tw.getObjectId(0));
564 }
565 });
566 }
567 }
568
569 return b;
570 }
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588 public <T extends AnyObjectId> T update(String ref, T obj) throws Exception {
589 ref = normalizeRef(ref);
590 RefUpdate u = db.updateRef(ref);
591 u.setNewObjectId(obj);
592 switch (u.forceUpdate()) {
593 case FAST_FORWARD:
594 case FORCED:
595 case NEW:
596 case NO_CHANGE:
597 updateServerInfo();
598 return obj;
599
600 default:
601 throw new IOException("Cannot write " + ref + " " + u.getResult());
602 }
603 }
604
605
606
607
608
609
610
611
612
613
614 public void delete(String ref) throws Exception {
615 ref = normalizeRef(ref);
616 RefUpdate u = db.updateRef(ref);
617 u.setForceUpdate(true);
618 switch (u.delete()) {
619 case FAST_FORWARD:
620 case FORCED:
621 case NEW:
622 case NO_CHANGE:
623 updateServerInfo();
624 return;
625
626 default:
627 throw new IOException("Cannot delete " + ref + " " + u.getResult());
628 }
629 }
630
631 private static String normalizeRef(String ref) {
632 if (Constants.HEAD.equals(ref)) {
633
634 } else if ("FETCH_HEAD".equals(ref)) {
635
636 } else if ("MERGE_HEAD".equals(ref)) {
637
638 } else if (ref.startsWith(Constants.R_REFS)) {
639
640 } else
641 ref = Constants.R_HEADS + ref;
642 return ref;
643 }
644
645
646
647
648
649
650
651
652
653 public void reset(AnyObjectId id) throws Exception {
654 RefUpdate ru = db.updateRef(Constants.HEAD, true);
655 ru.setNewObjectId(id);
656 RefUpdate.Result result = ru.forceUpdate();
657 switch (result) {
658 case FAST_FORWARD:
659 case FORCED:
660 case NEW:
661 case NO_CHANGE:
662 break;
663 default:
664 throw new IOException(String.format(
665 "Checkout \"%s\" failed: %s", id.name(), result));
666 }
667 }
668
669
670
671
672
673
674
675
676
677
678
679
680 public void reset(String name) throws Exception {
681 RefUpdate.Result result;
682 ObjectId id = db.resolve(name);
683 if (id == null)
684 throw new IOException("Not a revision: " + name);
685 RefUpdate ru = db.updateRef(Constants.HEAD, false);
686 ru.setNewObjectId(id);
687 result = ru.forceUpdate();
688 switch (result) {
689 case FAST_FORWARD:
690 case FORCED:
691 case NEW:
692 case NO_CHANGE:
693 break;
694 default:
695 throw new IOException(String.format(
696 "Checkout \"%s\" failed: %s", name, result));
697 }
698 }
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713 public RevCommit cherryPick(AnyObjectId id) throws Exception {
714 RevCommit commit = pool.parseCommit(id);
715 pool.parseBody(commit);
716 if (commit.getParentCount() != 1)
717 throw new IOException(String.format(
718 "Expected 1 parent for %s, found: %s",
719 id.name(), Arrays.asList(commit.getParents())));
720 RevCommit parent = commit.getParent(0);
721 pool.parseHeaders(parent);
722
723 Ref headRef = db.exactRef(Constants.HEAD);
724 if (headRef == null)
725 throw new IOException("Missing HEAD");
726 RevCommit head = pool.parseCommit(headRef.getObjectId());
727
728 ThreeWayMerger merger = MergeStrategy.RECURSIVE.newMerger(db, true);
729 merger.setBase(parent.getTree());
730 if (merger.merge(head, commit)) {
731 if (AnyObjectId.equals(head.getTree(), merger.getResultTreeId()))
732 return null;
733 tick(1);
734 org.eclipse.jgit.lib.CommitBuilder b =
735 new org.eclipse.jgit.lib.CommitBuilder();
736 b.setParentId(head);
737 b.setTreeId(merger.getResultTreeId());
738 b.setAuthor(commit.getAuthorIdent());
739 b.setCommitter(new PersonIdent(defaultCommitter, getDate()));
740 b.setMessage(commit.getFullMessage());
741 ObjectId result;
742 try (ObjectInserter ins = inserter) {
743 result = ins.insert(b);
744 ins.flush();
745 }
746 update(Constants.HEAD, result);
747 return pool.parseCommit(result);
748 } else {
749 throw new IOException("Merge conflict");
750 }
751 }
752
753
754
755
756
757
758 public void updateServerInfo() throws Exception {
759 if (db instanceof FileRepository) {
760 final FileRepository fr = (FileRepository) db;
761 RefWriter rw = new RefWriter(fr.getRefDatabase().getRefs()) {
762 @Override
763 protected void writeFile(String name, byte[] bin)
764 throws IOException {
765 File path = new File(fr.getDirectory(), name);
766 TestRepository.this.writeFile(path, bin);
767 }
768 };
769 rw.writePackedRefs();
770 rw.writeInfoRefs();
771
772 final StringBuilder w = new StringBuilder();
773 for (PackFile p : fr.getObjectDatabase().getPacks()) {
774 w.append("P ");
775 w.append(p.getPackFile().getName());
776 w.append('\n');
777 }
778 writeFile(new File(new File(fr.getObjectDatabase().getDirectory(),
779 "info"), "packs"), Constants.encodeASCII(w.toString()));
780 }
781 }
782
783
784
785
786
787
788
789
790
791
792
793
794
795 public <T extends RevObject> T parseBody(T object) throws Exception {
796 pool.parseBody(object);
797 return object;
798 }
799
800
801
802
803
804
805
806
807
808
809 public BranchBuilder branch(String ref) {
810 if (Constants.HEAD.equals(ref)) {
811
812 } else if (ref.startsWith(Constants.R_REFS)) {
813
814 } else
815 ref = Constants.R_HEADS + ref;
816 return new BranchBuilder(ref);
817 }
818
819
820
821
822
823
824
825
826
827
828
829
830 public ObjectId lightweightTag(String name, ObjectId obj) throws Exception {
831 if (!name.startsWith(Constants.R_TAGS))
832 name = Constants.R_TAGS + name;
833 return update(name, obj);
834 }
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849 public void fsck(RevObject... tips) throws MissingObjectException,
850 IncorrectObjectTypeException, IOException {
851 try (ObjectWalk ow = new ObjectWalk(db)) {
852 if (tips.length != 0) {
853 for (RevObject o : tips)
854 ow.markStart(ow.parseAny(o));
855 } else {
856 for (Ref r : db.getRefDatabase().getRefs())
857 ow.markStart(ow.parseAny(r.getObjectId()));
858 }
859
860 ObjectChecker oc = new ObjectChecker();
861 for (;;) {
862 final RevCommit o = ow.next();
863 if (o == null)
864 break;
865
866 final byte[] bin = db.open(o, o.getType()).getCachedBytes();
867 oc.checkCommit(o, bin);
868 assertHash(o, bin);
869 }
870
871 for (;;) {
872 final RevObject o = ow.nextObject();
873 if (o == null)
874 break;
875
876 final byte[] bin = db.open(o, o.getType()).getCachedBytes();
877 oc.check(o, o.getType(), bin);
878 assertHash(o, bin);
879 }
880 }
881 }
882
883 private static void assertHash(RevObject id, byte[] bin) {
884 MessageDigest md = Constants.newMessageDigest();
885 md.update(Constants.encodedTypeString(id.getType()));
886 md.update((byte) ' ');
887 md.update(Constants.encodeASCII(bin.length));
888 md.update((byte) 0);
889 md.update(bin);
890 assertEquals(id, ObjectId.fromRaw(md.digest()));
891 }
892
893
894
895
896
897
898
899
900
901 public void packAndPrune() throws Exception {
902 if (db.getObjectDatabase() instanceof ObjectDirectory) {
903 ObjectDirectory odb = (ObjectDirectory) db.getObjectDatabase();
904 NullProgressMonitor m = NullProgressMonitor.INSTANCE;
905
906 final File pack, idx;
907 try (PackWriter pw = new PackWriter(db)) {
908 Set<ObjectId> all = new HashSet<>();
909 for (Ref r : db.getRefDatabase().getRefs())
910 all.add(r.getObjectId());
911 pw.preparePack(m, all, PackWriter.NONE);
912
913 final ObjectId name = pw.computeName();
914
915 pack = nameFor(odb, name, ".pack");
916 try (OutputStream out =
917 new BufferedOutputStream(new FileOutputStream(pack))) {
918 pw.writePack(m, m, out);
919 }
920 pack.setReadOnly();
921
922 idx = nameFor(odb, name, ".idx");
923 try (OutputStream out =
924 new BufferedOutputStream(new FileOutputStream(idx))) {
925 pw.writeIndex(out);
926 }
927 idx.setReadOnly();
928 }
929
930 odb.openPack(pack);
931 updateServerInfo();
932 prunePacked(odb);
933 }
934 }
935
936 private static void prunePacked(ObjectDirectory odb) throws IOException {
937 for (PackFile p : odb.getPacks()) {
938 for (MutableEntry e : p)
939 FileUtils.delete(odb.fileFor(e.toObjectId()));
940 }
941 }
942
943 private static File nameFor(ObjectDirectory odb, ObjectId name, String t) {
944 File packdir = odb.getPackDirectory();
945 return new File(packdir, "pack-" + name.name() + t);
946 }
947
948 private void writeFile(File p, byte[] bin) throws IOException,
949 ObjectWritingException {
950 final LockFile lck = new LockFile(p);
951 if (!lck.lock())
952 throw new ObjectWritingException("Can't write " + p);
953 try {
954 lck.write(bin);
955 } catch (IOException ioe) {
956 throw new ObjectWritingException("Can't write " + p);
957 }
958 if (!lck.commit())
959 throw new ObjectWritingException("Can't write " + p);
960 }
961
962
963 public class BranchBuilder {
964 private final String ref;
965
966 BranchBuilder(String ref) {
967 this.ref = ref;
968 }
969
970
971
972
973
974
975
976
977
978 public CommitBuilder commit() throws Exception {
979 return new CommitBuilder(this);
980 }
981
982
983
984
985
986
987
988
989
990 public RevCommit update(CommitBuilder to) throws Exception {
991 return update(to.create());
992 }
993
994
995
996
997
998
999
1000
1001
1002 public RevCommit update(RevCommit to) throws Exception {
1003 return TestRepository.this.update(ref, to);
1004 }
1005
1006
1007
1008
1009
1010
1011 public void delete() throws Exception {
1012 TestRepository.this.delete(ref);
1013 }
1014 }
1015
1016
1017 public class CommitBuilder {
1018 private final BranchBuilder branch;
1019
1020 private final DirCache tree = DirCache.newInCore();
1021
1022 private ObjectId topLevelTree;
1023
1024 private final List<RevCommit> parents = new ArrayList<>(2);
1025
1026 private int tick = 1;
1027
1028 private String message = "";
1029
1030 private RevCommit self;
1031
1032 private PersonIdent author;
1033 private PersonIdent committer;
1034
1035 private String changeId;
1036
1037 private boolean updateCommitterTime;
1038
1039 CommitBuilder() {
1040 branch = null;
1041 }
1042
1043 CommitBuilder(BranchBuilder b) throws Exception {
1044 branch = b;
1045
1046 Ref ref = db.exactRef(branch.ref);
1047 if (ref != null && ref.getObjectId() != null)
1048 parent(pool.parseCommit(ref.getObjectId()));
1049 }
1050
1051 CommitBuilder(CommitBuilder prior) throws Exception {
1052 branch = prior.branch;
1053
1054 DirCacheBuilder b = tree.builder();
1055 for (int i = 0; i < prior.tree.getEntryCount(); i++)
1056 b.add(prior.tree.getEntry(i));
1057 b.finish();
1058
1059 parents.add(prior.create());
1060 }
1061
1062 public CommitBuilder parent(RevCommit p) throws Exception {
1063 if (parents.isEmpty()) {
1064 DirCacheBuilder b = tree.builder();
1065 parseBody(p);
1066 b.addTree(new byte[0], DirCacheEntry.STAGE_0, pool
1067 .getObjectReader(), p.getTree());
1068 b.finish();
1069 }
1070 parents.add(p);
1071 return this;
1072 }
1073
1074 public List<RevCommit> parents() {
1075 return Collections.unmodifiableList(parents);
1076 }
1077
1078 public CommitBuilder noParents() {
1079 parents.clear();
1080 return this;
1081 }
1082
1083 public CommitBuilder noFiles() {
1084 tree.clear();
1085 return this;
1086 }
1087
1088 public CommitBuilder setTopLevelTree(ObjectId treeId) {
1089 topLevelTree = treeId;
1090 return this;
1091 }
1092
1093 public CommitBuilder add(String path, String content) throws Exception {
1094 return add(path, blob(content));
1095 }
1096
1097 public CommitBuilder add(String path, RevBlob id)
1098 throws Exception {
1099 return edit(new PathEdit(path) {
1100 @Override
1101 public void apply(DirCacheEntry ent) {
1102 ent.setFileMode(FileMode.REGULAR_FILE);
1103 ent.setObjectId(id);
1104 }
1105 });
1106 }
1107
1108 public CommitBuilder edit(PathEdit edit) {
1109 DirCacheEditor e = tree.editor();
1110 e.add(edit);
1111 e.finish();
1112 return this;
1113 }
1114
1115 public CommitBuilder rm(String path) {
1116 DirCacheEditor e = tree.editor();
1117 e.add(new DeletePath(path));
1118 e.add(new DeleteTree(path));
1119 e.finish();
1120 return this;
1121 }
1122
1123 public CommitBuilder message(String m) {
1124 message = m;
1125 return this;
1126 }
1127
1128 public String message() {
1129 return message;
1130 }
1131
1132 public CommitBuilder tick(int secs) {
1133 tick = secs;
1134 return this;
1135 }
1136
1137 public CommitBuilder ident(PersonIdent ident) {
1138 author = ident;
1139 committer = ident;
1140 return this;
1141 }
1142
1143 public CommitBuilder author(PersonIdent a) {
1144 author = a;
1145 return this;
1146 }
1147
1148 public PersonIdent author() {
1149 return author;
1150 }
1151
1152 public CommitBuilder committer(PersonIdent c) {
1153 committer = c;
1154 return this;
1155 }
1156
1157 public PersonIdent committer() {
1158 return committer;
1159 }
1160
1161 public CommitBuilder insertChangeId() {
1162 changeId = "";
1163 return this;
1164 }
1165
1166 public CommitBuilder insertChangeId(String c) {
1167
1168 ObjectId.fromString(c);
1169 changeId = c;
1170 return this;
1171 }
1172
1173 public RevCommit create() throws Exception {
1174 if (self == null) {
1175 TestRepository.this.tick(tick);
1176
1177 final org.eclipse.jgit.lib.CommitBuilder c;
1178
1179 c = new org.eclipse.jgit.lib.CommitBuilder();
1180 c.setParentIds(parents);
1181 setAuthorAndCommitter(c);
1182 if (author != null)
1183 c.setAuthor(author);
1184 if (committer != null) {
1185 if (updateCommitterTime)
1186 committer = new PersonIdent(committer, getDate());
1187 c.setCommitter(committer);
1188 }
1189
1190 ObjectId commitId;
1191 try (ObjectInserter ins = inserter) {
1192 if (topLevelTree != null)
1193 c.setTreeId(topLevelTree);
1194 else
1195 c.setTreeId(tree.writeTree(ins));
1196 insertChangeId(c);
1197 c.setMessage(message);
1198 commitId = ins.insert(c);
1199 ins.flush();
1200 }
1201 self = pool.parseCommit(commitId);
1202
1203 if (branch != null)
1204 branch.update(self);
1205 }
1206 return self;
1207 }
1208
1209 private void insertChangeId(org.eclipse.jgit.lib.CommitBuilder c) {
1210 if (changeId == null)
1211 return;
1212 int idx = ChangeIdUtil.indexOfChangeId(message, "\n");
1213 if (idx >= 0)
1214 return;
1215
1216 ObjectId firstParentId = null;
1217 if (!parents.isEmpty())
1218 firstParentId = parents.get(0);
1219
1220 ObjectId cid;
1221 if (changeId.equals(""))
1222 cid = ChangeIdUtil.computeChangeId(c.getTreeId(), firstParentId,
1223 c.getAuthor(), c.getCommitter(), message);
1224 else
1225 cid = ObjectId.fromString(changeId);
1226 message = ChangeIdUtil.insertId(message, cid);
1227 if (cid != null)
1228 message = message.replaceAll("\nChange-Id: I"
1229 + ObjectId.zeroId().getName() + "\n", "\nChange-Id: I"
1230 + cid.getName() + "\n");
1231 }
1232
1233 public CommitBuilder child() throws Exception {
1234 return new CommitBuilder(this);
1235 }
1236 }
1237 }