Merge "Push implementation of option strings"
This commit is contained in:
commit
e9e2f7e6b5
|
@ -108,6 +108,9 @@ void nothin(@SuppressWarnings("unused") final boolean ignored) {
|
|||
@Option(name = "--dry-run")
|
||||
private boolean dryRun;
|
||||
|
||||
@Option(name = "--push-option", aliases = { "-t" })
|
||||
private List<String> pushOptions = new ArrayList<>();
|
||||
|
||||
private boolean shownURI;
|
||||
|
||||
@Override
|
||||
|
@ -127,6 +130,7 @@ protected void run() throws Exception {
|
|||
push.setThin(thin);
|
||||
push.setAtomic(atomic);
|
||||
push.setTimeout(timeout);
|
||||
push.setPushOptions(pushOptions);
|
||||
Iterable<PushResult> results = push.call();
|
||||
for (PushResult result : results) {
|
||||
try (ObjectReader reader = db.newObjectReader()) {
|
||||
|
|
|
@ -0,0 +1,363 @@
|
|||
/*
|
||||
* Copyright (C) 2016, Google Inc.
|
||||
* 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.transport;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.PushCommand;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.api.errors.NoFilepatternException;
|
||||
import org.eclipse.jgit.api.errors.TransportException;
|
||||
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
|
||||
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
|
||||
import org.eclipse.jgit.junit.RepositoryTestCase;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.NullProgressMonitor;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectInserter;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.lib.StoredConfig;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
|
||||
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
|
||||
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PushOptionsTest extends RepositoryTestCase {
|
||||
private URIish uri;
|
||||
private TestProtocol<Object> testProtocol;
|
||||
private Object ctx = new Object();
|
||||
private InMemoryRepository server;
|
||||
private InMemoryRepository client;
|
||||
private ObjectId obj1;
|
||||
private ObjectId obj2;
|
||||
private BaseReceivePack baseReceivePack;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
server = newRepo("server");
|
||||
client = newRepo("client");
|
||||
|
||||
testProtocol = new TestProtocol<>(null,
|
||||
new ReceivePackFactory<Object>() {
|
||||
@Override
|
||||
public ReceivePack create(Object req, Repository database)
|
||||
throws ServiceNotEnabledException,
|
||||
ServiceNotAuthorizedException {
|
||||
ReceivePack receivePack = new ReceivePack(database);
|
||||
receivePack.setAllowPushOptions(true);
|
||||
receivePack.setAtomic(true);
|
||||
baseReceivePack = receivePack;
|
||||
return receivePack;
|
||||
}
|
||||
});
|
||||
|
||||
uri = testProtocol.register(ctx, server);
|
||||
|
||||
try (ObjectInserter ins = client.newObjectInserter()) {
|
||||
obj1 = ins.insert(Constants.OBJ_BLOB, Constants.encode("test"));
|
||||
obj2 = ins.insert(Constants.OBJ_BLOB, Constants.encode("file"));
|
||||
ins.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
baseReceivePack = null;
|
||||
Transport.unregister(testProtocol);
|
||||
}
|
||||
|
||||
private static InMemoryRepository newRepo(String name) {
|
||||
return new InMemoryRepository(new DfsRepositoryDescription(name));
|
||||
}
|
||||
|
||||
private List<RemoteRefUpdate> commands(boolean atomicSafe)
|
||||
throws IOException {
|
||||
List<RemoteRefUpdate> cmds = new ArrayList<>();
|
||||
cmds.add(new RemoteRefUpdate(null, null, obj1, "refs/heads/one",
|
||||
true /* force update */, null /* no local tracking ref */,
|
||||
ObjectId.zeroId()));
|
||||
cmds.add(new RemoteRefUpdate(null, null, obj2, "refs/heads/two",
|
||||
true /* force update */, null /* no local tracking ref */,
|
||||
atomicSafe ? ObjectId.zeroId() : obj1));
|
||||
return cmds;
|
||||
}
|
||||
|
||||
private void connectLocalToRemote(Git local, Git remote)
|
||||
throws URISyntaxException, IOException {
|
||||
StoredConfig config = local.getRepository().getConfig();
|
||||
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
|
||||
remoteConfig.addURI(new URIish(
|
||||
remote.getRepository().getDirectory().toURI().toURL()));
|
||||
remoteConfig.addFetchRefSpec(
|
||||
new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
|
||||
remoteConfig.update(config);
|
||||
config.save();
|
||||
}
|
||||
|
||||
private RevCommit addCommit(Git git)
|
||||
throws IOException, NoFilepatternException, GitAPIException {
|
||||
writeTrashFile("f", "content of f");
|
||||
git.add().addFilepattern("f").call();
|
||||
return git.commit().setMessage("adding f").call();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNonAtomicPushWithOptions() throws Exception {
|
||||
PushResult r;
|
||||
server.setPerformsAtomicTransactions(false);
|
||||
List<String> pushOptions = Arrays.asList("Hello", "World!");
|
||||
|
||||
try (Transport tn = testProtocol.open(uri, client, "server")) {
|
||||
tn.setPushAtomic(false);
|
||||
tn.setPushOptions(pushOptions);
|
||||
|
||||
r = tn.push(NullProgressMonitor.INSTANCE, commands(false));
|
||||
}
|
||||
|
||||
RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
|
||||
RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
|
||||
|
||||
assertSame(RemoteRefUpdate.Status.OK, one.getStatus());
|
||||
assertSame(RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
|
||||
two.getStatus());
|
||||
assertEquals(pushOptions, baseReceivePack.getPushOptions());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAtomicPushWithOptions() throws Exception {
|
||||
PushResult r;
|
||||
server.setPerformsAtomicTransactions(true);
|
||||
List<String> pushOptions = Arrays.asList("Hello", "World!");
|
||||
|
||||
try (Transport tn = testProtocol.open(uri, client, "server")) {
|
||||
tn.setPushAtomic(true);
|
||||
tn.setPushOptions(pushOptions);
|
||||
|
||||
r = tn.push(NullProgressMonitor.INSTANCE, commands(true));
|
||||
}
|
||||
|
||||
RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
|
||||
RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
|
||||
|
||||
assertSame(RemoteRefUpdate.Status.OK, one.getStatus());
|
||||
assertSame(RemoteRefUpdate.Status.OK, two.getStatus());
|
||||
assertEquals(pushOptions, baseReceivePack.getPushOptions());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailedAtomicPushWithOptions() throws Exception {
|
||||
PushResult r;
|
||||
server.setPerformsAtomicTransactions(true);
|
||||
List<String> pushOptions = Arrays.asList("Hello", "World!");
|
||||
|
||||
try (Transport tn = testProtocol.open(uri, client, "server")) {
|
||||
tn.setPushAtomic(true);
|
||||
tn.setPushOptions(pushOptions);
|
||||
|
||||
r = tn.push(NullProgressMonitor.INSTANCE, commands(false));
|
||||
}
|
||||
|
||||
RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
|
||||
RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
|
||||
|
||||
assertSame(RemoteRefUpdate.Status.REJECTED_OTHER_REASON,
|
||||
one.getStatus());
|
||||
assertSame(RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
|
||||
two.getStatus());
|
||||
assertEquals(new ArrayList<String>(), baseReceivePack.getPushOptions());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThinPushWithOptions() throws Exception {
|
||||
PushResult r;
|
||||
List<String> pushOptions = Arrays.asList("Hello", "World!");
|
||||
|
||||
try (Transport tn = testProtocol.open(uri, client, "server")) {
|
||||
tn.setPushThin(true);
|
||||
tn.setPushOptions(pushOptions);
|
||||
|
||||
r = tn.push(NullProgressMonitor.INSTANCE, commands(false));
|
||||
}
|
||||
|
||||
RemoteRefUpdate one = r.getRemoteUpdate("refs/heads/one");
|
||||
RemoteRefUpdate two = r.getRemoteUpdate("refs/heads/two");
|
||||
|
||||
assertSame(RemoteRefUpdate.Status.OK, one.getStatus());
|
||||
assertSame(RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED,
|
||||
two.getStatus());
|
||||
assertEquals(pushOptions, baseReceivePack.getPushOptions());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPushWithoutOptions() throws Exception {
|
||||
try (Git local = new Git(db);
|
||||
Git remote = new Git(createBareRepository())) {
|
||||
connectLocalToRemote(local, remote);
|
||||
|
||||
final StoredConfig config2 = remote.getRepository().getConfig();
|
||||
config2.setBoolean("receive", null, "pushoptions", true);
|
||||
config2.save();
|
||||
|
||||
RevCommit commit = addCommit(local);
|
||||
|
||||
local.checkout().setName("not-pushed").setCreateBranch(true).call();
|
||||
local.checkout().setName("branchtopush").setCreateBranch(true).call();
|
||||
|
||||
assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
|
||||
assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
|
||||
assertNull(remote.getRepository().resolve("refs/heads/master"));
|
||||
|
||||
PushCommand pushCommand = local.push().setRemote("test");
|
||||
pushCommand.call();
|
||||
|
||||
assertEquals(commit.getId(),
|
||||
remote.getRepository().resolve("refs/heads/branchtopush"));
|
||||
assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
|
||||
assertNull(remote.getRepository().resolve("refs/heads/master"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPushWithEmptyOptions() throws Exception {
|
||||
try (Git local = new Git(db);
|
||||
Git remote = new Git(createBareRepository())) {
|
||||
connectLocalToRemote(local, remote);
|
||||
|
||||
final StoredConfig config2 = remote.getRepository().getConfig();
|
||||
config2.setBoolean("receive", null, "pushoptions", true);
|
||||
config2.save();
|
||||
|
||||
RevCommit commit = addCommit(local);
|
||||
|
||||
local.checkout().setName("not-pushed").setCreateBranch(true).call();
|
||||
local.checkout().setName("branchtopush").setCreateBranch(true).call();
|
||||
assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
|
||||
assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
|
||||
assertNull(remote.getRepository().resolve("refs/heads/master"));
|
||||
|
||||
List<String> pushOptions = new ArrayList<>();
|
||||
PushCommand pushCommand = local.push().setRemote("test")
|
||||
.setPushOptions(pushOptions);
|
||||
pushCommand.call();
|
||||
|
||||
assertEquals(commit.getId(),
|
||||
remote.getRepository().resolve("refs/heads/branchtopush"));
|
||||
assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
|
||||
assertNull(remote.getRepository().resolve("refs/heads/master"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdvertisedButUnusedPushOptions() throws Exception {
|
||||
try (Git local = new Git(db);
|
||||
Git remote = new Git(createBareRepository())) {
|
||||
connectLocalToRemote(local, remote);
|
||||
|
||||
final StoredConfig config2 = remote.getRepository().getConfig();
|
||||
config2.setBoolean("receive", null, "pushoptions", true);
|
||||
config2.save();
|
||||
|
||||
RevCommit commit = addCommit(local);
|
||||
|
||||
local.checkout().setName("not-pushed").setCreateBranch(true).call();
|
||||
local.checkout().setName("branchtopush").setCreateBranch(true).call();
|
||||
|
||||
assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
|
||||
assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
|
||||
assertNull(remote.getRepository().resolve("refs/heads/master"));
|
||||
|
||||
PushCommand pushCommand = local.push().setRemote("test")
|
||||
.setPushOptions(null);
|
||||
pushCommand.call();
|
||||
|
||||
assertEquals(commit.getId(),
|
||||
remote.getRepository().resolve("refs/heads/branchtopush"));
|
||||
assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
|
||||
assertNull(remote.getRepository().resolve("refs/heads/master"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = TransportException.class)
|
||||
public void testPushOptionsNotSupported() throws Exception {
|
||||
try (Git local = new Git(db);
|
||||
Git remote = new Git(createBareRepository())) {
|
||||
connectLocalToRemote(local, remote);
|
||||
|
||||
final StoredConfig config2 = remote.getRepository().getConfig();
|
||||
config2.setBoolean("receive", null, "pushoptions", false);
|
||||
config2.save();
|
||||
|
||||
addCommit(local);
|
||||
|
||||
local.checkout().setName("not-pushed").setCreateBranch(true).call();
|
||||
local.checkout().setName("branchtopush").setCreateBranch(true).call();
|
||||
|
||||
assertNull(remote.getRepository().resolve("refs/heads/branchtopush"));
|
||||
assertNull(remote.getRepository().resolve("refs/heads/not-pushed"));
|
||||
assertNull(remote.getRepository().resolve("refs/heads/master"));
|
||||
|
||||
List<String> pushOptions = new ArrayList<>();
|
||||
PushCommand pushCommand = local.push().setRemote("test")
|
||||
.setPushOptions(pushOptions);
|
||||
pushCommand.call();
|
||||
|
||||
fail("should already have thrown TransportException");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -498,6 +498,7 @@ pushCertificateInvalidHeader=Push certificate has invalid header format
|
|||
pushCertificateInvalidSignature=Push certificate has invalid signature format
|
||||
pushIsNotSupportedForBundleTransport=Push is not supported for bundle transport
|
||||
pushNotPermitted=push not permitted
|
||||
pushOptionsNotSupported=Push options not supported; received {0}
|
||||
rawLogMessageDoesNotParseAsLogEntry=Raw log message does not parse as log entry
|
||||
readingObjectsFromLocalRepositoryFailed=reading objects from local repository failed: {0}
|
||||
readTimedOut=Read timed out after {0} ms
|
||||
|
|
|
@ -96,6 +96,8 @@ public class PushCommand extends
|
|||
|
||||
private OutputStream out;
|
||||
|
||||
private List<String> pushOptions;
|
||||
|
||||
/**
|
||||
* @param repo
|
||||
*/
|
||||
|
@ -149,6 +151,7 @@ public Iterable<PushResult> call() throws GitAPIException,
|
|||
if (receivePack != null)
|
||||
transport.setOptionReceivePack(receivePack);
|
||||
transport.setDryRun(dryRun);
|
||||
transport.setPushOptions(pushOptions);
|
||||
configure(transport);
|
||||
|
||||
final Collection<RemoteRefUpdate> toPush = transport
|
||||
|
@ -189,7 +192,6 @@ public Iterable<PushResult> call() throws GitAPIException,
|
|||
}
|
||||
|
||||
return pushResults;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -453,4 +455,24 @@ public PushCommand setOutputStream(OutputStream out) {
|
|||
this.out = out;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the option strings associated with the push operation
|
||||
* @since 4.5
|
||||
*/
|
||||
public List<String> getPushOptions() {
|
||||
return pushOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the option strings associated with the push operation.
|
||||
*
|
||||
* @param pushOptions
|
||||
* @return {@code this}
|
||||
* @since 4.5
|
||||
*/
|
||||
public PushCommand setPushOptions(List<String> pushOptions) {
|
||||
this.pushOptions = pushOptions;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -557,6 +557,7 @@ public static JGitText get() {
|
|||
/***/ public String pushCertificateInvalidSignature;
|
||||
/***/ public String pushIsNotSupportedForBundleTransport;
|
||||
/***/ public String pushNotPermitted;
|
||||
/***/ public String pushOptionsNotSupported;
|
||||
/***/ public String rawLogMessageDoesNotParseAsLogEntry;
|
||||
/***/ public String readingObjectsFromLocalRepositoryFailed;
|
||||
/***/ public String readTimedOut;
|
||||
|
|
|
@ -92,6 +92,9 @@ public class BatchRefUpdate {
|
|||
/** Whether updates should be atomic. */
|
||||
private boolean atomic;
|
||||
|
||||
/** Push options associated with this update. */
|
||||
private List<String> pushOptions;
|
||||
|
||||
/**
|
||||
* Initialize a new batch update.
|
||||
*
|
||||
|
@ -300,6 +303,16 @@ public BatchRefUpdate addCommand(Collection<ReceiveCommand> cmd) {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of option strings associated with this update.
|
||||
*
|
||||
* @return pushOptions
|
||||
* @since 4.5
|
||||
*/
|
||||
public List<String> getPushOptions() {
|
||||
return pushOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute this batch update.
|
||||
* <p>
|
||||
|
@ -307,21 +320,24 @@ public BatchRefUpdate addCommand(Collection<ReceiveCommand> cmd) {
|
|||
* update over each reference.
|
||||
* <p>
|
||||
* Implementations must respect the atomicity requirements of the underlying
|
||||
* database as described in {@link #setAtomic(boolean)} and {@link
|
||||
* RefDatabase#performsAtomicTransactions()}.
|
||||
* database as described in {@link #setAtomic(boolean)} and
|
||||
* {@link RefDatabase#performsAtomicTransactions()}.
|
||||
*
|
||||
* @param walk
|
||||
* a RevWalk to parse tags in case the storage system wants to
|
||||
* store them pre-peeled, a common performance optimization.
|
||||
* @param monitor
|
||||
* progress monitor to receive update status on.
|
||||
* @param options
|
||||
* a list of option strings; set null to execute without
|
||||
* @throws IOException
|
||||
* the database is unable to accept the update. Individual
|
||||
* command status must be tested to determine if there is a
|
||||
* partial failure, or a total failure.
|
||||
* @since 4.5
|
||||
*/
|
||||
public void execute(RevWalk walk, ProgressMonitor monitor)
|
||||
throws IOException {
|
||||
public void execute(RevWalk walk, ProgressMonitor monitor,
|
||||
List<String> options) throws IOException {
|
||||
|
||||
if (atomic && !refdb.performsAtomicTransactions()) {
|
||||
for (ReceiveCommand c : commands) {
|
||||
|
@ -333,6 +349,10 @@ public void execute(RevWalk walk, ProgressMonitor monitor)
|
|||
return;
|
||||
}
|
||||
|
||||
if (options != null) {
|
||||
pushOptions = options;
|
||||
}
|
||||
|
||||
monitor.beginTask(JGitText.get().updatingReferences, commands.size());
|
||||
List<ReceiveCommand> commands2 = new ArrayList<ReceiveCommand>(
|
||||
commands.size());
|
||||
|
@ -412,6 +432,24 @@ public void execute(RevWalk walk, ProgressMonitor monitor)
|
|||
monitor.endTask();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute this batch update without option strings.
|
||||
*
|
||||
* @param walk
|
||||
* a RevWalk to parse tags in case the storage system wants to
|
||||
* store them pre-peeled, a common performance optimization.
|
||||
* @param monitor
|
||||
* progress monitor to receive update status on.
|
||||
* @throws IOException
|
||||
* the database is unable to accept the update. Individual
|
||||
* command status must be tested to determine if there is a
|
||||
* partial failure, or a total failure.
|
||||
*/
|
||||
public void execute(RevWalk walk, ProgressMonitor monitor)
|
||||
throws IOException {
|
||||
execute(walk, monitor, null);
|
||||
}
|
||||
|
||||
private static Collection<String> getTakenPrefixes(
|
||||
final Collection<String> names) {
|
||||
Collection<String> ref = new HashSet<String>();
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
import java.text.MessageFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -113,14 +114,24 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen
|
|||
*/
|
||||
public static final String CAPABILITY_SIDE_BAND_64K = GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
|
||||
|
||||
/**
|
||||
* The server supports the receiving of push options.
|
||||
* @since 4.5
|
||||
*/
|
||||
public static final String CAPABILITY_PUSH_OPTIONS = GitProtocolConstants.CAPABILITY_PUSH_OPTIONS;
|
||||
|
||||
private final boolean thinPack;
|
||||
private final boolean atomic;
|
||||
|
||||
/** A list of option strings associated with this push. */
|
||||
private List<String> pushOptions;
|
||||
|
||||
private boolean capableAtomic;
|
||||
private boolean capableDeleteRefs;
|
||||
private boolean capableReport;
|
||||
private boolean capableSideBand;
|
||||
private boolean capableOfsDelta;
|
||||
private boolean capablePushOptions;
|
||||
|
||||
private boolean sentCommand;
|
||||
private boolean writePack;
|
||||
|
@ -138,6 +149,7 @@ public BasePackPushConnection(final PackTransport packTransport) {
|
|||
super(packTransport);
|
||||
thinPack = transport.isPushThin();
|
||||
atomic = transport.isPushAtomic();
|
||||
pushOptions = transport.getPushOptions();
|
||||
}
|
||||
|
||||
public void push(final ProgressMonitor monitor,
|
||||
|
@ -197,6 +209,9 @@ protected void doPush(final ProgressMonitor monitor,
|
|||
OutputStream outputStream) throws TransportException {
|
||||
try {
|
||||
writeCommands(refUpdates.values(), monitor, outputStream);
|
||||
|
||||
if (pushOptions != null && capablePushOptions)
|
||||
transmitOptions();
|
||||
if (writePack)
|
||||
writePack(refUpdates, monitor);
|
||||
if (sentCommand) {
|
||||
|
@ -232,6 +247,12 @@ private void writeCommands(final Collection<RemoteRefUpdate> refUpdates,
|
|||
JGitText.get().atomicPushNotSupported);
|
||||
}
|
||||
|
||||
if (pushOptions != null && !capablePushOptions) {
|
||||
throw new TransportException(uri,
|
||||
MessageFormat.format(JGitText.get().pushOptionsNotSupported,
|
||||
pushOptions.toString()));
|
||||
}
|
||||
|
||||
for (final RemoteRefUpdate rru : refUpdates) {
|
||||
if (!capableDeleteRefs && rru.isDelete()) {
|
||||
rru.setStatus(Status.REJECTED_NODELETE);
|
||||
|
@ -269,6 +290,14 @@ private void writeCommands(final Collection<RemoteRefUpdate> refUpdates,
|
|||
outNeedsEnd = false;
|
||||
}
|
||||
|
||||
private void transmitOptions() throws IOException {
|
||||
for (final String pushOption : pushOptions) {
|
||||
pckOut.writeString(pushOption);
|
||||
}
|
||||
|
||||
pckOut.end();
|
||||
}
|
||||
|
||||
private String enableCapabilities(final ProgressMonitor monitor,
|
||||
OutputStream outputStream) {
|
||||
final StringBuilder line = new StringBuilder();
|
||||
|
@ -278,6 +307,10 @@ private String enableCapabilities(final ProgressMonitor monitor,
|
|||
capableDeleteRefs = wantCapability(line, CAPABILITY_DELETE_REFS);
|
||||
capableOfsDelta = wantCapability(line, CAPABILITY_OFS_DELTA);
|
||||
|
||||
if (pushOptions != null) {
|
||||
capablePushOptions = wantCapability(line, CAPABILITY_PUSH_OPTIONS);
|
||||
}
|
||||
|
||||
capableSideBand = wantCapability(line, CAPABILITY_SIDE_BAND_64K);
|
||||
if (capableSideBand) {
|
||||
in = new SideBandInputStream(in, monitor, getMessageWriter(),
|
||||
|
@ -333,7 +366,8 @@ private void readStatusReport(final Map<String, RemoteRefUpdate> refUpdates)
|
|||
throws IOException {
|
||||
final String unpackLine = readStringLongTimeout();
|
||||
if (!unpackLine.startsWith("unpack ")) //$NON-NLS-1$
|
||||
throw new PackProtocolException(uri, MessageFormat.format(JGitText.get().unexpectedReportLine, unpackLine));
|
||||
throw new PackProtocolException(uri, MessageFormat
|
||||
.format(JGitText.get().unexpectedReportLine, unpackLine));
|
||||
final String unpackStatus = unpackLine.substring("unpack ".length()); //$NON-NLS-1$
|
||||
if (unpackStatus.startsWith("error Pack exceeds the limit of")) {//$NON-NLS-1$
|
||||
throw new TooLargePackException(uri,
|
||||
|
@ -404,6 +438,16 @@ private String readStringLongTimeout() throws IOException {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of option strings associated with this push.
|
||||
*
|
||||
* @return pushOptions
|
||||
* @since 4.5
|
||||
*/
|
||||
public List<String> getPushOptions() {
|
||||
return pushOptions;
|
||||
}
|
||||
|
||||
private static class CheckingSideBandOutputStream extends OutputStream {
|
||||
private final InputStream in;
|
||||
private final OutputStream out;
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_OFS_DELTA;
|
||||
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_QUIET;
|
||||
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_REPORT_STATUS;
|
||||
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_PUSH_OPTIONS;
|
||||
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_SIDE_BAND_64K;
|
||||
import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_AGENT;
|
||||
import static org.eclipse.jgit.transport.SideBandOutputStream.CH_DATA;
|
||||
|
@ -178,6 +179,9 @@ public Set<String> getCapabilities() {
|
|||
/** Should an incoming transfer permit non-fast-forward requests? */
|
||||
private boolean allowNonFastForwards;
|
||||
|
||||
/** Should an incoming transfer permit push options? **/
|
||||
private boolean allowPushOptions;
|
||||
|
||||
/**
|
||||
* Should the requested ref updates be performed as a single atomic
|
||||
* transaction?
|
||||
|
@ -247,6 +251,18 @@ public Set<String> getCapabilities() {
|
|||
|
||||
private boolean quiet;
|
||||
|
||||
/**
|
||||
* A list of option strings associated with a push.
|
||||
* @since 4.5
|
||||
*/
|
||||
protected List<String> pushOptions;
|
||||
|
||||
/**
|
||||
* Whether the client intends to use push options.
|
||||
* @since 4.5
|
||||
*/
|
||||
protected boolean usePushOptions;
|
||||
|
||||
/** Lock around the received pack file, while updating refs. */
|
||||
private PackLock packLock;
|
||||
|
||||
|
@ -311,6 +327,7 @@ protected BaseReceivePack(final Repository into) {
|
|||
allowBranchDeletes = rc.allowDeletes;
|
||||
allowNonFastForwards = rc.allowNonFastForwards;
|
||||
allowOfsDelta = rc.allowOfsDelta;
|
||||
allowPushOptions = rc.allowPushOptions;
|
||||
advertiseRefsHook = AdvertiseRefsHook.DEFAULT;
|
||||
refFilter = RefFilter.DEFAULT;
|
||||
advertisedHaves = new HashSet<ObjectId>();
|
||||
|
@ -330,6 +347,8 @@ public ReceiveConfig parse(final Config cfg) {
|
|||
final boolean allowDeletes;
|
||||
final boolean allowNonFastForwards;
|
||||
final boolean allowOfsDelta;
|
||||
final boolean allowPushOptions;
|
||||
|
||||
final SignedPushConfig signedPush;
|
||||
|
||||
ReceiveConfig(final Config config) {
|
||||
|
@ -339,6 +358,8 @@ public ReceiveConfig parse(final Config cfg) {
|
|||
"denynonfastforwards", false); //$NON-NLS-1$
|
||||
allowOfsDelta = config.getBoolean("repack", "usedeltabaseoffset", //$NON-NLS-1$ //$NON-NLS-2$
|
||||
true);
|
||||
allowPushOptions = config.getBoolean("receive", "pushoptions", //$NON-NLS-1$ //$NON-NLS-2$
|
||||
false);
|
||||
signedPush = SignedPushConfig.KEY.parse(config);
|
||||
}
|
||||
}
|
||||
|
@ -787,6 +808,25 @@ public void setAllowQuiet(boolean allow) {
|
|||
allowQuiet = allow;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the server supports the receiving of push options.
|
||||
* @since 4.5
|
||||
*/
|
||||
public boolean isAllowPushOptions() {
|
||||
return allowPushOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure if the server supports the receiving of push options.
|
||||
*
|
||||
* @param allow
|
||||
* true to permit option strings.
|
||||
* @since 4.5
|
||||
*/
|
||||
public void setAllowPushOptions(boolean allow) {
|
||||
allowPushOptions = allow;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the client wants less verbose output.
|
||||
*
|
||||
|
@ -804,6 +844,24 @@ public boolean isQuiet() throws RequestNotYetReadException {
|
|||
return quiet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of string options associated with this push.
|
||||
*
|
||||
* @return pushOptions
|
||||
* @throws RequestNotYetReadException
|
||||
* if the client's request has not yet been read from the wire,
|
||||
* so we do not know if they expect push options. Note that the
|
||||
* client may have already written the request, it just has not
|
||||
* been read.
|
||||
* @since 4.5
|
||||
*/
|
||||
public List<String> getPushOptions() throws RequestNotYetReadException {
|
||||
if (enabledCapabilities == null) {
|
||||
throw new RequestNotYetReadException();
|
||||
}
|
||||
return Collections.unmodifiableList(pushOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the configuration for push certificate verification.
|
||||
*
|
||||
|
@ -1076,6 +1134,10 @@ public void sendAdvertisedRefs(final RefAdvertiser adv)
|
|||
adv.advertiseCapability(CAPABILITY_ATOMIC);
|
||||
if (allowOfsDelta)
|
||||
adv.advertiseCapability(CAPABILITY_OFS_DELTA);
|
||||
if (allowPushOptions) {
|
||||
adv.advertiseCapability(CAPABILITY_PUSH_OPTIONS);
|
||||
pushOptions = new ArrayList<>();
|
||||
}
|
||||
adv.advertiseCapability(OPTION_AGENT, UserAgent.get());
|
||||
adv.send(getAdvertisedOrDefaultRefs());
|
||||
for (ObjectId obj : advertisedHaves)
|
||||
|
@ -1192,6 +1254,8 @@ static ReceiveCommand parseCommand(String line) throws PackProtocolException {
|
|||
protected void enableCapabilities() {
|
||||
sideBand = isCapabilityEnabled(CAPABILITY_SIDE_BAND_64K);
|
||||
quiet = allowQuiet && isCapabilityEnabled(CAPABILITY_QUIET);
|
||||
usePushOptions = allowPushOptions
|
||||
&& isCapabilityEnabled(CAPABILITY_PUSH_OPTIONS);
|
||||
if (sideBand) {
|
||||
OutputStream out = rawOut;
|
||||
|
||||
|
@ -1204,6 +1268,17 @@ protected void enableCapabilities() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client's intention regarding push options.
|
||||
*
|
||||
* @param usePushOptions
|
||||
* whether the client intends to use push options
|
||||
* @since 4.5
|
||||
*/
|
||||
public void setUsePushOptions(boolean usePushOptions) {
|
||||
this.usePushOptions = usePushOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the peer requested a capability.
|
||||
*
|
||||
|
|
|
@ -208,6 +208,13 @@ public class GitProtocolConstants {
|
|||
*/
|
||||
public static final String OPTION_AGENT = "agent"; //$NON-NLS-1$
|
||||
|
||||
/**
|
||||
* The server supports the receiving of push options.
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
public static final String CAPABILITY_PUSH_OPTIONS = "push-options"; //$NON-NLS-1$
|
||||
|
||||
static enum MultiAck {
|
||||
OFF, CONTINUE, DETAILED;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jgit.errors.MissingObjectException;
|
||||
|
@ -87,6 +88,9 @@ class PushProcess {
|
|||
/** an outputstream to write messages to */
|
||||
private final OutputStream out;
|
||||
|
||||
/** A list of option strings associated with this push */
|
||||
private List<String> pushOptions;
|
||||
|
||||
/**
|
||||
* Create process for specified transport and refs updates specification.
|
||||
*
|
||||
|
@ -122,6 +126,7 @@ class PushProcess {
|
|||
this.transport = transport;
|
||||
this.toPush = new HashMap<String, RemoteRefUpdate>();
|
||||
this.out = out;
|
||||
this.pushOptions = transport.getPushOptions();
|
||||
for (final RemoteRefUpdate rru : toPush) {
|
||||
if (this.toPush.put(rru.getRemoteName(), rru) != null)
|
||||
throw new TransportException(MessageFormat.format(
|
||||
|
@ -294,4 +299,14 @@ private void updateTrackingRefs() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of option strings associated with this push.
|
||||
*
|
||||
* @return pushOptions
|
||||
* @since 4.5
|
||||
*/
|
||||
public List<String> getPushOptions() {
|
||||
return pushOptions;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -174,6 +174,15 @@ protected void enableCapabilities() {
|
|||
super.enableCapabilities();
|
||||
}
|
||||
|
||||
private void readPushOptions() throws IOException {
|
||||
String pushOption = pckIn.readString();
|
||||
|
||||
while (pushOption != PacketLineIn.END) {
|
||||
pushOptions.add(pushOption);
|
||||
pushOption = pckIn.readString();
|
||||
}
|
||||
}
|
||||
|
||||
private void service() throws IOException {
|
||||
if (isBiDirectionalPipe()) {
|
||||
sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut));
|
||||
|
@ -184,6 +193,10 @@ private void service() throws IOException {
|
|||
return;
|
||||
recvCommands();
|
||||
if (hasCommands()) {
|
||||
if (usePushOptions) {
|
||||
readPushOptions();
|
||||
}
|
||||
|
||||
Throwable unpackError = null;
|
||||
if (needPack()) {
|
||||
try {
|
||||
|
|
|
@ -773,6 +773,9 @@ private static String findTrackingRefName(final String remoteName,
|
|||
/** Assists with authentication the connection. */
|
||||
private CredentialsProvider credentialsProvider;
|
||||
|
||||
/** The option strings associated with the push operation. */
|
||||
private List<String> pushOptions;
|
||||
|
||||
private PrintStream hookOutRedirect;
|
||||
|
||||
private PrePushHook prePush;
|
||||
|
@ -1120,6 +1123,25 @@ public CredentialsProvider getCredentialsProvider() {
|
|||
return credentialsProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the option strings associated with the push operation
|
||||
* @since 4.5
|
||||
*/
|
||||
public List<String> getPushOptions() {
|
||||
return pushOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the option strings associated with the push operation.
|
||||
*
|
||||
* @param pushOptions
|
||||
* null if push options are unsupported
|
||||
* @since 4.5
|
||||
*/
|
||||
public void setPushOptions(final List<String> pushOptions) {
|
||||
this.pushOptions = pushOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch objects and refs from the remote repository to the local one.
|
||||
* <p>
|
||||
|
|
Loading…
Reference in New Issue