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