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