Package org.locationtech.geogig.api.porcelain

Source Code of org.locationtech.geogig.api.porcelain.PushOp

/* Copyright (c) 2012-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.api.porcelain;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import java.util.ArrayList;
import java.util.List;

import org.locationtech.geogig.api.AbstractGeoGigOp;
import org.locationtech.geogig.api.Ref;
import org.locationtech.geogig.api.Remote;
import org.locationtech.geogig.api.SymRef;
import org.locationtech.geogig.api.hooks.Hookable;
import org.locationtech.geogig.api.plumbing.ForEachRef;
import org.locationtech.geogig.api.plumbing.RefParse;
import org.locationtech.geogig.api.plumbing.SendPack;
import org.locationtech.geogig.api.plumbing.SendPack.TransferableRef;

import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;

/**
* Update remote refs along with associated objects.
*
* <b>NOTE:</b> so far we don't have the ability to merge non conflicting changes. Instead, the diff
* list we get acts on whole objects, , so its possible that this operation overrides non
* conflicting changes when pushing a branch that has non conflicting changes at both sides. This
* needs to be revisited once we get more merge tools.
*/
@Hookable(name = "push")
public class PushOp extends AbstractGeoGigOp<TransferSummary> {

    private boolean all;

    private List<String> refSpecs = new ArrayList<String>();

    private String remoteName;

    /**
     * @param all if {@code true}, push all refs under refs/heads/
     * @return {@code this}
     */
    public PushOp setAll(final boolean all) {
        this.all = all;
        return this;
    }

    /**
     * @param refSpec the refspec of a remote branch
     * @return {@code this}
     */
    public PushOp addRefSpec(final String refSpec) {
        refSpecs.add(refSpec);
        return this;
    }

    public List<String> getRefSpecs() {
        return refSpecs;
    }

    /**
     * @param remoteName the name or URL of a remote repository to push to
     * @return {@code this}
     */
    public PushOp setRemote(final String remoteName) {
        checkNotNull(remoteName);
        this.remoteName = remoteName;
        return this;
    }

    public String getRemoteName() {
        return remoteName;
    }

    public Optional<Remote> getRemote() {
        try {
            return Optional.of(resolveRemote(remoteName));
        } catch (IllegalArgumentException e) {
            return Optional.absent();
        }
    }

    /**
     * Executes the push operation.
     *
     * @return {@code null}
     * @see org.locationtech.geogig.api.AbstractGeoGigOp#call()
     */
    @Override
    protected TransferSummary _call() {

        final String remoteName = this.remoteName == null ? "origin" : this.remoteName;

        final Remote remote = resolveRemote(remoteName);

        final List<TransferableRef> refsToPush = resolveRefs();

        SendPack sendPack = command(SendPack.class);
        sendPack.setRemote(remote);
        sendPack.setRefs(refsToPush);
        sendPack.setProgressListener(getProgressListener());
        TransferSummary result = sendPack.call();
        return result;
    }

    private List<TransferableRef> resolveRefs() {
        List<TransferableRef> refsToPush = new ArrayList<>();

        List<String> refSpecsArg = this.refSpecs;

        if (refSpecsArg.isEmpty()) {
            if (all) {
                ImmutableSet<Ref> localRefs = getLocalRefs();
                for (Ref ref : localRefs) {
                    String localRef = ref.getName();
                    String remoteRef = null;
                    boolean forceUpdate = false;
                    boolean delete = false;
                    refsToPush.add(new TransferableRef(localRef, remoteRef, forceUpdate, delete));
                }
            } else {
                // push current branch
                Ref currentBranch = resolveHeadTarget();
                String localRef = currentBranch.getName();
                String remoteRef = null;
                boolean forceUpdate = false;
                boolean delete = false;
                refsToPush.add(new TransferableRef(localRef, remoteRef, forceUpdate, delete));
            }
        } else {
            for (String refspec : refSpecsArg) {
                String[] refs = refspec.split(":");
                if (refs.length == 0) {
                    refs = new String[2];
                    refs[0] = resolveHeadTarget().getName();
                    refs[1] = null;
                } else {
                    if (refs[0].startsWith("+")) {
                        refs[0] = refs[0].substring(1);
                    }
                    for (int i = 0; i < refs.length; i++) {
                        if (refs[i].trim().isEmpty()) {
                            refs[i] = null;
                        }
                    }
                }
                checkArgument(refs.length < 3,
                        "Invalid refspec, please use [+][<localref>][:][<remoteref>].");

                boolean force = refspec.startsWith("+");
                String localrefspec = refs[0];
                boolean delete = localrefspec == null;
                String remoterefspec = refs[refs.length == 2 ? 1 : 0];
                refsToPush.add(new TransferableRef(localrefspec, remoterefspec, force, delete));
            }
        }
        return refsToPush;
    }

    private Remote resolveRemote(String remoteName) {
        Optional<Remote> pushRemote = command(RemoteResolve.class).setName(remoteName).call();

        checkArgument(pushRemote.isPresent(), "Remote could not be resolved.");

        return pushRemote.get();
    }

    private ImmutableSet<Ref> getLocalRefs() {
        Predicate<Ref> filter = new Predicate<Ref>() {
            final String prefix = Ref.HEADS_PREFIX;

            @Override
            public boolean apply(Ref input) {
                return !(input instanceof SymRef) && input.getName().startsWith(prefix);
            }
        };
        ImmutableSet<Ref> localRefs = command(ForEachRef.class).setFilter(filter).call();
        return localRefs;
    }

    private Ref resolveHeadTarget() {
        final Optional<Ref> currHead = refParse(Ref.HEAD);
        checkState(currHead.isPresent(), "Repository has no HEAD, can't push.");
        checkState(currHead.get() instanceof SymRef, "Can't push from detached HEAD");

        final Optional<Ref> headTarget = refParse(((SymRef) currHead.get()).getTarget());
        checkState(headTarget.isPresent());
        return headTarget.get();
    }

    private Optional<Ref> refParse(String refSpec) {
        return command(RefParse.class).setName(refSpec).call();
    }

}
TOP

Related Classes of org.locationtech.geogig.api.porcelain.PushOp

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.