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

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

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

import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdSubclassMap;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.revwalk.ObjectWalk;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevSort;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.transport.ConnectivityChecker;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.transport.ReceiveCommand.Result;

A connectivity checker that uses the entire reference database to perform reachability checks when checking the connectivity of objects. If info.isCheckObjects() is set it will also check that objects referenced by deltas are either provided or reachable as well.
/** * A connectivity checker that uses the entire reference database to perform * reachability checks when checking the connectivity of objects. If * info.isCheckObjects() is set it will also check that objects referenced by * deltas are either provided or reachable as well. */
public final class FullConnectivityChecker implements ConnectivityChecker { @Override public void checkConnectivity(ConnectivityCheckInfo connectivityCheckInfo, Set<ObjectId> haves, ProgressMonitor pm) throws MissingObjectException, IOException { pm.beginTask(JGitText.get().countingObjects, ProgressMonitor.UNKNOWN); try (ObjectWalk ow = new ObjectWalk(connectivityCheckInfo.getRepository())) { if (!markStartAndKnownNodes(connectivityCheckInfo, ow, haves, pm)) { return; } checkCommitTree(connectivityCheckInfo, ow, pm); checkObjects(connectivityCheckInfo, ow, pm); } finally { pm.endTask(); } }
Params:
  • connectivityCheckInfo – Source for connectivity check.
  • ow – Walk which can also check blobs.
  • haves – Set of references known for client.
  • pm – Monitor to publish progress to.
Throws:
  • IOException – an error occurred during connectivity checking.
Returns:true if at least one new node was marked.
/** * @param connectivityCheckInfo * Source for connectivity check. * @param ow * Walk which can also check blobs. * @param haves * Set of references known for client. * @param pm * Monitor to publish progress to. * @return true if at least one new node was marked. * @throws IOException * an error occurred during connectivity checking. */
private boolean markStartAndKnownNodes( ConnectivityCheckInfo connectivityCheckInfo, ObjectWalk ow, Set<ObjectId> haves, ProgressMonitor pm) throws IOException { boolean markTrees = connectivityCheckInfo .isCheckObjects() && !connectivityCheckInfo.getParser().getBaseObjectIds() .isEmpty(); if (connectivityCheckInfo.isCheckObjects()) { ow.sort(RevSort.TOPO); if (!connectivityCheckInfo.getParser().getBaseObjectIds() .isEmpty()) { ow.sort(RevSort.BOUNDARY, true); } } boolean hasInteresting = false; for (ReceiveCommand cmd : connectivityCheckInfo.getCommands()) { if (cmd.getResult() != Result.NOT_ATTEMPTED) { continue; } if (cmd.getType() == ReceiveCommand.Type.DELETE) { continue; } if (haves.contains(cmd.getNewId())) { continue; } ow.markStart(ow.parseAny(cmd.getNewId())); pm.update(1); hasInteresting = true; } if (!hasInteresting) { return false; } for (ObjectId have : haves) { RevObject o = ow.parseAny(have); ow.markUninteresting(o); pm.update(1); if (markTrees) { o = ow.peel(o); if (o instanceof RevCommit) { o = ((RevCommit) o).getTree(); } if (o instanceof RevTree) { ow.markUninteresting(o); } } } return true; }
Params:
  • connectivityCheckInfo – Source for connectivity check.
  • ow – Walk which can also check blobs.
  • pm – Monitor to publish progress to.
Throws:
  • IOException – an error occurred during connectivity checking.
/** * @param connectivityCheckInfo * Source for connectivity check. * @param ow * Walk which can also check blobs. * @param pm * Monitor to publish progress to. * @throws IOException * an error occurred during connectivity checking. */
private void checkCommitTree(ConnectivityCheckInfo connectivityCheckInfo, ObjectWalk ow, ProgressMonitor pm) throws IOException { RevCommit c; ObjectIdSubclassMap<ObjectId> newObjectIds = connectivityCheckInfo .getParser() .getNewObjectIds(); while ((c = ow.next()) != null) { pm.update(1); if (connectivityCheckInfo.isCheckObjects() && !c.has(RevFlag.UNINTERESTING) && !newObjectIds.contains(c)) { throw new MissingObjectException(c, Constants.TYPE_COMMIT); } } }
Params:
  • connectivityCheckInfo – Source for connectivity check.
  • ow – Walk which can also check blobs.
  • pm – Monitor to publish progress to.
Throws:
  • IOException – an error occurred during connectivity checking.
/** * @param connectivityCheckInfo * Source for connectivity check. * @param ow * Walk which can also check blobs. * @param pm * Monitor to publish progress to. * @throws IOException * an error occurred during connectivity checking. * */
private void checkObjects(ConnectivityCheckInfo connectivityCheckInfo, ObjectWalk ow, ProgressMonitor pm) throws IOException { RevObject o; ObjectIdSubclassMap<ObjectId> newObjectIds = connectivityCheckInfo .getParser() .getNewObjectIds(); while ((o = ow.nextObject()) != null) { pm.update(1); if (o.has(RevFlag.UNINTERESTING)) { continue; } if (connectivityCheckInfo.isCheckObjects()) { if (newObjectIds.contains(o)) { continue; } throw new MissingObjectException(o, o.getType()); } if (o instanceof RevBlob && !connectivityCheckInfo.getRepository().getObjectDatabase() .has(o)) { throw new MissingObjectException(o, Constants.TYPE_BLOB); } } if (connectivityCheckInfo.isCheckObjects()) { for (ObjectId id : connectivityCheckInfo.getParser() .getBaseObjectIds()) { o = ow.parseAny(id); if (!o.has(RevFlag.UNINTERESTING)) { throw new MissingObjectException(o, o.getType()); } } } } }