Initial EGit contribution to eclipse.org
[egit.git] / org.eclipse.egit.core / src / org / eclipse / egit / core / op / CloneOperation.java
blob57ea79b0e6df70f5a02efed99ad7118bf199460e
1 /*******************************************************************************
2 * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
3 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
4 * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br>
5 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
6 * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
8 * All rights reserved. This program and the accompanying materials
9 * are made available under the terms of the Eclipse Public License v1.0
10 * which accompanies this distribution, and is available at
11 * http://www.eclipse.org/legal/epl-v10.html
12 *******************************************************************************/
13 package org.eclipse.egit.core.op;
15 import java.io.File;
16 import java.io.IOException;
17 import java.lang.reflect.InvocationTargetException;
18 import java.net.URISyntaxException;
19 import java.util.Collection;
21 import org.eclipse.core.runtime.IProgressMonitor;
22 import org.eclipse.core.runtime.NullProgressMonitor;
23 import org.eclipse.core.runtime.SubProgressMonitor;
24 import org.eclipse.egit.core.CoreText;
25 import org.eclipse.egit.core.EclipseGitProgressTransformer;
26 import org.eclipse.jface.operation.IRunnableWithProgress;
27 import org.eclipse.osgi.util.NLS;
28 import org.eclipse.jgit.errors.NotSupportedException;
29 import org.eclipse.jgit.errors.TransportException;
30 import org.eclipse.jgit.lib.Commit;
31 import org.eclipse.jgit.lib.Constants;
32 import org.eclipse.jgit.lib.GitIndex;
33 import org.eclipse.jgit.lib.Ref;
34 import org.eclipse.jgit.lib.RefUpdate;
35 import org.eclipse.jgit.lib.Repository;
36 import org.eclipse.jgit.lib.RepositoryConfig;
37 import org.eclipse.jgit.lib.Tree;
38 import org.eclipse.jgit.lib.WorkDirCheckout;
39 import org.eclipse.jgit.transport.FetchResult;
40 import org.eclipse.jgit.transport.RefSpec;
41 import org.eclipse.jgit.transport.RemoteConfig;
42 import org.eclipse.jgit.transport.Transport;
43 import org.eclipse.jgit.transport.URIish;
45 /**
46 * Clones a repository from a remote location to a local location.
48 public class CloneOperation implements IRunnableWithProgress {
49 private final URIish uri;
51 private final boolean allSelected;
53 private final Collection<Ref> selectedBranches;
55 private final File workdir;
57 private final String branch;
59 private final String remoteName;
61 private Repository local;
63 private RemoteConfig remoteConfig;
65 private FetchResult fetchResult;
67 /**
68 * Create a new clone operation.
70 * @param uri
71 * remote we should fetch from.
72 * @param allSelected
73 * true when all branches have to be fetched (indicates wildcard
74 * in created fetch refspec), false otherwise.
75 * @param selectedBranches
76 * collection of branches to fetch. Ignored when allSelected is
77 * true.
78 * @param workdir
79 * working directory to clone to. The directory may or may not
80 * already exist.
81 * @param branch
82 * branch to initially clone from.
83 * @param remoteName
84 * name of created remote config as source remote (typically
85 * named "origin").
87 public CloneOperation(final URIish uri, final boolean allSelected,
88 final Collection<Ref> selectedBranches, final File workdir,
89 final String branch, final String remoteName) {
90 this.uri = uri;
91 this.allSelected = allSelected;
92 this.selectedBranches = selectedBranches;
93 this.workdir = workdir;
94 this.branch = branch;
95 this.remoteName = remoteName;
98 public void run(final IProgressMonitor pm)
99 throws InvocationTargetException, InterruptedException {
100 final IProgressMonitor monitor;
101 if (pm == null)
102 monitor = new NullProgressMonitor();
103 else
104 monitor = pm;
106 try {
107 monitor.beginTask(NLS.bind(CoreText.CloneOperation_title, uri),
108 5000);
109 try {
110 doInit(new SubProgressMonitor(monitor, 100));
111 doFetch(new SubProgressMonitor(monitor, 4000));
112 doCheckout(new SubProgressMonitor(monitor, 900));
113 } finally {
114 closeLocal();
116 } catch (final Exception e) {
117 delete(workdir);
118 if (monitor.isCanceled())
119 throw new InterruptedException();
120 else
121 throw new InvocationTargetException(e);
122 } finally {
123 monitor.done();
127 private void closeLocal() {
128 if (local != null) {
129 local.close();
130 local = null;
134 private void doInit(final IProgressMonitor monitor)
135 throws URISyntaxException, IOException {
136 monitor.setTaskName("Initializing local repository");
138 final File gitdir = new File(workdir, ".git");
139 local = new Repository(gitdir);
140 local.create();
141 local.writeSymref(Constants.HEAD, branch);
143 remoteConfig = new RemoteConfig(local.getConfig(), remoteName);
144 remoteConfig.addURI(uri);
146 final String dst = Constants.R_REMOTES + remoteConfig.getName();
147 RefSpec wcrs = new RefSpec();
148 wcrs = wcrs.setForceUpdate(true);
149 wcrs = wcrs.setSourceDestination(Constants.R_HEADS + "*", dst + "/*");
151 if (allSelected) {
152 remoteConfig.addFetchRefSpec(wcrs);
153 } else {
154 for (final Ref ref : selectedBranches)
155 if (wcrs.matchSource(ref))
156 remoteConfig.addFetchRefSpec(wcrs.expandFromSource(ref));
159 // we're setting up for a clone with a checkout
160 local.getConfig().setBoolean("core", null, "bare", false);
162 remoteConfig.update(local.getConfig());
164 // branch is like 'Constants.R_HEADS + branchName', we need only
165 // the 'branchName' part
166 String branchName = branch.substring(Constants.R_HEADS.length());
168 // setup the default remote branch for branchName
169 local.getConfig().setString(RepositoryConfig.BRANCH_SECTION,
170 branchName, "remote", remoteName);
171 local.getConfig().setString(RepositoryConfig.BRANCH_SECTION,
172 branchName, "merge", branch);
174 local.getConfig().save();
177 private void doFetch(final IProgressMonitor monitor)
178 throws NotSupportedException, TransportException {
179 final Transport tn = Transport.open(local, remoteConfig);
180 try {
181 final EclipseGitProgressTransformer pm;
182 pm = new EclipseGitProgressTransformer(monitor);
183 fetchResult = tn.fetch(pm, null);
184 } finally {
185 tn.close();
189 private void doCheckout(final IProgressMonitor monitor) throws IOException {
190 final Ref head = fetchResult.getAdvertisedRef(branch);
191 if (head == null || head.getObjectId() == null)
192 return;
194 final GitIndex index = new GitIndex(local);
195 final Commit mapCommit = local.mapCommit(head.getObjectId());
196 final Tree tree = mapCommit.getTree();
197 final RefUpdate u;
198 final WorkDirCheckout co;
200 u = local.updateRef(Constants.HEAD);
201 u.setNewObjectId(mapCommit.getCommitId());
202 u.forceUpdate();
204 monitor.setTaskName("Checking out files");
205 co = new WorkDirCheckout(local, local.getWorkDir(), index, tree);
206 co.checkout();
207 monitor.setTaskName("Writing index");
208 index.write();
211 private static void delete(final File d) {
212 if (d.isDirectory()) {
213 final File[] items = d.listFiles();
214 if (items != null) {
215 for (final File c : items)
216 delete(c);
219 d.delete();