PushCommand: consider push.default when no RefSpecs are given

When no RefSpecs are given, PushCommand until now simply fell back to
pushing the current branch to an upstream branch of the same name. This
corresponds to push.default=current. Any setting from the git config
for push.default was simply ignored.

Implement the other modes (nothing, matching, upstream, and simple),
too. Add a setter and getter for the PushDefault so that an application
can force a particular mode to be used. For backwards compatibility,
use "current" as the default setting; to figure out the value from the
git config, which defaults to "simple", call setPushDefault(null).

Bug: 351314
Change-Id: I86c5402318771e47d80b137e99947762e1150bb4
Signed-off-by: Rolf Theunissen <rolf.theunissen@gmail.com>
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
This commit is contained in:
Rolf Theunissen 2019-02-10 18:40:26 +01:00 committed by Matthias Sohn
parent c3fbd2cdf9
commit 504001228b
4 changed files with 690 additions and 12 deletions

View File

@ -11,6 +11,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@ -19,7 +20,9 @@
import java.net.URISyntaxException;
import java.util.Properties;
import org.eclipse.jgit.api.errors.DetachedHeadException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.errors.MissingObjectException;
@ -32,6 +35,7 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.PushConfig.PushDefault;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RefLeaseSpec;
import org.eclipse.jgit.transport.RefSpec;
@ -289,6 +293,565 @@ public void testPushWithoutPushRefSpec() throws Exception {
}
}
/**
* Check that pushing from a detached HEAD without refspec throws a
* DetachedHeadException.
*
* @throws Exception
*/
@Test
public void testPushDefaultDetachedHead() throws Exception {
try (Git git = new Git(db);
Git git2 = new Git(createBareRepository())) {
final StoredConfig config = git.getRepository().getConfig();
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
URIish uri = new URIish(
git2.getRepository().getDirectory().toURI().toURL());
remoteConfig.addURI(uri);
remoteConfig.addFetchRefSpec(
new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
remoteConfig.update(config);
config.save();
writeTrashFile("f", "content of f");
git.add().addFilepattern("f").call();
RevCommit commit = git.commit().setMessage("adding f").call();
git.checkout().setName("not-pushed").setCreateBranch(true).call();
git.checkout().setName("branchtopush").setCreateBranch(true).call();
git.checkout().setName(commit.getName()).call();
String head = git.getRepository().getFullBranch();
assertTrue(ObjectId.isId(head));
assertEquals(commit.getName(), head);
assertThrows(DetachedHeadException.class,
() -> git.push().setRemote("test").call());
}
}
/**
* Check that push.default=nothing without refspec throws an
* InvalidRefNameException.
*
* @throws Exception
*/
@Test
public void testPushDefaultNothing() throws Exception {
try (Git git = new Git(db);
Git git2 = new Git(createBareRepository())) {
final StoredConfig config = git.getRepository().getConfig();
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
URIish uri = new URIish(
git2.getRepository().getDirectory().toURI().toURL());
remoteConfig.addURI(uri);
remoteConfig.addFetchRefSpec(
new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
remoteConfig.update(config);
config.save();
writeTrashFile("f", "content of f");
git.add().addFilepattern("f").call();
git.commit().setMessage("adding f").call();
git.checkout().setName("not-pushed").setCreateBranch(true).call();
git.checkout().setName("branchtopush").setCreateBranch(true).call();
assertEquals(null,
git2.getRepository().resolve("refs/heads/branchtopush"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/not-pushed"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/master"));
assertThrows(InvalidRefNameException.class,
() -> git.push().setRemote("test")
.setPushDefault(PushDefault.NOTHING).call());
}
}
/**
* Check that push.default=matching without refspec pushes all matching
* branches.
*
* @throws Exception
*/
@Test
public void testPushDefaultMatching() throws Exception {
try (Git git = new Git(db);
Git git2 = new Git(createBareRepository())) {
final StoredConfig config = git.getRepository().getConfig();
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
URIish uri = new URIish(
git2.getRepository().getDirectory().toURI().toURL());
remoteConfig.addURI(uri);
remoteConfig.addFetchRefSpec(
new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
remoteConfig.update(config);
config.save();
writeTrashFile("f", "content of f");
git.add().addFilepattern("f").call();
RevCommit commit = git.commit().setMessage("adding f").call();
git.checkout().setName("also-pushed").setCreateBranch(true).call();
git.checkout().setName("branchtopush").setCreateBranch(true).call();
assertEquals(null,
git2.getRepository().resolve("refs/heads/branchtopush"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/also-pushed"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/master"));
git.push().setRemote("test").setPushDefault(PushDefault.MATCHING)
.call();
assertEquals(commit.getId(),
git2.getRepository().resolve("refs/heads/branchtopush"));
assertEquals(commit.getId(),
git2.getRepository().resolve("refs/heads/also-pushed"));
assertEquals(commit.getId(),
git2.getRepository().resolve("refs/heads/master"));
assertEquals(commit.getId(), git.getRepository()
.resolve("refs/remotes/origin/branchtopush"));
assertEquals(commit.getId(), git.getRepository()
.resolve("refs/remotes/origin/also-pushed"));
assertEquals(commit.getId(),
git.getRepository().resolve("refs/remotes/origin/master"));
}
}
/**
* Check that push.default=upstream without refspec pushes only the current
* branch to the configured upstream.
*
* @throws Exception
*/
@Test
public void testPushDefaultUpstream() throws Exception {
try (Git git = new Git(db);
Git git2 = new Git(createBareRepository())) {
StoredConfig config = git.getRepository().getConfig();
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
URIish uri = new URIish(
git2.getRepository().getDirectory().toURI().toURL());
remoteConfig.addURI(uri);
remoteConfig.addFetchRefSpec(
new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
remoteConfig.update(config);
config.save();
writeTrashFile("f", "content of f");
git.add().addFilepattern("f").call();
RevCommit commit = git.commit().setMessage("adding f").call();
git.checkout().setName("not-pushed").setCreateBranch(true).call();
git.checkout().setName("branchtopush").setCreateBranch(true).call();
config = git.getRepository().getConfig();
config.setString("branch", "branchtopush", "remote", "test");
config.setString("branch", "branchtopush", "merge",
"refs/heads/upstreambranch");
config.save();
assertEquals(null,
git2.getRepository().resolve("refs/heads/branchtopush"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/upstreambranch"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/not-pushed"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/master"));
git.push().setRemote("test").setPushDefault(PushDefault.UPSTREAM)
.call();
assertEquals(null,
git2.getRepository().resolve("refs/heads/branchtopush"));
assertEquals(commit.getId(),
git2.getRepository().resolve("refs/heads/upstreambranch"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/not-pushed"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/master"));
assertEquals(commit.getId(), git.getRepository()
.resolve("refs/remotes/origin/upstreambranch"));
assertEquals(null, git.getRepository()
.resolve("refs/remotes/origin/branchtopush"));
}
}
/**
* Check that push.default=upstream without refspec throws an
* InvalidRefNameException if the current branch has no upstream.
*
* @throws Exception
*/
@Test
public void testPushDefaultUpstreamNoTracking() throws Exception {
try (Git git = new Git(db);
Git git2 = new Git(createBareRepository())) {
StoredConfig config = git.getRepository().getConfig();
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
URIish uri = new URIish(
git2.getRepository().getDirectory().toURI().toURL());
remoteConfig.addURI(uri);
remoteConfig.addFetchRefSpec(
new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
remoteConfig.update(config);
config.save();
writeTrashFile("f", "content of f");
git.add().addFilepattern("f").call();
git.commit().setMessage("adding f").call();
git.checkout().setName("not-pushed").setCreateBranch(true).call();
git.checkout().setName("branchtopush").setCreateBranch(true).call();
config = git.getRepository().getConfig();
config.setString("branch", "branchtopush", "remote", "test");
config.save();
assertThrows(InvalidRefNameException.class,
() -> git.push().setRemote("test")
.setPushDefault(PushDefault.UPSTREAM).call());
}
}
/**
* Check that push.default=upstream without refspec throws an
* InvalidRefNameException if the push remote is not the same as the fetch
* remote.
*
* @throws Exception
*/
@Test
public void testPushDefaultUpstreamTriangular() throws Exception {
try (Git git = new Git(db);
Git git2 = new Git(createBareRepository())) {
StoredConfig config = git.getRepository().getConfig();
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
URIish uri = new URIish(
git2.getRepository().getDirectory().toURI().toURL());
remoteConfig.addURI(uri);
remoteConfig.addFetchRefSpec(
new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
remoteConfig.update(config);
config.save();
writeTrashFile("f", "content of f");
git.add().addFilepattern("f").call();
git.commit().setMessage("adding f").call();
git.checkout().setName("not-pushed").setCreateBranch(true).call();
git.checkout().setName("branchtopush").setCreateBranch(true).call();
config = git.getRepository().getConfig();
// Don't configure a remote; it'll default to "origin".
config.setString("branch", "branchtopush", "merge",
"upstreambranch");
config.save();
assertThrows(InvalidRefNameException.class,
() -> git.push().setRemote("test")
.setPushDefault(PushDefault.UPSTREAM).call());
}
}
/**
* Check that push.default=simple without refspec pushes only the current
* branch to the configured upstream name.
*
* @throws Exception
*/
@Test
public void testPushDefaultSimple() throws Exception {
try (Git git = new Git(db);
Git git2 = new Git(createBareRepository())) {
StoredConfig config = git.getRepository().getConfig();
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
URIish uri = new URIish(
git2.getRepository().getDirectory().toURI().toURL());
remoteConfig.addURI(uri);
remoteConfig.addFetchRefSpec(
new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
remoteConfig.update(config);
config.save();
writeTrashFile("f", "content of f");
git.add().addFilepattern("f").call();
RevCommit commit = git.commit().setMessage("adding f").call();
git.checkout().setName("not-pushed").setCreateBranch(true).call();
git.checkout().setName("branchtopush").setCreateBranch(true).call();
config = git.getRepository().getConfig();
config.setString("branch", "branchtopush", "remote", "test");
config.setString("branch", "branchtopush", "merge",
"refs/heads/branchtopush");
config.save();
assertEquals(null,
git2.getRepository().resolve("refs/heads/branchtopush"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/not-pushed"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/master"));
git.push().setRemote("test").setPushDefault(PushDefault.SIMPLE)
.call();
assertEquals(commit.getId(),
git2.getRepository().resolve("refs/heads/branchtopush"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/not-pushed"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/master"));
assertEquals(commit.getId(), git.getRepository()
.resolve("refs/remotes/origin/branchtopush"));
}
}
/**
* Check that push.default=simple without refspec pushes only the current
* branch to a branch with the same name in a triangular workflow.
*
* @throws Exception
*/
@Test
public void testPushDefaultSimpleTriangular() throws Exception {
try (Git git = new Git(db);
Git git2 = new Git(createBareRepository())) {
StoredConfig config = git.getRepository().getConfig();
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
URIish uri = new URIish(
git2.getRepository().getDirectory().toURI().toURL());
remoteConfig.addURI(uri);
remoteConfig.addFetchRefSpec(
new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
remoteConfig.update(config);
config.save();
writeTrashFile("f", "content of f");
git.add().addFilepattern("f").call();
RevCommit commit = git.commit().setMessage("adding f").call();
git.checkout().setName("not-pushed").setCreateBranch(true).call();
git.checkout().setName("branchtopush").setCreateBranch(true).call();
config = git.getRepository().getConfig();
// Don't set remote, it'll default to "origin". Configure a
// different
// branch name; should be ignored.
config.setString("branch", "branchtopush", "merge",
"refs/heads/upstreambranch");
config.save();
assertEquals(null,
git2.getRepository().resolve("refs/heads/branchtopush"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/upstreambranch"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/not-pushed"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/master"));
git.push().setRemote("test").setPushDefault(PushDefault.SIMPLE)
.call();
assertEquals(commit.getId(),
git2.getRepository().resolve("refs/heads/branchtopush"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/upstreambranch"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/not-pushed"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/master"));
assertEquals(commit.getId(), git.getRepository()
.resolve("refs/remotes/origin/branchtopush"));
}
}
/**
* Check that push.default=simple without refspec throws an
* InvalidRefNameException if the current branch has no upstream.
*
* @throws Exception
*/
@Test
public void testPushDefaultSimpleNoTracking() throws Exception {
try (Git git = new Git(db);
Git git2 = new Git(createBareRepository())) {
StoredConfig config = git.getRepository().getConfig();
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
URIish uri = new URIish(
git2.getRepository().getDirectory().toURI().toURL());
remoteConfig.addURI(uri);
remoteConfig.addFetchRefSpec(
new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
remoteConfig.update(config);
config.save();
writeTrashFile("f", "content of f");
git.add().addFilepattern("f").call();
git.commit().setMessage("adding f").call();
git.checkout().setName("not-pushed").setCreateBranch(true).call();
git.checkout().setName("branchtopush").setCreateBranch(true).call();
config = git.getRepository().getConfig();
config.setString("branch", "branchtopush", "remote", "test");
config.save();
assertThrows(InvalidRefNameException.class,
() -> git.push().setRemote("test")
.setPushDefault(PushDefault.SIMPLE).call());
}
}
/**
* Check that push.default=simple without refspec throws an
* InvalidRefNameException if the current branch has an upstream with a
* different name.
*
* @throws Exception
*/
@Test
public void testPushDefaultSimpleDifferentTracking() throws Exception {
try (Git git = new Git(db);
Git git2 = new Git(createBareRepository())) {
StoredConfig config = git.getRepository().getConfig();
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
URIish uri = new URIish(
git2.getRepository().getDirectory().toURI().toURL());
remoteConfig.addURI(uri);
remoteConfig.addFetchRefSpec(
new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
remoteConfig.update(config);
config.save();
writeTrashFile("f", "content of f");
git.add().addFilepattern("f").call();
git.commit().setMessage("adding f").call();
git.checkout().setName("not-pushed").setCreateBranch(true).call();
git.checkout().setName("branchtopush").setCreateBranch(true).call();
config = git.getRepository().getConfig();
config.setString("branch", "branchtopush", "remote", "test");
config.setString("branch", "branchtopush", "merge",
"refs/heads/upstreambranch");
config.save();
assertThrows(InvalidRefNameException.class,
() -> git.push().setRemote("test")
.setPushDefault(PushDefault.SIMPLE).call());
}
}
/**
* Check that if no PushDefault is set, the value is read from the git
* config.
*
* @throws Exception
*/
@Test
public void testPushDefaultFromConfig() throws Exception {
try (Git git = new Git(db);
Git git2 = new Git(createBareRepository())) {
StoredConfig config = git.getRepository().getConfig();
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
URIish uri = new URIish(
git2.getRepository().getDirectory().toURI().toURL());
remoteConfig.addURI(uri);
remoteConfig.addFetchRefSpec(
new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
remoteConfig.update(config);
config.setString("push", null, "default", "upstream");
config.save();
writeTrashFile("f", "content of f");
git.add().addFilepattern("f").call();
RevCommit commit = git.commit().setMessage("adding f").call();
git.checkout().setName("not-pushed").setCreateBranch(true).call();
git.checkout().setName("branchtopush").setCreateBranch(true).call();
config = git.getRepository().getConfig();
config.setString("branch", "branchtopush", "remote", "test");
config.setString("branch", "branchtopush", "merge",
"refs/heads/upstreambranch");
config.save();
assertEquals(null,
git2.getRepository().resolve("refs/heads/branchtopush"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/upstreambranch"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/not-pushed"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/master"));
PushCommand cmd = git.push();
cmd.setRemote("test").setPushDefault(null).call();
assertEquals(PushDefault.UPSTREAM, cmd.getPushDefault());
assertEquals(null,
git2.getRepository().resolve("refs/heads/branchtopush"));
assertEquals(commit.getId(),
git2.getRepository().resolve("refs/heads/upstreambranch"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/not-pushed"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/master"));
assertEquals(commit.getId(), git.getRepository()
.resolve("refs/remotes/origin/upstreambranch"));
assertEquals(null, git.getRepository()
.resolve("refs/remotes/origin/branchtopush"));
}
}
/**
* Check that if no PushDefault is set and none is set in the git config, it
* defaults to "simple".
*
* @throws Exception
*/
@Test
public void testPushDefaultFromConfigDefault() throws Exception {
try (Git git = new Git(db);
Git git2 = new Git(createBareRepository())) {
StoredConfig config = git.getRepository().getConfig();
RemoteConfig remoteConfig = new RemoteConfig(config, "test");
URIish uri = new URIish(
git2.getRepository().getDirectory().toURI().toURL());
remoteConfig.addURI(uri);
remoteConfig.addFetchRefSpec(
new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
remoteConfig.update(config);
config.save();
writeTrashFile("f", "content of f");
git.add().addFilepattern("f").call();
RevCommit commit = git.commit().setMessage("adding f").call();
git.checkout().setName("not-pushed").setCreateBranch(true).call();
git.checkout().setName("branchtopush").setCreateBranch(true).call();
config = git.getRepository().getConfig();
config.setString("branch", "branchtopush", "remote", "test");
config.setString("branch", "branchtopush", "merge",
"refs/heads/branchtopush");
config.save();
assertEquals(null,
git2.getRepository().resolve("refs/heads/branchtopush"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/not-pushed"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/master"));
PushCommand cmd = git.push();
cmd.setRemote("test").setPushDefault(null).call();
assertEquals(PushDefault.SIMPLE, cmd.getPushDefault());
assertEquals(commit.getId(),
git2.getRepository().resolve("refs/heads/branchtopush"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/not-pushed"));
assertEquals(null,
git2.getRepository().resolve("refs/heads/master"));
assertEquals(commit.getId(), git.getRepository()
.resolve("refs/remotes/origin/branchtopush"));
}
}
/**
* Check that missing refs don't cause errors during push
*

View File

@ -564,6 +564,11 @@ pushCertificateInvalidField=Push certificate has missing or invalid value for {0
pushCertificateInvalidFieldValue=Push certificate has missing or invalid value for {0}: {1}
pushCertificateInvalidHeader=Push certificate has invalid header format
pushCertificateInvalidSignature=Push certificate has invalid signature format
pushDefaultNothing=No refspec given and push.default=nothing; no upstream branch can be determined
pushDefaultNoUpstream=No upstream branch found for local branch ''{0}''
pushDefaultSimple=push.default=simple requires local branch name ''{0}'' to be equal to upstream tracked branch name ''{1}''
pushDefaultTriangularUpstream=push.default=upstream cannot be used when the push remote ''{0}'' is different from the fetch remote ''{1}''
pushDefaultUnknown=Unknown push.default={0}; cannot push
pushIsNotSupportedForBundleTransport=Push is not supported for bundle transport
pushNotPermitted=push not permitted
pushOptionsNotSupported=Push options not supported; received {0}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
* Copyright (C) 2010, 2022 Chris Aniszczyk <caniszczyk@gmail.com> 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
@ -21,7 +21,9 @@
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.api.errors.DetachedHeadException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.errors.NotSupportedException;
@ -29,11 +31,15 @@
import org.eclipse.jgit.errors.TooLargePackException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.BranchConfig;
import org.eclipse.jgit.lib.Config;
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.PushConfig;
import org.eclipse.jgit.transport.PushConfig.PushDefault;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RefLeaseSpec;
import org.eclipse.jgit.transport.RefSpec;
@ -71,6 +77,10 @@ public class PushCommand extends
private List<String> pushOptions;
// Legacy behavior as default. Use setPushDefault(null) to determine the
// value from the git config.
private PushDefault pushDefault = PushDefault.CURRENT;
/**
* <p>
* Constructor for PushCommand.
@ -103,15 +113,14 @@ public Iterable<PushResult> call() throws GitAPIException,
ArrayList<PushResult> pushResults = new ArrayList<>(3);
try {
Config config = repo.getConfig();
if (refSpecs.isEmpty()) {
RemoteConfig config = new RemoteConfig(repo.getConfig(),
RemoteConfig rc = new RemoteConfig(config,
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()));
refSpecs.addAll(rc.getPushRefSpecs());
if (refSpecs.isEmpty()) {
determineDefaultRefSpecs(config);
}
}
if (force) {
@ -119,8 +128,8 @@ public Iterable<PushResult> call() throws GitAPIException,
refSpecs.set(i, refSpecs.get(i).setForceUpdate(true));
}
final List<Transport> transports;
transports = Transport.openAll(repo, remote, Transport.Operation.PUSH);
List<Transport> transports = Transport.openAll(repo, remote,
Transport.Operation.PUSH);
for (@SuppressWarnings("resource") // Explicitly closed in finally
final Transport transport : transports) {
transport.setPushThin(thin);
@ -172,6 +181,74 @@ public Iterable<PushResult> call() throws GitAPIException,
return pushResults;
}
private String getCurrentBranch()
throws IOException, DetachedHeadException {
Ref head = repo.exactRef(Constants.HEAD);
if (head != null && head.isSymbolic()) {
return head.getLeaf().getName();
}
throw new DetachedHeadException();
}
private void determineDefaultRefSpecs(Config config)
throws IOException, GitAPIException {
if (pushDefault == null) {
pushDefault = config.get(PushConfig::new).getPushDefault();
}
switch (pushDefault) {
case CURRENT:
refSpecs.add(new RefSpec(getCurrentBranch()));
break;
case MATCHING:
setPushAll();
break;
case NOTHING:
throw new InvalidRefNameException(
JGitText.get().pushDefaultNothing);
case SIMPLE:
case UPSTREAM:
String currentBranch = getCurrentBranch();
BranchConfig branchCfg = new BranchConfig(config,
Repository.shortenRefName(currentBranch));
String fetchRemote = branchCfg.getRemote();
if (fetchRemote == null) {
fetchRemote = Constants.DEFAULT_REMOTE_NAME;
}
boolean isTriangular = !fetchRemote.equals(remote);
if (isTriangular) {
if (PushDefault.UPSTREAM.equals(pushDefault)) {
throw new InvalidRefNameException(MessageFormat.format(
JGitText.get().pushDefaultTriangularUpstream,
remote, fetchRemote));
}
// Strange, but consistent with C git: "simple" doesn't even
// check whether there is a configured upstream, and if so, that
// it is equal to the local branch name. It just becomes
// "current".
refSpecs.add(new RefSpec(currentBranch));
} else {
String trackedBranch = branchCfg.getMerge();
if (branchCfg.isRemoteLocal() || trackedBranch == null
|| !trackedBranch.startsWith(Constants.R_HEADS)) {
throw new InvalidRefNameException(MessageFormat.format(
JGitText.get().pushDefaultNoUpstream,
currentBranch));
}
if (PushDefault.SIMPLE.equals(pushDefault)
&& !trackedBranch.equals(currentBranch)) {
throw new InvalidRefNameException(MessageFormat.format(
JGitText.get().pushDefaultSimple, currentBranch,
trackedBranch));
}
refSpecs.add(new RefSpec(currentBranch + ':' + trackedBranch));
}
break;
default:
throw new InvalidRefNameException(MessageFormat
.format(JGitText.get().pushDefaultUnknown, pushDefault));
}
}
/**
* 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
@ -336,10 +413,38 @@ public PushCommand setRefSpecs(List<RefSpec> specs) {
return this;
}
/**
* Retrieves the {@link PushDefault} currently set.
*
* @return the {@link PushDefault}, or {@code null} if not set
* @since 6.1
*/
public PushDefault getPushDefault() {
return pushDefault;
}
/**
* Sets an explicit {@link PushDefault}. The default used if this is not
* called is {@link PushDefault#CURRENT} for compatibility reasons with
* earlier JGit versions.
*
* @param pushDefault
* {@link PushDefault} to set; if {@code null} the value defined
* in the git config will be used.
*
* @return {@code this}
* @since 6.1
*/
public PushCommand setPushDefault(PushDefault pushDefault) {
checkCallable();
this.pushDefault = pushDefault;
return this;
}
/**
* Push all branches under refs/heads/*.
*
* @return {code this}
* @return {@code this}
*/
public PushCommand setPushAll() {
refSpecs.add(Transport.REFSPEC_PUSH_ALL);
@ -349,7 +454,7 @@ public PushCommand setPushAll() {
/**
* Push all tags under refs/tags/*.
*
* @return {code this}
* @return {@code this}
*/
public PushCommand setPushTags() {
refSpecs.add(Transport.REFSPEC_TAGS);

View File

@ -592,6 +592,11 @@ public static JGitText get() {
/***/ public String pushCertificateInvalidFieldValue;
/***/ public String pushCertificateInvalidHeader;
/***/ public String pushCertificateInvalidSignature;
/***/ public String pushDefaultNothing;
/***/ public String pushDefaultNoUpstream;
/***/ public String pushDefaultSimple;
/***/ public String pushDefaultTriangularUpstream;
/***/ public String pushDefaultUnknown;
/***/ public String pushIsNotSupportedForBundleTransport;
/***/ public String pushNotPermitted;
/***/ public String pushOptionsNotSupported;