| /* |
| * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Distribution License v. 1.0 which is available at |
| * https://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| package org.eclipse.jgit.revwalk; |
| |
| import java.io.IOException; |
| |
| import org.eclipse.jgit.errors.IncorrectObjectTypeException; |
| import org.eclipse.jgit.errors.MissingObjectException; |
| import org.eclipse.jgit.revwalk.filter.RevFilter; |
| |
| /** |
| * An ordered list of {@link org.eclipse.jgit.revwalk.RevCommit} subclasses. |
| * |
| * @param <E> |
| * type of subclass of RevCommit the list is storing. |
| */ |
| public class RevCommitList<E extends RevCommit> extends RevObjectList<E> { |
| private RevWalk walker; |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void clear() { |
| super.clear(); |
| walker = null; |
| } |
| |
| /** |
| * Apply a flag to all commits matching the specified filter. |
| * <p> |
| * Same as <code>applyFlag(matching, flag, 0, size())</code>, but without |
| * the incremental behavior. |
| * |
| * @param matching |
| * the filter to test commits with. If the filter includes a |
| * commit it will have the flag set; if the filter does not |
| * include the commit the flag will be unset. |
| * @param flag |
| * the flag to apply (or remove). Applications are responsible |
| * for allocating this flag from the source RevWalk. |
| * @throws java.io.IOException |
| * revision filter needed to read additional objects, but an |
| * error occurred while reading the pack files or loose objects |
| * of the repository. |
| * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException |
| * revision filter needed to read additional objects, but an |
| * object was not of the correct type. Repository corruption may |
| * have occurred. |
| * @throws org.eclipse.jgit.errors.MissingObjectException |
| * revision filter needed to read additional objects, but an |
| * object that should be present was not found. Repository |
| * corruption may have occurred. |
| */ |
| public void applyFlag(RevFilter matching, RevFlag flag) |
| throws MissingObjectException, IncorrectObjectTypeException, |
| IOException { |
| applyFlag(matching, flag, 0, size()); |
| } |
| |
| /** |
| * Apply a flag to all commits matching the specified filter. |
| * <p> |
| * This version allows incremental testing and application, such as from a |
| * background thread that needs to periodically halt processing and send |
| * updates to the UI. |
| * |
| * @param matching |
| * the filter to test commits with. If the filter includes a |
| * commit it will have the flag set; if the filter does not |
| * include the commit the flag will be unset. |
| * @param flag |
| * the flag to apply (or remove). Applications are responsible |
| * for allocating this flag from the source RevWalk. |
| * @param rangeBegin |
| * first commit within the list to begin testing at, inclusive. |
| * Must not be negative, but may be beyond the end of the list. |
| * @param rangeEnd |
| * last commit within the list to end testing at, exclusive. If |
| * smaller than or equal to <code>rangeBegin</code> then no |
| * commits will be tested. |
| * @throws java.io.IOException |
| * revision filter needed to read additional objects, but an |
| * error occurred while reading the pack files or loose objects |
| * of the repository. |
| * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException |
| * revision filter needed to read additional objects, but an |
| * object was not of the correct type. Repository corruption may |
| * have occurred. |
| * @throws org.eclipse.jgit.errors.MissingObjectException |
| * revision filter needed to read additional objects, but an |
| * object that should be present was not found. Repository |
| * corruption may have occurred. |
| */ |
| public void applyFlag(final RevFilter matching, final RevFlag flag, |
| int rangeBegin, int rangeEnd) throws MissingObjectException, |
| IncorrectObjectTypeException, IOException { |
| final RevWalk w = flag.getRevWalk(); |
| rangeEnd = Math.min(rangeEnd, size()); |
| while (rangeBegin < rangeEnd) { |
| int index = rangeBegin; |
| Block s = contents; |
| while (s.shift > 0) { |
| final int i = index >> s.shift; |
| index -= i << s.shift; |
| s = (Block) s.contents[i]; |
| } |
| |
| while (rangeBegin++ < rangeEnd && index < BLOCK_SIZE) { |
| final RevCommit c = (RevCommit) s.contents[index++]; |
| if (matching.include(w, c)) |
| c.add(flag); |
| else |
| c.remove(flag); |
| } |
| } |
| } |
| |
| /** |
| * Remove the given flag from all commits. |
| * <p> |
| * Same as <code>clearFlag(flag, 0, size())</code>, but without the |
| * incremental behavior. |
| * |
| * @param flag |
| * the flag to remove. Applications are responsible for |
| * allocating this flag from the source RevWalk. |
| */ |
| public void clearFlag(RevFlag flag) { |
| clearFlag(flag, 0, size()); |
| } |
| |
| /** |
| * Remove the given flag from all commits. |
| * <p> |
| * This method is actually implemented in terms of: |
| * <code>applyFlag(RevFilter.NONE, flag, rangeBegin, rangeEnd)</code>. |
| * |
| * @param flag |
| * the flag to remove. Applications are responsible for |
| * allocating this flag from the source RevWalk. |
| * @param rangeBegin |
| * first commit within the list to begin testing at, inclusive. |
| * Must not be negative, but may be beyond the end of the list. |
| * @param rangeEnd |
| * last commit within the list to end testing at, exclusive. If |
| * smaller than or equal to <code>rangeBegin</code> then no |
| * commits will be tested. |
| */ |
| public void clearFlag(final RevFlag flag, final int rangeBegin, |
| final int rangeEnd) { |
| try { |
| applyFlag(RevFilter.NONE, flag, rangeBegin, rangeEnd); |
| } catch (IOException e) { |
| // Never happen. The filter we use does not throw any |
| // exceptions, for any reason. |
| } |
| } |
| |
| /** |
| * Find the next commit that has the given flag set. |
| * |
| * @param flag |
| * the flag to test commits against. |
| * @param begin |
| * first commit index to test at. Applications may wish to begin |
| * at 0, to test the first commit in the list. |
| * @return index of the first commit at or after index <code>begin</code> |
| * that has the specified flag set on it; -1 if no match is found. |
| */ |
| public int indexOf(RevFlag flag, int begin) { |
| while (begin < size()) { |
| int index = begin; |
| Block s = contents; |
| while (s.shift > 0) { |
| final int i = index >> s.shift; |
| index -= i << s.shift; |
| s = (Block) s.contents[i]; |
| } |
| |
| while (begin++ < size() && index < BLOCK_SIZE) { |
| final RevCommit c = (RevCommit) s.contents[index++]; |
| if (c.has(flag)) |
| return begin; |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Find the next commit that has the given flag set. |
| * |
| * @param flag |
| * the flag to test commits against. |
| * @param begin |
| * first commit index to test at. Applications may wish to begin |
| * at <code>size()-1</code>, to test the last commit in the |
| * list. |
| * @return index of the first commit at or before index <code>begin</code> |
| * that has the specified flag set on it; -1 if no match is found. |
| */ |
| public int lastIndexOf(RevFlag flag, int begin) { |
| begin = Math.min(begin, size() - 1); |
| while (begin >= 0) { |
| int index = begin; |
| Block s = contents; |
| while (s.shift > 0) { |
| final int i = index >> s.shift; |
| index -= i << s.shift; |
| s = (Block) s.contents[i]; |
| } |
| |
| while (begin-- >= 0 && index >= 0) { |
| final RevCommit c = (RevCommit) s.contents[index--]; |
| if (c.has(flag)) |
| return begin; |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Set the revision walker this list populates itself from. |
| * |
| * @param w |
| * the walker to populate from. |
| * @see #fillTo(int) |
| */ |
| public void source(RevWalk w) { |
| walker = w; |
| } |
| |
| /** |
| * Is this list still pending more items? |
| * |
| * @return true if {@link #fillTo(int)} might be able to extend the list |
| * size when called. |
| */ |
| public boolean isPending() { |
| return walker != null; |
| } |
| |
| /** |
| * Ensure this list contains at least a specified number of commits. |
| * <p> |
| * The revision walker specified by {@link #source(RevWalk)} is pumped until |
| * the given number of commits are contained in this list. If there are |
| * fewer total commits available from the walk then the method will return |
| * early. Callers can test the final size of the list by {@link #size()} to |
| * determine if the high water mark specified was met. |
| * |
| * @param highMark |
| * number of commits the caller wants this list to contain when |
| * the fill operation is complete. |
| * @throws java.io.IOException |
| * see {@link org.eclipse.jgit.revwalk.RevWalk#next()} |
| * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException |
| * see {@link org.eclipse.jgit.revwalk.RevWalk#next()} |
| * @throws org.eclipse.jgit.errors.MissingObjectException |
| * see {@link org.eclipse.jgit.revwalk.RevWalk#next()} |
| */ |
| @SuppressWarnings("unchecked") |
| public void fillTo(int highMark) throws MissingObjectException, |
| IncorrectObjectTypeException, IOException { |
| if (walker == null || size > highMark) |
| return; |
| |
| RevCommit c = walker.next(); |
| if (c == null) { |
| walker = null; |
| return; |
| } |
| enter(size, (E) c); |
| add((E) c); |
| |
| while (size <= highMark) { |
| int index = size; |
| Block s = contents; |
| while (index >> s.shift >= BLOCK_SIZE) { |
| s = new Block(s.shift + BLOCK_SHIFT); |
| s.contents[0] = contents; |
| contents = s; |
| } |
| while (s.shift > 0) { |
| final int i = index >> s.shift; |
| index -= i << s.shift; |
| if (s.contents[i] == null) |
| s.contents[i] = new Block(s.shift - BLOCK_SHIFT); |
| s = (Block) s.contents[i]; |
| } |
| |
| final Object[] dst = s.contents; |
| while (size <= highMark && index < BLOCK_SIZE) { |
| c = walker.next(); |
| if (c == null) { |
| walker = null; |
| return; |
| } |
| enter(size++, (E) c); |
| dst[index++] = c; |
| } |
| } |
| } |
| |
| /** |
| * Ensures all commits until the given commit are loaded. The revision |
| * walker specified by {@link #source(RevWalk)} is pumped until the |
| * specified commit is loaded. Callers can test the final size of the list |
| * by {@link #size()} to determine if the high water mark specified was met. |
| * <p> |
| * |
| * @param commitToLoad |
| * commit the caller wants this list to contain when the fill |
| * operation is complete. |
| * @param highMark |
| * maximum number of commits the caller wants this list to |
| * contain when the fill operation is complete. If highMark is 0 |
| * the walk is pumped until the specified commit or the end of |
| * the walk is reached. |
| * @throws java.io.IOException |
| * see {@link org.eclipse.jgit.revwalk.RevWalk#next()} |
| * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException |
| * see {@link org.eclipse.jgit.revwalk.RevWalk#next()} |
| * @throws org.eclipse.jgit.errors.MissingObjectException |
| * see {@link org.eclipse.jgit.revwalk.RevWalk#next()} |
| */ |
| @SuppressWarnings("unchecked") |
| public void fillTo(RevCommit commitToLoad, int highMark) |
| throws MissingObjectException, IncorrectObjectTypeException, |
| IOException { |
| if (walker == null || commitToLoad == null |
| || (highMark > 0 && size > highMark)) |
| return; |
| |
| RevCommit c = walker.next(); |
| if (c == null) { |
| walker = null; |
| return; |
| } |
| enter(size, (E) c); |
| add((E) c); |
| |
| while ((highMark == 0 || size <= highMark) && !c.equals(commitToLoad)) { |
| int index = size; |
| Block s = contents; |
| while (index >> s.shift >= BLOCK_SIZE) { |
| s = new Block(s.shift + BLOCK_SHIFT); |
| s.contents[0] = contents; |
| contents = s; |
| } |
| while (s.shift > 0) { |
| final int i = index >> s.shift; |
| index -= i << s.shift; |
| if (s.contents[i] == null) |
| s.contents[i] = new Block(s.shift - BLOCK_SHIFT); |
| s = (Block) s.contents[i]; |
| } |
| |
| final Object[] dst = s.contents; |
| while ((highMark == 0 || size <= highMark) && index < BLOCK_SIZE |
| && !c.equals(commitToLoad)) { |
| c = walker.next(); |
| if (c == null) { |
| walker = null; |
| return; |
| } |
| enter(size++, (E) c); |
| dst[index++] = c; |
| } |
| } |
| } |
| |
| /** |
| * Optional callback invoked when commits enter the list by fillTo. |
| * <p> |
| * This method is only called during {@link #fillTo(int)}. |
| * |
| * @param index |
| * the list position this object will appear at. |
| * @param e |
| * the object being added (or set) into the list. |
| */ |
| protected void enter(int index, E e) { |
| // Do nothing by default. |
| } |
| } |