RevCommitList.java

  1. /*
  2.  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.revwalk;

  11. import java.io.IOException;

  12. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  13. import org.eclipse.jgit.errors.MissingObjectException;
  14. import org.eclipse.jgit.revwalk.filter.RevFilter;

  15. /**
  16.  * An ordered list of {@link org.eclipse.jgit.revwalk.RevCommit} subclasses.
  17.  *
  18.  * @param <E>
  19.  *            type of subclass of RevCommit the list is storing.
  20.  */
  21. public class RevCommitList<E extends RevCommit> extends RevObjectList<E> {
  22.     private RevWalk walker;

  23.     /** {@inheritDoc} */
  24.     @Override
  25.     public void clear() {
  26.         super.clear();
  27.         walker = null;
  28.     }

  29.     /**
  30.      * Apply a flag to all commits matching the specified filter.
  31.      * <p>
  32.      * Same as <code>applyFlag(matching, flag, 0, size())</code>, but without
  33.      * the incremental behavior.
  34.      *
  35.      * @param matching
  36.      *            the filter to test commits with. If the filter includes a
  37.      *            commit it will have the flag set; if the filter does not
  38.      *            include the commit the flag will be unset.
  39.      * @param flag
  40.      *            the flag to apply (or remove). Applications are responsible
  41.      *            for allocating this flag from the source RevWalk.
  42.      * @throws java.io.IOException
  43.      *             revision filter needed to read additional objects, but an
  44.      *             error occurred while reading the pack files or loose objects
  45.      *             of the repository.
  46.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  47.      *             revision filter needed to read additional objects, but an
  48.      *             object was not of the correct type. Repository corruption may
  49.      *             have occurred.
  50.      * @throws org.eclipse.jgit.errors.MissingObjectException
  51.      *             revision filter needed to read additional objects, but an
  52.      *             object that should be present was not found. Repository
  53.      *             corruption may have occurred.
  54.      */
  55.     public void applyFlag(RevFilter matching, RevFlag flag)
  56.             throws MissingObjectException, IncorrectObjectTypeException,
  57.             IOException {
  58.         applyFlag(matching, flag, 0, size());
  59.     }

  60.     /**
  61.      * Apply a flag to all commits matching the specified filter.
  62.      * <p>
  63.      * This version allows incremental testing and application, such as from a
  64.      * background thread that needs to periodically halt processing and send
  65.      * updates to the UI.
  66.      *
  67.      * @param matching
  68.      *            the filter to test commits with. If the filter includes a
  69.      *            commit it will have the flag set; if the filter does not
  70.      *            include the commit the flag will be unset.
  71.      * @param flag
  72.      *            the flag to apply (or remove). Applications are responsible
  73.      *            for allocating this flag from the source RevWalk.
  74.      * @param rangeBegin
  75.      *            first commit within the list to begin testing at, inclusive.
  76.      *            Must not be negative, but may be beyond the end of the list.
  77.      * @param rangeEnd
  78.      *            last commit within the list to end testing at, exclusive. If
  79.      *            smaller than or equal to <code>rangeBegin</code> then no
  80.      *            commits will be tested.
  81.      * @throws java.io.IOException
  82.      *             revision filter needed to read additional objects, but an
  83.      *             error occurred while reading the pack files or loose objects
  84.      *             of the repository.
  85.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  86.      *             revision filter needed to read additional objects, but an
  87.      *             object was not of the correct type. Repository corruption may
  88.      *             have occurred.
  89.      * @throws org.eclipse.jgit.errors.MissingObjectException
  90.      *             revision filter needed to read additional objects, but an
  91.      *             object that should be present was not found. Repository
  92.      *             corruption may have occurred.
  93.      */
  94.     public void applyFlag(final RevFilter matching, final RevFlag flag,
  95.             int rangeBegin, int rangeEnd) throws MissingObjectException,
  96.             IncorrectObjectTypeException, IOException {
  97.         final RevWalk w = flag.getRevWalk();
  98.         rangeEnd = Math.min(rangeEnd, size());
  99.         while (rangeBegin < rangeEnd) {
  100.             int index = rangeBegin;
  101.             Block s = contents;
  102.             while (s.shift > 0) {
  103.                 final int i = index >> s.shift;
  104.                 index -= i << s.shift;
  105.                 s = (Block) s.contents[i];
  106.             }

  107.             while (rangeBegin++ < rangeEnd && index < BLOCK_SIZE) {
  108.                 final RevCommit c = (RevCommit) s.contents[index++];
  109.                 if (matching.include(w, c))
  110.                     c.add(flag);
  111.                 else
  112.                     c.remove(flag);
  113.             }
  114.         }
  115.     }

  116.     /**
  117.      * Remove the given flag from all commits.
  118.      * <p>
  119.      * Same as <code>clearFlag(flag, 0, size())</code>, but without the
  120.      * incremental behavior.
  121.      *
  122.      * @param flag
  123.      *            the flag to remove. Applications are responsible for
  124.      *            allocating this flag from the source RevWalk.
  125.      */
  126.     public void clearFlag(RevFlag flag) {
  127.         clearFlag(flag, 0, size());
  128.     }

  129.     /**
  130.      * Remove the given flag from all commits.
  131.      * <p>
  132.      * This method is actually implemented in terms of:
  133.      * <code>applyFlag(RevFilter.NONE, flag, rangeBegin, rangeEnd)</code>.
  134.      *
  135.      * @param flag
  136.      *            the flag to remove. Applications are responsible for
  137.      *            allocating this flag from the source RevWalk.
  138.      * @param rangeBegin
  139.      *            first commit within the list to begin testing at, inclusive.
  140.      *            Must not be negative, but may be beyond the end of the list.
  141.      * @param rangeEnd
  142.      *            last commit within the list to end testing at, exclusive. If
  143.      *            smaller than or equal to <code>rangeBegin</code> then no
  144.      *            commits will be tested.
  145.      */
  146.     public void clearFlag(final RevFlag flag, final int rangeBegin,
  147.             final int rangeEnd) {
  148.         try {
  149.             applyFlag(RevFilter.NONE, flag, rangeBegin, rangeEnd);
  150.         } catch (IOException e) {
  151.             // Never happen. The filter we use does not throw any
  152.             // exceptions, for any reason.
  153.         }
  154.     }

  155.     /**
  156.      * Find the next commit that has the given flag set.
  157.      *
  158.      * @param flag
  159.      *            the flag to test commits against.
  160.      * @param begin
  161.      *            first commit index to test at. Applications may wish to begin
  162.      *            at 0, to test the first commit in the list.
  163.      * @return index of the first commit at or after index <code>begin</code>
  164.      *         that has the specified flag set on it; -1 if no match is found.
  165.      */
  166.     public int indexOf(RevFlag flag, int begin) {
  167.         while (begin < size()) {
  168.             int index = begin;
  169.             Block s = contents;
  170.             while (s.shift > 0) {
  171.                 final int i = index >> s.shift;
  172.                 index -= i << s.shift;
  173.                 s = (Block) s.contents[i];
  174.             }

  175.             while (begin++ < size() && index < BLOCK_SIZE) {
  176.                 final RevCommit c = (RevCommit) s.contents[index++];
  177.                 if (c.has(flag))
  178.                     return begin;
  179.             }
  180.         }
  181.         return -1;
  182.     }

  183.     /**
  184.      * Find the next commit that has the given flag set.
  185.      *
  186.      * @param flag
  187.      *            the flag to test commits against.
  188.      * @param begin
  189.      *            first commit index to test at. Applications may wish to begin
  190.      *            at <code>size()-1</code>, to test the last commit in the
  191.      *            list.
  192.      * @return index of the first commit at or before index <code>begin</code>
  193.      *         that has the specified flag set on it; -1 if no match is found.
  194.      */
  195.     public int lastIndexOf(RevFlag flag, int begin) {
  196.         begin = Math.min(begin, size() - 1);
  197.         while (begin >= 0) {
  198.             int index = begin;
  199.             Block s = contents;
  200.             while (s.shift > 0) {
  201.                 final int i = index >> s.shift;
  202.                 index -= i << s.shift;
  203.                 s = (Block) s.contents[i];
  204.             }

  205.             while (begin-- >= 0 && index >= 0) {
  206.                 final RevCommit c = (RevCommit) s.contents[index--];
  207.                 if (c.has(flag))
  208.                     return begin;
  209.             }
  210.         }
  211.         return -1;
  212.     }

  213.     /**
  214.      * Set the revision walker this list populates itself from.
  215.      *
  216.      * @param w
  217.      *            the walker to populate from.
  218.      * @see #fillTo(int)
  219.      */
  220.     public void source(RevWalk w) {
  221.         walker = w;
  222.     }

  223.     /**
  224.      * Is this list still pending more items?
  225.      *
  226.      * @return true if {@link #fillTo(int)} might be able to extend the list
  227.      *         size when called.
  228.      */
  229.     public boolean isPending() {
  230.         return walker != null;
  231.     }

  232.     /**
  233.      * Ensure this list contains at least a specified number of commits.
  234.      * <p>
  235.      * The revision walker specified by {@link #source(RevWalk)} is pumped until
  236.      * the given number of commits are contained in this list. If there are
  237.      * fewer total commits available from the walk then the method will return
  238.      * early. Callers can test the final size of the list by {@link #size()} to
  239.      * determine if the high water mark specified was met.
  240.      *
  241.      * @param highMark
  242.      *            number of commits the caller wants this list to contain when
  243.      *            the fill operation is complete.
  244.      * @throws java.io.IOException
  245.      *             see {@link org.eclipse.jgit.revwalk.RevWalk#next()}
  246.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  247.      *             see {@link org.eclipse.jgit.revwalk.RevWalk#next()}
  248.      * @throws org.eclipse.jgit.errors.MissingObjectException
  249.      *             see {@link org.eclipse.jgit.revwalk.RevWalk#next()}
  250.      */
  251.     @SuppressWarnings("unchecked")
  252.     public void fillTo(int highMark) throws MissingObjectException,
  253.             IncorrectObjectTypeException, IOException {
  254.         if (walker == null || size > highMark)
  255.             return;

  256.         RevCommit c = walker.next();
  257.         if (c == null) {
  258.             walker = null;
  259.             return;
  260.         }
  261.         enter(size, (E) c);
  262.         add((E) c);

  263.         while (size <= highMark) {
  264.             int index = size;
  265.             Block s = contents;
  266.             while (index >> s.shift >= BLOCK_SIZE) {
  267.                 s = new Block(s.shift + BLOCK_SHIFT);
  268.                 s.contents[0] = contents;
  269.                 contents = s;
  270.             }
  271.             while (s.shift > 0) {
  272.                 final int i = index >> s.shift;
  273.                 index -= i << s.shift;
  274.                 if (s.contents[i] == null)
  275.                     s.contents[i] = new Block(s.shift - BLOCK_SHIFT);
  276.                 s = (Block) s.contents[i];
  277.             }

  278.             final Object[] dst = s.contents;
  279.             while (size <= highMark && index < BLOCK_SIZE) {
  280.                 c = walker.next();
  281.                 if (c == null) {
  282.                     walker = null;
  283.                     return;
  284.                 }
  285.                 enter(size++, (E) c);
  286.                 dst[index++] = c;
  287.             }
  288.         }
  289.     }

  290.     /**
  291.      * Ensures all commits until the given commit are loaded. The revision
  292.      * walker specified by {@link #source(RevWalk)} is pumped until the
  293.      * specified commit is loaded. Callers can test the final size of the list
  294.      * by {@link #size()} to determine if the high water mark specified was met.
  295.      * <p>
  296.      *
  297.      * @param commitToLoad
  298.      *            commit the caller wants this list to contain when the fill
  299.      *            operation is complete.
  300.      * @param highMark
  301.      *            maximum number of commits the caller wants this list to
  302.      *            contain when the fill operation is complete. If highMark is 0
  303.      *            the walk is pumped until the specified commit or the end of
  304.      *            the walk is reached.
  305.      * @throws java.io.IOException
  306.      *             see {@link org.eclipse.jgit.revwalk.RevWalk#next()}
  307.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  308.      *             see {@link org.eclipse.jgit.revwalk.RevWalk#next()}
  309.      * @throws org.eclipse.jgit.errors.MissingObjectException
  310.      *             see {@link org.eclipse.jgit.revwalk.RevWalk#next()}
  311.      */
  312.     @SuppressWarnings("unchecked")
  313.     public void fillTo(RevCommit commitToLoad, int highMark)
  314.             throws MissingObjectException, IncorrectObjectTypeException,
  315.             IOException {
  316.         if (walker == null || commitToLoad == null
  317.                 || (highMark > 0 && size > highMark))
  318.             return;

  319.         RevCommit c = walker.next();
  320.         if (c == null) {
  321.             walker = null;
  322.             return;
  323.         }
  324.         enter(size, (E) c);
  325.         add((E) c);

  326.         while ((highMark == 0 || size <= highMark) && !c.equals(commitToLoad)) {
  327.             int index = size;
  328.             Block s = contents;
  329.             while (index >> s.shift >= BLOCK_SIZE) {
  330.                 s = new Block(s.shift + BLOCK_SHIFT);
  331.                 s.contents[0] = contents;
  332.                 contents = s;
  333.             }
  334.             while (s.shift > 0) {
  335.                 final int i = index >> s.shift;
  336.                 index -= i << s.shift;
  337.                 if (s.contents[i] == null)
  338.                     s.contents[i] = new Block(s.shift - BLOCK_SHIFT);
  339.                 s = (Block) s.contents[i];
  340.             }

  341.             final Object[] dst = s.contents;
  342.             while ((highMark == 0 || size <= highMark) && index < BLOCK_SIZE
  343.                     && !c.equals(commitToLoad)) {
  344.                 c = walker.next();
  345.                 if (c == null) {
  346.                     walker = null;
  347.                     return;
  348.                 }
  349.                 enter(size++, (E) c);
  350.                 dst[index++] = c;
  351.             }
  352.         }
  353.     }

  354.     /**
  355.      * Optional callback invoked when commits enter the list by fillTo.
  356.      * <p>
  357.      * This method is only called during {@link #fillTo(int)}.
  358.      *
  359.      * @param index
  360.      *            the list position this object will appear at.
  361.      * @param e
  362.      *            the object being added (or set) into the list.
  363.      */
  364.     protected void enter(int index, E e) {
  365.         // Do nothing by default.
  366.     }
  367. }