1 /* 2 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com> 3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> 4 * Copyright (C) 2014, Gustaf Lundh <gustaf.lundh@sonymobile.com> 5 * and other copyright owners as documented in the project's IP log. 6 * 7 * This program and the accompanying materials are made available 8 * under the terms of the Eclipse Distribution License v1.0 which 9 * accompanies this distribution, is reproduced below, and is 10 * available at http://www.eclipse.org/org/documents/edl-v10.php 11 * 12 * All rights reserved. 13 * 14 * Redistribution and use in source and binary forms, with or 15 * without modification, are permitted provided that the following 16 * conditions are met: 17 * 18 * - Redistributions of source code must retain the above copyright 19 * notice, this list of conditions and the following disclaimer. 20 * 21 * - Redistributions in binary form must reproduce the above 22 * copyright notice, this list of conditions and the following 23 * disclaimer in the documentation and/or other materials provided 24 * with the distribution. 25 * 26 * - Neither the name of the Eclipse Foundation, Inc. nor the 27 * names of its contributors may be used to endorse or promote 28 * products derived from this software without specific prior 29 * written permission. 30 * 31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 32 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 33 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 34 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 35 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 36 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 37 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 38 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 39 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 40 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 41 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 42 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 43 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 44 */ 45 46 package org.eclipse.jgit.revwalk; 47 48 import java.io.IOException; 49 import java.text.MessageFormat; 50 import java.util.ArrayList; 51 import java.util.Collection; 52 import java.util.EnumSet; 53 import java.util.Iterator; 54 import java.util.List; 55 56 import org.eclipse.jgit.annotations.NonNull; 57 import org.eclipse.jgit.annotations.Nullable; 58 import org.eclipse.jgit.errors.CorruptObjectException; 59 import org.eclipse.jgit.errors.IncorrectObjectTypeException; 60 import org.eclipse.jgit.errors.LargeObjectException; 61 import org.eclipse.jgit.errors.MissingObjectException; 62 import org.eclipse.jgit.errors.RevWalkException; 63 import org.eclipse.jgit.internal.JGitText; 64 import org.eclipse.jgit.lib.AnyObjectId; 65 import org.eclipse.jgit.lib.AsyncObjectLoaderQueue; 66 import org.eclipse.jgit.lib.Constants; 67 import org.eclipse.jgit.lib.MutableObjectId; 68 import org.eclipse.jgit.lib.ObjectId; 69 import org.eclipse.jgit.lib.ObjectIdOwnerMap; 70 import org.eclipse.jgit.lib.ObjectLoader; 71 import org.eclipse.jgit.lib.ObjectReader; 72 import org.eclipse.jgit.lib.Repository; 73 import org.eclipse.jgit.revwalk.filter.RevFilter; 74 import org.eclipse.jgit.treewalk.filter.TreeFilter; 75 import org.eclipse.jgit.util.References; 76 77 /** 78 * Walks a commit graph and produces the matching commits in order. 79 * <p> 80 * A RevWalk instance can only be used once to generate results. Running a 81 * second time requires creating a new RevWalk instance, or invoking 82 * {@link #reset()} before starting again. Resetting an existing instance may be 83 * faster for some applications as commit body parsing can be avoided on the 84 * later invocations. 85 * <p> 86 * RevWalk instances are not thread-safe. Applications must either restrict 87 * usage of a RevWalk instance to a single thread, or implement their own 88 * synchronization at a higher level. 89 * <p> 90 * Multiple simultaneous RevWalk instances per 91 * {@link org.eclipse.jgit.lib.Repository} are permitted, even from concurrent 92 * threads. Equality of {@link org.eclipse.jgit.revwalk.RevCommit}s from two 93 * different RevWalk instances is never true, even if their 94 * {@link org.eclipse.jgit.lib.ObjectId}s are equal (and thus they describe the 95 * same commit). 96 * <p> 97 * The offered iterator is over the list of RevCommits described by the 98 * configuration of this instance. Applications should restrict themselves to 99 * using either the provided Iterator or {@link #next()}, but never use both on 100 * the same RevWalk at the same time. The Iterator may buffer RevCommits, while 101 * {@link #next()} does not. 102 */ 103 public class RevWalk implements Iterable<RevCommit>, AutoCloseable { 104 private static final int MB = 1 << 20; 105 106 /** 107 * Set on objects whose important header data has been loaded. 108 * <p> 109 * For a RevCommit this indicates we have pulled apart the tree and parent 110 * references from the raw bytes available in the repository and translated 111 * those to our own local RevTree and RevCommit instances. The raw buffer is 112 * also available for message and other header filtering. 113 * <p> 114 * For a RevTag this indicates we have pulled part the tag references to 115 * find out who the tag refers to, and what that object's type is. 116 */ 117 static final int PARSED = 1 << 0; 118 119 /** 120 * Set on RevCommit instances added to our {@link #pending} queue. 121 * <p> 122 * We use this flag to avoid adding the same commit instance twice to our 123 * queue, especially if we reached it by more than one path. 124 */ 125 static final int SEEN = 1 << 1; 126 127 /** 128 * Set on RevCommit instances the caller does not want output. 129 * <p> 130 * We flag commits as uninteresting if the caller does not want commits 131 * reachable from a commit given to {@link #markUninteresting(RevCommit)}. 132 * This flag is always carried into the commit's parents and is a key part 133 * of the "rev-list B --not A" feature; A is marked UNINTERESTING. 134 */ 135 static final int UNINTERESTING = 1 << 2; 136 137 /** 138 * Set on a RevCommit that can collapse out of the history. 139 * <p> 140 * If the {@link #treeFilter} concluded that this commit matches his 141 * parents' for all of the paths that the filter is interested in then we 142 * mark the commit REWRITE. Later we can rewrite the parents of a REWRITE 143 * child to remove chains of REWRITE commits before we produce the child to 144 * the application. 145 * 146 * @see RewriteGenerator 147 */ 148 static final int REWRITE = 1 << 3; 149 150 /** 151 * Temporary mark for use within generators or filters. 152 * <p> 153 * This mark is only for local use within a single scope. If someone sets 154 * the mark they must unset it before any other code can see the mark. 155 */ 156 static final int TEMP_MARK = 1 << 4; 157 158 /** 159 * Temporary mark for use within {@link TopoSortGenerator}. 160 * <p> 161 * This mark indicates the commit could not produce when it wanted to, as at 162 * least one child was behind it. Commits with this flag are delayed until 163 * all children have been output first. 164 */ 165 static final int TOPO_DELAY = 1 << 5; 166 167 /** Number of flag bits we keep internal for our own use. See above flags. */ 168 static final int RESERVED_FLAGS = 6; 169 170 private static final int APP_FLAGS = -1 & ~((1 << RESERVED_FLAGS) - 1); 171 172 final ObjectReader reader; 173 174 private final boolean closeReader; 175 176 final MutableObjectId idBuffer; 177 178 ObjectIdOwnerMap<RevObject> objects; 179 180 int freeFlags = APP_FLAGS; 181 182 private int delayFreeFlags; 183 184 private int retainOnReset; 185 186 int carryFlags = UNINTERESTING; 187 188 final ArrayList<RevCommit> roots; 189 190 AbstractRevQueue queue; 191 192 Generator pending; 193 194 private final EnumSet<RevSort> sorting; 195 196 private RevFilter filter; 197 198 private TreeFilter treeFilter; 199 200 private boolean retainBody = true; 201 202 private boolean rewriteParents = true; 203 204 boolean shallowCommitsInitialized; 205 206 /** 207 * Create a new revision walker for a given repository. 208 * 209 * @param repo 210 * the repository the walker will obtain data from. An 211 * ObjectReader will be created by the walker, and will be closed 212 * when the walker is closed. 213 */ 214 public RevWalk(Repository repo) { 215 this(repo.newObjectReader(), true); 216 } 217 218 /** 219 * Create a new revision walker for a given repository. 220 * <p> 221 * 222 * @param or 223 * the reader the walker will obtain data from. The reader is not 224 * closed when the walker is closed (but is closed by {@link 225 * #dispose()}. 226 */ 227 public RevWalk(ObjectReader or) { 228 this(or, false); 229 } 230 231 private RevWalk(ObjectReader or, boolean closeReader) { 232 reader = or; 233 idBuffer = new MutableObjectId(); 234 objects = new ObjectIdOwnerMap<>(); 235 roots = new ArrayList<>(); 236 queue = new DateRevQueue(); 237 pending = new StartGenerator(this); 238 sorting = EnumSet.of(RevSort.NONE); 239 filter = RevFilter.ALL; 240 treeFilter = TreeFilter.ALL; 241 this.closeReader = closeReader; 242 } 243 244 /** 245 * Get the reader this walker is using to load objects. 246 * 247 * @return the reader this walker is using to load objects. 248 */ 249 public ObjectReader getObjectReader() { 250 return reader; 251 } 252 253 /** 254 * Get a reachability checker for commits over this revwalk. 255 * 256 * @return the most efficient reachability checker for this repository. 257 * @throws IOException 258 * if it cannot open any of the underlying indices. 259 * 260 * @since 5.4 261 */ 262 public ReachabilityChecker createReachabilityChecker() throws IOException { 263 if (reader.getBitmapIndex() != null) { 264 return new BitmappedReachabilityChecker(this); 265 } 266 267 return new PedestrianReachabilityChecker(true, this); 268 } 269 270 /** 271 * {@inheritDoc} 272 * <p> 273 * Release any resources used by this walker's reader. 274 * <p> 275 * A walker that has been released can be used again, but may need to be 276 * released after the subsequent usage. 277 * 278 * @since 4.0 279 */ 280 @Override 281 public void close() { 282 if (closeReader) { 283 reader.close(); 284 } 285 } 286 287 /** 288 * Mark a commit to start graph traversal from. 289 * <p> 290 * Callers are encouraged to use {@link #parseCommit(AnyObjectId)} to obtain 291 * the commit reference, rather than {@link #lookupCommit(AnyObjectId)}, as 292 * this method requires the commit to be parsed before it can be added as a 293 * root for the traversal. 294 * <p> 295 * The method will automatically parse an unparsed commit, but error 296 * handling may be more difficult for the application to explain why a 297 * RevCommit is not actually a commit. The object pool of this walker would 298 * also be 'poisoned' by the non-commit RevCommit. 299 * 300 * @param c 301 * the commit to start traversing from. The commit passed must be 302 * from this same revision walker. 303 * @throws org.eclipse.jgit.errors.MissingObjectException 304 * the commit supplied is not available from the object 305 * database. This usually indicates the supplied commit is 306 * invalid, but the reference was constructed during an earlier 307 * invocation to {@link #lookupCommit(AnyObjectId)}. 308 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException 309 * the object was not parsed yet and it was discovered during 310 * parsing that it is not actually a commit. This usually 311 * indicates the caller supplied a non-commit SHA-1 to 312 * {@link #lookupCommit(AnyObjectId)}. 313 * @throws java.io.IOException 314 * a pack file or loose object could not be read. 315 */ 316 public void markStart(RevCommit c) throws MissingObjectException, 317 IncorrectObjectTypeException, IOException { 318 if ((c.flags & SEEN) != 0) 319 return; 320 if ((c.flags & PARSED) == 0) 321 c.parseHeaders(this); 322 c.flags |= SEEN; 323 roots.add(c); 324 queue.add(c); 325 } 326 327 /** 328 * Mark commits to start graph traversal from. 329 * 330 * @param list 331 * commits to start traversing from. The commits passed must be 332 * from this same revision walker. 333 * @throws org.eclipse.jgit.errors.MissingObjectException 334 * one of the commits supplied is not available from the object 335 * database. This usually indicates the supplied commit is 336 * invalid, but the reference was constructed during an earlier 337 * invocation to {@link #lookupCommit(AnyObjectId)}. 338 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException 339 * the object was not parsed yet and it was discovered during 340 * parsing that it is not actually a commit. This usually 341 * indicates the caller supplied a non-commit SHA-1 to 342 * {@link #lookupCommit(AnyObjectId)}. 343 * @throws java.io.IOException 344 * a pack file or loose object could not be read. 345 */ 346 public void markStart(Collection<RevCommit> list) 347 throws MissingObjectException, IncorrectObjectTypeException, 348 IOException { 349 for (RevCommit c : list) 350 markStart(c); 351 } 352 353 /** 354 * Mark a commit to not produce in the output. 355 * <p> 356 * Uninteresting commits denote not just themselves but also their entire 357 * ancestry chain, back until the merge base of an uninteresting commit and 358 * an otherwise interesting commit. 359 * <p> 360 * Callers are encouraged to use {@link #parseCommit(AnyObjectId)} to obtain 361 * the commit reference, rather than {@link #lookupCommit(AnyObjectId)}, as 362 * this method requires the commit to be parsed before it can be added as a 363 * root for the traversal. 364 * <p> 365 * The method will automatically parse an unparsed commit, but error 366 * handling may be more difficult for the application to explain why a 367 * RevCommit is not actually a commit. The object pool of this walker would 368 * also be 'poisoned' by the non-commit RevCommit. 369 * 370 * @param c 371 * the commit to start traversing from. The commit passed must be 372 * from this same revision walker. 373 * @throws org.eclipse.jgit.errors.MissingObjectException 374 * the commit supplied is not available from the object 375 * database. This usually indicates the supplied commit is 376 * invalid, but the reference was constructed during an earlier 377 * invocation to {@link #lookupCommit(AnyObjectId)}. 378 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException 379 * the object was not parsed yet and it was discovered during 380 * parsing that it is not actually a commit. This usually 381 * indicates the caller supplied a non-commit SHA-1 to 382 * {@link #lookupCommit(AnyObjectId)}. 383 * @throws java.io.IOException 384 * a pack file or loose object could not be read. 385 */ 386 public void markUninteresting(RevCommit c) 387 throws MissingObjectException, IncorrectObjectTypeException, 388 IOException { 389 c.flags |= UNINTERESTING; 390 carryFlagsImpl(c); 391 markStart(c); 392 } 393 394 /** 395 * Determine if a commit is reachable from another commit. 396 * <p> 397 * A commit <code>base</code> is an ancestor of <code>tip</code> if we 398 * can find a path of commits that leads from <code>tip</code> and ends at 399 * <code>base</code>. 400 * <p> 401 * This utility function resets the walker, inserts the two supplied 402 * commits, and then executes a walk until an answer can be obtained. 403 * Currently allocated RevFlags that have been added to RevCommit instances 404 * will be retained through the reset. 405 * 406 * @param base 407 * commit the caller thinks is reachable from <code>tip</code>. 408 * @param tip 409 * commit to start iteration from, and which is most likely a 410 * descendant (child) of <code>base</code>. 411 * @return true if there is a path directly from <code>tip</code> to 412 * <code>base</code> (and thus <code>base</code> is fully merged 413 * into <code>tip</code>); false otherwise. 414 * @throws org.eclipse.jgit.errors.MissingObjectException 415 * one or more of the next commit's parents are not available 416 * from the object database, but were thought to be candidates 417 * for traversal. This usually indicates a broken link. 418 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException 419 * one or more of the next commit's parents are not actually 420 * commit objects. 421 * @throws java.io.IOException 422 * a pack file or loose object could not be read. 423 */ 424 public boolean isMergedInto(RevCommit"../../../../org/eclipse/jgit/revwalk/RevCommit.html#RevCommit">RevCommit base, RevCommit tip) 425 throws MissingObjectException, IncorrectObjectTypeException, 426 IOException { 427 final RevFilter oldRF = filter; 428 final TreeFilter oldTF = treeFilter; 429 try { 430 finishDelayedFreeFlags(); 431 reset(~freeFlags & APP_FLAGS); 432 filter = RevFilter.MERGE_BASE; 433 treeFilter = TreeFilter.ALL; 434 markStart(tip); 435 markStart(base); 436 RevCommit mergeBase; 437 while ((mergeBase = next()) != null) { 438 if (References.isSameObject(mergeBase, base)) { 439 return true; 440 } 441 } 442 return false; 443 } finally { 444 filter = oldRF; 445 treeFilter = oldTF; 446 } 447 } 448 449 /** 450 * Pop the next most recent commit. 451 * 452 * @return next most recent commit; null if traversal is over. 453 * @throws org.eclipse.jgit.errors.MissingObjectException 454 * one or more of the next commit's parents are not available 455 * from the object database, but were thought to be candidates 456 * for traversal. This usually indicates a broken link. 457 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException 458 * one or more of the next commit's parents are not actually 459 * commit objects. 460 * @throws java.io.IOException 461 * a pack file or loose object could not be read. 462 */ 463 public RevCommit next() throws MissingObjectException, 464 IncorrectObjectTypeException, IOException { 465 return pending.next(); 466 } 467 468 /** 469 * Obtain the sort types applied to the commits returned. 470 * 471 * @return the sorting strategies employed. At least one strategy is always 472 * used, but that strategy may be 473 * {@link org.eclipse.jgit.revwalk.RevSort#NONE}. 474 */ 475 public EnumSet<RevSort> getRevSort() { 476 return sorting.clone(); 477 } 478 479 /** 480 * Check whether the provided sorting strategy is enabled. 481 * 482 * @param sort 483 * a sorting strategy to look for. 484 * @return true if this strategy is enabled, false otherwise 485 */ 486 public boolean hasRevSort(RevSort sort) { 487 return sorting.contains(sort); 488 } 489 490 /** 491 * Select a single sorting strategy for the returned commits. 492 * <p> 493 * Disables all sorting strategies, then enables only the single strategy 494 * supplied by the caller. 495 * 496 * @param s 497 * a sorting strategy to enable. 498 */ 499 public void sort(RevSort s) { 500 assertNotStarted(); 501 sorting.clear(); 502 sorting.add(s); 503 } 504 505 /** 506 * Add or remove a sorting strategy for the returned commits. 507 * <p> 508 * Multiple strategies can be applied at once, in which case some strategies 509 * may take precedence over others. As an example, 510 * {@link org.eclipse.jgit.revwalk.RevSort#TOPO} must take precedence over 511 * {@link org.eclipse.jgit.revwalk.RevSort#COMMIT_TIME_DESC}, otherwise it 512 * cannot enforce its ordering. 513 * 514 * @param s 515 * a sorting strategy to enable or disable. 516 * @param use 517 * true if this strategy should be used, false if it should be 518 * removed. 519 */ 520 public void sort(RevSort s, boolean use) { 521 assertNotStarted(); 522 if (use) 523 sorting.add(s); 524 else 525 sorting.remove(s); 526 527 if (sorting.size() > 1) 528 sorting.remove(RevSort.NONE); 529 else if (sorting.isEmpty()) 530 sorting.add(RevSort.NONE); 531 } 532 533 /** 534 * Get the currently configured commit filter. 535 * 536 * @return the current filter. Never null as a filter is always needed. 537 */ 538 @NonNull 539 public RevFilter getRevFilter() { 540 return filter; 541 } 542 543 /** 544 * Set the commit filter for this walker. 545 * <p> 546 * Multiple filters may be combined by constructing an arbitrary tree of 547 * <code>AndRevFilter</code> or <code>OrRevFilter</code> instances to 548 * describe the boolean expression required by the application. Custom 549 * filter implementations may also be constructed by applications. 550 * <p> 551 * Note that filters are not thread-safe and may not be shared by concurrent 552 * RevWalk instances. Every RevWalk must be supplied its own unique filter, 553 * unless the filter implementation specifically states it is (and always 554 * will be) thread-safe. Callers may use 555 * {@link org.eclipse.jgit.revwalk.filter.RevFilter#clone()} to create a 556 * unique filter tree for this RevWalk instance. 557 * 558 * @param newFilter 559 * the new filter. If null the special 560 * {@link org.eclipse.jgit.revwalk.filter.RevFilter#ALL} filter 561 * will be used instead, as it matches every commit. 562 * @see org.eclipse.jgit.revwalk.filter.AndRevFilter 563 * @see org.eclipse.jgit.revwalk.filter.OrRevFilter 564 */ 565 public void setRevFilter(RevFilter newFilter) { 566 assertNotStarted(); 567 filter = newFilter != null ? newFilter : RevFilter.ALL; 568 } 569 570 /** 571 * Get the tree filter used to simplify commits by modified paths. 572 * 573 * @return the current filter. Never null as a filter is always needed. If 574 * no filter is being applied 575 * {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} is 576 * returned. 577 */ 578 @NonNull 579 public TreeFilter getTreeFilter() { 580 return treeFilter; 581 } 582 583 /** 584 * Set the tree filter used to simplify commits by modified paths. 585 * <p> 586 * If null or {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} the 587 * path limiter is removed. Commits will not be simplified. 588 * <p> 589 * If non-null and not 590 * {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} then the tree 591 * filter will be installed. Commits will have their ancestry simplified to 592 * hide commits that do not contain tree entries matched by the filter, 593 * unless {@code setRewriteParents(false)} is called. 594 * <p> 595 * Usually callers should be inserting a filter graph including 596 * {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ANY_DIFF} along with 597 * one or more {@link org.eclipse.jgit.treewalk.filter.PathFilter} 598 * instances. 599 * 600 * @param newFilter 601 * new filter. If null the special 602 * {@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} filter 603 * will be used instead, as it matches everything. 604 * @see org.eclipse.jgit.treewalk.filter.PathFilter 605 */ 606 public void setTreeFilter(TreeFilter newFilter) { 607 assertNotStarted(); 608 treeFilter = newFilter != null ? newFilter : TreeFilter.ALL; 609 } 610 611 /** 612 * Set whether to rewrite parent pointers when filtering by modified paths. 613 * <p> 614 * By default, when {@link #setTreeFilter(TreeFilter)} is called with non- 615 * null and non-{@link org.eclipse.jgit.treewalk.filter.TreeFilter#ALL} 616 * filter, commits will have their ancestry simplified and parents rewritten 617 * to hide commits that do not match the filter. 618 * <p> 619 * This behavior can be bypassed by passing false to this method. 620 * 621 * @param rewrite 622 * whether to rewrite parents; defaults to true. 623 * @since 3.4 624 */ 625 public void setRewriteParents(boolean rewrite) { 626 rewriteParents = rewrite; 627 } 628 629 boolean getRewriteParents() { 630 return rewriteParents; 631 } 632 633 /** 634 * Should the body of a commit or tag be retained after parsing its headers? 635 * <p> 636 * Usually the body is always retained, but some application code might not 637 * care and would prefer to discard the body of a commit as early as 638 * possible, to reduce memory usage. 639 * <p> 640 * True by default on {@link org.eclipse.jgit.revwalk.RevWalk} and false by 641 * default for {@link org.eclipse.jgit.revwalk.ObjectWalk}. 642 * 643 * @return true if the body should be retained; false it is discarded. 644 */ 645 public boolean isRetainBody() { 646 return retainBody; 647 } 648 649 /** 650 * Set whether or not the body of a commit or tag is retained. 651 * <p> 652 * If a body of a commit or tag is not retained, the application must call 653 * {@link #parseBody(RevObject)} before the body can be safely accessed 654 * through the type specific access methods. 655 * <p> 656 * True by default on {@link org.eclipse.jgit.revwalk.RevWalk} and false by 657 * default for {@link org.eclipse.jgit.revwalk.ObjectWalk}. 658 * 659 * @param retain 660 * true to retain bodies; false to discard them early. 661 */ 662 public void setRetainBody(boolean retain) { 663 retainBody = retain; 664 } 665 666 /** 667 * Locate a reference to a blob without loading it. 668 * <p> 669 * The blob may or may not exist in the repository. It is impossible to tell 670 * from this method's return value. 671 * 672 * @param id 673 * name of the blob object. 674 * @return reference to the blob object. Never null. 675 */ 676 @NonNull 677 public RevBlob lookupBlob(AnyObjectId id) { 678 RevBlob c = (RevBlob) objects.get(id); 679 if (c == null) { 680 c = new RevBlob(id); 681 objects.add(c); 682 } 683 return c; 684 } 685 686 /** 687 * Locate a reference to a tree without loading it. 688 * <p> 689 * The tree may or may not exist in the repository. It is impossible to tell 690 * from this method's return value. 691 * 692 * @param id 693 * name of the tree object. 694 * @return reference to the tree object. Never null. 695 */ 696 @NonNull 697 public RevTree lookupTree(AnyObjectId id) { 698 RevTree c = (RevTree) objects.get(id); 699 if (c == null) { 700 c = new RevTree(id); 701 objects.add(c); 702 } 703 return c; 704 } 705 706 /** 707 * Locate a reference to a commit without loading it. 708 * <p> 709 * The commit may or may not exist in the repository. It is impossible to 710 * tell from this method's return value. 711 * <p> 712 * See {@link #parseHeaders(RevObject)} and {@link #parseBody(RevObject)} 713 * for loading contents. 714 * 715 * @param id 716 * name of the commit object. 717 * @return reference to the commit object. Never null. 718 */ 719 @NonNull 720 public RevCommit lookupCommit(AnyObjectId id) { 721 RevCommit c = (RevCommit) objects.get(id); 722 if (c == null) { 723 c = createCommit(id); 724 objects.add(c); 725 } 726 return c; 727 } 728 729 /** 730 * Locate a reference to a tag without loading it. 731 * <p> 732 * The tag may or may not exist in the repository. It is impossible to tell 733 * from this method's return value. 734 * 735 * @param id 736 * name of the tag object. 737 * @return reference to the tag object. Never null. 738 */ 739 @NonNull 740 public RevTag lookupTag(AnyObjectId id) { 741 RevTag c = (RevTag) objects.get(id); 742 if (c == null) { 743 c = new RevTag(id); 744 objects.add(c); 745 } 746 return c; 747 } 748 749 /** 750 * Locate a reference to any object without loading it. 751 * <p> 752 * The object may or may not exist in the repository. It is impossible to 753 * tell from this method's return value. 754 * 755 * @param id 756 * name of the object. 757 * @param type 758 * type of the object. Must be a valid Git object type. 759 * @return reference to the object. Never null. 760 */ 761 @NonNull 762 public RevObject lookupAny(AnyObjectId id, int type) { 763 RevObject r = objects.get(id); 764 if (r == null) { 765 switch (type) { 766 case Constants.OBJ_COMMIT: 767 r = createCommit(id); 768 break; 769 case Constants.OBJ_TREE: 770 r = new RevTree(id); 771 break; 772 case Constants.OBJ_BLOB: 773 r = new RevBlob(id); 774 break; 775 case Constants.OBJ_TAG: 776 r = new RevTag(id); 777 break; 778 default: 779 throw new IllegalArgumentException(MessageFormat.format( 780 JGitText.get().invalidGitType, Integer.valueOf(type))); 781 } 782 objects.add(r); 783 } 784 return r; 785 } 786 787 /** 788 * Locate an object that was previously allocated in this walk. 789 * 790 * @param id 791 * name of the object. 792 * @return reference to the object if it has been previously located; 793 * otherwise null. 794 */ 795 public RevObject lookupOrNull(AnyObjectId id) { 796 return objects.get(id); 797 } 798 799 /** 800 * Locate a reference to a commit and immediately parse its content. 801 * <p> 802 * Unlike {@link #lookupCommit(AnyObjectId)} this method only returns 803 * successfully if the commit object exists, is verified to be a commit, and 804 * was parsed without error. 805 * 806 * @param id 807 * name of the commit object. 808 * @return reference to the commit object. Never null. 809 * @throws org.eclipse.jgit.errors.MissingObjectException 810 * the supplied commit does not exist. 811 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException 812 * the supplied id is not a commit or an annotated tag. 813 * @throws java.io.IOException 814 * a pack file or loose object could not be read. 815 */ 816 @NonNull 817 public RevCommit parseCommit(AnyObjectId id) 818 throws MissingObjectException, IncorrectObjectTypeException, 819 IOException { 820 RevObject c = peel(parseAny(id)); 821 if (!(c instanceof RevCommit)) 822 throw new IncorrectObjectTypeException(id.toObjectId(), 823 Constants.TYPE_COMMIT); 824 return (RevCommit) c; 825 } 826 827 /** 828 * Locate a reference to a tree. 829 * <p> 830 * This method only returns successfully if the tree object exists, is 831 * verified to be a tree. 832 * 833 * @param id 834 * name of the tree object, or a commit or annotated tag that may 835 * reference a tree. 836 * @return reference to the tree object. Never null. 837 * @throws org.eclipse.jgit.errors.MissingObjectException 838 * the supplied tree does not exist. 839 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException 840 * the supplied id is not a tree, a commit or an annotated tag. 841 * @throws java.io.IOException 842 * a pack file or loose object could not be read. 843 */ 844 @NonNull 845 public RevTree parseTree(AnyObjectId id) 846 throws MissingObjectException, IncorrectObjectTypeException, 847 IOException { 848 RevObject c = peel(parseAny(id)); 849 850 final RevTree t; 851 if (c instanceof RevCommit) 852 t = ((RevCommit) c).getTree(); 853 else if (!(c instanceof RevTree)) 854 throw new IncorrectObjectTypeException(id.toObjectId(), 855 Constants.TYPE_TREE); 856 else 857 t = (RevTree) c; 858 parseHeaders(t); 859 return t; 860 } 861 862 /** 863 * Locate a reference to an annotated tag and immediately parse its content. 864 * <p> 865 * Unlike {@link #lookupTag(AnyObjectId)} this method only returns 866 * successfully if the tag object exists, is verified to be a tag, and was 867 * parsed without error. 868 * 869 * @param id 870 * name of the tag object. 871 * @return reference to the tag object. Never null. 872 * @throws org.eclipse.jgit.errors.MissingObjectException 873 * the supplied tag does not exist. 874 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException 875 * the supplied id is not a tag or an annotated tag. 876 * @throws java.io.IOException 877 * a pack file or loose object could not be read. 878 */ 879 @NonNull 880 public RevTag parseTag(AnyObjectId id) throws MissingObjectException, 881 IncorrectObjectTypeException, IOException { 882 RevObject c = parseAny(id); 883 if (!(c instanceof RevTag)) 884 throw new IncorrectObjectTypeException(id.toObjectId(), 885 Constants.TYPE_TAG); 886 return (RevTag) c; 887 } 888 889 /** 890 * Locate a reference to any object and immediately parse its headers. 891 * <p> 892 * This method only returns successfully if the object exists and was parsed 893 * without error. Parsing an object can be expensive as the type must be 894 * determined. For blobs this may mean the blob content was unpacked 895 * unnecessarily, and thrown away. 896 * 897 * @param id 898 * name of the object. 899 * @return reference to the object. Never null. 900 * @throws org.eclipse.jgit.errors.MissingObjectException 901 * the supplied does not exist. 902 * @throws java.io.IOException 903 * a pack file or loose object could not be read. 904 */ 905 @NonNull 906 public RevObject parseAny(AnyObjectId id) 907 throws MissingObjectException, IOException { 908 RevObject r = objects.get(id); 909 if (r == null) 910 r = parseNew(id, reader.open(id)); 911 else 912 parseHeaders(r); 913 return r; 914 } 915 916 private RevObject parseNew(AnyObjectId id, ObjectLoader ldr) 917 throws LargeObjectException, CorruptObjectException, 918 MissingObjectException, IOException { 919 RevObject r; 920 int type = ldr.getType(); 921 switch (type) { 922 case Constants.OBJ_COMMIT: { 923 final RevCommit c = createCommit(id); 924 c.parseCanonical(this, getCachedBytes(c, ldr)); 925 r = c; 926 break; 927 } 928 case Constants.OBJ_TREE: { 929 r = new RevTree(id); 930 r.flags |= PARSED; 931 break; 932 } 933 case Constants.OBJ_BLOB: { 934 r = new RevBlob(id); 935 r.flags |= PARSED; 936 break; 937 } 938 case Constants.OBJ_TAG: { 939 final RevTagk/RevTag.html#RevTag">RevTag t = new RevTag(id); 940 t.parseCanonical(this, getCachedBytes(t, ldr)); 941 r = t; 942 break; 943 } 944 default: 945 throw new IllegalArgumentException(MessageFormat.format( 946 JGitText.get().badObjectType, Integer.valueOf(type))); 947 } 948 objects.add(r); 949 return r; 950 } 951 952 byte[] getCachedBytes(RevObject obj) throws LargeObjectException, 953 MissingObjectException, IncorrectObjectTypeException, IOException { 954 return getCachedBytes(obj, reader.open(obj, obj.getType())); 955 } 956 957 byte[] getCachedBytes(RevObject obj, ObjectLoader ldr) 958 throws LargeObjectException, MissingObjectException, IOException { 959 try { 960 return ldr.getCachedBytes(5 * MB); 961 } catch (LargeObjectException tooBig) { 962 tooBig.setObjectId(obj); 963 throw tooBig; 964 } 965 } 966 967 /** 968 * Asynchronous object parsing. 969 * 970 * @param objectIds 971 * objects to open from the object store. The supplied collection 972 * must not be modified until the queue has finished. 973 * @param reportMissing 974 * if true missing objects are reported by calling failure with a 975 * MissingObjectException. This may be more expensive for the 976 * implementation to guarantee. If false the implementation may 977 * choose to report MissingObjectException, or silently skip over 978 * the object with no warning. 979 * @return queue to read the objects from. 980 */ 981 public <T extends ObjectId> AsyncRevObjectQueue parseAny( 982 Iterable<T> objectIds, boolean reportMissing) { 983 List<T> need = new ArrayList<>(); 984 List<RevObject> have = new ArrayList<>(); 985 for (T id : objectIds) { 986 RevObject r = objects.get(id); 987 if (r != null && (r.flags & PARSED) != 0) 988 have.add(r); 989 else 990 need.add(id); 991 } 992 993 final Iterator<RevObject> objItr = have.iterator(); 994 if (need.isEmpty()) { 995 return new AsyncRevObjectQueue() { 996 @Override 997 public RevObject next() { 998 return objItr.hasNext() ? objItr.next() : null; 999 } 1000 1001 @Override 1002 public boolean cancel(boolean mayInterruptIfRunning) { 1003 return true; 1004 } 1005 1006 @Override 1007 public void release() { 1008 // In-memory only, no action required. 1009 } 1010 }; 1011 } 1012 1013 final AsyncObjectLoaderQueue<T> lItr = reader.open(need, reportMissing); 1014 return new AsyncRevObjectQueue() { 1015 @Override 1016 public RevObject next() throws MissingObjectException, 1017 IncorrectObjectTypeException, IOException { 1018 if (objItr.hasNext()) 1019 return objItr.next(); 1020 if (!lItr.next()) 1021 return null; 1022 1023 ObjectId id = lItr.getObjectId(); 1024 ObjectLoader ldr = lItr.open(); 1025 RevObject r = objects.get(id); 1026 if (r == null) 1027 r = parseNew(id, ldr); 1028 else if (r instanceof RevCommit) { 1029 byte[] raw = ldr.getCachedBytes(); 1030 ((RevCommit) r).parseCanonical(RevWalk.this, raw); 1031 } else if (r instanceof RevTag) { 1032 byte[] raw = ldr.getCachedBytes(); 1033 ((RevTag) r).parseCanonical(RevWalk.this, raw); 1034 } else 1035 r.flags |= PARSED; 1036 return r; 1037 } 1038 1039 @Override 1040 public boolean cancel(boolean mayInterruptIfRunning) { 1041 return lItr.cancel(mayInterruptIfRunning); 1042 } 1043 1044 @Override 1045 public void release() { 1046 lItr.release(); 1047 } 1048 }; 1049 } 1050 1051 /** 1052 * Ensure the object's critical headers have been parsed. 1053 * <p> 1054 * This method only returns successfully if the object exists and was parsed 1055 * without error. 1056 * 1057 * @param obj 1058 * the object the caller needs to be parsed. 1059 * @throws org.eclipse.jgit.errors.MissingObjectException 1060 * the supplied does not exist. 1061 * @throws java.io.IOException 1062 * a pack file or loose object could not be read. 1063 */ 1064 public void parseHeaders(RevObject obj) 1065 throws MissingObjectException, IOException { 1066 if ((obj.flags & PARSED) == 0) 1067 obj.parseHeaders(this); 1068 } 1069 1070 /** 1071 * Ensure the object's full body content is available. 1072 * <p> 1073 * This method only returns successfully if the object exists and was parsed 1074 * without error. 1075 * 1076 * @param obj 1077 * the object the caller needs to be parsed. 1078 * @throws org.eclipse.jgit.errors.MissingObjectException 1079 * the supplied does not exist. 1080 * @throws java.io.IOException 1081 * a pack file or loose object could not be read. 1082 */ 1083 public void parseBody(RevObject obj) 1084 throws MissingObjectException, IOException { 1085 obj.parseBody(this); 1086 } 1087 1088 /** 1089 * Peel back annotated tags until a non-tag object is found. 1090 * 1091 * @param obj 1092 * the starting object. 1093 * @return If {@code obj} is not an annotated tag, {@code obj}. Otherwise 1094 * the first non-tag object that {@code obj} references. The 1095 * returned object's headers have been parsed. 1096 * @throws org.eclipse.jgit.errors.MissingObjectException 1097 * a referenced object cannot be found. 1098 * @throws java.io.IOException 1099 * a pack file or loose object could not be read. 1100 */ 1101 public RevObject="../../../../org/eclipse/jgit/revwalk/RevObject.html#RevObject">RevObject peel(RevObject obj) throws MissingObjectException, 1102 IOException { 1103 while (obj instanceof RevTag) { 1104 parseHeaders(obj); 1105 obj = ((RevTag) obj).getObject(); 1106 } 1107 parseHeaders(obj); 1108 return obj; 1109 } 1110 1111 /** 1112 * Create a new flag for application use during walking. 1113 * <p> 1114 * Applications are only assured to be able to create 24 unique flags on any 1115 * given revision walker instance. Any flags beyond 24 are offered only if 1116 * the implementation has extra free space within its internal storage. 1117 * 1118 * @param name 1119 * description of the flag, primarily useful for debugging. 1120 * @return newly constructed flag instance. 1121 * @throws java.lang.IllegalArgumentException 1122 * too many flags have been reserved on this revision walker. 1123 */ 1124 public RevFlag newFlag(String name) { 1125 final int m = allocFlag(); 1126 return new RevFlag(this, name, m); 1127 } 1128 1129 int allocFlag() { 1130 if (freeFlags == 0) 1131 throw new IllegalArgumentException(MessageFormat.format( 1132 JGitText.get().flagsAlreadyCreated, 1133 Integer.valueOf(32 - RESERVED_FLAGS))); 1134 final int m = Integer.lowestOneBit(freeFlags); 1135 freeFlags &= ~m; 1136 return m; 1137 } 1138 1139 /** 1140 * Automatically carry a flag from a child commit to its parents. 1141 * <p> 1142 * A carried flag is copied from the child commit onto its parents when the 1143 * child commit is popped from the lowest level of walk's internal graph. 1144 * 1145 * @param flag 1146 * the flag to carry onto parents, if set on a descendant. 1147 */ 1148 public void carry(RevFlag flag) { 1149 if ((freeFlags & flag.mask) != 0) 1150 throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagIsDisposed, flag.name)); 1151 if (flag.walker != this) 1152 throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagNotFromThis, flag.name)); 1153 carryFlags |= flag.mask; 1154 } 1155 1156 /** 1157 * Automatically carry flags from a child commit to its parents. 1158 * <p> 1159 * A carried flag is copied from the child commit onto its parents when the 1160 * child commit is popped from the lowest level of walk's internal graph. 1161 * 1162 * @param set 1163 * the flags to carry onto parents, if set on a descendant. 1164 */ 1165 public void carry(Collection<RevFlag> set) { 1166 for (RevFlag flag : set) 1167 carry(flag); 1168 } 1169 1170 /** 1171 * Preserve a RevFlag during all {@code reset} methods. 1172 * <p> 1173 * Calling {@code retainOnReset(flag)} avoids needing to pass the flag 1174 * during each {@code resetRetain()} invocation on this instance. 1175 * <p> 1176 * Clearing flags marked retainOnReset requires disposing of the flag with 1177 * {@code #disposeFlag(RevFlag)} or disposing of the entire RevWalk by 1178 * {@code #dispose()}. 1179 * 1180 * @param flag 1181 * the flag to retain during all resets. 1182 * @since 3.6 1183 */ 1184 public final void retainOnReset(RevFlag flag) { 1185 if ((freeFlags & flag.mask) != 0) 1186 throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagIsDisposed, flag.name)); 1187 if (flag.walker != this) 1188 throw new IllegalArgumentException(MessageFormat.format(JGitText.get().flagNotFromThis, flag.name)); 1189 retainOnReset |= flag.mask; 1190 } 1191 1192 /** 1193 * Preserve a set of RevFlags during all {@code reset} methods. 1194 * <p> 1195 * Calling {@code retainOnReset(set)} avoids needing to pass the flags 1196 * during each {@code resetRetain()} invocation on this instance. 1197 * <p> 1198 * Clearing flags marked retainOnReset requires disposing of the flag with 1199 * {@code #disposeFlag(RevFlag)} or disposing of the entire RevWalk by 1200 * {@code #dispose()}. 1201 * 1202 * @param flags 1203 * the flags to retain during all resets. 1204 * @since 3.6 1205 */ 1206 public final void retainOnReset(Collection<RevFlag> flags) { 1207 for (RevFlag f : flags) 1208 retainOnReset(f); 1209 } 1210 1211 /** 1212 * Allow a flag to be recycled for a different use. 1213 * <p> 1214 * Recycled flags always come back as a different Java object instance when 1215 * assigned again by {@link #newFlag(String)}. 1216 * <p> 1217 * If the flag was previously being carried, the carrying request is 1218 * removed. Disposing of a carried flag while a traversal is in progress has 1219 * an undefined behavior. 1220 * 1221 * @param flag 1222 * the to recycle. 1223 */ 1224 public void disposeFlag(RevFlag flag) { 1225 freeFlag(flag.mask); 1226 } 1227 1228 void freeFlag(int mask) { 1229 retainOnReset &= ~mask; 1230 if (isNotStarted()) { 1231 freeFlags |= mask; 1232 carryFlags &= ~mask; 1233 } else { 1234 delayFreeFlags |= mask; 1235 } 1236 } 1237 1238 private void finishDelayedFreeFlags() { 1239 if (delayFreeFlags != 0) { 1240 freeFlags |= delayFreeFlags; 1241 carryFlags &= ~delayFreeFlags; 1242 delayFreeFlags = 0; 1243 } 1244 } 1245 1246 /** 1247 * Resets internal state and allows this instance to be used again. 1248 * <p> 1249 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit) 1250 * instances are not invalidated. RevFlag instances are not invalidated, but 1251 * are removed from all RevObjects. 1252 */ 1253 public final void reset() { 1254 reset(0); 1255 } 1256 1257 /** 1258 * Resets internal state and allows this instance to be used again. 1259 * <p> 1260 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit) 1261 * instances are not invalidated. RevFlag instances are not invalidated, but 1262 * are removed from all RevObjects. 1263 * 1264 * @param retainFlags 1265 * application flags that should <b>not</b> be cleared from 1266 * existing commit objects. 1267 */ 1268 public final void resetRetain(RevFlagSet retainFlags) { 1269 reset(retainFlags.mask); 1270 } 1271 1272 /** 1273 * Resets internal state and allows this instance to be used again. 1274 * <p> 1275 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit) 1276 * instances are not invalidated. RevFlag instances are not invalidated, but 1277 * are removed from all RevObjects. 1278 * <p> 1279 * See {@link #retainOnReset(RevFlag)} for an alternative that does not 1280 * require passing the flags during each reset. 1281 * 1282 * @param retainFlags 1283 * application flags that should <b>not</b> be cleared from 1284 * existing commit objects. 1285 */ 1286 public final void resetRetain(RevFlag... retainFlags) { 1287 int mask = 0; 1288 for (RevFlag flag : retainFlags) 1289 mask |= flag.mask; 1290 reset(mask); 1291 } 1292 1293 /** 1294 * Resets internal state and allows this instance to be used again. 1295 * <p> 1296 * Unlike {@link #dispose()} previously acquired RevObject (and RevCommit) 1297 * instances are not invalidated. RevFlag instances are not invalidated, but 1298 * are removed from all RevObjects. 1299 * 1300 * @param retainFlags 1301 * application flags that should <b>not</b> be cleared from 1302 * existing commit objects. 1303 */ 1304 protected void reset(int retainFlags) { 1305 finishDelayedFreeFlags(); 1306 retainFlags |= PARSED | retainOnReset; 1307 final int clearFlags = ~retainFlags; 1308 1309 final FIFORevQueueRevQueue.html#FIFORevQueue">FIFORevQueue q = new FIFORevQueue(); 1310 for (RevCommit c : roots) { 1311 if ((c.flags & clearFlags) == 0) 1312 continue; 1313 c.flags &= retainFlags; 1314 c.reset(); 1315 q.add(c); 1316 } 1317 1318 for (;;) { 1319 final RevCommit c = q.next(); 1320 if (c == null) 1321 break; 1322 if (c.parents == null) 1323 continue; 1324 for (RevCommit p : c.parents) { 1325 if ((p.flags & clearFlags) == 0) 1326 continue; 1327 p.flags &= retainFlags; 1328 p.reset(); 1329 q.add(p); 1330 } 1331 } 1332 1333 roots.clear(); 1334 queue = new DateRevQueue(); 1335 pending = new StartGenerator(this); 1336 } 1337 1338 /** 1339 * Dispose all internal state and invalidate all RevObject instances. 1340 * <p> 1341 * All RevObject (and thus RevCommit, etc.) instances previously acquired 1342 * from this RevWalk are invalidated by a dispose call. Applications must 1343 * not retain or use RevObject instances obtained prior to the dispose call. 1344 * All RevFlag instances are also invalidated, and must not be reused. 1345 */ 1346 public void dispose() { 1347 reader.close(); 1348 freeFlags = APP_FLAGS; 1349 delayFreeFlags = 0; 1350 retainOnReset = 0; 1351 carryFlags = UNINTERESTING; 1352 objects.clear(); 1353 roots.clear(); 1354 queue = new DateRevQueue(); 1355 pending = new StartGenerator(this); 1356 shallowCommitsInitialized = false; 1357 } 1358 1359 /** 1360 * Like {@link #next()}, but if a checked exception is thrown during the 1361 * walk it is rethrown as a {@link RevWalkException}. 1362 * 1363 * @throws RevWalkException if an {@link IOException} was thrown. 1364 * @return next most recent commit; null if traversal is over. 1365 */ 1366 @Nullable 1367 private RevCommit nextForIterator() { 1368 try { 1369 return next(); 1370 } catch (IOException e) { 1371 throw new RevWalkException(e); 1372 } 1373 } 1374 1375 /** 1376 * {@inheritDoc} 1377 * <p> 1378 * Returns an Iterator over the commits of this walker. 1379 * <p> 1380 * The returned iterator is only useful for one walk. If this RevWalk gets 1381 * reset a new iterator must be obtained to walk over the new results. 1382 * <p> 1383 * Applications must not use both the Iterator and the {@link #next()} API 1384 * at the same time. Pick one API and use that for the entire walk. 1385 * <p> 1386 * If a checked exception is thrown during the walk (see {@link #next()}) it 1387 * is rethrown from the Iterator as a {@link RevWalkException}. 1388 * 1389 * @see RevWalkException 1390 */ 1391 @Override 1392 public Iterator<RevCommit> iterator() { 1393 RevCommit first = nextForIterator(); 1394 1395 return new Iterator<RevCommit>() { 1396 RevCommit next = first; 1397 1398 @Override 1399 public boolean hasNext() { 1400 return next != null; 1401 } 1402 1403 @Override 1404 public RevCommit next() { 1405 RevCommit r = next; 1406 next = nextForIterator(); 1407 return r; 1408 } 1409 1410 @Override 1411 public void remove() { 1412 throw new UnsupportedOperationException(); 1413 } 1414 }; 1415 } 1416 1417 /** 1418 * Throws an exception if we have started producing output. 1419 */ 1420 protected void assertNotStarted() { 1421 if (isNotStarted()) 1422 return; 1423 throw new IllegalStateException(JGitText.get().outputHasAlreadyBeenStarted); 1424 } 1425 1426 private boolean isNotStarted() { 1427 return pending instanceof StartGenerator; 1428 } 1429 1430 /** 1431 * Create and return an {@link org.eclipse.jgit.revwalk.ObjectWalk} using 1432 * the same objects. 1433 * <p> 1434 * Prior to using this method, the caller must reset this RevWalk to clean 1435 * any flags that were used during the last traversal. 1436 * <p> 1437 * The returned ObjectWalk uses the same ObjectReader, internal object pool, 1438 * and free RevFlags. Once the ObjectWalk is created, this RevWalk should 1439 * not be used anymore. 1440 * 1441 * @return a new walk, using the exact same object pool. 1442 */ 1443 public ObjectWalk toObjectWalkWithSameObjects() { 1444 ObjectWalk ow = new ObjectWalk(reader); 1445 RevWalk rw = ow; 1446 rw.objects = objects; 1447 rw.freeFlags = freeFlags; 1448 return ow; 1449 } 1450 1451 /** 1452 * Construct a new unparsed commit for the given object. 1453 * 1454 * @param id 1455 * the object this walker requires a commit reference for. 1456 * @return a new unparsed reference for the object. 1457 */ 1458 protected RevCommit createCommit(AnyObjectId id) { 1459 return new RevCommit(id); 1460 } 1461 1462 void carryFlagsImpl(RevCommit c) { 1463 final int carry = c.flags & carryFlags; 1464 if (carry != 0) 1465 RevCommit.carryFlags(c, carry); 1466 } 1467 1468 /** 1469 * Assume additional commits are shallow (have no parents). 1470 * <p> 1471 * This method is a No-op if the collection is empty. 1472 * 1473 * @param ids 1474 * commits that should be treated as shallow commits, in addition 1475 * to any commits already known to be shallow by the repository. 1476 * @since 3.3 1477 */ 1478 public void assumeShallow(Collection<? extends ObjectId> ids) { 1479 for (ObjectId id : ids) 1480 lookupCommit(id).parents = RevCommit.NO_PARENTS; 1481 } 1482 1483 /** 1484 * Reads the "shallow" file and applies it by setting the parents of shallow 1485 * commits to an empty array. 1486 * <p> 1487 * There is a sequencing problem if the first commit being parsed is a 1488 * shallow commit, since {@link RevCommit#parseCanonical(RevWalk, byte[])} 1489 * calls this method before its callers add the new commit to the 1490 * {@link RevWalk#objects} map. That means a call from this method to 1491 * {@link #lookupCommit(AnyObjectId)} fails to find that commit and creates 1492 * a new one, which is promptly discarded. 1493 * <p> 1494 * To avoid that, {@link RevCommit#parseCanonical(RevWalk, byte[])} passes 1495 * its commit to this method, so that this method can apply the shallow 1496 * state to it directly and avoid creating the duplicate commit object. 1497 * 1498 * @param rc 1499 * the initial commit being parsed 1500 * @throws IOException 1501 * if the shallow commits file can't be read 1502 */ 1503 void initializeShallowCommits(RevCommit rc) throws IOException { 1504 if (shallowCommitsInitialized) { 1505 throw new IllegalStateException( 1506 JGitText.get().shallowCommitsAlreadyInitialized); 1507 } 1508 1509 shallowCommitsInitialized = true; 1510 1511 if (reader == null) { 1512 return; 1513 } 1514 1515 for (ObjectId id : reader.getShallowCommits()) { 1516 if (id.equals(rc.getId())) { 1517 rc.parents = RevCommit.NO_PARENTS; 1518 } else { 1519 lookupCommit(id).parents = RevCommit.NO_PARENTS; 1520 } 1521 } 1522 } 1523 }