DepthGenerator.java

  1. /*
  2.  * Copyright (C) 2010, Garmin International
  3.  * Copyright (C) 2010, Matt Fischer <matt.fischer@garmin.com> and others
  4.  *
  5.  * This program and the accompanying materials are made available under the
  6.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  7.  * https://www.eclipse.org/org/documents/edl-v10.php.
  8.  *
  9.  * SPDX-License-Identifier: BSD-3-Clause
  10.  */

  11. package org.eclipse.jgit.revwalk;

  12. import java.io.IOException;

  13. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  14. import org.eclipse.jgit.errors.MissingObjectException;
  15. import org.eclipse.jgit.lib.ObjectId;

  16. /**
  17.  * Only produce commits which are below a specified depth.
  18.  *
  19.  * @see DepthWalk
  20.  */
  21. class DepthGenerator extends Generator {
  22.     private final FIFORevQueue pending;

  23.     private final int depth;

  24.     private final int deepenSince;

  25.     private final RevWalk walk;

  26.     /**
  27.      * Commits which used to be shallow in the client, but which are
  28.      * being extended as part of this fetch.  These commits should be
  29.      * returned to the caller as UNINTERESTING so that their blobs/trees
  30.      * can be marked appropriately in the pack writer.
  31.      */
  32.     private final RevFlag UNSHALLOW;

  33.     /**
  34.      * Commits which the normal framework has marked as UNINTERESTING,
  35.      * but which we now care about again.  This happens if a client is
  36.      * extending a shallow checkout to become deeper--the new commits at
  37.      * the bottom of the graph need to be sent, even though they are
  38.      * below other commits which the client already has.
  39.      */
  40.     private final RevFlag REINTERESTING;

  41.     /**
  42.      * Commits reachable from commits that the client specified using --shallow-exclude.
  43.      */
  44.     private final RevFlag DEEPEN_NOT;

  45.     /**
  46.      * @param w
  47.      * @param s Parent generator
  48.      * @throws MissingObjectException
  49.      * @throws IncorrectObjectTypeException
  50.      * @throws IOException
  51.      */
  52.     DepthGenerator(DepthWalk w, Generator s) throws MissingObjectException,
  53.             IncorrectObjectTypeException, IOException {
  54.         super(s.firstParent);
  55.         pending = new FIFORevQueue(firstParent);
  56.         walk = (RevWalk)w;

  57.         this.depth = w.getDepth();
  58.         this.deepenSince = w.getDeepenSince();
  59.         this.UNSHALLOW = w.getUnshallowFlag();
  60.         this.REINTERESTING = w.getReinterestingFlag();
  61.         this.DEEPEN_NOT = w.getDeepenNotFlag();

  62.         s.shareFreeList(pending);

  63.         // Begin by sucking out all of the source's commits, and
  64.         // adding them to the pending queue
  65.         FIFORevQueue unshallowCommits = new FIFORevQueue();
  66.         for (;;) {
  67.             RevCommit c = s.next();
  68.             if (c == null)
  69.                 break;
  70.             if (c.has(UNSHALLOW)) {
  71.                 unshallowCommits.add(c);
  72.             } else if (((DepthWalk.Commit) c).getDepth() == 0) {
  73.                 pending.add(c);
  74.             }
  75.         }
  76.         // Move unshallow commits to the front so that the REINTERESTING flag
  77.         // carry over code is executed first.
  78.         for (;;) {
  79.             RevCommit c = unshallowCommits.next();
  80.             if (c == null) {
  81.                 break;
  82.             }
  83.             pending.unpop(c);
  84.         }

  85.         // Mark DEEPEN_NOT on all deepen-not commits and their ancestors.
  86.         // TODO(jonathantanmy): This implementation is somewhat
  87.         // inefficient in that any "deepen-not <ref>" in the request
  88.         // results in all commits reachable from that ref being parsed
  89.         // and marked, even if the commit topology is such that it is
  90.         // not necessary.
  91.         for (ObjectId oid : w.getDeepenNots()) {
  92.             RevCommit c;
  93.             try {
  94.                 c = walk.parseCommit(oid);
  95.             } catch (IncorrectObjectTypeException notCommit) {
  96.                 // The C Git implementation silently tolerates
  97.                 // non-commits, so do the same here.
  98.                 continue;
  99.             }

  100.             FIFORevQueue queue = new FIFORevQueue();
  101.             queue.add(c);
  102.             while ((c = queue.next()) != null) {
  103.                 if (c.has(DEEPEN_NOT)) {
  104.                     continue;
  105.                 }

  106.                 walk.parseHeaders(c);
  107.                 c.add(DEEPEN_NOT);
  108.                 for (RevCommit p : c.getParents()) {
  109.                     queue.add(p);
  110.                 }
  111.             }
  112.         }
  113.     }

  114.     @Override
  115.     int outputType() {
  116.         return pending.outputType() | HAS_UNINTERESTING;
  117.     }

  118.     @Override
  119.     void shareFreeList(BlockRevQueue q) {
  120.         pending.shareFreeList(q);
  121.     }

  122.     @Override
  123.     RevCommit next() throws MissingObjectException,
  124.             IncorrectObjectTypeException, IOException {
  125.         // Perform a breadth-first descent into the commit graph,
  126.         // marking depths as we go.  This means that if a commit is
  127.         // reachable by more than one route, we are guaranteed to
  128.         // arrive by the shortest route first.
  129.         for (;;) {
  130.             final DepthWalk.Commit c = (DepthWalk.Commit) pending.next();
  131.             if (c == null)
  132.                 return null;

  133.             if ((c.flags & RevWalk.PARSED) == 0)
  134.                 c.parseHeaders(walk);

  135.             if (c.getCommitTime() < deepenSince) {
  136.                 continue;
  137.             }

  138.             if (c.has(DEEPEN_NOT)) {
  139.                 continue;
  140.             }

  141.             int newDepth = c.depth + 1;

  142.             int n = c.getParentCount();
  143.             for (int i = 0; i < n; i++) {
  144.                 if (firstParent && i > 0) {
  145.                     break;
  146.                 }
  147.                 RevCommit p = c.getParent(i);
  148.                 DepthWalk.Commit dp = (DepthWalk.Commit) p;

  149.                 // If no depth has been assigned to this commit, assign
  150.                 // it now.  Since we arrive by the shortest route first,
  151.                 // this depth is guaranteed to be the smallest value that
  152.                 // any path could produce.
  153.                 if (dp.depth == -1) {
  154.                     boolean failsDeepenSince = false;
  155.                     if (deepenSince != 0) {
  156.                         if ((p.flags & RevWalk.PARSED) == 0) {
  157.                             p.parseHeaders(walk);
  158.                         }
  159.                         failsDeepenSince =
  160.                             p.getCommitTime() < deepenSince;
  161.                     }

  162.                     dp.depth = newDepth;

  163.                     // If the parent is not too deep and was not excluded, add
  164.                     // it to the queue so that we can produce it later
  165.                     if (newDepth <= depth && !failsDeepenSince &&
  166.                             !p.has(DEEPEN_NOT)) {
  167.                         pending.add(p);
  168.                     } else {
  169.                         dp.makesChildBoundary = true;
  170.                     }
  171.                 }

  172.                 if (dp.makesChildBoundary) {
  173.                     c.isBoundary = true;
  174.                 }

  175.                 // If the current commit has become unshallowed, everything
  176.                 // below us is new to the client.  Mark its parent as
  177.                 // re-interesting, and carry that flag downward to all
  178.                 // of its ancestors.
  179.                 if(c.has(UNSHALLOW) || c.has(REINTERESTING)) {
  180.                     p.add(REINTERESTING);
  181.                     p.flags &= ~RevWalk.UNINTERESTING;
  182.                 }
  183.             }

  184.             boolean produce = true;

  185.             // Unshallow commits are uninteresting, but still need to be sent
  186.             // up to the PackWriter so that it will exclude objects correctly.
  187.             // All other uninteresting commits should be omitted.
  188.             if ((c.flags & RevWalk.UNINTERESTING) != 0 && !c.has(UNSHALLOW))
  189.                 produce = false;

  190.             if (c.getCommitTime() < deepenSince) {
  191.                 produce = false;
  192.             }

  193.             if (produce)
  194.                 return c;
  195.         }
  196.     }
  197. }