/*******************************************************************************
* Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
* Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
* Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br>
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
* Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
* Copyright (C) 2017, Thomas Wolf <thomas.wolf@paranor.ch>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.egit.core.op;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.egit.core.EclipseGitProgressTransformer;
import org.eclipse.egit.core.internal.CoreText;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.FileUtils;
import org.eclipse.osgi.util.NLS;
/**
* Clones a repository from a remote location to a local location.
*/
public class CloneOperation {
private final URIish uri;
private final boolean allSelected;
private boolean cloneSubmodules;
private final Collection<Ref> selectedBranches;
private final File workdir;
private final File gitdir;
private final String refName;
private final String remoteName;
private final int timeout;
private CredentialsProvider credentialsProvider;
private final List<PostCloneTask> postCloneTasks = new CopyOnWriteArrayList<>();
/**
* Create a new clone operation.
*
* @param uri
* remote we should fetch from.
* @param allSelected
* true when all branches have to be fetched (indicates wildcard
* in created fetch refspec), false otherwise.
* @param selectedBranches
* collection of branches to fetch. Ignored when allSelected is
* true.
* @param workdir
* working directory to clone to. The directory may or may not
* already exist.
* @param refName
* name of ref (usually tag or branch) to be checked out after
* clone, e.g. full <code>refs/heads/master</code> or short
* <code>v3.1.0</code>, or null for no checkout
* @param remoteName
* name of created remote config as source remote (typically
* named "origin").
* @param timeout
* timeout in seconds
*/
public CloneOperation(final URIish uri, final boolean allSelected,
final Collection<Ref> selectedBranches, final File workdir,
final String refName, final String remoteName, int timeout) {
this.uri = uri;
this.allSelected = allSelected;
this.selectedBranches = selectedBranches;
this.workdir = workdir;
this.gitdir = new File(workdir, Constants.DOT_GIT);
this.refName = refName;
this.remoteName = remoteName;
this.timeout = timeout;
}
/**
* Sets a credentials provider
* @param credentialsProvider
*/
public void setCredentialsProvider(CredentialsProvider credentialsProvider) {
this.credentialsProvider = credentialsProvider;
}
/**
* @param cloneSubmodules
* true to initialize and update submodules
*/
public void setCloneSubmodules(boolean cloneSubmodules) {
this.cloneSubmodules = cloneSubmodules;
}
/**
* @param monitor
* the monitor to be used for reporting progress and responding
* to cancellation. The monitor is never <code>null</code>
* @throws InvocationTargetException
* @throws InterruptedException
*/
public void run(final IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException {
String title = NLS.bind(CoreText.CloneOperation_title, uri);
SubMonitor progress = SubMonitor.convert(monitor, title,
postCloneTasks.isEmpty() ? 10 : 11);
EclipseGitProgressTransformer gitMonitor = new EclipseGitProgressTransformer(
progress.newChild(10));
CloneCommand.Callback callback = new CloneCommand.Callback() {
@Override
public void initializedSubmodules(Collection<String> submodules) {
// Nothing to do
}
@Override
public void cloningSubmodule(String path) {
progress.setTaskName(NLS.bind(
CoreText.CloneOperation_submodule_title, uri, path));
}
@Override
public void checkingOut(AnyObjectId commit, String path) {
// Nothing to do
}
};
Repository repository = null;
try {
CloneCommand cloneRepository = Git.cloneRepository();
cloneRepository.setCredentialsProvider(credentialsProvider);
if (refName != null) {
cloneRepository.setBranch(refName);
} else {
cloneRepository.setNoCheckout(true);
}
cloneRepository.setDirectory(workdir);
cloneRepository.setProgressMonitor(gitMonitor);
cloneRepository.setRemote(remoteName);
cloneRepository.setURI(uri.toString());
cloneRepository.setTimeout(timeout);
cloneRepository.setCloneAllBranches(allSelected);
cloneRepository.setCloneSubmodules(cloneSubmodules);
if (cloneSubmodules) {
cloneRepository.setCallback(callback);
}
if (selectedBranches != null) {
List<String> branches = new ArrayList<String>();
for (Ref branch : selectedBranches) {
branches.add(branch.getName());
}
cloneRepository.setBranchesToClone(branches);
}
Git git = cloneRepository.call();
repository = git.getRepository();
if (!postCloneTasks.isEmpty()) {
progress.setTaskName(title);
progress.setWorkRemaining(postCloneTasks.size());
progress.subTask(CoreText.CloneOperation_configuring);
for (PostCloneTask task : postCloneTasks) {
task.execute(repository, progress.newChild(1));
}
}
} catch (final Exception e) {
try {
if (repository != null) {
repository.close();
repository = null;
}
FileUtils.delete(workdir, FileUtils.RECURSIVE);
} catch (IOException ioe) {
throw new InvocationTargetException(e, NLS.bind(
CoreText.CloneOperation_failed_cleanup,
ioe.getLocalizedMessage()));
}
if (monitor.isCanceled()) {
throw new InterruptedException();
} else {
throw new InvocationTargetException(e);
}
} finally {
if (repository != null) {
repository.close();
}
}
}
/**
* @return The git directory which will contain the repository
*/
public File getGitDir() {
return gitdir;
}
/**
* @param task to be performed after clone
*/
public void addPostCloneTask(PostCloneTask task) {
postCloneTasks.add(task);
}
/**
* A task which can be added to be performed after clone
*/
public interface PostCloneTask {
/**
* Executes the task
* @param repository the cloned git repository
*
* @param monitor
* a progress monitor, or <code>null</code> if progress reporting
* and cancellation are not desired
* @throws CoreException
*/
void execute(Repository repository, IProgressMonitor monitor) throws CoreException;
}
}