FullConnectivityChecker.java

  1. /*
  2.  * Copyright (c) 2019, Google LLC  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.  * http://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.internal.transport.connectivity;

  11. import java.io.IOException;
  12. import java.util.Set;

  13. import org.eclipse.jgit.errors.MissingObjectException;
  14. import org.eclipse.jgit.internal.JGitText;
  15. import org.eclipse.jgit.lib.Constants;
  16. import org.eclipse.jgit.lib.ObjectId;
  17. import org.eclipse.jgit.lib.ObjectIdSubclassMap;
  18. import org.eclipse.jgit.lib.ProgressMonitor;
  19. import org.eclipse.jgit.revwalk.ObjectWalk;
  20. import org.eclipse.jgit.revwalk.RevBlob;
  21. import org.eclipse.jgit.revwalk.RevCommit;
  22. import org.eclipse.jgit.revwalk.RevFlag;
  23. import org.eclipse.jgit.revwalk.RevObject;
  24. import org.eclipse.jgit.revwalk.RevSort;
  25. import org.eclipse.jgit.revwalk.RevTree;
  26. import org.eclipse.jgit.transport.ConnectivityChecker;
  27. import org.eclipse.jgit.transport.ReceiveCommand;
  28. import org.eclipse.jgit.transport.ReceiveCommand.Result;

  29. /**
  30.  * A connectivity checker that uses the entire reference database to perform
  31.  * reachability checks when checking the connectivity of objects. If
  32.  * info.isCheckObjects() is set it will also check that objects referenced by
  33.  * deltas are either provided or reachable as well.
  34.  */
  35. public final class FullConnectivityChecker implements ConnectivityChecker {
  36.     @Override
  37.     public void checkConnectivity(ConnectivityCheckInfo connectivityCheckInfo,
  38.             Set<ObjectId> haves, ProgressMonitor pm)
  39.             throws MissingObjectException, IOException {
  40.         pm.beginTask(JGitText.get().countingObjects,
  41.                 ProgressMonitor.UNKNOWN);
  42.         try (ObjectWalk ow = new ObjectWalk(connectivityCheckInfo.getRepository())) {
  43.             if (!markStartAndKnownNodes(connectivityCheckInfo, ow, haves,
  44.                     pm)) {
  45.                 return;
  46.             }
  47.             checkCommitTree(connectivityCheckInfo, ow, pm);
  48.             checkObjects(connectivityCheckInfo, ow, pm);
  49.         } finally {
  50.             pm.endTask();
  51.         }
  52.     }

  53.     /**
  54.      * @param connectivityCheckInfo
  55.      *            Source for connectivity check.
  56.      * @param ow
  57.      *            Walk which can also check blobs.
  58.      * @param haves
  59.      *            Set of references known for client.
  60.      * @param pm
  61.      *            Monitor to publish progress to.
  62.      * @return true if at least one new node was marked.
  63.      * @throws IOException
  64.      *             an error occurred during connectivity checking.
  65.      */
  66.     private boolean markStartAndKnownNodes(
  67.             ConnectivityCheckInfo connectivityCheckInfo,
  68.             ObjectWalk ow,
  69.             Set<ObjectId> haves, ProgressMonitor pm)
  70.             throws IOException {
  71.         boolean markTrees = connectivityCheckInfo
  72.                 .isCheckObjects()
  73.                 && !connectivityCheckInfo.getParser().getBaseObjectIds()
  74.                         .isEmpty();
  75.         if (connectivityCheckInfo.isCheckObjects()) {
  76.             ow.sort(RevSort.TOPO);
  77.             if (!connectivityCheckInfo.getParser().getBaseObjectIds()
  78.                     .isEmpty()) {
  79.                 ow.sort(RevSort.BOUNDARY, true);
  80.             }
  81.         }
  82.         boolean hasInteresting = false;

  83.         for (ReceiveCommand cmd : connectivityCheckInfo.getCommands()) {
  84.             if (cmd.getResult() != Result.NOT_ATTEMPTED) {
  85.                 continue;
  86.             }
  87.             if (cmd.getType() == ReceiveCommand.Type.DELETE) {
  88.                 continue;
  89.             }
  90.             if (haves.contains(cmd.getNewId())) {
  91.                 continue;
  92.             }
  93.             ow.markStart(ow.parseAny(cmd.getNewId()));
  94.             pm.update(1);
  95.             hasInteresting = true;
  96.         }
  97.         if (!hasInteresting) {
  98.             return false;
  99.         }
  100.         for (ObjectId have : haves) {
  101.             RevObject o = ow.parseAny(have);
  102.             ow.markUninteresting(o);
  103.             pm.update(1);

  104.             if (markTrees) {
  105.                 o = ow.peel(o);
  106.                 if (o instanceof RevCommit) {
  107.                     o = ((RevCommit) o).getTree();
  108.                 }
  109.                 if (o instanceof RevTree) {
  110.                     ow.markUninteresting(o);
  111.                 }
  112.             }
  113.         }
  114.         return true;
  115.     }

  116.     /**
  117.      * @param connectivityCheckInfo
  118.      *            Source for connectivity check.
  119.      * @param ow
  120.      *            Walk which can also check blobs.
  121.      * @param pm
  122.      *            Monitor to publish progress to.
  123.      * @throws IOException
  124.      *             an error occurred during connectivity checking.
  125.      */
  126.     private void checkCommitTree(ConnectivityCheckInfo connectivityCheckInfo,
  127.             ObjectWalk ow,
  128.             ProgressMonitor pm) throws IOException {
  129.         RevCommit c;
  130.         ObjectIdSubclassMap<ObjectId> newObjectIds = connectivityCheckInfo
  131.                 .getParser()
  132.                 .getNewObjectIds();
  133.         while ((c = ow.next()) != null) {
  134.             pm.update(1);
  135.             if (connectivityCheckInfo.isCheckObjects()
  136.                     && !c.has(RevFlag.UNINTERESTING)
  137.                     && !newObjectIds.contains(c)) {
  138.                 throw new MissingObjectException(c, Constants.TYPE_COMMIT);
  139.             }
  140.         }
  141.     }

  142.     /**
  143.      * @param connectivityCheckInfo
  144.      *            Source for connectivity check.
  145.      * @param ow
  146.      *            Walk which can also check blobs.
  147.      * @param pm
  148.      *            Monitor to publish progress to.
  149.      * @throws IOException
  150.      *             an error occurred during connectivity checking.
  151.      *
  152.      */
  153.     private void checkObjects(ConnectivityCheckInfo connectivityCheckInfo,
  154.             ObjectWalk ow,
  155.             ProgressMonitor pm) throws IOException {
  156.         RevObject o;
  157.         ObjectIdSubclassMap<ObjectId> newObjectIds = connectivityCheckInfo
  158.                 .getParser()
  159.                 .getNewObjectIds();

  160.         while ((o = ow.nextObject()) != null) {
  161.             pm.update(1);
  162.             if (o.has(RevFlag.UNINTERESTING)) {
  163.                 continue;
  164.             }

  165.             if (connectivityCheckInfo.isCheckObjects()) {
  166.                 if (newObjectIds.contains(o)) {
  167.                     continue;
  168.                 }
  169.                 throw new MissingObjectException(o, o.getType());

  170.             }

  171.             if (o instanceof RevBlob
  172.                     && !connectivityCheckInfo.getRepository().getObjectDatabase()
  173.                             .has(o)) {
  174.                 throw new MissingObjectException(o, Constants.TYPE_BLOB);
  175.             }
  176.         }

  177.         if (connectivityCheckInfo.isCheckObjects()) {
  178.             for (ObjectId id : connectivityCheckInfo.getParser()
  179.                     .getBaseObjectIds()) {
  180.                 o = ow.parseAny(id);
  181.                 if (!o.has(RevFlag.UNINTERESTING)) {
  182.                     throw new MissingObjectException(o, o.getType());
  183.                 }
  184.             }
  185.         }
  186.     }
  187. }