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