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