Package org.locationtech.geogig.remote

Source Code of org.locationtech.geogig.remote.AbstractRemoteRepo$ShallowCommitTraverser

/* Copyright (c) 2013-2014 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Johnathan Garrett (LMN Solutions) - initial implementation
*/
package org.locationtech.geogig.remote;

import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.ProgressListener;
import org.locationtech.geogig.api.Ref;
import org.locationtech.geogig.api.SymRef;
import org.locationtech.geogig.api.plumbing.FindCommonAncestor;
import org.locationtech.geogig.api.porcelain.SynchronizationException;
import org.locationtech.geogig.api.porcelain.SynchronizationException.StatusCode;
import org.locationtech.geogig.repository.Repository;

import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;

/**
* Provides a base implementation for different representations of the {@link IRemoteRepo}.
*
* @see IRemoteRepo
*/
abstract class AbstractRemoteRepo implements IRemoteRepo {

    protected Repository localRepository;

    /**
     * Constructs a new {@code AbstractRemoteRepo} with the provided reference repository.
     *
     * @param localRepository the local repository
     */
    public AbstractRemoteRepo(Repository localRepository) {
        this.localRepository = localRepository;
    }

    /**
     * CommitTraverser for transfers from a shallow clone to a full repository. This works just like
     * a normal commit traverser, but will throw an appropriate synchronization exception when the
     * history is not deep enough to perform the traversal.
     */
    protected class ShallowFullCommitTraverser extends CommitTraverser {

        private RepositoryWrapper source;

        private RepositoryWrapper destination;

        public ShallowFullCommitTraverser(RepositoryWrapper source, RepositoryWrapper destination) {
            this.source = source;
            this.destination = destination;
        }

        @Override
        protected Evaluation evaluate(CommitNode commitNode) {

            if (destination.objectExists(commitNode.getObjectId())) {
                return Evaluation.EXCLUDE_AND_PRUNE;
            }

            if (!commitNode.getObjectId().equals(ObjectId.NULL)
                    && !source.objectExists(commitNode.getObjectId())) {
                // Source is too shallow
                throw new SynchronizationException(StatusCode.HISTORY_TOO_SHALLOW);
            }

            return Evaluation.INCLUDE_AND_CONTINUE;
        }

        @Override
        protected ImmutableList<ObjectId> getParentsInternal(ObjectId commitId) {
            return source.getParents(commitId);
        }

        @Override
        protected boolean existsInDestination(ObjectId commitId) {
            return destination.objectExists(commitId);
        }
    }

    /**
     * CommitTraverser for transfering data to a shallow clone. This traverser will fetch data up to
     * the fetch limit. If no fetch limit is defined, one will be calculated when a commit is
     * fetched that I already have. The new fetch depth will be the depth from the starting commit
     * to beginning of the orphaned branch.
     */
    protected class ShallowCommitTraverser extends CommitTraverser {

        Optional<Integer> limit;

        private RepositoryWrapper source;

        private RepositoryWrapper destination;

        public ShallowCommitTraverser(RepositoryWrapper source, RepositoryWrapper destination,
                Optional<Integer> limit) {
            this.source = source;
            this.destination = destination;
            this.limit = limit;
        }

        @Override
        protected Evaluation evaluate(CommitNode commitNode) {
            if (limit.isPresent() && commitNode.getDepth() > limit.get()) {
                return Evaluation.EXCLUDE_AND_PRUNE;
            } else if (!source.objectExists(commitNode.getObjectId())) {
                // remote history is shallow
                return Evaluation.EXCLUDE_AND_PRUNE;
            }

            boolean exists = destination.objectExists(commitNode.getObjectId());
            if (!limit.isPresent() && exists) {
                // calculate the new fetch limit
                limit = Optional.of(destination.getDepth(commitNode.getObjectId())
                        + commitNode.getDepth() - 1);
            }
            if (exists) {
                return Evaluation.EXCLUDE_AND_CONTINUE;
            }
            return Evaluation.INCLUDE_AND_CONTINUE;
        }

        @Override
        protected ImmutableList<ObjectId> getParentsInternal(ObjectId commitId) {
            return source.getParents(commitId);
        }

        @Override
        protected boolean existsInDestination(ObjectId commitId) {
            return destination.objectExists(commitId);
        }
    };

    /**
     * CommitTraverser for synchronizing data between two full (non-shallow) repositories. The
     * traverser will copy data from the source to the destination until there is nothing left to
     * copy.
     */
    protected class FullCommitTraverser extends CommitTraverser {

        private RepositoryWrapper source;

        private RepositoryWrapper destination;

        public FullCommitTraverser(RepositoryWrapper source, RepositoryWrapper destination) {
            this.source = source;
            this.destination = destination;
        }

        @Override
        protected Evaluation evaluate(CommitNode commitNode) {
            if (destination.objectExists(commitNode.getObjectId())) {
                return Evaluation.EXCLUDE_AND_PRUNE;
            } else {
                return Evaluation.INCLUDE_AND_CONTINUE;
            }
        }

        @Override
        protected ImmutableList<ObjectId> getParentsInternal(ObjectId commitId) {
            return source.getParents(commitId);
        }

        @Override
        protected boolean existsInDestination(ObjectId commitId) {
            return destination.objectExists(commitId);
        }

    };

    /**
     * @return the {@link RepositoryWrapper} for this remote
     */
    public abstract RepositoryWrapper getRemoteWrapper();

    /**
     * Returns the appropriate commit traverser to use for the fetch operation.
     *
     * @param fetchLimit the fetch limit to use
     * @return the {@link CommitTraverser} to use.
     */
    protected CommitTraverser getFetchTraverser(Optional<Integer> fetchLimit) {

        RepositoryWrapper localWrapper = new LocalRepositoryWrapper(localRepository);
        RepositoryWrapper remoteWrapper = getRemoteWrapper();

        CommitTraverser traverser;
        if (localWrapper.getRepoDepth().isPresent()) {
            traverser = new ShallowCommitTraverser(remoteWrapper, localWrapper, fetchLimit);
        } else if (remoteWrapper.getRepoDepth().isPresent()) {
            traverser = new ShallowFullCommitTraverser(remoteWrapper, localWrapper);
        } else {
            traverser = new FullCommitTraverser(remoteWrapper, localWrapper);
        }

        return traverser;
    }

    /**
     * Returns the appropriate commit traverser to use for the push operation.
     *
     * @param remoteRef the remote ref to push to
     * @return the {@link CommitTraverser} to use.
     */
    protected CommitTraverser getPushTraverser(Optional<Ref> remoteRef)
            throws SynchronizationException {

        RepositoryWrapper localWrapper = new LocalRepositoryWrapper(localRepository);
        RepositoryWrapper remoteWrapper = getRemoteWrapper();

        CommitTraverser traverser;
        if (remoteWrapper.getRepoDepth().isPresent()) {
            Optional<Integer> pushDepth = Optional.absent();
            if (!remoteRef.isPresent()) {
                pushDepth = remoteWrapper.getRepoDepth();
            }
            traverser = new ShallowCommitTraverser(localWrapper, remoteWrapper, pushDepth);
        } else if (localRepository.getDepth().isPresent()) {
            traverser = new ShallowFullCommitTraverser(localWrapper, remoteWrapper);
        } else {
            traverser = new FullCommitTraverser(localWrapper, remoteWrapper);
        }

        return traverser;
    }

    /**
     * Push all new objects from the specified {@link Ref} to the remote.
     *
     * @param ref the local ref that points to new commit data
     */
    @Override
    public void pushNewData(Ref ref, ProgressListener progress) throws SynchronizationException {
        pushNewData(ref, ref.getName(), progress);
    }

    /**
     * Determine if it is safe to push to the remote repository.
     *
     * @param ref the ref to push
     * @param remoteRefOpt the ref to push to
     * @throws SynchronizationException if its not safe or possible to push to the given remote ref
     *         (see {@link StatusCode} for the possible reasons)
     */
    protected void checkPush(Ref ref, Optional<Ref> remoteRefOpt) throws SynchronizationException {
        if (!remoteRefOpt.isPresent()) {
            return;// safe to push
        }
        final Ref remoteRef = remoteRefOpt.get();
        if (remoteRef instanceof SymRef) {
            throw new SynchronizationException(StatusCode.CANNOT_PUSH_TO_SYMBOLIC_REF);
        }
        final ObjectId remoteObjectId = remoteRef.getObjectId();
        final ObjectId localObjectId = ref.getObjectId();
        if (remoteObjectId.equals(localObjectId)) {
            // The branches are equal, no need to push.
            throw new SynchronizationException(StatusCode.NOTHING_TO_PUSH);
        } else if (localRepository.blobExists(remoteObjectId)) {
            Optional<ObjectId> ancestor = localRepository.command(FindCommonAncestor.class)
                    .setLeftId(remoteObjectId).setRightId(localObjectId).call();
            if (!ancestor.isPresent()) {
                // There is no common ancestor, a push will overwrite history
                throw new SynchronizationException(StatusCode.REMOTE_HAS_CHANGES);
            } else if (ancestor.get().equals(localObjectId)) {
                // My last commit is the common ancestor, the remote already has my data.
                throw new SynchronizationException(StatusCode.NOTHING_TO_PUSH);
            } else if (!ancestor.get().equals(remoteObjectId)) {
                // The remote branch's latest commit is not my ancestor, a push will cause a
                // loss of history.
                throw new SynchronizationException(StatusCode.REMOTE_HAS_CHANGES);
            }
        } else if (!remoteObjectId.isNull()) {
            // The remote has data that I do not, a push will cause this data to be lost.
            throw new SynchronizationException(StatusCode.REMOTE_HAS_CHANGES);
        }
    }

}
TOP

Related Classes of org.locationtech.geogig.remote.AbstractRemoteRepo$ShallowCommitTraverser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.