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.revwalk;
45
46 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
47 import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
48 import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
49
50 import java.io.IOException;
51 import java.text.MessageFormat;
52 import java.util.ArrayList;
53 import java.util.List;
54
55 import org.eclipse.jgit.errors.CorruptObjectException;
56 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
57 import org.eclipse.jgit.errors.LargeObjectException;
58 import org.eclipse.jgit.errors.MissingObjectException;
59 import org.eclipse.jgit.internal.JGitText;
60 import org.eclipse.jgit.lib.AnyObjectId;
61 import org.eclipse.jgit.lib.ObjectReader;
62 import org.eclipse.jgit.lib.Repository;
63 import org.eclipse.jgit.revwalk.filter.ObjectFilter;
64 import org.eclipse.jgit.util.RawParseUtils;
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83 public class ObjectWalk extends RevWalk {
84 private static final int ID_SZ = 20;
85 private static final int TYPE_SHIFT = 12;
86 private static final int TYPE_TREE = 0040000 >>> TYPE_SHIFT;
87 private static final int TYPE_SYMLINK = 0120000 >>> TYPE_SHIFT;
88 private static final int TYPE_FILE = 0100000 >>> TYPE_SHIFT;
89 private static final int TYPE_GITLINK = 0160000 >>> TYPE_SHIFT;
90
91
92
93
94
95
96
97
98 private static final int IN_PENDING = RevWalk.REWRITE;
99
100 private List<RevObject> rootObjects;
101
102 private BlockObjQueue pendingObjects;
103
104 private ObjectFilter objectFilter;
105
106 private TreeVisit freeVisit;
107
108 private TreeVisit currVisit;
109
110 private byte[] pathBuf;
111
112 private int pathLen;
113
114 private boolean boundary;
115
116
117
118
119
120
121
122 public ObjectWalk(Repository repo) {
123 this(repo.newObjectReader());
124 }
125
126
127
128
129
130
131
132
133
134 public ObjectWalk(ObjectReader or) {
135 super(or);
136 setRetainBody(false);
137 rootObjects = new ArrayList<>();
138 pendingObjects = new BlockObjQueue();
139 objectFilter = ObjectFilter.ALL;
140 pathBuf = new byte[256];
141 }
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181 public void markStart(RevObject o) throws MissingObjectException,
182 IncorrectObjectTypeException, IOException {
183 while (o instanceof RevTag) {
184 addObject(o);
185 o = ((RevTag) o).getObject();
186 parseHeaders(o);
187 }
188
189 if (o instanceof RevCommit)
190 super.markStart((RevCommit) o);
191 else
192 addObject(o);
193 }
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236 public void markUninteresting(RevObject o) throws MissingObjectException,
237 IncorrectObjectTypeException, IOException {
238 while (o instanceof RevTag) {
239 o.flags |= UNINTERESTING;
240 if (boundary)
241 addObject(o);
242 o = ((RevTag) o).getObject();
243 parseHeaders(o);
244 }
245
246 if (o instanceof RevCommit)
247 super.markUninteresting((RevCommit) o);
248 else if (o instanceof RevTree)
249 markTreeUninteresting((RevTree) o);
250 else
251 o.flags |= UNINTERESTING;
252
253 if (o.getType() != OBJ_COMMIT && boundary)
254 addObject(o);
255 }
256
257
258 @Override
259 public void sort(RevSort s) {
260 super.sort(s);
261 boundary = hasRevSort(RevSort.BOUNDARY);
262 }
263
264
265 @Override
266 public void sort(RevSort s, boolean use) {
267 super.sort(s, use);
268 boundary = hasRevSort(RevSort.BOUNDARY);
269 }
270
271
272
273
274
275
276
277 public ObjectFilter getObjectFilter() {
278 return objectFilter;
279 }
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297 public void setObjectFilter(ObjectFilter newFilter) {
298 assertNotStarted();
299 objectFilter = newFilter != null ? newFilter : ObjectFilter.ALL;
300 }
301
302
303 @Override
304 public RevCommit next() throws MissingObjectException,
305 IncorrectObjectTypeException, IOException {
306 for (;;) {
307 final RevCommit r = super.next();
308 if (r == null) {
309 return null;
310 }
311 final RevTree t = r.getTree();
312 if ((r.flags & UNINTERESTING) != 0) {
313 if (objectFilter.include(this, t)) {
314 markTreeUninteresting(t);
315 }
316 if (boundary) {
317 return r;
318 }
319 continue;
320 }
321 if (objectFilter.include(this, t)) {
322 pendingObjects.add(t);
323 }
324 return r;
325 }
326 }
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342 public RevObject nextObject() throws MissingObjectException,
343 IncorrectObjectTypeException, IOException {
344 pathLen = 0;
345
346 TreeVisit tv = currVisit;
347 while (tv != null) {
348 byte[] buf = tv.buf;
349 for (int ptr = tv.ptr; ptr < buf.length;) {
350 int startPtr = ptr;
351 ptr = findObjectId(buf, ptr);
352 idBuffer.fromRaw(buf, ptr);
353 ptr += ID_SZ;
354
355 if (!objectFilter.include(this, idBuffer)) {
356 continue;
357 }
358
359 RevObject obj = objects.get(idBuffer);
360 if (obj != null && (obj.flags & SEEN) != 0)
361 continue;
362
363 int mode = parseMode(buf, startPtr, ptr, tv);
364 int flags;
365 switch (mode >>> TYPE_SHIFT) {
366 case TYPE_FILE:
367 case TYPE_SYMLINK:
368 if (obj == null) {
369 obj = new RevBlob(idBuffer);
370 obj.flags = SEEN;
371 objects.add(obj);
372 return obj;
373 }
374 if (!(obj instanceof RevBlob))
375 throw new IncorrectObjectTypeException(obj, OBJ_BLOB);
376 obj.flags = flags = obj.flags | SEEN;
377 if ((flags & UNINTERESTING) == 0)
378 return obj;
379 if (boundary)
380 return obj;
381 continue;
382
383 case TYPE_TREE:
384 if (obj == null) {
385 obj = new RevTree(idBuffer);
386 obj.flags = SEEN;
387 objects.add(obj);
388 return enterTree(obj);
389 }
390 if (!(obj instanceof RevTree))
391 throw new IncorrectObjectTypeException(obj, OBJ_TREE);
392 obj.flags = flags = obj.flags | SEEN;
393 if ((flags & UNINTERESTING) == 0)
394 return enterTree(obj);
395 if (boundary)
396 return enterTree(obj);
397 continue;
398
399 case TYPE_GITLINK:
400 continue;
401
402 default:
403 throw new CorruptObjectException(MessageFormat.format(
404 JGitText.get().corruptObjectInvalidMode3,
405 String.format("%o", Integer.valueOf(mode)),
406 idBuffer.name(),
407 RawParseUtils.decode(buf, tv.namePtr, tv.nameEnd),
408 tv.obj));
409 }
410 }
411
412 currVisit = tv.parent;
413 releaseTreeVisit(tv);
414 tv = currVisit;
415 }
416
417 for (;;) {
418 RevObject o = pendingObjects.next();
419 if (o == null) {
420 return null;
421 }
422 int flags = o.flags;
423 if ((flags & SEEN) != 0)
424 continue;
425 flags |= SEEN;
426 o.flags = flags;
427 if ((flags & UNINTERESTING) == 0 | boundary) {
428 if (o instanceof RevTree) {
429 tv = newTreeVisit(o);
430 tv.parent = null;
431 currVisit = tv;
432 }
433 return o;
434 }
435 }
436 }
437
438 private RevObject enterTree(RevObject obj) throws MissingObjectException,
439 IncorrectObjectTypeException, IOException {
440 TreeVisit tv = newTreeVisit(obj);
441 tv.parent = currVisit;
442 currVisit = tv;
443 return obj;
444 }
445
446 private static int findObjectId(byte[] buf, int ptr) {
447
448
449 for (;;) {
450 if (buf[++ptr] == 0) return ++ptr;
451 if (buf[++ptr] == 0) return ++ptr;
452 if (buf[++ptr] == 0) return ++ptr;
453 if (buf[++ptr] == 0) return ++ptr;
454
455 if (buf[++ptr] == 0) return ++ptr;
456 if (buf[++ptr] == 0) return ++ptr;
457 if (buf[++ptr] == 0) return ++ptr;
458 if (buf[++ptr] == 0) return ++ptr;
459
460 if (buf[++ptr] == 0) return ++ptr;
461 if (buf[++ptr] == 0) return ++ptr;
462 if (buf[++ptr] == 0) return ++ptr;
463 if (buf[++ptr] == 0) return ++ptr;
464
465 if (buf[++ptr] == 0) return ++ptr;
466 if (buf[++ptr] == 0) return ++ptr;
467 if (buf[++ptr] == 0) return ++ptr;
468 if (buf[++ptr] == 0) return ++ptr;
469 }
470 }
471
472 private static int parseMode(byte[] buf, int startPtr, int recEndPtr, TreeVisit tv) {
473 int mode = buf[startPtr] - '0';
474 for (;;) {
475 byte c = buf[++startPtr];
476 if (' ' == c)
477 break;
478 mode <<= 3;
479 mode += c - '0';
480
481 c = buf[++startPtr];
482 if (' ' == c)
483 break;
484 mode <<= 3;
485 mode += c - '0';
486
487 c = buf[++startPtr];
488 if (' ' == c)
489 break;
490 mode <<= 3;
491 mode += c - '0';
492
493 c = buf[++startPtr];
494 if (' ' == c)
495 break;
496 mode <<= 3;
497 mode += c - '0';
498
499 c = buf[++startPtr];
500 if (' ' == c)
501 break;
502 mode <<= 3;
503 mode += c - '0';
504
505 c = buf[++startPtr];
506 if (' ' == c)
507 break;
508 mode <<= 3;
509 mode += c - '0';
510
511 c = buf[++startPtr];
512 if (' ' == c)
513 break;
514 mode <<= 3;
515 mode += c - '0';
516 }
517
518 tv.ptr = recEndPtr;
519 tv.namePtr = startPtr + 1;
520 tv.nameEnd = recEndPtr - (ID_SZ + 1);
521 return mode;
522 }
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546 public void checkConnectivity() throws MissingObjectException,
547 IncorrectObjectTypeException, IOException {
548 for (;;) {
549 final RevCommit c = next();
550 if (c == null)
551 break;
552 }
553 for (;;) {
554 final RevObject o = nextObject();
555 if (o == null)
556 break;
557 if (o instanceof RevBlob && !reader.has(o))
558 throw new MissingObjectException(o, OBJ_BLOB);
559 }
560 }
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575 public String getPathString() {
576 if (pathLen == 0) {
577 pathLen = updatePathBuf(currVisit);
578 if (pathLen == 0)
579 return null;
580 }
581 return RawParseUtils.decode(pathBuf, 0, pathLen);
582 }
583
584
585
586
587
588
589
590
591
592 public int getPathHashCode() {
593 TreeVisit tv = currVisit;
594 if (tv == null)
595 return 0;
596
597 int nameEnd = tv.nameEnd;
598 if (nameEnd == 0) {
599
600
601
602
603 tv = tv.parent;
604 if (tv == null)
605 return 0;
606 nameEnd = tv.nameEnd;
607 }
608
609 byte[] buf;
610 int ptr;
611
612 if (16 <= (nameEnd - tv.namePtr)) {
613 buf = tv.buf;
614 ptr = nameEnd - 16;
615 } else {
616 nameEnd = pathLen;
617 if (nameEnd == 0) {
618 nameEnd = updatePathBuf(currVisit);
619 pathLen = nameEnd;
620 }
621 buf = pathBuf;
622 ptr = Math.max(0, nameEnd - 16);
623 }
624
625 int hash = 0;
626 for (; ptr < nameEnd; ptr++) {
627 byte c = buf[ptr];
628 if (c != ' ')
629 hash = (hash >>> 2) + (c << 24);
630 }
631 return hash;
632 }
633
634
635
636
637
638
639 public byte[] getPathBuffer() {
640 if (pathLen == 0)
641 pathLen = updatePathBuf(currVisit);
642 return pathBuf;
643 }
644
645
646
647
648
649
650 public int getPathLength() {
651 if (pathLen == 0)
652 pathLen = updatePathBuf(currVisit);
653 return pathLen;
654 }
655
656 private int updatePathBuf(TreeVisit tv) {
657 if (tv == null)
658 return 0;
659
660
661
662 int nameEnd = tv.nameEnd;
663 if (nameEnd == 0)
664 return updatePathBuf(tv.parent);
665
666 int ptr = tv.pathLen;
667 if (ptr == 0) {
668 ptr = updatePathBuf(tv.parent);
669 if (ptr == pathBuf.length)
670 growPathBuf(ptr);
671 if (ptr != 0)
672 pathBuf[ptr++] = '/';
673 tv.pathLen = ptr;
674 }
675
676 int namePtr = tv.namePtr;
677 int nameLen = nameEnd - namePtr;
678 int end = ptr + nameLen;
679 while (pathBuf.length < end)
680 growPathBuf(ptr);
681 System.arraycopy(tv.buf, namePtr, pathBuf, ptr, nameLen);
682 return end;
683 }
684
685 private void growPathBuf(int ptr) {
686 byte[] newBuf = new byte[pathBuf.length << 1];
687 System.arraycopy(pathBuf, 0, newBuf, 0, ptr);
688 pathBuf = newBuf;
689 }
690
691
692 @Override
693 public void dispose() {
694 super.dispose();
695 pendingObjects = new BlockObjQueue();
696 currVisit = null;
697 freeVisit = null;
698 }
699
700
701 @Override
702 protected void reset(int retainFlags) {
703 super.reset(retainFlags);
704
705 for (RevObject obj : rootObjects)
706 obj.flags &= ~IN_PENDING;
707
708 rootObjects = new ArrayList<>();
709 pendingObjects = new BlockObjQueue();
710 currVisit = null;
711 freeVisit = null;
712 }
713
714 private void addObject(RevObject o) {
715 if ((o.flags & IN_PENDING) == 0) {
716 o.flags |= IN_PENDING;
717 rootObjects.add(o);
718 pendingObjects.add(o);
719 }
720 }
721
722 private void markTreeUninteresting(RevTree tree)
723 throws MissingObjectException, IncorrectObjectTypeException,
724 IOException {
725 if ((tree.flags & UNINTERESTING) != 0)
726 return;
727 tree.flags |= UNINTERESTING;
728
729 byte[] raw = reader.open(tree, OBJ_TREE).getCachedBytes();
730 for (int ptr = 0; ptr < raw.length;) {
731 byte c = raw[ptr];
732 int mode = c - '0';
733 for (;;) {
734 c = raw[++ptr];
735 if (' ' == c)
736 break;
737 mode <<= 3;
738 mode += c - '0';
739 }
740 while (raw[++ptr] != 0) {
741
742 }
743 ptr++;
744
745 switch (mode >>> TYPE_SHIFT) {
746 case TYPE_FILE:
747 case TYPE_SYMLINK:
748 idBuffer.fromRaw(raw, ptr);
749 lookupBlob(idBuffer).flags |= UNINTERESTING;
750 break;
751
752 case TYPE_TREE:
753 idBuffer.fromRaw(raw, ptr);
754 markTreeUninteresting(lookupTree(idBuffer));
755 break;
756
757 case TYPE_GITLINK:
758 break;
759
760 default:
761 idBuffer.fromRaw(raw, ptr);
762 throw new CorruptObjectException(MessageFormat.format(
763 JGitText.get().corruptObjectInvalidMode3,
764 String.format("%o", Integer.valueOf(mode)),
765 idBuffer.name(), "", tree));
766 }
767 ptr += ID_SZ;
768 }
769 }
770
771 private TreeVisit newTreeVisit(RevObject obj) throws LargeObjectException,
772 MissingObjectException, IncorrectObjectTypeException, IOException {
773 TreeVisit tv = freeVisit;
774 if (tv != null) {
775 freeVisit = tv.parent;
776 tv.ptr = 0;
777 tv.namePtr = 0;
778 tv.nameEnd = 0;
779 tv.pathLen = 0;
780 } else {
781 tv = new TreeVisit();
782 }
783 tv.obj = obj;
784 tv.buf = reader.open(obj, OBJ_TREE).getCachedBytes();
785 return tv;
786 }
787
788 private void releaseTreeVisit(TreeVisit tv) {
789 tv.buf = null;
790 tv.parent = freeVisit;
791 freeVisit = tv;
792 }
793
794 private static class TreeVisit {
795
796 TreeVisit parent;
797
798
799 RevObject obj;
800
801
802 byte[] buf;
803
804
805 int ptr;
806
807
808 int namePtr;
809
810
811 int nameEnd;
812
813
814 int pathLen;
815 }
816 }