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 7f20bb8f1..19f074ea5 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,5 +1,5 @@ /* - * Copyright (C) 2010, Chris Aniszczyk + * Copyright (C) 2010, 2014 Chris Aniszczyk * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available @@ -44,13 +44,16 @@ package org.eclipse.jgit.api; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.net.URISyntaxException; +import java.util.Properties; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.JGitInternalException; +import org.eclipse.jgit.api.errors.TransportException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.lib.Ref; @@ -268,4 +271,70 @@ public class PushCommandTest extends RepositoryTestCase { assertEquals(null, git2.getRepository().resolve("refs/heads/master")); } + + /** + * Check that missing refs don't cause errors during push + * + * @throws Exception + */ + @Test + public void testPushAfterGC() throws Exception { + // 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(); + + 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(); + + 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(); + + 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(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(); + + 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}")); + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java index def6033b8..e367ab44c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/BasePackPushConnection.java @@ -283,8 +283,12 @@ public abstract class BasePackPushConnection extends BasePackConnection implemen local.newObjectReader()); try { - for (final Ref r : getRefs()) - remoteObjects.add(r.getObjectId()); + for (final Ref r : getRefs()) { + // only add objects that we actually have + ObjectId oid = r.getObjectId(); + if (local.hasObject(oid)) + remoteObjects.add(oid); + } remoteObjects.addAll(additionalHaves); for (final RemoteRefUpdate r : refUpdates.values()) { if (!ObjectId.zeroId().equals(r.getNewObjectId()))