From 855a734875c2f178a8adfd0789527afc8754117f Mon Sep 17 00:00:00 2001 From: Thomas Wolf Date: Thu, 10 Feb 2022 19:04:21 +0100 Subject: [PATCH] PushCommand: determine remote from git config if not given Add ConfigConstants and expose branch..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 --- .../org/eclipse/jgit/api/PushCommandTest.java | 174 +++++++++++++++++- .../src/org/eclipse/jgit/api/PushCommand.java | 32 +++- .../org/eclipse/jgit/lib/BranchConfig.java | 12 ++ .../org/eclipse/jgit/lib/ConfigConstants.java | 14 ++ 4 files changed, 229 insertions(+), 3 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java index 3f8921c152..1a1f5b487e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java @@ -26,6 +26,7 @@ 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; +import org.eclipse.jgit.errors.NoRemoteRepositoryException; import org.eclipse.jgit.hooks.PrePushHook; import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.RepositoryTestCase; @@ -633,8 +634,7 @@ public class PushCommandTest extends RepositoryTestCase { config = git.getRepository().getConfig(); // Don't set remote, it'll default to "origin". Configure a - // different - // branch name; should be ignored. + // different branch name; should be ignored. config.setString("branch", "branchtopush", "merge", "refs/heads/upstreambranch"); config.save(); @@ -852,6 +852,176 @@ public class PushCommandTest extends RepositoryTestCase { } } + /** + * Check that branch..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..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 * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java index 0deb7ed29c..4f57f35127 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/PushCommand.java @@ -33,6 +33,7 @@ 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.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.NullProgressMonitor; import org.eclipse.jgit.lib.ProgressMonitor; @@ -58,7 +59,7 @@ import org.eclipse.jgit.transport.Transport; public class PushCommand extends TransportCommand> { - private String remote = Constants.DEFAULT_REMOTE_NAME; + private String remote; private final List refSpecs; @@ -114,6 +115,7 @@ public class PushCommand extends try { Config config = repo.getConfig(); + remote = determineRemote(config, remote); if (refSpecs.isEmpty()) { RemoteConfig rc = new RemoteConfig(config, getRemote()); @@ -181,6 +183,34 @@ public class PushCommand extends 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() throws IOException, DetachedHeadException { Ref head = repo.exactRef(Constants.HEAD); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java index 6da6f1204a..aa613d07eb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BranchConfig.java @@ -137,6 +137,18 @@ public class BranchConfig { 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 * diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java index 205999f8c9..42d8aa5447 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java @@ -329,6 +329,20 @@ public final class ConfigConstants { /** The "remote" key */ 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 */ public static final String CONFIG_KEY_MERGE = "merge"; -- 2.39.5