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