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