Browse Source

Implement mirror option in CloneCommand

Bug: 552173
Change-Id: If79adf578b303890314a3285d7a6d2c71f48d091
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
next
Matthias Sohn 5 years ago
parent
commit
6216b0de8a
  1. 34
      org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java
  2. 92
      org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java

34
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CloneCommandTest.java

@ -67,6 +67,7 @@ import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
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.RevBlob; import org.eclipse.jgit.revwalk.RevBlob;
@ -96,10 +97,15 @@ public class CloneCommandTest extends RepositoryTestCase {
writeTrashFile("Test.txt", "Hello world"); writeTrashFile("Test.txt", "Hello world");
git.add().addFilepattern("Test.txt").call(); git.add().addFilepattern("Test.txt").call();
git.commit().setMessage("Initial commit").call(); git.commit().setMessage("Initial commit").call();
git.tag().setName("tag-initial").setMessage("Tag initial").call(); Ref head = git.tag().setName("tag-initial").setMessage("Tag initial")
.call();
// create a test branch and switch to it // create a test branch and switch to it
git.checkout().setCreateBranch(true).setName("test").call(); git.checkout().setCreateBranch(true).setName("test").call();
// create a non-standard ref
RefUpdate ru = db.updateRef("refs/meta/foo/bar");
ru.setNewObjectId(head.getObjectId());
ru.update();
// commit something on the test branch // commit something on the test branch
writeTrashFile("Test.txt", "Some change"); writeTrashFile("Test.txt", "Some change");
@ -424,6 +430,32 @@ public class CloneCommandTest extends RepositoryTestCase {
specs.get(0)); specs.get(0));
} }
@Test
public void testBareCloneRepositoryMirror() throws Exception {
File directory = createTempDirectory(
"testCloneRepositoryWithBranch_mirror");
CloneCommand command = Git.cloneRepository();
command.setBranch("refs/heads/master");
command.setMirror(true); // implies bare repository
command.setDirectory(directory);
command.setURI(fileUri());
Git git2 = command.call();
addRepoToClose(git2.getRepository());
assertNotNull(git2);
assertNotNull(git2.getRepository().resolve("tag-for-blob"));
assertNotNull(git2.getRepository().resolve("tag-initial"));
assertEquals(git2.getRepository().getFullBranch(), "refs/heads/master");
assertEquals("refs/heads/master, refs/heads/test", allRefNames(
git2.branchList().setListMode(ListMode.ALL).call()));
assertNotNull(git2.getRepository().exactRef("refs/meta/foo/bar"));
RemoteConfig cfg = new RemoteConfig(git2.getRepository().getConfig(),
Constants.DEFAULT_REMOTE_NAME);
List<RefSpec> specs = cfg.getFetchRefSpecs();
assertEquals(1, specs.size());
assertEquals(new RefSpec("+refs/*:refs/*"),
specs.get(0));
}
@Test @Test
public void testCloneRepositoryOnlyOneTag() throws Exception { public void testCloneRepositoryOnlyOneTag() throws Exception {
File directory = createTempDirectory("testCloneRepositoryWithBranch"); File directory = createTempDirectory("testCloneRepositoryWithBranch");

92
org.eclipse.jgit/src/org/eclipse/jgit/api/CloneCommand.java

@ -77,8 +77,8 @@ 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.TagOpt;
import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.FileUtils;
/** /**
* Clone a repository into a new working directory * Clone a repository into a new working directory
@ -104,7 +104,7 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
private ProgressMonitor monitor = NullProgressMonitor.INSTANCE; private ProgressMonitor monitor = NullProgressMonitor.INSTANCE;
private boolean cloneAllBranches; private FETCH_TYPE fetchType = FETCH_TYPE.ALL_BRANCHES;
private boolean cloneSubmodules; private boolean cloneSubmodules;
@ -118,6 +118,10 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
private boolean gitDirExistsInitially; private boolean gitDirExistsInitially;
private enum FETCH_TYPE {
MULTIPLE_BRANCHES, ALL_BRANCHES, MIRROR
}
/** /**
* Callback for status of clone operation. * Callback for status of clone operation.
* *
@ -282,12 +286,11 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
RemoteConfig config = new RemoteConfig(clonedRepo.getConfig(), remote); RemoteConfig config = new RemoteConfig(clonedRepo.getConfig(), remote);
config.addURI(u); config.addURI(u);
final String dst = (bare ? Constants.R_HEADS : Constants.R_REMOTES boolean fetchAll = fetchType == FETCH_TYPE.ALL_BRANCHES
+ config.getName() + '/') + '*'; || fetchType == FETCH_TYPE.MIRROR;
boolean fetchAll = cloneAllBranches || branchesToClone == null
|| branchesToClone.isEmpty();
config.setFetchRefSpecs(calculateRefSpecs(fetchAll, dst)); config.setFetchRefSpecs(calculateRefSpecs(fetchType, config.getName()));
config.setMirror(fetchType == FETCH_TYPE.MIRROR);
config.update(clonedRepo.getConfig()); config.update(clonedRepo.getConfig());
clonedRepo.getConfig().save(); clonedRepo.getConfig().save();
@ -302,26 +305,33 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
return command.call(); return command.call();
} }
private List<RefSpec> calculateRefSpecs(boolean fetchAll, String dst) { private List<RefSpec> calculateRefSpecs(FETCH_TYPE type,
RefSpec heads = new RefSpec(); String remoteName) {
heads = heads.setForceUpdate(true);
heads = heads.setSourceDestination(Constants.R_HEADS + '*', dst);
List<RefSpec> specs = new ArrayList<>(); List<RefSpec> specs = new ArrayList<>();
if (!fetchAll) { if (type == FETCH_TYPE.MIRROR) {
RefSpec tags = new RefSpec(); specs.add(new RefSpec().setForceUpdate(true).setSourceDestination(
tags = tags.setForceUpdate(true); Constants.R_REFS + '*', Constants.R_REFS + '*'));
tags = tags.setSourceDestination(Constants.R_TAGS + '*', } else {
Constants.R_TAGS + '*'); RefSpec heads = new RefSpec();
for (String selectedRef : branchesToClone) { heads = heads.setForceUpdate(true);
if (heads.matchSource(selectedRef)) { final String dst = (bare ? Constants.R_HEADS
specs.add(heads.expandFromSource(selectedRef)); : Constants.R_REMOTES + remoteName + '/') + '*';
} else if (tags.matchSource(selectedRef)) { heads = heads.setSourceDestination(Constants.R_HEADS + '*', dst);
specs.add(tags.expandFromSource(selectedRef)); if (type == FETCH_TYPE.MULTIPLE_BRANCHES) {
RefSpec tags = new RefSpec().setForceUpdate(true)
.setSourceDestination(Constants.R_TAGS + '*',
Constants.R_TAGS + '*');
for (String selectedRef : branchesToClone) {
if (heads.matchSource(selectedRef)) {
specs.add(heads.expandFromSource(selectedRef));
} else if (tags.matchSource(selectedRef)) {
specs.add(tags.expandFromSource(selectedRef));
}
} }
} else {
// We'll fetch the tags anyway.
specs.add(heads);
} }
} else {
// We'll fetch the tags anyway.
specs.add(heads);
} }
return specs; return specs;
} }
@ -609,7 +619,31 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
* @return {@code this} * @return {@code this}
*/ */
public CloneCommand setCloneAllBranches(boolean cloneAllBranches) { public CloneCommand setCloneAllBranches(boolean cloneAllBranches) {
this.cloneAllBranches = cloneAllBranches; this.fetchType = cloneAllBranches ? FETCH_TYPE.ALL_BRANCHES
: this.fetchType;
return this;
}
/**
* Set up a mirror of the source repository. This implies that a bare
* repository will be created. Compared to {@link #setBare},
* {@code #setMirror} not only maps local branches of the source to local
* branches of the target, it maps all refs (including remote-tracking
* branches, notes etc.) and sets up a refspec configuration such that all
* these refs are overwritten by a git remote update in the target
* repository.
*
* @param mirror
* whether to mirror all refs from the source repository
*
* @return {@code this}
* @since 5.6
*/
public CloneCommand setMirror(boolean mirror) {
if (mirror) {
this.fetchType = FETCH_TYPE.MIRROR;
setBare(true);
}
return this; return this;
} }
@ -641,7 +675,13 @@ public class CloneCommand extends TransportCommand<CloneCommand, Git> {
* @return {@code this} * @return {@code this}
*/ */
public CloneCommand setBranchesToClone(Collection<String> branchesToClone) { public CloneCommand setBranchesToClone(Collection<String> branchesToClone) {
this.branchesToClone = branchesToClone; if (branchesToClone == null || branchesToClone.isEmpty()) {
// fallback to default
fetchType = FETCH_TYPE.ALL_BRANCHES;
} else {
this.fetchType = FETCH_TYPE.MULTIPLE_BRANCHES;
this.branchesToClone = branchesToClone;
}
return this; return this;
} }

Loading…
Cancel
Save