diff options
Diffstat (limited to 'org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java')
-rw-r--r-- | org.eclipse.jgit.test/tst/org/eclipse/jgit/api/PushCommandTest.java | 1287 |
1 files changed, 1090 insertions, 197 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 19f074ea55..d1696d62a8 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 @@ -1,71 +1,55 @@ /* - * Copyright (C) 2010, 2014 Chris Aniszczyk <caniszczyk@gmail.com> - * and other copyright owners as documented in the project's IP log. + * Copyright (C) 2010, 2014 Chris Aniszczyk <caniszczyk@gmail.com> and others * - * 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 + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0 which is available at + * https://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. + * SPDX-License-Identifier: BSD-3-Clause */ package org.eclipse.jgit.api; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; +import java.io.PrintStream; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.time.Instant; 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; +import org.eclipse.jgit.errors.NoRemoteRepositoryException; +import org.eclipse.jgit.hooks.PrePushHook; +import org.eclipse.jgit.junit.JGitTestUtil; import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.RefUpdate; 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; import org.eclipse.jgit.transport.RemoteConfig; +import org.eclipse.jgit.transport.RemoteRefUpdate; import org.eclipse.jgit.transport.TrackingRefUpdate; import org.eclipse.jgit.transport.URIish; +import org.eclipse.jgit.util.FS; import org.junit.Test; public class PushCommandTest extends RepositoryTestCase { @@ -76,6 +60,12 @@ public class PushCommandTest extends RepositoryTestCase { // create other repository Repository db2 = createWorkRepository(); + addRepoToClose(db2); + final StoredConfig config2 = db2.getConfig(); + + // this tests that this config can be parsed properly + config2.setString("fsck", "", "missingEmail", "ignore"); + config2.save(); // setup the first repository final StoredConfig config = db.getConfig(); @@ -85,75 +75,165 @@ public class PushCommandTest extends RepositoryTestCase { remoteConfig.update(config); config.save(); - Git git1 = new Git(db); - // create some refs via commits and tag - RevCommit commit = git1.commit().setMessage("initial commit").call(); - Ref tagRef = git1.tag().setName("tag").call(); + try (Git git1 = new Git(db)) { + // create some refs via commits and tag + RevCommit commit = git1.commit().setMessage("initial commit").call(); + Ref tagRef = git1.tag().setName("tag").call(); - try { - db2.resolve(commit.getId().getName() + "^{commit}"); - fail("id shouldn't exist yet"); - } catch (MissingObjectException e) { - // we should get here - } + try { + db2.resolve(commit.getId().getName() + "^{commit}"); + fail("id shouldn't exist yet"); + } catch (MissingObjectException e) { + // we should get here + } - RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x"); - git1.push().setRemote("test").setRefSpecs(spec) - .call(); + RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x"); + git1.push().setRemote("test").setRefSpecs(spec) + .call(); - assertEquals(commit.getId(), - db2.resolve(commit.getId().getName() + "^{commit}")); - assertEquals(tagRef.getObjectId(), - db2.resolve(tagRef.getObjectId().getName())); + assertEquals(commit.getId(), + db2.resolve(commit.getId().getName() + "^{commit}")); + assertEquals(tagRef.getObjectId(), + db2.resolve(tagRef.getObjectId().getName())); + } } @Test - public void testTrackingUpdate() throws Exception { - Repository db2 = createBareRepository(); + public void testPrePushHook() throws JGitInternalException, IOException, + GitAPIException, URISyntaxException { - String remote = "origin"; - String branch = "refs/heads/master"; - String trackingBranch = "refs/remotes/" + remote + "/master"; + // create other repository + Repository db2 = createWorkRepository(); + + // setup the first repository + final StoredConfig config = db.getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish(db2.getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.update(config); + config.save(); + + File hookOutput = new File(getTemporaryDirectory(), "hookOutput"); + writeHookFile(PrePushHook.NAME, "#!/bin/sh\necho 1:$1, 2:$2, 3:$3 >\"" + + hookOutput.toPath() + "\"\ncat - >>\"" + hookOutput.toPath() + + "\"\nexit 0"); - Git git = new Git(db); + try (Git git1 = new Git(db)) { + // create a commit + RevCommit commit = git1.commit().setMessage("initial commit").call(); - RevCommit commit1 = git.commit().setMessage("Initial commit") - .call(); + RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x"); + git1.push().setRemote("test").setRefSpecs(spec).call(); + assertEquals("1:test, 2:" + uri + ", 3:\n" + "refs/heads/master " + + commit.getName() + " refs/heads/x " + + ObjectId.zeroId().name() + "\n", read(hookOutput)); + } + } - RefUpdate branchRefUpdate = db.updateRef(branch); - branchRefUpdate.setNewObjectId(commit1.getId()); - branchRefUpdate.update(); + @Test + public void testPrePushHookRedirects() throws JGitInternalException, + IOException, GitAPIException, URISyntaxException { - RefUpdate trackingBranchRefUpdate = db.updateRef(trackingBranch); - trackingBranchRefUpdate.setNewObjectId(commit1.getId()); - trackingBranchRefUpdate.update(); + // create other repository + Repository db2 = createWorkRepository(); + // setup the first repository final StoredConfig config = db.getConfig(); - RemoteConfig remoteConfig = new RemoteConfig(config, remote); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); URIish uri = new URIish(db2.getDirectory().toURI().toURL()); remoteConfig.addURI(uri); - remoteConfig.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/" - + remote + "/*")); remoteConfig.update(config); config.save(); + writeHookFile(PrePushHook.NAME, "#!/bin/sh\n" + + "echo \"1:$1, 2:$2, 3:$3\"\n" // to stdout + + "cat - 1>&2\n" // to stderr + + "exit 0\n"); - RevCommit commit2 = git.commit().setMessage("Commit to push").call(); + try (Git git1 = new Git(db)) { + // create a commit + RevCommit commit = git1.commit().setMessage("initial commit") + .call(); - RefSpec spec = new RefSpec(branch + ":" + branch); - Iterable<PushResult> resultIterable = git.push().setRemote(remote) - .setRefSpecs(spec).call(); + RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x"); + try (ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); + ByteArrayOutputStream errBytes = new ByteArrayOutputStream(); + PrintStream stdout = new PrintStream(outBytes, true, + StandardCharsets.UTF_8); + PrintStream stderr = new PrintStream(errBytes, true, + StandardCharsets.UTF_8)) { + git1.push() + .setRemote("test") + .setRefSpecs(spec) + .setHookOutputStream(stdout) + .setHookErrorStream(stderr) + .call(); + String out = outBytes.toString(StandardCharsets.UTF_8); + String err = errBytes.toString(StandardCharsets.UTF_8); + assertEquals("1:test, 2:" + uri + ", 3:\n", out); + assertEquals("refs/heads/master " + commit.getName() + + " refs/heads/x " + ObjectId.zeroId().name() + '\n', + err); + } + } + } - PushResult result = resultIterable.iterator().next(); - TrackingRefUpdate trackingRefUpdate = result - .getTrackingRefUpdate(trackingBranch); + private File writeHookFile(String name, String data) + throws IOException { + File path = new File(db.getWorkTree() + "/.git/hooks/", name); + JGitTestUtil.write(path, data); + FS.DETECTED.setExecute(path, true); + return path; + } - assertNotNull(trackingRefUpdate); - assertEquals(trackingBranch, trackingRefUpdate.getLocalName()); - assertEquals(branch, trackingRefUpdate.getRemoteName()); - assertEquals(commit2.getId(), trackingRefUpdate.getNewObjectId()); - assertEquals(commit2.getId(), db.resolve(trackingBranch)); - assertEquals(commit2.getId(), db2.resolve(branch)); + + @Test + public void testTrackingUpdate() throws Exception { + Repository db2 = createBareRepository(); + + String remote = "origin"; + String branch = "refs/heads/master"; + String trackingBranch = "refs/remotes/" + remote + "/master"; + + try (Git git = new Git(db)) { + RevCommit commit1 = git.commit().setMessage("Initial commit") + .call(); + + RefUpdate branchRefUpdate = db.updateRef(branch); + branchRefUpdate.setNewObjectId(commit1.getId()); + branchRefUpdate.update(); + + RefUpdate trackingBranchRefUpdate = db.updateRef(trackingBranch); + trackingBranchRefUpdate.setNewObjectId(commit1.getId()); + trackingBranchRefUpdate.update(); + + final StoredConfig config = db.getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, remote); + URIish uri = new URIish(db2.getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/" + + remote + "/*")); + remoteConfig.update(config); + config.save(); + + + RevCommit commit2 = git.commit().setMessage("Commit to push").call(); + + RefSpec spec = new RefSpec(branch + ":" + branch); + Iterable<PushResult> resultIterable = git.push().setRemote(remote) + .setRefSpecs(spec).call(); + + PushResult result = resultIterable.iterator().next(); + TrackingRefUpdate trackingRefUpdate = result + .getTrackingRefUpdate(trackingBranch); + + assertNotNull(trackingRefUpdate); + assertEquals(trackingBranch, trackingRefUpdate.getLocalName()); + assertEquals(branch, trackingRefUpdate.getRemoteName()); + assertEquals(commit2.getId(), trackingRefUpdate.getNewObjectId()); + assertEquals(commit2.getId(), db.resolve(trackingBranch)); + assertEquals(commit2.getId(), db2.resolve(branch)); + } } /** @@ -163,113 +243,872 @@ public class PushCommandTest extends RepositoryTestCase { */ @Test public void testPushRefUpdate() throws Exception { - Git git = new Git(db); - Git git2 = new Git(createBareRepository()); + 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.addPushRefSpec(new RefSpec("+refs/heads/*:refs/heads/*")); + remoteConfig.update(config); + config.save(); - 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.addPushRefSpec(new RefSpec("+refs/heads/*:refs/heads/*")); - remoteConfig.update(config); - config.save(); + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + RevCommit commit = git.commit().setMessage("adding f").call(); - writeTrashFile("f", "content of f"); - git.add().addFilepattern("f").call(); - RevCommit commit = git.commit().setMessage("adding f").call(); + assertEquals(null, git2.getRepository().resolve("refs/heads/master")); + git.push().setRemote("test").call(); + assertEquals(commit.getId(), + git2.getRepository().resolve("refs/heads/master")); - assertEquals(null, git2.getRepository().resolve("refs/heads/master")); - git.push().setRemote("test").call(); - assertEquals(commit.getId(), - git2.getRepository().resolve("refs/heads/master")); + git.branchCreate().setName("refs/heads/test").call(); + git.checkout().setName("refs/heads/test").call(); - git.branchCreate().setName("refs/heads/test").call(); - git.checkout().setName("refs/heads/test").call(); + for (int i = 0; i < 6; i++) { + writeTrashFile("f" + i, "content of f" + i); + git.add().addFilepattern("f" + i).call(); + commit = git.commit().setMessage("adding f" + i).call(); + git.push().setRemote("test").call(); + git2.getRepository().getRefDatabase().getRefs(); + assertEquals("failed to update on attempt " + i, commit.getId(), + git2.getRepository().resolve("refs/heads/test")); + } + } + } + /** + * Check that the push refspec is read from config. + * + * @throws Exception + */ + @Test + public void testPushWithRefSpecFromConfig() 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.addPushRefSpec(new RefSpec("HEAD:refs/heads/newbranch")); + remoteConfig.update(config); + config.save(); + + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + RevCommit commit = git.commit().setMessage("adding f").call(); - for (int i = 0; i < 6; i++) { - writeTrashFile("f" + i, "content of f" + i); - git.add().addFilepattern("f" + i).call(); - commit = git.commit().setMessage("adding f" + i).call(); + assertEquals(null, git2.getRepository().resolve("refs/heads/master")); git.push().setRemote("test").call(); - git2.getRepository().getAllRefs(); - assertEquals("failed to update on attempt " + i, commit.getId(), - git2.getRepository().resolve("refs/heads/test")); + assertEquals(commit.getId(), + git2.getRepository().resolve("refs/heads/newbranch")); + } + } + /** + * Check that only HEAD is pushed if no refspec is given. + * + * @throws Exception + */ + @Test + public void testPushWithoutPushRefSpec() 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(); + + 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").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")); } } /** - * Check that the push refspec is read from config. + * Check that pushing from a detached HEAD without refspec throws a + * DetachedHeadException. * * @throws Exception */ @Test - public void testPushWithRefSpecFromConfig() throws Exception { - Git git = new Git(db); - Git git2 = new Git(createBareRepository()); + 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(); - 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.addPushRefSpec(new RefSpec("HEAD:refs/heads/newbranch")); - remoteConfig.update(config); - config.save(); + writeTrashFile("f", "content of f"); + git.add().addFilepattern("f").call(); + RevCommit commit = git.commit().setMessage("adding f").call(); - 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()); + } + } - assertEquals(null, git2.getRepository().resolve("refs/heads/master")); - git.push().setRemote("test").call(); - assertEquals(commit.getId(), - git2.getRepository().resolve("refs/heads/newbranch")); + /** + * 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 only HEAD is pushed if no refspec is given. + * Check that push.default=matching without refspec pushes all matching + * branches. * * @throws Exception */ @Test - public void testPushWithoutPushRefSpec() throws Exception { - Git git = new Git(db); - Git git2 = new Git(createBareRepository()); + 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(); - 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(); + + 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")); + // push master and branchtopush + git.push().setRemote("test").setRefSpecs( + new RefSpec("refs/heads/master:refs/heads/master"), + new RefSpec( + "refs/heads/branchtopush:refs/heads/branchtopush")) + .call(); + assertEquals(commit.getId(), + git2.getRepository().resolve("refs/heads/master")); + assertEquals(commit.getId(), + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + // Create two different commits on these two branches + writeTrashFile("b", "on branchtopush"); + git.add().addFilepattern("b").call(); + RevCommit bCommit = git.commit().setMessage("on branchtopush") + .call(); + git.checkout().setName("master").call(); + writeTrashFile("m", "on master"); + git.add().addFilepattern("m").call(); + RevCommit mCommit = git.commit().setMessage("on master").call(); + // Now push with mode "matching": should push both branches. + Iterable<PushResult> result = git.push().setRemote("test") + .setPushDefault(PushDefault.MATCHING) + .call(); + int n = 0; + for (PushResult r : result) { + n++; + assertEquals(1, n); + assertEquals(2, r.getRemoteUpdates().size()); + for (RemoteRefUpdate update : r.getRemoteUpdates()) { + assertFalse(update.isMatching()); + assertTrue(update.getSrcRef() + .equals("refs/heads/branchtopush") + || update.getSrcRef().equals("refs/heads/master")); + assertEquals(RemoteRefUpdate.Status.OK, update.getStatus()); + } + } + assertEquals(bCommit.getId(), + git2.getRepository().resolve("refs/heads/branchtopush")); + assertEquals(null, + git2.getRepository().resolve("refs/heads/not-pushed")); + assertEquals(mCommit.getId(), + git2.getRepository().resolve("refs/heads/master")); + assertEquals(bCommit.getId(), git.getRepository() + .resolve("refs/remotes/origin/branchtopush")); + assertEquals(null, git.getRepository() + .resolve("refs/remotes/origin/not-pushed")); + assertEquals(mCommit.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(); - RevCommit commit = 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")); - git.push().setRemote("test").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")); + 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 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")); + } } /** @@ -281,6 +1120,7 @@ public class PushCommandTest extends RepositoryTestCase { public void testPushAfterGC() throws Exception { // create other repository Repository db2 = createWorkRepository(); + addRepoToClose(db2); // setup the first repository final StoredConfig config = db.getConfig(); @@ -290,51 +1130,104 @@ public class PushCommandTest extends RepositoryTestCase { remoteConfig.update(config); config.save(); - Git git1 = new Git(db); - Git git2 = new Git(db2); + try (Git git1 = new Git(db); + Git git2 = new Git(db2)) { + // push master (with a new commit) to the remote + git1.commit().setMessage("initial commit").call(); - // push master (with a new commit) to the remote - git1.commit().setMessage("initial commit").call(); + RefSpec spec = new RefSpec("refs/heads/*:refs/heads/*"); + git1.push().setRemote("test").setRefSpecs(spec).call(); - RefSpec spec = new RefSpec("refs/heads/*:refs/heads/*"); - git1.push().setRemote("test").setRefSpecs(spec).call(); + // create an unrelated ref and a commit on our remote + git2.branchCreate().setName("refs/heads/other").call(); + git2.checkout().setName("refs/heads/other").call(); - // create an unrelated ref and a commit on our remote - git2.branchCreate().setName("refs/heads/other").call(); - git2.checkout().setName("refs/heads/other").call(); + writeTrashFile("a", "content of a"); + git2.add().addFilepattern("a").call(); + RevCommit commit2 = git2.commit().setMessage("adding a").call(); - writeTrashFile("a", "content of a"); - git2.add().addFilepattern("a").call(); - RevCommit commit2 = git2.commit().setMessage("adding a").call(); + // run a gc to ensure we have a bitmap index + Properties res = git1.gc().setExpire((Instant) null).call(); + assertEquals(8, res.size()); - // run a gc to ensure we have a bitmap index - Properties res = git1.gc().setExpire(null).call(); - assertEquals(7, res.size()); + // create another commit so we have something else to push + writeTrashFile("b", "content of b"); + git1.add().addFilepattern("b").call(); + RevCommit commit3 = git1.commit().setMessage("adding b").call(); - // create another commit so we have something else to push - writeTrashFile("b", "content of b"); - git1.add().addFilepattern("b").call(); - RevCommit commit3 = git1.commit().setMessage("adding b").call(); + try { + // Re-run the push. Failure may happen here. + git1.push().setRemote("test").setRefSpecs(spec).call(); + } catch (TransportException e) { + assertTrue("should be caused by a MissingObjectException", e + .getCause().getCause() instanceof MissingObjectException); + fail("caught MissingObjectException for a change we don't have"); + } - try { - // Re-run the push. Failure may happen here. - git1.push().setRemote("test").setRefSpecs(spec).call(); - } catch (TransportException e) { - assertTrue("should be caused by a MissingObjectException", e - .getCause().getCause() instanceof MissingObjectException); - fail("caught MissingObjectException for a change we don't have"); + // Remote will have both a and b. Master will have only b + try { + db.resolve(commit2.getId().getName() + "^{commit}"); + fail("id shouldn't exist locally"); + } catch (MissingObjectException e) { + // we should get here + } + assertEquals(commit2.getId(), + db2.resolve(commit2.getId().getName() + "^{commit}")); + assertEquals(commit3.getId(), + db2.resolve(commit3.getId().getName() + "^{commit}")); } + } + + @Test + public void testPushWithLease() throws JGitInternalException, IOException, + GitAPIException, URISyntaxException { + + // create other repository + Repository db2 = createWorkRepository(); + addRepoToClose(db2); + + // setup the first repository + final StoredConfig config = db.getConfig(); + RemoteConfig remoteConfig = new RemoteConfig(config, "test"); + URIish uri = new URIish(db2.getDirectory().toURI().toURL()); + remoteConfig.addURI(uri); + remoteConfig.update(config); + config.save(); + + try (Git git1 = new Git(db)) { + // create one commit and push it + RevCommit commit = git1.commit().setMessage("initial commit").call(); + git1.branchCreate().setName("initial").call(); + + RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x"); + git1.push().setRemote("test").setRefSpecs(spec) + .call(); + + assertEquals(commit.getId(), + db2.resolve(commit.getId().getName() + "^{commit}")); + //now try to force-push a new commit, with a good lease + + git1.commit().setMessage("second commit").call(); + Iterable<PushResult> results = + git1.push().setRemote("test").setRefSpecs(spec) + .setRefLeaseSpecs(new RefLeaseSpec("refs/heads/x", "initial")) + .call(); + for (PushResult result : results) { + RemoteRefUpdate update = result.getRemoteUpdate("refs/heads/x"); + assertEquals(update.getStatus(), RemoteRefUpdate.Status.OK); + } + + git1.commit().setMessage("third commit").call(); + //now try to force-push a new commit, with a bad lease - // Remote will have both a and b. Master will have only b - try { - db.resolve(commit2.getId().getName() + "^{commit}"); - fail("id shouldn't exist locally"); - } catch (MissingObjectException e) { - // we should get here + results = + git1.push().setRemote("test").setRefSpecs(spec) + .setRefLeaseSpecs(new RefLeaseSpec("refs/heads/x", "initial")) + .call(); + for (PushResult result : results) { + RemoteRefUpdate update = result.getRemoteUpdate("refs/heads/x"); + assertEquals(update.getStatus(), RemoteRefUpdate.Status.REJECTED_REMOTE_CHANGED); + } } - assertEquals(commit2.getId(), - db2.resolve(commit2.getId().getName() + "^{commit}")); - assertEquals(commit3.getId(), - db2.resolve(commit3.getId().getName() + "^{commit}")); } } |