Only fetch tags that do not exist locally with auto-follow

This corresponds to what C Git does, quoting from the fetch man page:

  This is done by first fetching from the remote using the given
  <refspec>s, and if the repository has objects that are pointed by
  remote tags that it does not yet have, then fetch those missing tags.

Before, JGit would also fetch tags that exist locally but point to a
different object, resulting in REJECTED results for these.

Also add some test cases to cover more cases.

Bug: 388095
Change-Id: Ib03d2d82e9c4b60179d626cfd5174be1da6388b2
Also-by: Stefan Lay <stefan.lay@sap.com>
This commit is contained in:
Robin Stocker 2013-04-27 16:14:46 +02:00
parent 7be5cfa4d6
commit 68b378a4b5
2 changed files with 95 additions and 28 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> * Copyright (C) 2010, 2013 Chris Aniszczyk <caniszczyk@gmail.com>
* and other copyright owners as documented in the project's IP log. * and other copyright owners as documented in the project's IP log.
* *
* This program and the accompanying materials are made available * This program and the accompanying materials are made available
@ -43,55 +43,129 @@
package org.eclipse.jgit.api; package org.eclipse.jgit.api;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.io.IOException; import java.io.IOException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Collection;
import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.junit.RepositoryTestCase; import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig; import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.TagOpt;
import org.eclipse.jgit.transport.TrackingRefUpdate;
import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.transport.URIish;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
public class FetchCommandTest extends RepositoryTestCase { public class FetchCommandTest extends RepositoryTestCase {
@Test private Git git;
public void testFetch() throws JGitInternalException, IOException, private Git remoteGit;
GitAPIException, URISyntaxException {
@Before
public void setupRemoteRepository() throws IOException, URISyntaxException {
git = new Git(db);
// create other repository // create other repository
Repository db2 = createWorkRepository(); Repository remoteRepository = createWorkRepository();
Git git2 = new Git(db2); remoteGit = new Git(remoteRepository);
// setup the first repository to fetch from the second repository // setup the first repository to fetch from the second repository
final StoredConfig config = db.getConfig(); final StoredConfig config = db.getConfig();
RemoteConfig remoteConfig = new RemoteConfig(config, "test"); RemoteConfig remoteConfig = new RemoteConfig(config, "test");
URIish uri = new URIish(db2.getDirectory().toURI().toURL()); URIish uri = new URIish(remoteRepository.getDirectory().toURI().toURL());
remoteConfig.addURI(uri); remoteConfig.addURI(uri);
remoteConfig.update(config); remoteConfig.update(config);
config.save(); config.save();
}
@Test
public void testFetch() throws JGitInternalException, IOException,
GitAPIException {
// create some refs via commits and tag // create some refs via commits and tag
RevCommit commit = git2.commit().setMessage("initial commit").call(); RevCommit commit = remoteGit.commit().setMessage("initial commit").call();
Ref tagRef = git2.tag().setName("tag").call(); Ref tagRef = remoteGit.tag().setName("tag").call();
Git git1 = new Git(db);
RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x"); RefSpec spec = new RefSpec("refs/heads/master:refs/heads/x");
git1.fetch().setRemote("test").setRefSpecs(spec) git.fetch().setRemote("test").setRefSpecs(spec)
.call(); .call();
assertEquals(commit.getId(), assertEquals(commit.getId(),
db.resolve(commit.getId().getName() + "^{commit}")); db.resolve(commit.getId().getName() + "^{commit}"));
assertEquals(tagRef.getObjectId(), assertEquals(tagRef.getObjectId(),
db.resolve(tagRef.getObjectId().getName())); db.resolve(tagRef.getObjectId().getName()));
} }
@Test
public void fetchShouldAutoFollowTag() throws Exception {
remoteGit.commit().setMessage("commit").call();
Ref tagRef = remoteGit.tag().setName("foo").call();
RefSpec spec = new RefSpec("refs/heads/*:refs/remotes/origin/*");
git.fetch().setRemote("test").setRefSpecs(spec)
.setTagOpt(TagOpt.AUTO_FOLLOW).call();
assertEquals(tagRef.getObjectId(), db.resolve("foo"));
}
@Test
public void fetchShouldAutoFollowTagForFetchedObjects() throws Exception {
remoteGit.commit().setMessage("commit").call();
Ref tagRef = remoteGit.tag().setName("foo").call();
remoteGit.commit().setMessage("commit2").call();
RefSpec spec = new RefSpec("refs/heads/*:refs/remotes/origin/*");
git.fetch().setRemote("test").setRefSpecs(spec)
.setTagOpt(TagOpt.AUTO_FOLLOW).call();
assertEquals(tagRef.getObjectId(), db.resolve("foo"));
}
@Test
public void fetchShouldNotFetchTagsFromOtherBranches() throws Exception {
remoteGit.commit().setMessage("commit").call();
remoteGit.checkout().setName("other").setCreateBranch(true).call();
remoteGit.commit().setMessage("commit2").call();
remoteGit.tag().setName("foo").call();
RefSpec spec = new RefSpec(
"refs/heads/master:refs/remotes/origin/master");
git.fetch().setRemote("test").setRefSpecs(spec)
.setTagOpt(TagOpt.AUTO_FOLLOW).call();
assertNull(db.resolve("foo"));
}
@Test
public void fetchWithUpdatedTagShouldNotTryToUpdateLocal() throws Exception {
final String tagName = "foo";
remoteGit.commit().setMessage("commit").call();
Ref tagRef = remoteGit.tag().setName(tagName).call();
ObjectId originalId = tagRef.getObjectId();
RefSpec spec = new RefSpec("refs/heads/*:refs/remotes/origin/*");
git.fetch().setRemote("test").setRefSpecs(spec)
.setTagOpt(TagOpt.AUTO_FOLLOW).call();
assertEquals(originalId, db.resolve(tagName));
remoteGit.commit().setMessage("commit 2").call();
remoteGit.tag().setName(tagName).setForceUpdate(true).call();
FetchResult result = git.fetch().setRemote("test").setRefSpecs(spec)
.setTagOpt(TagOpt.AUTO_FOLLOW).call();
Collection<TrackingRefUpdate> refUpdates = result
.getTrackingRefUpdates();
assertEquals(1, refUpdates.size());
TrackingRefUpdate update = refUpdates.iterator().next();
assertEquals("refs/heads/master", update.getRemoteName());
assertEquals(originalId, db.resolve(tagName));
}
} }

View File

@ -382,23 +382,16 @@ private Collection<Ref> expandAutoFollowTags() throws TransportException {
continue; continue;
Ref local = haveRefs.get(r.getName()); Ref local = haveRefs.get(r.getName());
ObjectId obj = r.getObjectId(); if (local != null)
// We already have a tag with this name, don't fetch it (even if
if (r.getPeeledObjectId() == null) { // the local is different).
if (local != null && obj.equals(local.getObjectId()))
continue;
if (askFor.containsKey(obj) || transport.local.hasObject(obj))
wantTag(r);
else
additionalTags.add(r);
continue; continue;
}
if (local != null) { ObjectId obj = r.getPeeledObjectId();
if (!obj.equals(local.getObjectId())) if (obj == null)
wantTag(r); obj = r.getObjectId();
} else if (askFor.containsKey(r.getPeeledObjectId())
|| transport.local.hasObject(r.getPeeledObjectId())) if (askFor.containsKey(obj) || transport.local.hasObject(obj))
wantTag(r); wantTag(r);
else else
additionalTags.add(r); additionalTags.add(r);