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