Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 99e4227296d0454b0491ed0f987ab18f5adfde4c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/*******************************************************************************
 * 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;
	}
}

Back to the top