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