git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[EGIT PATCH 20/31] Move clone logic away from GitCloneWizard to CloneOperation

From
MZMarek Zawirski <marek.zawirski@gmail.com>
Date
Aug 17, 2008, 20:44 UTC
Message-ID
<1219005852-21496-21-git-send-email-marek.zawirski@gmail.com>
In-Reply-To
<1219005852-21496-20-git-send-email-marek.zawirski@gmail.com>

Clone logic was unnaturally splitted between GitCloneWizard and CloneOperation classes. Now, repository initialization part is moved to CloneOperation, so there is cleaner separation of responsibilties.

Failure handling is also improved, incompletely cloned repository directory is removed before reporting problem to user.

Directory creation is problematic issue because of potential errors, so it's keeped in GitCloneWizard.

Signed-off-by: Marek Zawirski <marek.zawirski@gmail.com>
---
 .../org/spearce/egit/core/op/CloneOperation.java   |  129 +++++++++++++++++---
 .../src/org/spearce/egit/ui/UIText.java            |    4 +
 .../egit/ui/internal/clone/GitCloneWizard.java     |   78 +++----------
 .../src/org/spearce/egit/ui/uitext.properties      |    1 +
 4 files changed, 130 insertions(+), 82 deletions(-)
diff --git a/org.spearce.egit.core/src/org/spearce/egit/core/op/CloneOperation.java b/org.spearce.egit.core/src/org/spearce/egit/core/op/CloneOperation.java
index 656f3cb..531045b 100644
--- a/org.spearce.egit.core/src/org/spearce/egit/core/op/CloneOperation.java
+++ b/org.spearce.egit.core/src/org/spearce/egit/core/op/CloneOperation.java
@@ -3,6 +3,7 @@
  * 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>
  *
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
@@ -10,8 +11,11 @@
  *******************************************************************************/
 package org.spearce.egit.core.op;
 
+import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
+import java.net.URISyntaxException;
+import java.util.Collection;
 
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.NullProgressMonitor;
@@ -31,39 +35,71 @@ import org.spearce.jgit.lib.Repository;
 import org.spearce.jgit.lib.Tree;
 import org.spearce.jgit.lib.WorkDirCheckout;
 import org.spearce.jgit.transport.FetchResult;
+import org.spearce.jgit.transport.RefSpec;
 import org.spearce.jgit.transport.RemoteConfig;
 import org.spearce.jgit.transport.Transport;
+import org.spearce.jgit.transport.URIish;
 
 /**
  * Clones a repository from a remote location to a local location.
  */
 public class CloneOperation implements IRunnableWithProgress {
-	private final Repository local;
+	private static final String HEADS_PREFIX = Constants.HEADS_PREFIX;
 
-	private final RemoteConfig remote;
+	private static final String REMOTES_PREFIX_S = Constants.REMOTES_PREFIX
+			+ "/";
+
+	private final URIish uri;
+
+	private final boolean allSelected;
+
+	private final Collection<Ref> selectedBranches;
+
+	private final File workdir;
 
 	private final String branch;
 
+	private final String remoteName;
+
+	private Repository local;
+
+	private RemoteConfig remoteConfig;
+
 	private FetchResult fetchResult;
 
 	/**
 	 * Create a new clone operation.
 	 * 
-	 * @param r
-	 *            repository the checkout will happen within.
-	 * @param t
+	 * @param uri
 	 *            remote we should fetch from.
-	 * @param b
+	 * @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 branch
 	 *            branch to initially clone from.
+	 * @param remoteName
+	 *            name of created remote config as source remote (typically
+	 *            named "origin").
 	 */
-	public CloneOperation(final Repository r, final RemoteConfig t,
-			final String b) {
-		local = r;
-		remote = t;
-		branch = b;
+	public CloneOperation(final URIish uri, final boolean allSelected,
+			final Collection<Ref> selectedBranches, final File workdir,
+			final String branch, final String remoteName) {
+		this.uri = uri;
+		this.allSelected = allSelected;
+		this.selectedBranches = selectedBranches;
+		this.workdir = workdir;
+		this.branch = branch;
+		this.remoteName = remoteName;
 	}
 
-	public void run(final IProgressMonitor pm) throws InvocationTargetException {
+	public void run(final IProgressMonitor pm)
+			throws InvocationTargetException, InterruptedException {
 		final IProgressMonitor monitor;
 		if (pm == null)
 			monitor = new NullProgressMonitor();
@@ -71,21 +107,65 @@ public class CloneOperation implements IRunnableWithProgress {
 			monitor = pm;
 
 		try {
-			monitor.beginTask(NLS.bind(CoreText.CloneOperation_title, remote
-					.getURIs().get(0).toString()), 5000);
-			doFetch(new SubProgressMonitor(monitor, 4000));
-			doCheckout(new SubProgressMonitor(monitor, 1000));
-		} catch (IOException e) {
-			if (!monitor.isCanceled())
+			monitor.beginTask(NLS.bind(CoreText.CloneOperation_title, uri),
+					5000);
+			try {
+				doInit(new SubProgressMonitor(monitor, 100));
+				doFetch(new SubProgressMonitor(monitor, 4000));
+				doCheckout(new SubProgressMonitor(monitor, 900));
+			} finally {
+				closeLocal();
+			}
+		} catch (final Exception e) {
+			delete(workdir);
+			if (monitor.isCanceled())
+				throw new InterruptedException();
+			else
 				throw new InvocationTargetException(e);
 		} finally {
 			monitor.done();
 		}
 	}
 
+	private void closeLocal() {
+		if (local != null) {
+			local.close();
+			local = null;
+		}
+	}
+
+	private void doInit(final IProgressMonitor monitor)
+			throws URISyntaxException, IOException {
+		monitor.setTaskName("Initializing local repository");
+
+		final File gitdir = new File(workdir, ".git");
+		local = new Repository(gitdir);
+		local.create();
+		local.writeSymref(Constants.HEAD, branch);
+
+		remoteConfig = new RemoteConfig(local.getConfig(), remoteName);
+		remoteConfig.addURI(uri);
+
+		final String dst = REMOTES_PREFIX_S + remoteConfig.getName();
+		RefSpec wcrs = new RefSpec();
+		wcrs = wcrs.setForceUpdate(true);
+		wcrs = wcrs.setSourceDestination(HEADS_PREFIX + "/*", dst + "/*");
+
+		if (allSelected) {
+			remoteConfig.addFetchRefSpec(wcrs);
+		} else {
+			for (final Ref ref : selectedBranches)
+				if (wcrs.matchSource(ref))
+					remoteConfig.addFetchRefSpec(wcrs.expandFromSource(ref));
+		}
+
+		remoteConfig.update(local.getConfig());
+		local.getConfig().save();
+	}
+
 	private void doFetch(final IProgressMonitor monitor)
 			throws NotSupportedException, TransportException {
-		final Transport tn = Transport.open(local, remote);
+		final Transport tn = Transport.open(local, remoteConfig);
 		try {
 			final EclipseGitProgressTransformer pm;
 			pm = new EclipseGitProgressTransformer(monitor);
@@ -116,4 +196,15 @@ public class CloneOperation implements IRunnableWithProgress {
 		monitor.setTaskName("Writing index");
 		index.write();
 	}
+
+	private static void delete(final File d) {
+		if (d.isDirectory()) {
+			final File[] items = d.listFiles();
+			if (items != null) {
+				for (final File c : items)
+					delete(c);
+			}
+		}
+		d.delete();
+	}
 }
\ No newline at end of file
diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
index 0d39440..9150832 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java
@@ -53,6 +53,9 @@ public class UIText extends NLS {
 	public static String GitCloneWizard_failed;
 
 	/** */
+	public static String GitCloneWizard_errorCannotCreate;
+
+	/** */
 	public static String RepositorySelectionPage_sourceSelectionTitle;
 
 	/** */
@@ -180,6 +183,7 @@ public class UIText extends NLS {
 
 	/** */
 	public static String ResourceHistory_toggleCommentFill;
+
 	/** */
 	public static String ResourceHistory_toggleRevDetail;
 
diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java
index 9c1d691..efcf57f 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/clone/GitCloneWizard.java
@@ -10,9 +10,8 @@
 package org.spearce.egit.ui.internal.clone;
 
 import java.io.File;
-import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
-import java.net.URISyntaxException;
+import java.util.Collection;
 
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
@@ -29,22 +28,13 @@ import org.spearce.egit.ui.Activator;
 import org.spearce.egit.ui.UIIcons;
 import org.spearce.egit.ui.UIText;
 import org.spearce.egit.ui.internal.components.RepositorySelectionPage;
-import org.spearce.jgit.lib.Constants;
 import org.spearce.jgit.lib.Ref;
-import org.spearce.jgit.lib.Repository;
-import org.spearce.jgit.transport.RefSpec;
-import org.spearce.jgit.transport.RemoteConfig;
 import org.spearce.jgit.transport.URIish;
 
 /**
  * Import Git Repository Wizard. A front end to a git clone operation.
  */
 public class GitCloneWizard extends Wizard implements IImportWizard {
-	private static final String HEADS_PREFIX = Constants.HEADS_PREFIX;
-
-	private static final String REMOTES_PREFIX_S = Constants.REMOTES_PREFIX
-			+ "/";
-
 	private RepositorySelectionPage cloneSource;
 
 	private SourceBranchPage validSource;
@@ -68,62 +58,35 @@ public class GitCloneWizard extends Wizard implements IImportWizard {
 
 	@Override
 	public boolean performFinish() {
-		final URIish uri;
-		final Repository db;
-		final RemoteConfig origin;
-
-		uri = cloneSource.getSelection().getURI();
-
+		final URIish uri = cloneSource.getSelection().getURI();
+		final boolean allSelected = validSource.isAllSelected();
+		final Collection<Ref> selectedBranches = validSource
+				.getSelectedBranches();
 		final File workdir = cloneDestination.getDestinationFile();
 		final String branch = cloneDestination.getInitialBranch();
-		final File gitdir = new File(workdir, ".git");
-		try {
-			db = new Repository(gitdir);
-			db.create();
-			db.writeSymref(Constants.HEAD, branch);
+		final String remoteName = cloneDestination.getRemote();
 
-			final String rn = cloneDestination.getRemote();
-			origin = new RemoteConfig(db.getConfig(), rn);
-			origin.addURI(uri);
-
-			final String dst = REMOTES_PREFIX_S + origin.getName();
-			RefSpec wcrs = new RefSpec();
-			wcrs = wcrs.setForceUpdate(true);
-			wcrs = wcrs.setSourceDestination(HEADS_PREFIX + "/*", dst + "/*");
-
-			if (validSource.isAllSelected()) {
-				origin.addFetchRefSpec(wcrs);
-			} else {
-				for (final Ref ref : validSource.getSelectedBranches())
-					if (wcrs.matchSource(ref))
-						origin.addFetchRefSpec(wcrs.expandFromSource(ref));
-			}
-
-			origin.update(db.getConfig());
-			db.getConfig().save();
-		} catch (IOException err) {
-			Activator.logError(UIText.GitCloneWizard_failed, err);
+		if (!workdir.mkdirs()) {
+			final String errorMessage = NLS.bind(
+					UIText.GitCloneWizard_errorCannotCreate, workdir.getPath());
 			ErrorDialog.openError(getShell(), getWindowTitle(),
 					UIText.GitCloneWizard_failed, new Status(IStatus.ERROR,
-							Activator.getPluginId(), 0, err.getMessage(), err));
-			return false;
-		} catch (URISyntaxException e) {
+							Activator.getPluginId(), 0, errorMessage, null));
+			// let's give user a chance to fix this minor problem
 			return false;
 		}
 
-		final CloneOperation op = new CloneOperation(db, origin, branch);
+		final CloneOperation op = new CloneOperation(uri, allSelected,
+				selectedBranches, workdir, branch, remoteName);
 		final Job job = new Job(NLS.bind(UIText.GitCloneWizard_jobName, uri
 				.toString())) {
 			@Override
 			protected IStatus run(final IProgressMonitor monitor) {
 				try {
 					op.run(monitor);
-					if (monitor.isCanceled()) {
-						db.close();
-						delete(workdir);
-						return Status.CANCEL_STATUS;
-					}
 					return Status.OK_STATUS;
+				} catch (InterruptedException e) {
+					return Status.CANCEL_STATUS;
 				} catch (InvocationTargetException e) {
 					Throwable thr = e.getCause();
 					return new Status(IStatus.ERROR, Activator.getPluginId(),
@@ -135,15 +98,4 @@ public class GitCloneWizard extends Wizard implements IImportWizard {
 		job.schedule();
 		return true;
 	}
-
-	private static void delete(final File d) {
-		if (d.isDirectory()) {
-			final File[] items = d.listFiles();
-			if (items != null) {
-				for (final File c : items)
-					delete(c);
-			}
-		}
-		d.delete();
-	}
 }
diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties
index 857568a..420b610 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties
@@ -30,6 +30,7 @@ ExistingOrNewPage_createInParent=Create repository in project's parent directory
 GitCloneWizard_title=Import Git Repository
 GitCloneWizard_jobName=Cloning from {0}
 GitCloneWizard_failed=Git repository clone failed.
+GitCloneWizard_errorCannotCreate=Cannot create directory {0}.
 
 RepositorySelectionPage_sourceSelectionTitle=Source Git Repository
 RepositorySelectionPage_sourceSelectionDescription=Enter the location of the source repository.
-- 
1.5.6.3
Previous: Marek ZawirskiNext: Marek Zawirski
Message 21 of 62 in “Push GUI, GUI improvements, various jgit stuff”
  1. 00/31 Push GUI, GUI improvements, various jgit stuffMarek Zawirski, Aug 17, 2008
  2. 01/31 Fix Repository.mapObject() for missing objectsMarek Zawirski, Aug 17, 2008
  3. 02/31 Fix Repository isValidRefName() for empty namesMarek Zawirski, Aug 17, 2008
  4. 03/31 Fix Repository.resolve() to not throw runtime exceptionsMarek Zawirski, Aug 17, 2008
  5. 04/31 Document/fix Transport open method for specific caseMarek Zawirski, Aug 17, 2008
  6. 05/31 Fix RefSpec javadoc regarding spec expandingMarek Zawirski, Aug 17, 2008
  7. 06/31 Make wildcard checking public in RefSpecMarek Zawirski, Aug 17, 2008
  8. 07/31 Add openAll() and applyConfig() methods to TransportMarek Zawirski, Aug 17, 2008
  9. 08/31 Add dryRun option to Transport and console pushMarek Zawirski, Aug 17, 2008
  10. 09/31 Extract Transport findRemoteRefUpdatesFor() as static methodMarek Zawirski, Aug 17, 2008
  11. 10/31 Improve javadoc of Transport push()Marek Zawirski, Aug 17, 2008
  12. 11/31 Clean up exception issues in RemoteRefUpdateMarek Zawirski, Aug 17, 2008
  13. 12/31 Add another RemoteRefUpdate constructor, useful for 2-stage pushMarek Zawirski, Aug 17, 2008
  14. 13/31 Add getAllRemoteConfigs() to RemoteConfigMarek Zawirski, Aug 17, 2008
  15. 14/31 Add setFetchRefSpecs and setPushRefSpecs to RemoteConfigMarek Zawirski, Aug 17, 2008
  16. 15/31 Add simple abbreviate() method to ObjectIdMarek Zawirski, Aug 17, 2008
  17. 16/31 Remove debug/test console output from GitIndexMarek Zawirski, Aug 17, 2008
  18. 17/31 Fix typo in uitext.properties messageMarek Zawirski, Aug 17, 2008
  19. 18/31 Refactor/rewrite CloneSourcePage to universal RepositorySelectionPageMarek Zawirski, Aug 17, 2008
  20. 19/31 Clone wizard and related: refactor, clean-up, fixes or improvementsMarek Zawirski, Aug 17, 2008
  21. 20/31 Move clone logic away from GitCloneWizard to CloneOperationMarek Zawirski, Aug 17, 2008
  22. 21/31 Add canCreateSubdir() heuristic in CloneDestinationPageMarek Zawirski, Aug 17, 2008
  23. 22/31 Set FileDialog selection appropriately in clone wizardMarek Zawirski, Aug 17, 2008
  24. 23/31 Allow selecting empty dir in clone wizardMarek Zawirski, Aug 17, 2008
  25. 24/31 Clone wizard: force dir to suggested path only if repo selection changeMarek Zawirski, Aug 17, 2008
  26. 25/31 Create ListRemoteOperation for listing remote repo branchesMarek Zawirski, Aug 17, 2008
  27. 26/31 Make Clone's SourceBranchPage more user-friendlyMarek Zawirski, Aug 17, 2008
  28. 27/31 Add few EPL Eclipse iconsMarek Zawirski, Aug 17, 2008
  29. 28/31 Checkbox images/screenshotsMarek Zawirski, Aug 17, 2008
  30. 29/31 Universal GUI for specifications edition: RefSpecPanel and relatedMarek Zawirski, Aug 17, 2008
  31. 30/31 Add PushOperation to pluginMarek Zawirski, Aug 17, 2008
  32. 31/31 Push GUIMarek Zawirski, Aug 17, 2008
  33. Robin RosenbergAug 19, 2008
  34. Shawn O. PearceAug 19, 2008
  35. Shawn O. PearceAug 19, 2008
  36. Shawn O. PearceAug 19, 2008
  37. Robin RosenbergAug 19, 2008
  38. Marek ZawirskiAug 20, 2008
  39. 1/6 Remove DEFAULT_DRY_RUN constant from TransportMarek Zawirski, Aug 20, 2008
  40. 2/6 Emphasize that db is a local one in RemoteRefUpdateMarek Zawirski, Aug 20, 2008
  41. 3/6 Handle URIs parsing errors in PushAction betterMarek Zawirski, Aug 20, 2008
  42. 4/6 Fix proposal provider for fetch in RefSpecPanelMarek Zawirski, Aug 20, 2008
  43. 5/6 Fix disappearing "save configuration" label in RefSpecPageMarek Zawirski, Aug 20, 2008
  44. 6/6 Fix RefSpecPage formattingMarek Zawirski, Aug 20, 2008
  45. Shawn O. PearceAug 20, 2008
  46. Git-aware Issue Tracking?Petr Baudis, Aug 20, 2008
  47. Shawn O. PearceAug 20, 2008
  48. Robin RosenbergAug 20, 2008
  49. Martin LanghoffAug 21, 2008
  50. Jakub NarebskiAug 20, 2008
  51. Pierre HabouzitAug 21, 2008
  52. Jakub NarebskiAug 21, 2008
  53. Mike DalessioAug 20, 2008
  54. Imran M YousufAug 21, 2008
  55. Marek ZawirskiAug 25, 2008
  56. Shawn O. PearceAug 25, 2008
  57. Robin RosenbergAug 21, 2008
  58. Shawn O. PearceAug 21, 2008
  59. Robin RosenbergAug 21, 2008
  60. Shawn O. PearceAug 22, 2008
  61. Marek ZawirskiAug 25, 2008
  62. Shawn O. PearceAug 25, 2008

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.