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