BitmapWalker.java

  1. /*
  2.  * Copyright (C) 2012, Google Inc. 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 java.util.Arrays;

  13. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  14. import org.eclipse.jgit.errors.MissingObjectException;
  15. import org.eclipse.jgit.internal.revwalk.AddToBitmapFilter;
  16. import org.eclipse.jgit.internal.revwalk.AddToBitmapWithCacheFilter;
  17. import org.eclipse.jgit.internal.revwalk.AddUnseenToBitmapFilter;
  18. import org.eclipse.jgit.lib.AnyObjectId;
  19. import org.eclipse.jgit.lib.BitmapIndex;
  20. import org.eclipse.jgit.lib.BitmapIndex.Bitmap;
  21. import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
  22. import org.eclipse.jgit.lib.NullProgressMonitor;
  23. import org.eclipse.jgit.lib.ObjectId;
  24. import org.eclipse.jgit.lib.ProgressMonitor;
  25. import org.eclipse.jgit.revwalk.filter.ObjectFilter;

  26. /**
  27.  * Helper class to do ObjectWalks with pack index bitmaps.
  28.  *
  29.  * @since 4.10
  30.  */
  31. public final class BitmapWalker {

  32.     private final ObjectWalk walker;

  33.     private final BitmapIndex bitmapIndex;

  34.     private final ProgressMonitor pm;

  35.     private long countOfBitmapIndexMisses;

  36.     // Cached bitmap and commit to save walk time.
  37.     private AnyObjectId prevCommit;

  38.     private Bitmap prevBitmap;

  39.     /**
  40.      * Create a BitmapWalker.
  41.      *
  42.      * @param walker walker to use when traversing the object graph.
  43.      * @param bitmapIndex index to obtain bitmaps from.
  44.      * @param pm progress monitor to report progress on.
  45.      */
  46.     public BitmapWalker(
  47.             ObjectWalk walker, BitmapIndex bitmapIndex, ProgressMonitor pm) {
  48.         this.walker = walker;
  49.         this.bitmapIndex = bitmapIndex;
  50.         this.pm = (pm == null) ? NullProgressMonitor.INSTANCE : pm;
  51.     }

  52.     /**
  53.      * Set the cached commit for the walker.
  54.      *
  55.      * @param prevCommit
  56.      *            the cached commit.
  57.      * @since 5.8
  58.      */
  59.     public void setPrevCommit(AnyObjectId prevCommit) {
  60.         this.prevCommit = prevCommit;
  61.     }

  62.     /**
  63.      * Set the bitmap associated with the cached commit for the walker.
  64.      *
  65.      * @param prevBitmap
  66.      *            the bitmap associated with the cached commit.
  67.      * @since 5.8
  68.      */
  69.     public void setPrevBitmap(Bitmap prevBitmap) {
  70.         this.prevBitmap = prevBitmap;
  71.     }

  72.     /**
  73.      * Return the number of objects that had to be walked because they were not covered by a
  74.      * bitmap.
  75.      *
  76.      * @return the number of objects that had to be walked because they were not covered by a
  77.      *     bitmap.
  78.      */
  79.     public long getCountOfBitmapIndexMisses() {
  80.         return countOfBitmapIndexMisses;
  81.     }

  82.     /**
  83.      * Return, as a bitmap, the objects reachable from the objects in start.
  84.      *
  85.      * @param start
  86.      *            the objects to start the object traversal from.
  87.      * @param seen
  88.      *            the objects to skip if encountered during traversal.
  89.      * @param ignoreMissing
  90.      *            true to ignore missing objects, false otherwise.
  91.      * @return as a bitmap, the objects reachable from the objects in start.
  92.      * @throws org.eclipse.jgit.errors.MissingObjectException
  93.      *             the object supplied is not available from the object
  94.      *             database. This usually indicates the supplied object is
  95.      *             invalid, but the reference was constructed during an earlier
  96.      *             invocation to
  97.      *             {@link org.eclipse.jgit.revwalk.RevWalk#lookupAny(AnyObjectId, int)}.
  98.      * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
  99.      *             the object was not parsed yet and it was discovered during
  100.      *             parsing that it is not actually the type of the instance
  101.      *             passed in. This usually indicates the caller used the wrong
  102.      *             type in a
  103.      *             {@link org.eclipse.jgit.revwalk.RevWalk#lookupAny(AnyObjectId, int)}
  104.      *             call.
  105.      * @throws java.io.IOException
  106.      *             a pack file or loose object could not be read.
  107.      */
  108.     public BitmapBuilder findObjects(Iterable<? extends ObjectId> start, BitmapBuilder seen,
  109.             boolean ignoreMissing)
  110.             throws MissingObjectException, IncorrectObjectTypeException,
  111.                    IOException {
  112.         if (!ignoreMissing) {
  113.             return findObjectsWalk(start, seen, false);
  114.         }

  115.         try {
  116.             return findObjectsWalk(start, seen, true);
  117.         } catch (MissingObjectException ignore) {
  118.             // An object reachable from one of the "start"s is missing.
  119.             // Walk from the "start"s one at a time so it can be excluded.
  120.         }

  121.         final BitmapBuilder result = bitmapIndex.newBitmapBuilder();
  122.         for (ObjectId obj : start) {
  123.             Bitmap bitmap = bitmapIndex.getBitmap(obj);
  124.             if (bitmap != null) {
  125.                 result.or(bitmap);
  126.             }
  127.         }

  128.         for (ObjectId obj : start) {
  129.             if (result.contains(obj)) {
  130.                 continue;
  131.             }
  132.             try {
  133.                 result.or(findObjectsWalk(Arrays.asList(obj), result, false));
  134.             } catch (MissingObjectException ignore) {
  135.                 // An object reachable from this "start" is missing.
  136.                 //
  137.                 // This can happen when the client specified a "have" line
  138.                 // pointing to an object that is present but unreachable:
  139.                 // "git prune" and "git fsck" only guarantee that the object
  140.                 // database will continue to contain all objects reachable
  141.                 // from a ref and does not guarantee connectivity for other
  142.                 // objects in the object database.
  143.                 //
  144.                 // In this situation, skip the relevant "start" and move on
  145.                 // to the next one.
  146.                 //
  147.                 // TODO(czhen): Make findObjectsWalk resume the walk instead
  148.                 // once RevWalk and ObjectWalk support that.
  149.             }
  150.         }
  151.         return result;
  152.     }

  153.     private BitmapBuilder findObjectsWalk(Iterable<? extends ObjectId> start, BitmapBuilder seen,
  154.             boolean ignoreMissingStart)
  155.             throws MissingObjectException, IncorrectObjectTypeException,
  156.             IOException {
  157.         walker.reset();
  158.         final BitmapBuilder bitmapResult = bitmapIndex.newBitmapBuilder();

  159.         for (ObjectId obj : start) {
  160.             Bitmap bitmap = bitmapIndex.getBitmap(obj);
  161.             if (bitmap != null)
  162.                 bitmapResult.or(bitmap);
  163.         }

  164.         boolean marked = false;
  165.         for (ObjectId obj : start) {
  166.             try {
  167.                 if (!bitmapResult.contains(obj)) {
  168.                     walker.markStart(walker.parseAny(obj));
  169.                     marked = true;
  170.                 }
  171.             } catch (MissingObjectException e) {
  172.                 if (ignoreMissingStart)
  173.                     continue;
  174.                 throw e;
  175.             }
  176.         }

  177.         if (marked) {
  178.             if (prevCommit != null) {
  179.                 walker.setRevFilter(new AddToBitmapWithCacheFilter(prevCommit,
  180.                         prevBitmap, bitmapResult));
  181.             } else if (seen == null) {
  182.                 walker.setRevFilter(new AddToBitmapFilter(bitmapResult));
  183.             } else {
  184.                 walker.setRevFilter(
  185.                         new AddUnseenToBitmapFilter(seen, bitmapResult));
  186.             }
  187.             walker.setObjectFilter(new BitmapObjectFilter(bitmapResult));

  188.             while (walker.next() != null) {
  189.                 // Iterate through all of the commits. The BitmapRevFilter does
  190.                 // the work.
  191.                 //
  192.                 // filter.include returns true for commits that do not have
  193.                 // a bitmap in bitmapIndex and are not reachable from a
  194.                 // bitmap in bitmapIndex encountered earlier in the walk.
  195.                 // Thus the number of commits returned by next() measures how
  196.                 // much history was traversed without being able to make use
  197.                 // of bitmaps.
  198.                 pm.update(1);
  199.                 countOfBitmapIndexMisses++;
  200.             }

  201.             RevObject ro;
  202.             while ((ro = walker.nextObject()) != null) {
  203.                 bitmapResult.addObject(ro, ro.getType());
  204.                 pm.update(1);
  205.             }
  206.         }

  207.         return bitmapResult;
  208.     }

  209.     /**
  210.      * Filter that excludes objects already in the given bitmap.
  211.      */
  212.     static class BitmapObjectFilter extends ObjectFilter {
  213.         private final BitmapBuilder bitmap;

  214.         BitmapObjectFilter(BitmapBuilder bitmap) {
  215.             this.bitmap = bitmap;
  216.         }

  217.         @Override
  218.         public final boolean include(ObjectWalk walker, AnyObjectId objid)
  219.             throws MissingObjectException, IncorrectObjectTypeException,
  220.                    IOException {
  221.             return !bitmap.contains(objid);
  222.         }
  223.     }
  224. }