PushCommand: determine remote from git config if not given

Add ConfigConstants and expose branch.<name>.pushRemote in the
BranchConfig. Use the branch configuration and remote.pushDefault
if no remote is given explicitly. If nothing is configured, fall
back to "origin".

Bug: 578676
Change-Id: I6bb141ff02c8b04980ec34b26ef248b72614c3c9
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
This commit is contained in:
Thomas Wolf 2022-02-10 19:04:21 +01:00 committed by Matthias Sohn
parent 504001228b
commit 855a734875
4 changed files with 229 additions and 3 deletions

View File

@ -26,6 +26,7 @@
import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.api.errors.TransportException; import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.NoRemoteRepositoryException;
import org.eclipse.jgit.hooks.PrePushHook; import org.eclipse.jgit.hooks.PrePushHook;
import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.JGitTestUtil;
import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.junit.RepositoryTestCase;
@ -633,8 +634,7 @@ public void testPushDefaultSimpleTriangular() throws Exception {
config = git.getRepository().getConfig(); config = git.getRepository().getConfig();
// Don't set remote, it'll default to "origin". Configure a // Don't set remote, it'll default to "origin". Configure a
// different // different branch name; should be ignored.
// branch name; should be ignored.
config.setString("branch", "branchtopush", "merge", config.setString("branch", "branchtopush", "merge",
"refs/heads/upstreambranch"); "refs/heads/upstreambranch");
config.save(); config.save();
@ -852,6 +852,176 @@ public void testPushDefaultFromConfigDefault() throws Exception {
} }
} }
/**
* Check that branch.<name>.pushRemote overrides anything else.
*
* @throws Exception
*/
@Test
public void testBranchPushRemote() 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("remote", null, "pushDefault", "test");
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", "pushremote", "origin");
config.setString("branch", "branchtopush", "merge",
"refs/heads/branchtopush");
config.save();
assertThrows(InvalidRefNameException.class, () -> git.push()
.setPushDefault(PushDefault.UPSTREAM).call());
}
}
/**
* Check that remote.pushDefault overrides branch.<name>.remote
*
* @throws Exception
*/
@Test
public void testRemotePushDefault() 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("remote", null, "pushDefault", "origin");
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/branchtopush");
config.save();
assertThrows(InvalidRefNameException.class, () -> git.push()
.setPushDefault(PushDefault.UPSTREAM).call());
}
}
/**
* Check that ultimately we fall back to "origin".
*
* @throws Exception
*/
@Test
public void testDefaultRemote() 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", "merge",
"refs/heads/branchtopush");
config.save();
PushCommand cmd = git.push().setPushDefault(PushDefault.UPSTREAM);
TransportException e = assertThrows(TransportException.class,
() -> cmd.call());
assertEquals(NoRemoteRepositoryException.class,
e.getCause().getClass());
assertEquals("origin", cmd.getRemote());
}
}
/**
* Check that a push without specifying a remote or mode or anything can
* succeed if the git config is correct.
*
* @throws Exception
*/
@Test
public void testDefaultPush() 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.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"));
// Should use remote "test", push.default=current
PushCommand cmd = git.push();
cmd.call();
assertEquals("test", cmd.getRemote());
assertEquals(PushDefault.CURRENT, 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 * Check that missing refs don't cause errors during push
* *

View File

@ -33,6 +33,7 @@
import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.BranchConfig; import org.eclipse.jgit.lib.BranchConfig;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ProgressMonitor; import org.eclipse.jgit.lib.ProgressMonitor;
@ -58,7 +59,7 @@
public class PushCommand extends public class PushCommand extends
TransportCommand<PushCommand, Iterable<PushResult>> { TransportCommand<PushCommand, Iterable<PushResult>> {
private String remote = Constants.DEFAULT_REMOTE_NAME; private String remote;
private final List<RefSpec> refSpecs; private final List<RefSpec> refSpecs;
@ -114,6 +115,7 @@ public Iterable<PushResult> call() throws GitAPIException,
try { try {
Config config = repo.getConfig(); Config config = repo.getConfig();
remote = determineRemote(config, remote);
if (refSpecs.isEmpty()) { if (refSpecs.isEmpty()) {
RemoteConfig rc = new RemoteConfig(config, RemoteConfig rc = new RemoteConfig(config,
getRemote()); getRemote());
@ -181,6 +183,34 @@ public Iterable<PushResult> call() throws GitAPIException,
return pushResults; return pushResults;
} }
private String determineRemote(Config config, String remoteName)
throws IOException {
if (remoteName != null) {
return remoteName;
}
Ref head = repo.exactRef(Constants.HEAD);
String effectiveRemote = null;
BranchConfig branchCfg = null;
if (head != null && head.isSymbolic()) {
String currentBranch = head.getLeaf().getName();
branchCfg = new BranchConfig(config,
Repository.shortenRefName(currentBranch));
effectiveRemote = branchCfg.getPushRemote();
}
if (effectiveRemote == null) {
effectiveRemote = config.getString(
ConfigConstants.CONFIG_REMOTE_SECTION, null,
ConfigConstants.CONFIG_KEY_PUSH_DEFAULT);
if (effectiveRemote == null && branchCfg != null) {
effectiveRemote = branchCfg.getRemote();
}
}
if (effectiveRemote == null) {
effectiveRemote = Constants.DEFAULT_REMOTE_NAME;
}
return effectiveRemote;
}
private String getCurrentBranch() private String getCurrentBranch()
throws IOException, DetachedHeadException { throws IOException, DetachedHeadException {
Ref head = repo.exactRef(Constants.HEAD); Ref head = repo.exactRef(Constants.HEAD);

View File

@ -137,6 +137,18 @@ public String getRemote() {
branchName, ConfigConstants.CONFIG_KEY_REMOTE); branchName, ConfigConstants.CONFIG_KEY_REMOTE);
} }
/**
* Get the remote this branch is configured to push to.
*
* @return the remote this branch is configured to push to, or {@code null}
* if not defined
* @since 6.1
*/
public String getPushRemote() {
return config.getString(ConfigConstants.CONFIG_BRANCH_SECTION,
branchName, ConfigConstants.CONFIG_KEY_PUSH_REMOTE);
}
/** /**
* Get the name of the upstream branch as it is called on the remote * Get the name of the upstream branch as it is called on the remote
* *

View File

@ -329,6 +329,20 @@ public final class ConfigConstants {
/** The "remote" key */ /** The "remote" key */
public static final String CONFIG_KEY_REMOTE = "remote"; public static final String CONFIG_KEY_REMOTE = "remote";
/**
* The "pushRemote" key.
*
* @since 6.1
*/
public static final String CONFIG_KEY_PUSH_REMOTE = "pushRemote";
/**
* The "pushDefault" key.
*
* @since 6.1
*/
public static final String CONFIG_KEY_PUSH_DEFAULT = "pushDefault";
/** The "merge" key */ /** The "merge" key */
public static final String CONFIG_KEY_MERGE = "merge"; public static final String CONFIG_KEY_MERGE = "merge";