| /* |
| * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> |
| * and other copyright owners as documented in the project's IP log. |
| * |
| * This program and the accompanying materials are made available |
| * under the terms of the Eclipse Distribution License v1.0 which |
| * accompanies this distribution, is reproduced below, and is |
| * available at http://www.eclipse.org/org/documents/edl-v10.php |
| * |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or |
| * without modification, are permitted provided that the following |
| * conditions are met: |
| * |
| * - Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * - Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * |
| * - Neither the name of the Eclipse Foundation, Inc. nor the |
| * names of its contributors may be used to endorse or promote |
| * products derived from this software without specific prior |
| * written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
| * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
| * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| package org.eclipse.jgit.api; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.net.URISyntaxException; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.jgit.api.errors.GitAPIException; |
| import org.eclipse.jgit.api.errors.InvalidRemoteException; |
| import org.eclipse.jgit.api.errors.JGitInternalException; |
| import org.eclipse.jgit.errors.NotSupportedException; |
| import org.eclipse.jgit.errors.TooLargeObjectInPackException; |
| import org.eclipse.jgit.errors.TooLargePackException; |
| import org.eclipse.jgit.errors.TransportException; |
| import org.eclipse.jgit.internal.JGitText; |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.lib.NullProgressMonitor; |
| import org.eclipse.jgit.lib.ProgressMonitor; |
| import org.eclipse.jgit.lib.Ref; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.transport.PushResult; |
| import org.eclipse.jgit.transport.RefLeaseSpec; |
| import org.eclipse.jgit.transport.RefSpec; |
| import org.eclipse.jgit.transport.RemoteConfig; |
| import org.eclipse.jgit.transport.RemoteRefUpdate; |
| import org.eclipse.jgit.transport.Transport; |
| |
| /** |
| * A class used to execute a {@code Push} command. It has setters for all |
| * supported options and arguments of this command and a {@link #call()} method |
| * to finally execute the command. |
| * |
| * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-push.html" |
| * >Git documentation about Push</a> |
| */ |
| public class PushCommand extends |
| TransportCommand<PushCommand, Iterable<PushResult>> { |
| |
| private String remote = Constants.DEFAULT_REMOTE_NAME; |
| |
| private final List<RefSpec> refSpecs; |
| |
| private final Map<String, RefLeaseSpec> refLeaseSpecs; |
| |
| private ProgressMonitor monitor = NullProgressMonitor.INSTANCE; |
| |
| private String receivePack = RemoteConfig.DEFAULT_RECEIVE_PACK; |
| |
| private boolean dryRun; |
| private boolean atomic; |
| private boolean force; |
| private boolean thin = Transport.DEFAULT_PUSH_THIN; |
| |
| private OutputStream out; |
| |
| private List<String> pushOptions; |
| |
| /** |
| * <p> |
| * Constructor for PushCommand. |
| * </p> |
| * |
| * @param repo |
| * the {@link org.eclipse.jgit.lib.Repository} |
| */ |
| protected PushCommand(Repository repo) { |
| super(repo); |
| refSpecs = new ArrayList<>(3); |
| refLeaseSpecs = new HashMap<>(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * <p> |
| * Execute the {@code push} command with all the options and parameters |
| * collected by the setter methods of this class. Each instance of this |
| * class should only be used for one invocation of the command (means: one |
| * call to {@link #call()}) |
| */ |
| @Override |
| public Iterable<PushResult> call() throws GitAPIException, |
| InvalidRemoteException, |
| org.eclipse.jgit.api.errors.TransportException { |
| checkCallable(); |
| |
| ArrayList<PushResult> pushResults = new ArrayList<>(3); |
| |
| try { |
| if (refSpecs.isEmpty()) { |
| RemoteConfig config = new RemoteConfig(repo.getConfig(), |
| getRemote()); |
| refSpecs.addAll(config.getPushRefSpecs()); |
| } |
| if (refSpecs.isEmpty()) { |
| Ref head = repo.exactRef(Constants.HEAD); |
| if (head != null && head.isSymbolic()) |
| refSpecs.add(new RefSpec(head.getLeaf().getName())); |
| } |
| |
| if (force) { |
| for (int i = 0; i < refSpecs.size(); i++) |
| refSpecs.set(i, refSpecs.get(i).setForceUpdate(true)); |
| } |
| |
| final List<Transport> transports; |
| transports = Transport.openAll(repo, remote, Transport.Operation.PUSH); |
| for (final Transport transport : transports) { |
| transport.setPushThin(thin); |
| transport.setPushAtomic(atomic); |
| if (receivePack != null) |
| transport.setOptionReceivePack(receivePack); |
| transport.setDryRun(dryRun); |
| transport.setPushOptions(pushOptions); |
| configure(transport); |
| |
| final Collection<RemoteRefUpdate> toPush = transport |
| .findRemoteRefUpdatesFor(refSpecs, refLeaseSpecs); |
| |
| try { |
| PushResult result = transport.push(monitor, toPush, out); |
| pushResults.add(result); |
| |
| } catch (TooLargePackException e) { |
| throw new org.eclipse.jgit.api.errors.TooLargePackException( |
| e.getMessage(), e); |
| } catch (TooLargeObjectInPackException e) { |
| throw new org.eclipse.jgit.api.errors.TooLargeObjectInPackException( |
| e.getMessage(), e); |
| } catch (TransportException e) { |
| throw new org.eclipse.jgit.api.errors.TransportException( |
| e.getMessage(), e); |
| } finally { |
| transport.close(); |
| } |
| } |
| |
| } catch (URISyntaxException e) { |
| throw new InvalidRemoteException(MessageFormat.format( |
| JGitText.get().invalidRemote, remote)); |
| } catch (TransportException e) { |
| throw new org.eclipse.jgit.api.errors.TransportException( |
| e.getMessage(), e); |
| } catch (NotSupportedException e) { |
| throw new JGitInternalException( |
| JGitText.get().exceptionCaughtDuringExecutionOfPushCommand, |
| e); |
| } catch (IOException e) { |
| throw new JGitInternalException( |
| JGitText.get().exceptionCaughtDuringExecutionOfPushCommand, |
| e); |
| } |
| |
| return pushResults; |
| } |
| |
| /** |
| * The remote (uri or name) used for the push operation. If no remote is |
| * set, the default value of <code>Constants.DEFAULT_REMOTE_NAME</code> will |
| * be used. |
| * |
| * @see Constants#DEFAULT_REMOTE_NAME |
| * @param remote |
| * the remote name |
| * @return {@code this} |
| */ |
| public PushCommand setRemote(String remote) { |
| checkCallable(); |
| this.remote = remote; |
| return this; |
| } |
| |
| /** |
| * Get remote name |
| * |
| * @return the remote used for the remote operation |
| */ |
| public String getRemote() { |
| return remote; |
| } |
| |
| /** |
| * The remote executable providing receive-pack service for pack transports. |
| * If no receive-pack is set, the default value of |
| * <code>RemoteConfig.DEFAULT_RECEIVE_PACK</code> will be used. |
| * |
| * @see RemoteConfig#DEFAULT_RECEIVE_PACK |
| * @param receivePack |
| * name of the remote executable providing the receive-pack |
| * service |
| * @return {@code this} |
| */ |
| public PushCommand setReceivePack(String receivePack) { |
| checkCallable(); |
| this.receivePack = receivePack; |
| return this; |
| } |
| |
| /** |
| * Get the name of the remote executable providing the receive-pack service |
| * |
| * @return the receive-pack used for the remote operation |
| */ |
| public String getReceivePack() { |
| return receivePack; |
| } |
| |
| /** |
| * Get timeout used for push operation |
| * |
| * @return the timeout used for the push operation |
| */ |
| public int getTimeout() { |
| return timeout; |
| } |
| |
| /** |
| * Get the progress monitor |
| * |
| * @return the progress monitor for the push operation |
| */ |
| public ProgressMonitor getProgressMonitor() { |
| return monitor; |
| } |
| |
| /** |
| * The progress monitor associated with the push operation. By default, this |
| * is set to <code>NullProgressMonitor</code> |
| * |
| * @see NullProgressMonitor |
| * @param monitor |
| * a {@link org.eclipse.jgit.lib.ProgressMonitor} |
| * @return {@code this} |
| */ |
| public PushCommand setProgressMonitor(ProgressMonitor monitor) { |
| checkCallable(); |
| if (monitor == null) { |
| monitor = NullProgressMonitor.INSTANCE; |
| } |
| this.monitor = monitor; |
| return this; |
| } |
| |
| /** |
| * Get the <code>RefLeaseSpec</code>s. |
| * |
| * @return the <code>RefLeaseSpec</code>s |
| * @since 4.7 |
| */ |
| public List<RefLeaseSpec> getRefLeaseSpecs() { |
| return new ArrayList<>(refLeaseSpecs.values()); |
| } |
| |
| /** |
| * The ref lease specs to be used in the push operation, for a |
| * force-with-lease push operation. |
| * |
| * @param specs |
| * a {@link org.eclipse.jgit.transport.RefLeaseSpec} object. |
| * @return {@code this} |
| * @since 4.7 |
| */ |
| public PushCommand setRefLeaseSpecs(RefLeaseSpec... specs) { |
| return setRefLeaseSpecs(Arrays.asList(specs)); |
| } |
| |
| /** |
| * The ref lease specs to be used in the push operation, for a |
| * force-with-lease push operation. |
| * |
| * @param specs |
| * list of {@code RefLeaseSpec}s |
| * @return {@code this} |
| * @since 4.7 |
| */ |
| public PushCommand setRefLeaseSpecs(List<RefLeaseSpec> specs) { |
| checkCallable(); |
| this.refLeaseSpecs.clear(); |
| for (RefLeaseSpec spec : specs) { |
| refLeaseSpecs.put(spec.getRef(), spec); |
| } |
| return this; |
| } |
| |
| /** |
| * Get {@code RefSpec}s. |
| * |
| * @return the ref specs |
| */ |
| public List<RefSpec> getRefSpecs() { |
| return refSpecs; |
| } |
| |
| /** |
| * The ref specs to be used in the push operation |
| * |
| * @param specs a {@link org.eclipse.jgit.transport.RefSpec} object. |
| * @return {@code this} |
| */ |
| public PushCommand setRefSpecs(RefSpec... specs) { |
| checkCallable(); |
| this.refSpecs.clear(); |
| Collections.addAll(refSpecs, specs); |
| return this; |
| } |
| |
| /** |
| * The ref specs to be used in the push operation |
| * |
| * @param specs |
| * list of {@link org.eclipse.jgit.transport.RefSpec}s |
| * @return {@code this} |
| */ |
| public PushCommand setRefSpecs(List<RefSpec> specs) { |
| checkCallable(); |
| this.refSpecs.clear(); |
| this.refSpecs.addAll(specs); |
| return this; |
| } |
| |
| /** |
| * Push all branches under refs/heads/*. |
| * |
| * @return {code this} |
| */ |
| public PushCommand setPushAll() { |
| refSpecs.add(Transport.REFSPEC_PUSH_ALL); |
| return this; |
| } |
| |
| /** |
| * Push all tags under refs/tags/*. |
| * |
| * @return {code this} |
| */ |
| public PushCommand setPushTags() { |
| refSpecs.add(Transport.REFSPEC_TAGS); |
| return this; |
| } |
| |
| /** |
| * Add a reference to push. |
| * |
| * @param ref |
| * the source reference. The remote name will match. |
| * @return {@code this}. |
| */ |
| public PushCommand add(Ref ref) { |
| refSpecs.add(new RefSpec(ref.getLeaf().getName())); |
| return this; |
| } |
| |
| /** |
| * Add a reference to push. |
| * |
| * @param nameOrSpec |
| * any reference name, or a reference specification. |
| * @return {@code this}. |
| * @throws JGitInternalException |
| * the reference name cannot be resolved. |
| */ |
| public PushCommand add(String nameOrSpec) { |
| if (0 <= nameOrSpec.indexOf(':')) { |
| refSpecs.add(new RefSpec(nameOrSpec)); |
| } else { |
| Ref src; |
| try { |
| src = repo.findRef(nameOrSpec); |
| } catch (IOException e) { |
| throw new JGitInternalException( |
| JGitText.get().exceptionCaughtDuringExecutionOfPushCommand, |
| e); |
| } |
| if (src != null) |
| add(src); |
| } |
| return this; |
| } |
| |
| /** |
| * Whether to run the push operation as a dry run |
| * |
| * @return the dry run preference for the push operation |
| */ |
| public boolean isDryRun() { |
| return dryRun; |
| } |
| |
| /** |
| * Sets whether the push operation should be a dry run |
| * |
| * @param dryRun a boolean. |
| * @return {@code this} |
| */ |
| public PushCommand setDryRun(boolean dryRun) { |
| checkCallable(); |
| this.dryRun = dryRun; |
| return this; |
| } |
| |
| /** |
| * Get the thin-pack preference |
| * |
| * @return the thin-pack preference for push operation |
| */ |
| public boolean isThin() { |
| return thin; |
| } |
| |
| /** |
| * Set the thin-pack preference for push operation. |
| * |
| * Default setting is Transport.DEFAULT_PUSH_THIN |
| * |
| * @param thin |
| * the thin-pack preference value |
| * @return {@code this} |
| */ |
| public PushCommand setThin(boolean thin) { |
| checkCallable(); |
| this.thin = thin; |
| return this; |
| } |
| |
| /** |
| * Whether this push should be executed atomically (all references updated, |
| * or none) |
| * |
| * @return true if all-or-nothing behavior is requested. |
| * @since 4.2 |
| */ |
| public boolean isAtomic() { |
| return atomic; |
| } |
| |
| /** |
| * Requests atomic push (all references updated, or no updates). |
| * |
| * Default setting is false. |
| * |
| * @param atomic |
| * whether to run the push atomically |
| * @return {@code this} |
| * @since 4.2 |
| */ |
| public PushCommand setAtomic(boolean atomic) { |
| checkCallable(); |
| this.atomic = atomic; |
| return this; |
| } |
| |
| /** |
| * Whether to push forcefully |
| * |
| * @return the force preference for push operation |
| */ |
| public boolean isForce() { |
| return force; |
| } |
| |
| /** |
| * Sets the force preference for push operation. |
| * |
| * @param force |
| * whether to push forcefully |
| * @return {@code this} |
| */ |
| public PushCommand setForce(boolean force) { |
| checkCallable(); |
| this.force = force; |
| return this; |
| } |
| |
| /** |
| * Sets the output stream to write sideband messages to |
| * |
| * @param out |
| * an {@link java.io.OutputStream} |
| * @return {@code this} |
| * @since 3.0 |
| */ |
| public PushCommand setOutputStream(OutputStream out) { |
| this.out = out; |
| return this; |
| } |
| |
| /** |
| * Get push options |
| * |
| * @return the option strings associated with the push operation |
| * @since 4.5 |
| */ |
| public List<String> getPushOptions() { |
| return pushOptions; |
| } |
| |
| /** |
| * Set the option strings associated with the push operation. |
| * |
| * @param pushOptions |
| * a {@link java.util.List} of push option strings |
| * @return {@code this} |
| * @since 4.5 |
| */ |
| public PushCommand setPushOptions(List<String> pushOptions) { |
| this.pushOptions = pushOptions; |
| return this; |
| } |
| } |