Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Sawicki2012-02-11 01:10:49 +0000
committerMatthias Sohn2012-02-11 01:10:49 +0000
commit5fa082ee31d6f1b13c5f0027a4736215db78ef00 (patch)
treed3e301ed1cb7322404315f8103990e602315cb23
parent227df83c0394e3bbc940ad663c9b7fd89407afd2 (diff)
downloadegit-5fa082ee31d6f1b13c5f0027a4736215db78ef00.tar.gz
egit-5fa082ee31d6f1b13c5f0027a4736215db78ef00.tar.xz
egit-5fa082ee31d6f1b13c5f0027a4736215db78ef00.zip
[repoView] Add submodule add/sync/update support
The Submodules node and all child repository nodes now have an Update and Sync action available from the context menu. Root repositories and 'Submodules' nodes now have an Add action that opens a wizard to configure a new submodule that will be configured in the parent repository and cloned on completion. Change-Id: I2afea7dca9e40a2748c10a7f835b2deae301ba73 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
-rw-r--r--org.eclipse.egit.core/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/SubmoduleAddOperation.java75
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/SubmoduleSyncOperation.java88
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/SubmoduleUpdateOperation.java120
-rw-r--r--org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleAddTest.java91
-rw-r--r--org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleSyncTest.java119
-rw-r--r--org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleTests.java27
-rw-r--r--org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleUpdateTest.java101
-rw-r--r--org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/test/AllLocalTests.java4
-rw-r--r--org.eclipse.egit.ui/plugin.properties6
-rw-r--r--org.eclipse.egit.ui/plugin.xml203
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/JobFamilies.java15
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java33
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/RepositoriesViewPropertyTester.java6
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleAddCommand.java82
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleCommand.java64
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleSyncCommand.java75
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleUpdateCommand.java77
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/submodule/AddSubmoduleWizard.java77
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/submodule/SubmodulePathWizardPage.java108
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties11
21 files changed, 1381 insertions, 2 deletions
diff --git a/org.eclipse.egit.core/META-INF/MANIFEST.MF b/org.eclipse.egit.core/META-INF/MANIFEST.MF
index 5c158f1645..a77fd61c73 100644
--- a/org.eclipse.egit.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.egit.core/META-INF/MANIFEST.MF
@@ -38,6 +38,7 @@ Import-Package: org.eclipse.jgit.api;version="[1.3.0,1.4.0)",
org.eclipse.jgit.revwalk;version="[1.3.0,1.4.0)",
org.eclipse.jgit.revwalk.filter;version="[1.3.0,1.4.0)",
org.eclipse.jgit.storage.file;version="[1.3.0,1.4.0)",
+ org.eclipse.jgit.submodule;version="[1.3.0,1.4.0)",
org.eclipse.jgit.transport;version="[1.3.0,1.4.0)",
org.eclipse.jgit.treewalk;version="[1.3.0,1.4.0)",
org.eclipse.jgit.treewalk.filter;version="[1.3.0,1.4.0)",
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/SubmoduleAddOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/SubmoduleAddOperation.java
new file mode 100644
index 0000000000..780fcaf033
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/SubmoduleAddOperation.java
@@ -0,0 +1,75 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * 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
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.core.op;
+
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.egit.core.EclipseGitProgressTransformer;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.SubmoduleAddCommand;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.team.core.TeamException;
+
+/**
+ * Operation to add a submodule to a repository
+ */
+public class SubmoduleAddOperation implements IEGitOperation {
+
+ private final Repository repo;
+
+ private final String path;
+
+ private final String uri;
+
+ /**
+ * Create operation
+ *
+ * @param repo
+ * @param path
+ * @param uri
+ */
+ public SubmoduleAddOperation(final Repository repo, final String path,
+ final String uri) {
+ this.repo = repo;
+ this.path = path;
+ this.uri = uri;
+ }
+
+ public void execute(IProgressMonitor monitor) throws CoreException {
+ IWorkspaceRunnable action = new IWorkspaceRunnable() {
+
+ public void run(IProgressMonitor pm) throws CoreException {
+ final SubmoduleAddCommand add = Git.wrap(repo).submoduleAdd();
+ add.setProgressMonitor(new EclipseGitProgressTransformer(pm));
+ add.setPath(path);
+ add.setURI(uri);
+ try {
+ if (add.call() != null)
+ repo.notifyIndexChanged();
+ } catch (JGitInternalException e) {
+ throw new TeamException(e.getLocalizedMessage(),
+ e.getCause());
+ }
+ }
+ };
+ ResourcesPlugin.getWorkspace().run(action,
+ monitor != null ? monitor : new NullProgressMonitor());
+ }
+
+ public ISchedulingRule getSchedulingRule() {
+ return ResourcesPlugin.getWorkspace().getRoot();
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/SubmoduleSyncOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/SubmoduleSyncOperation.java
new file mode 100644
index 0000000000..d95755583c
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/SubmoduleSyncOperation.java
@@ -0,0 +1,88 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * 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
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.SubmoduleSyncCommand;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.team.core.TeamException;
+
+/**
+ * Operation that syncs a repository's submodule configurations
+ */
+public class SubmoduleSyncOperation implements IEGitOperation {
+
+ private final Repository repository;
+
+ private final Collection<String> paths;
+
+ /**
+ * Create submodule sync operation
+ *
+ * @param repository
+ */
+ public SubmoduleSyncOperation(final Repository repository) {
+ this.repository = repository;
+ paths = new ArrayList<String>();
+ }
+
+ /**
+ * Add path of submodule to update
+ *
+ * @param path
+ * @return this operation
+ */
+ public SubmoduleSyncOperation addPath(final String path) {
+ paths.add(path);
+ return this;
+ }
+
+ public void execute(final IProgressMonitor monitor) throws CoreException {
+ IWorkspaceRunnable action = new IWorkspaceRunnable() {
+
+ public void run(IProgressMonitor pm) throws CoreException {
+ pm.beginTask("", 1); //$NON-NLS-1$
+ Map<String, String> updates = null;
+ try {
+ SubmoduleSyncCommand sync = Git.wrap(repository)
+ .submoduleSync();
+ for (String path : paths)
+ sync.addPath(path);
+ updates = sync.call();
+ } catch (JGitInternalException e) {
+ throw new TeamException(e.getLocalizedMessage(),
+ e.getCause());
+ } finally {
+ if (updates != null && !updates.isEmpty())
+ repository.notifyIndexChanged();
+ pm.done();
+ }
+ }
+ };
+ ResourcesPlugin.getWorkspace().run(action,
+ monitor != null ? monitor : new NullProgressMonitor());
+ }
+
+ public ISchedulingRule getSchedulingRule() {
+ return null;
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/SubmoduleUpdateOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/SubmoduleUpdateOperation.java
new file mode 100644
index 0000000000..2ce9326272
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/SubmoduleUpdateOperation.java
@@ -0,0 +1,120 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * 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
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.core.op;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.egit.core.EclipseGitProgressTransformer;
+import org.eclipse.egit.core.internal.util.ProjectUtil;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.SubmoduleInitCommand;
+import org.eclipse.jgit.api.SubmoduleUpdateCommand;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.submodule.SubmoduleWalk;
+import org.eclipse.team.core.TeamException;
+
+/**
+ * Operation that updates a repository's submodules
+ */
+public class SubmoduleUpdateOperation implements IEGitOperation {
+
+ private final Repository repository;
+
+ private final Collection<String> paths;
+
+ /**
+ * Create submodule update operation
+ *
+ * @param repository
+ */
+ public SubmoduleUpdateOperation(final Repository repository) {
+ this.repository = repository;
+ paths = new ArrayList<String>();
+ }
+
+ /**
+ * Add path of submodule to update
+ *
+ * @param path
+ * @return this operation
+ */
+ public SubmoduleUpdateOperation addPath(final String path) {
+ paths.add(path);
+ return this;
+ }
+
+ public void execute(final IProgressMonitor monitor) throws CoreException {
+ IWorkspaceRunnable action = new IWorkspaceRunnable() {
+
+ public void run(IProgressMonitor pm) throws CoreException {
+ pm.beginTask("", 3); //$NON-NLS-1$
+ Git git = Git.wrap(repository);
+
+ Collection<String> updated = null;
+ try {
+ SubmoduleInitCommand init = git.submoduleInit();
+ for (String path : paths)
+ init.addPath(path);
+ init.call();
+ pm.worked(1);
+
+ SubmoduleUpdateCommand update = git.submoduleUpdate();
+ for (String path : paths)
+ update.addPath(path);
+ update.setProgressMonitor(new EclipseGitProgressTransformer(
+ new SubProgressMonitor(pm, 2)));
+ updated = update.call();
+ pm.worked(1);
+ SubProgressMonitor refreshMonitor = new SubProgressMonitor(
+ pm, 1);
+ refreshMonitor.beginTask("", updated.size()); //$NON-NLS-1$
+ for (String path : updated) {
+ Repository subRepo = SubmoduleWalk
+ .getSubmoduleRepository(repository, path);
+ if (subRepo != null)
+ ProjectUtil.refreshValidProjects(
+ ProjectUtil.getValidOpenProjects(subRepo),
+ new SubProgressMonitor(refreshMonitor, 1));
+ else
+ refreshMonitor.worked(1);
+ }
+ refreshMonitor.done();
+ } catch (JGitInternalException e) {
+ throw new TeamException(e.getLocalizedMessage(),
+ e.getCause());
+ } catch (IOException e) {
+ throw new TeamException(e.getLocalizedMessage(),
+ e.getCause());
+ } finally {
+ if (updated != null && !updated.isEmpty())
+ repository.notifyIndexChanged();
+ pm.done();
+ }
+ }
+ };
+ ResourcesPlugin.getWorkspace().run(action,
+ monitor != null ? monitor : new NullProgressMonitor());
+ }
+
+ public ISchedulingRule getSchedulingRule() {
+ return ResourcesPlugin.getWorkspace().getRoot();
+ }
+}
diff --git a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleAddTest.java b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleAddTest.java
new file mode 100644
index 0000000000..e9e4bfbfb3
--- /dev/null
+++ b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleAddTest.java
@@ -0,0 +1,91 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * 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
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.ui.submodule;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.File;
+
+import org.eclipse.egit.ui.Activator;
+import org.eclipse.egit.ui.JobFamilies;
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.egit.ui.test.ContextMenuHelper;
+import org.eclipse.egit.ui.test.TestUtil;
+import org.eclipse.egit.ui.view.repositories.GitRepositoriesViewTestBase;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotTreeItem;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for adding submodules to a repository
+ */
+@RunWith(SWTBotJunit4ClassRunner.class)
+public class SubmoduleAddTest extends GitRepositoriesViewTestBase {
+
+ private static final String ADD_SUBMODULE_CONTEXT_MENU_LABEL = "SubmoduleAddCommand.label";
+
+ private static File repositoryFile;
+
+ @Before
+ public void before() throws Exception {
+ repositoryFile = createProjectAndCommitToRepository();
+ }
+
+ @Test
+ public void addAtRoot() throws Exception {
+ deleteAllProjects();
+ assertProjectExistence(PROJ1, false);
+ clearView();
+ Activator.getDefault().getRepositoryUtil()
+ .addConfiguredRepository(repositoryFile);
+ shareProjects(repositoryFile);
+ assertProjectExistence(PROJ1, true);
+ refreshAndWait();
+ assertHasRepo(repositoryFile);
+ FileRepository repo = lookupRepository(repositoryFile);
+
+ SWTBotTree tree = getOrOpenView().bot().tree();
+ tree.getAllItems()[0].select();
+ ContextMenuHelper.clickContextMenu(tree, myUtil
+ .getPluginLocalizedValue(ADD_SUBMODULE_CONTEXT_MENU_LABEL));
+ SWTBotShell shell = bot.shell(UIText.AddSubmoduleWizard_WindowTitle);
+ shell.activate();
+ shell.bot().textWithLabel(UIText.SubmodulePathWizardPage_PathLabel)
+ .setText("sub");
+ shell.bot().button(IDialogConstants.NEXT_LABEL).click();
+
+ shell.bot()
+ .textWithLabel(UIText.RepositorySelectionPage_promptURI + ":")
+ .setText(repo.getDirectory().toURI().toString());
+
+ shell.bot().button(IDialogConstants.FINISH_LABEL).click();
+ waitInUI();
+ TestUtil.joinJobs(JobFamilies.SUBMODULE_ADD);
+ refreshAndWait();
+
+ tree = getOrOpenView().bot().tree();
+ SWTBotTreeItem submodules = tree.getAllItems()[0]
+ .select()
+ .expand()
+ .getNode(
+ UIText.RepositoriesViewLabelProvider_SubmodulesNodeText);
+ assertNotNull(submodules);
+ submodules.expand();
+ assertEquals(1, submodules.rowCount());
+ }
+}
diff --git a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleSyncTest.java b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleSyncTest.java
new file mode 100644
index 0000000000..6bbcfea38a
--- /dev/null
+++ b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleSyncTest.java
@@ -0,0 +1,119 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * 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
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.ui.submodule;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.File;
+
+import org.eclipse.egit.ui.Activator;
+import org.eclipse.egit.ui.JobFamilies;
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.egit.ui.test.ContextMenuHelper;
+import org.eclipse.egit.ui.test.TestUtil;
+import org.eclipse.egit.ui.view.repositories.GitRepositoriesViewTestBase;
+import org.eclipse.jgit.api.SubmoduleAddCommand;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests of running a submodule sync
+ */
+@RunWith(SWTBotJunit4ClassRunner.class)
+public class SubmoduleSyncTest extends GitRepositoriesViewTestBase {
+
+ private static final String SYNC_SUBMODULE_CONTEXT_MENU_LABEL = "SubmoduleSyncCommand.label";
+
+ private static File repositoryFile;
+
+ @Before
+ public void before() throws Exception {
+ repositoryFile = createProjectAndCommitToRepository();
+ }
+
+ @Test
+ public void syncSubmodule() throws Exception {
+ deleteAllProjects();
+ assertProjectExistence(PROJ1, false);
+ clearView();
+ Activator.getDefault().getRepositoryUtil()
+ .addConfiguredRepository(repositoryFile);
+ shareProjects(repositoryFile);
+ assertProjectExistence(PROJ1, true);
+ refreshAndWait();
+ assertHasRepo(repositoryFile);
+ FileRepository repo = lookupRepository(repositoryFile);
+
+ SubmoduleAddCommand command = new SubmoduleAddCommand(repo);
+ String path = "sub";
+ command.setPath(path);
+ String uri = new URIish(repo.getDirectory().toURI().toString())
+ .toString();
+ command.setURI(uri);
+ Repository subRepo = command.call();
+ assertNotNull(subRepo);
+
+ String newUri = "git://server/repo.git";
+ File modulesFile = new File(repo.getWorkTree(),
+ Constants.DOT_GIT_MODULES);
+ FileBasedConfig config = new FileBasedConfig(modulesFile, repo.getFS());
+ config.load();
+ config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_URL, newUri);
+ config.save();
+
+ assertEquals(
+ uri,
+ repo.getConfig().getString(
+ ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_URL));
+ assertEquals(
+ uri,
+ subRepo.getConfig().getString(
+ ConfigConstants.CONFIG_REMOTE_SECTION,
+ Constants.DEFAULT_REMOTE_NAME,
+ ConfigConstants.CONFIG_KEY_URL));
+
+ refreshAndWait();
+ SWTBotTree tree = getOrOpenView().bot().tree();
+ tree.getAllItems()[0]
+ .expand()
+ .expandNode(
+ UIText.RepositoriesViewLabelProvider_SubmodulesNodeText)
+ .select();
+ ContextMenuHelper.clickContextMenu(tree, myUtil
+ .getPluginLocalizedValue(SYNC_SUBMODULE_CONTEXT_MENU_LABEL));
+ TestUtil.joinJobs(JobFamilies.SUBMODULE_SYNC);
+ refreshAndWait();
+
+ assertEquals(
+ newUri,
+ repo.getConfig().getString(
+ ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_URL));
+ assertEquals(
+ newUri,
+ subRepo.getConfig().getString(
+ ConfigConstants.CONFIG_REMOTE_SECTION,
+ Constants.DEFAULT_REMOTE_NAME,
+ ConfigConstants.CONFIG_KEY_URL));
+ }
+}
diff --git a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleTests.java b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleTests.java
new file mode 100644
index 0000000000..1e4b20fcaf
--- /dev/null
+++ b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleTests.java
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * 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
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.ui.submodule;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+/**
+ * Search unit test suite
+ */
+@RunWith(Suite.class)
+@SuiteClasses({ SubmoduleAddTest.class, //
+ SubmoduleSyncTest.class, //
+ SubmoduleUpdateTest.class, //
+})
+public class SubmoduleTests {
+ // Intentionally left blank
+}
diff --git a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleUpdateTest.java b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleUpdateTest.java
new file mode 100644
index 0000000000..4f9dd9fe49
--- /dev/null
+++ b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/submodule/SubmoduleUpdateTest.java
@@ -0,0 +1,101 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * 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
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.ui.submodule;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.eclipse.egit.ui.Activator;
+import org.eclipse.egit.ui.JobFamilies;
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.egit.ui.test.ContextMenuHelper;
+import org.eclipse.egit.ui.test.TestUtil;
+import org.eclipse.egit.ui.view.repositories.GitRepositoriesViewTestBase;
+import org.eclipse.jgit.api.SubmoduleAddCommand;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.storage.file.FileRepository;
+import org.eclipse.jgit.transport.URIish;
+import org.eclipse.swtbot.swt.finder.junit.SWTBotJunit4ClassRunner;
+import org.eclipse.swtbot.swt.finder.widgets.SWTBotTree;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for running a submodule update
+ */
+@RunWith(SWTBotJunit4ClassRunner.class)
+public class SubmoduleUpdateTest extends GitRepositoriesViewTestBase {
+
+ private static final String UPDATE_SUBMODULE_CONTEXT_MENU_LABEL = "SubmoduleUpdateCommand.label";
+
+ private static File repositoryFile;
+
+ @Before
+ public void before() throws Exception {
+ repositoryFile = createProjectAndCommitToRepository();
+ }
+
+ @Test
+ public void updateSubmodule() throws Exception {
+ deleteAllProjects();
+ assertProjectExistence(PROJ1, false);
+ clearView();
+ Activator.getDefault().getRepositoryUtil()
+ .addConfiguredRepository(repositoryFile);
+ shareProjects(repositoryFile);
+ assertProjectExistence(PROJ1, true);
+ refreshAndWait();
+ assertHasRepo(repositoryFile);
+ FileRepository repo = lookupRepository(repositoryFile);
+ ObjectId repoHead = repo.resolve(Constants.HEAD);
+
+ SubmoduleAddCommand command = new SubmoduleAddCommand(repo);
+ String path = "sub";
+ command.setPath(path);
+ String uri = new URIish(repo.getDirectory().toURI().toString())
+ .toString();
+ command.setURI(uri);
+ Repository subRepo = command.call();
+ assertNotNull(subRepo);
+
+ Ref head = subRepo.getRef(Constants.HEAD);
+ assertNotNull(head);
+ assertTrue(head.isSymbolic());
+ assertEquals(Constants.R_HEADS + Constants.MASTER, head.getLeaf()
+ .getName());
+ assertEquals(repoHead, head.getObjectId());
+
+ refreshAndWait();
+ SWTBotTree tree = getOrOpenView().bot().tree();
+ tree.getAllItems()[0]
+ .expand()
+ .expandNode(
+ UIText.RepositoriesViewLabelProvider_SubmodulesNodeText)
+ .select();
+ ContextMenuHelper.clickContextMenu(tree, myUtil
+ .getPluginLocalizedValue(UPDATE_SUBMODULE_CONTEXT_MENU_LABEL));
+ TestUtil.joinJobs(JobFamilies.SUBMODULE_UPDATE);
+ refreshAndWait();
+
+ head = subRepo.getRef(Constants.HEAD);
+ assertNotNull(head);
+ assertFalse(head.isSymbolic());
+ assertEquals(repoHead, head.getObjectId());
+ }
+}
diff --git a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/test/AllLocalTests.java b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/test/AllLocalTests.java
index 33e3eaf72b..79b95b231b 100644
--- a/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/test/AllLocalTests.java
+++ b/org.eclipse.egit.ui.test/src/org/eclipse/egit/ui/test/AllLocalTests.java
@@ -14,6 +14,7 @@ import org.eclipse.egit.ui.internal.dialogs.SpellcheckableMessageAreaTest;
import org.eclipse.egit.ui.operations.GitScopeUtilTest;
import org.eclipse.egit.ui.prefpages.configuration.GlobalConfigurationPageTest;
import org.eclipse.egit.ui.search.SearchTests;
+import org.eclipse.egit.ui.submodule.SubmoduleTests;
import org.eclipse.egit.ui.test.commit.CommitTests;
import org.eclipse.egit.ui.test.history.HistoryViewTest;
import org.eclipse.egit.ui.test.team.actions.AllTeamActionTests;
@@ -44,7 +45,8 @@ import org.junit.runners.Suite.SuiteClasses;
SynchronizeViewGitChangeSetModelTest.class,
CommitTests.class,
SearchTests.class,
- BranchTests.class })
+ BranchTests.class,
+ SubmoduleTests.class })
public class AllLocalTests {
// empty class, don't need anything here
}
diff --git a/org.eclipse.egit.ui/plugin.properties b/org.eclipse.egit.ui/plugin.properties
index b6f7950513..a40955dce0 100644
--- a/org.eclipse.egit.ui/plugin.properties
+++ b/org.eclipse.egit.ui/plugin.properties
@@ -281,6 +281,12 @@ RebaseWithDialogCommand.name = Rebase...
AbortRebaseCommand.name = Abort Rebase
SkipRebaseCommand.name = Skip Rebase
ContinueRebaseCommand.name = Continue Rebase
+SubmoduleUpdateCommand.name = Update Submodule
+SubmoduleUpdateCommand.label = Update Submodule
+SubmoduleSyncCommand.name = Sync Submodule
+SubmoduleSyncCommand.label = Sync Submodule
+SubmoduleAddCommand.name = Add Submodule
+SubmoduleAddCommand.label = Add Submodule
MergeToolCommand.name = Merge Tool
PushUpstreamCommand.name = Push to Upstream
FetchUpstreamCommand.name = Fetch from Upstream
diff --git a/org.eclipse.egit.ui/plugin.xml b/org.eclipse.egit.ui/plugin.xml
index 733efadf6c..9e60d95dad 100644
--- a/org.eclipse.egit.ui/plugin.xml
+++ b/org.eclipse.egit.ui/plugin.xml
@@ -1516,6 +1516,97 @@
</and>
</activeWhen>
</handler>
+ <handler
+ commandId="org.eclipse.egit.ui.team.submodule.update">
+ <class
+ class="org.eclipse.egit.ui.internal.repository.tree.command.SubmoduleUpdateCommand">
+ </class>
+ <activeWhen>
+ <and>
+ <count
+ value="+">
+ </count>
+ <iterate>
+ <or>
+ <and>
+ <instanceof
+ value="org.eclipse.egit.ui.internal.repository.tree.RepositoryNode">
+ </instanceof>
+ <test
+ property="GitRepository.isSubmodule">
+ </test>
+ </and>
+ <instanceof
+ value="org.eclipse.egit.ui.internal.repository.tree.SubmodulesNode">
+ </instanceof>
+ </or>
+ </iterate>
+ </and>
+ </activeWhen>
+ </handler>
+ <handler
+ commandId="org.eclipse.egit.ui.team.submodule.sync">
+ <class
+ class="org.eclipse.egit.ui.internal.repository.tree.command.SubmoduleSyncCommand">
+ </class>
+ <activeWhen>
+ <and>
+ <count
+ value="+">
+ </count>
+ <iterate>
+ <or>
+ <and>
+ <instanceof
+ value="org.eclipse.egit.ui.internal.repository.tree.RepositoryNode">
+ </instanceof>
+ <test
+ property="GitRepository.isSubmodule">
+ </test>
+ </and>
+ <instanceof
+ value="org.eclipse.egit.ui.internal.repository.tree.SubmodulesNode">
+ </instanceof>
+ </or>
+ </iterate>
+ </and>
+ </activeWhen>
+ </handler>
+ <handler
+ commandId="org.eclipse.egit.ui.team.submodule.add">
+ <class
+ class="org.eclipse.egit.ui.internal.repository.tree.command.SubmoduleAddCommand">
+ </class>
+ <activeWhen>
+ <and>
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <or>
+ <and>
+ <instanceof
+ value="org.eclipse.egit.ui.internal.repository.tree.RepositoryNode">
+ </instanceof>
+ <not>
+ <test
+ property="GitRepository.isSubmodule">
+ </test>
+ </not>
+ <not>
+ <test
+ property="GitRepository.isBare">
+ </test>
+ </not>
+ </and>
+ <instanceof
+ value="org.eclipse.egit.ui.internal.repository.tree.SubmodulesNode">
+ </instanceof>
+ </or>
+ </iterate>
+ </and>
+ </activeWhen>
+ </handler>
</extension>
<extension
point="org.eclipse.ui.views">
@@ -2814,6 +2905,101 @@
visible="true">
</separator>
<command
+ commandId="org.eclipse.egit.ui.team.submodule.add"
+ label="%SubmoduleAddCommand.label"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <count
+ value="1">
+ </count>
+ <iterate>
+ <or>
+ <and>
+ <instanceof
+ value="org.eclipse.egit.ui.internal.repository.tree.RepositoryNode">
+ </instanceof>
+ <not>
+ <test
+ property="GitRepository.isSubmodule">
+ </test>
+ </not>
+ <not>
+ <test
+ property="GitRepository.isBare">
+ </test>
+ </not>
+ </and>
+ <instanceof
+ value="org.eclipse.egit.ui.internal.repository.tree.SubmodulesNode">
+ </instanceof>
+ </or>
+ </iterate>
+ </and>
+ </visibleWhen>
+ </command>
+ <command
+ commandId="org.eclipse.egit.ui.team.submodule.sync"
+ label="%SubmoduleSyncCommand.label"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <count
+ value="+">
+ </count>
+ <iterate>
+ <or>
+ <and>
+ <instanceof
+ value="org.eclipse.egit.ui.internal.repository.tree.RepositoryNode">
+ </instanceof>
+ <test
+ property="GitRepository.isSubmodule">
+ </test>
+ </and>
+ <instanceof
+ value="org.eclipse.egit.ui.internal.repository.tree.SubmodulesNode">
+ </instanceof>
+ </or>
+ </iterate>
+ </and>
+ </visibleWhen>
+ </command>
+ <command
+ commandId="org.eclipse.egit.ui.team.submodule.update"
+ label="%SubmoduleUpdateCommand.label"
+ style="push">
+ <visibleWhen
+ checkEnabled="false">
+ <and>
+ <count
+ value="+">
+ </count>
+ <iterate>
+ <or>
+ <and>
+ <instanceof
+ value="org.eclipse.egit.ui.internal.repository.tree.RepositoryNode">
+ </instanceof>
+ <test
+ property="GitRepository.isSubmodule">
+ </test>
+ </and>
+ <instanceof
+ value="org.eclipse.egit.ui.internal.repository.tree.SubmodulesNode">
+ </instanceof>
+ </or>
+ </iterate>
+ </and>
+ </visibleWhen>
+ </command>
+ <separator
+ name="##Repo-6"
+ visible="true">
+ </separator>
+ <command
commandId="org.eclipse.egit.ui.RepositoriesViewCopyPath"
label="%RepoViewCopyPath.label"
style="push">
@@ -3534,6 +3720,21 @@
id="org.eclipse.egit.ui.ContinueRebase"
name="%ContinueRebaseCommand.name">
</command>
+ <command
+ categoryId="org.eclipse.egit.ui.commandCategory"
+ id="org.eclipse.egit.ui.team.submodule.update"
+ name="%SubmoduleUpdateCommand.name">
+ </command>
+ <command
+ categoryId="org.eclipse.egit.ui.commandCategory"
+ id="org.eclipse.egit.ui.team.submodule.sync"
+ name="%SubmoduleSyncCommand.name">
+ </command>
+ <command
+ categoryId="org.eclipse.egit.ui.commandCategory"
+ id="org.eclipse.egit.ui.team.submodule.add"
+ name="%SubmoduleAddCommand.name">
+ </command>
</extension>
<extension
point="org.eclipse.ui.commandImages">
@@ -3764,7 +3965,7 @@
class="org.eclipse.egit.ui.internal.repository.tree.RepositoriesViewPropertyTester"
id="org.eclipse.egit.ui.RepositoryTester"
namespace="GitRepository"
- properties="isBare,isSafe,isRefCheckedOut,isLocalBranch,fetchExists,pushExists,canMerge,canAbortRebase"
+ properties="isBare,isSafe,isRefCheckedOut,isLocalBranch,fetchExists,pushExists,canMerge,canAbortRebase,isSubmodule"
type="org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNode">
</propertyTester>
<propertyTester
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/JobFamilies.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/JobFamilies.java
index 923dbfbbed..89e7013204 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/JobFamilies.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/JobFamilies.java
@@ -127,4 +127,19 @@ public class JobFamilies {
* Show annotations git job
*/
public static final Object BLAME = new Object();
+
+ /**
+ * Submodule add git job
+ */
+ public static final Object SUBMODULE_ADD = new Object();
+
+ /**
+ * Submodule sync git job
+ */
+ public static final Object SUBMODULE_SYNC = new Object();
+
+ /**
+ * Submodule update git job
+ */
+ public static final Object SUBMODULE_UPDATE = new Object();
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java
index e56b2604d1..5f921e1a41 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIText.java
@@ -94,6 +94,9 @@ public class UIText extends NLS {
public static String AddConfigEntryDialog_ValueLabel;
/** */
+ public static String AddSubmoduleWizard_WindowTitle;
+
+ /** */
public static String AddToIndexAction_addingFiles;
/** */
@@ -4214,6 +4217,36 @@ public class UIText extends NLS {
public static String StagingView_StageItemMenuLabel;
/** */
+ public static String SubmoduleAddCommand_AddError;
+
+ /** */
+ public static String SubmoduleAddCommand_JobTitle;
+
+ /** */
+ public static String SubmodulePathWizardPage_ErrorPathMustBeEmpty;
+
+ /** */
+ public static String SubmodulePathWizardPage_Message;
+
+ /** */
+ public static String SubmodulePathWizardPage_PathLabel;
+
+ /** */
+ public static String SubmodulePathWizardPage_Title;
+
+ /** */
+ public static String SubmoduleSyncCommand_SyncError;
+
+ /** */
+ public static String SubmoduleSyncCommand_Title;
+
+ /** */
+ public static String SubmoduleUpdateCommand_Title;
+
+ /** */
+ public static String SubmoduleUpdateCommand_UpdateError;
+
+ /** */
public static String SynchronizeWithMenu_custom;
/** */
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/RepositoriesViewPropertyTester.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/RepositoriesViewPropertyTester.java
index 06dd248974..39986fcf2a 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/RepositoriesViewPropertyTester.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/RepositoriesViewPropertyTester.java
@@ -126,6 +126,12 @@ public class RepositoriesViewPropertyTester extends PropertyTester {
default:
return false;
}
+
+ if ("isSubmodule".equals(property)) { //$NON-NLS-1$
+ RepositoryTreeNode<?> parent = node.getParent();
+ return parent != null
+ && parent.getType() == RepositoryTreeNodeType.SUBMODULES;
+ }
return false;
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleAddCommand.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleAddCommand.java
new file mode 100644
index 0000000000..9b16771e24
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleAddCommand.java
@@ -0,0 +1,82 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * 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
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.ui.internal.repository.tree.command;
+
+import java.text.MessageFormat;
+import java.util.List;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.egit.core.op.SubmoduleAddOperation;
+import org.eclipse.egit.ui.Activator;
+import org.eclipse.egit.ui.JobFamilies;
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNode;
+import org.eclipse.egit.ui.internal.submodule.AddSubmoduleWizard;
+import org.eclipse.jface.window.Window;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Command to add a new submodule to a repository
+ */
+public class SubmoduleAddCommand extends
+ RepositoriesViewCommandHandler<RepositoryTreeNode<?>> {
+
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ List<RepositoryTreeNode<?>> nodes = getSelectedNodes(event);
+ if (nodes.isEmpty())
+ return null;
+
+ final Repository repo = nodes.get(0).getRepository();
+ if (repo == null)
+ return null;
+
+ AddSubmoduleWizard wizard = new AddSubmoduleWizard(repo);
+ WizardDialog dialog = new WizardDialog(getShell(event), wizard);
+ if (dialog.open() == Window.OK) {
+ final String path = wizard.getPath();
+ final String uri = wizard.getUri().toPrivateASCIIString();
+ final SubmoduleAddOperation op = new SubmoduleAddOperation(repo,
+ path, uri);
+ Job job = new Job(MessageFormat.format(
+ UIText.SubmoduleAddCommand_JobTitle, path, uri)) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ monitor.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$
+ try {
+ op.execute(monitor);
+ } catch (CoreException e) {
+ Activator.logError(UIText.SubmoduleAddCommand_AddError,
+ e);
+ }
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public boolean belongsTo(Object family) {
+ if (JobFamilies.SUBMODULE_ADD.equals(family))
+ return true;
+ return super.belongsTo(family);
+ }
+ };
+ job.setUser(true);
+ job.setRule(op.getSchedulingRule());
+ job.schedule();
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleCommand.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleCommand.java
new file mode 100644
index 0000000000..677463125c
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleCommand.java
@@ -0,0 +1,64 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * 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
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.ui.internal.repository.tree.command;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNode;
+import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNodeType;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Base submodule command with helpers for finding the selected submodule paths
+ * and parent repositories
+ *
+ * @param <V>
+ */
+public abstract class SubmoduleCommand<V> extends
+ RepositoriesViewCommandHandler<V> {
+
+ /**
+ * Get submodule from selected nodes
+ * <p>
+ * Keys with null values denote repositories where all submodules should be
+ * used for the current command being executed
+ *
+ * @param nodes
+ * @return non-null but possibly empty map of parent repository's to
+ * submodule paths
+ */
+ protected Map<Repository, List<String>> getSubmodules(
+ final List<RepositoryTreeNode<?>> nodes) {
+ final Map<Repository, List<String>> repoPaths = new HashMap<Repository, List<String>>();
+ for (RepositoryTreeNode<?> node : nodes) {
+ if (node.getType() == RepositoryTreeNodeType.REPO) {
+ Repository parent = node.getParent().getRepository();
+ String path = Repository.stripWorkDir(parent.getWorkTree(),
+ node.getRepository().getWorkTree());
+ List<String> paths = repoPaths.get(parent);
+ if (paths == null) {
+ paths = new ArrayList<String>();
+ repoPaths.put(parent, paths);
+ }
+ paths.add(path);
+ }
+ }
+ for (RepositoryTreeNode<?> node : nodes)
+ if (node.getType() == RepositoryTreeNodeType.SUBMODULES)
+ // Clear paths so all submodules are updated
+ repoPaths.put(node.getParent().getRepository(), null);
+ return repoPaths;
+ }
+
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleSyncCommand.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleSyncCommand.java
new file mode 100644
index 0000000000..d865a423d8
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleSyncCommand.java
@@ -0,0 +1,75 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * 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
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.ui.internal.repository.tree.command;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.egit.core.op.SubmoduleSyncOperation;
+import org.eclipse.egit.ui.Activator;
+import org.eclipse.egit.ui.JobFamilies;
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNode;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Command to sync submodule configuration
+ */
+public class SubmoduleSyncCommand extends
+ SubmoduleCommand<RepositoryTreeNode<?>> {
+
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ final Map<Repository, List<String>> repoPaths = getSubmodules(getSelectedNodes(event));
+
+ if (!repoPaths.isEmpty()) {
+ Job job = new Job(UIText.SubmoduleSyncCommand_Title) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ monitor.beginTask("", repoPaths.size()); //$NON-NLS-1$
+ try {
+ for (Entry<Repository, List<String>> entry : repoPaths
+ .entrySet()) {
+ SubmoduleSyncOperation op = new SubmoduleSyncOperation(
+ entry.getKey());
+ if (entry.getValue() != null)
+ for (String path : entry.getValue())
+ op.addPath(path);
+ op.execute(new SubProgressMonitor(monitor, 1));
+ }
+ } catch (CoreException e) {
+ Activator.logError(
+ UIText.SubmoduleSyncCommand_SyncError, e);
+ }
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public boolean belongsTo(Object family) {
+ if (JobFamilies.SUBMODULE_SYNC.equals(family))
+ return true;
+ return super.belongsTo(family);
+ }
+ };
+ job.setUser(true);
+ job.schedule();
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleUpdateCommand.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleUpdateCommand.java
new file mode 100644
index 0000000000..c5b073736c
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/repository/tree/command/SubmoduleUpdateCommand.java
@@ -0,0 +1,77 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * 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
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.ui.internal.repository.tree.command;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.egit.core.op.SubmoduleUpdateOperation;
+import org.eclipse.egit.ui.Activator;
+import org.eclipse.egit.ui.JobFamilies;
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNode;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Command to update selected submodules
+ */
+public class SubmoduleUpdateCommand extends
+ SubmoduleCommand<RepositoryTreeNode<?>> {
+
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ final Map<Repository, List<String>> repoPaths = getSubmodules(getSelectedNodes(event));
+
+ if (!repoPaths.isEmpty()) {
+ Job job = new Job(UIText.SubmoduleUpdateCommand_Title) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ monitor.beginTask("", repoPaths.size()); //$NON-NLS-1$
+ try {
+ for (Entry<Repository, List<String>> entry : repoPaths
+ .entrySet()) {
+ SubmoduleUpdateOperation op = new SubmoduleUpdateOperation(
+ entry.getKey());
+ if (entry.getValue() != null)
+ for (String path : entry.getValue())
+ op.addPath(path);
+ op.execute(new SubProgressMonitor(monitor, 1));
+ }
+ } catch (CoreException e) {
+ Activator.logError(
+ UIText.SubmoduleUpdateCommand_UpdateError, e);
+ }
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ public boolean belongsTo(Object family) {
+ if (JobFamilies.SUBMODULE_UPDATE.equals(family))
+ return true;
+ return super.belongsTo(family);
+ }
+ };
+ job.setUser(true);
+ job.setRule(ResourcesPlugin.getWorkspace().getRoot());
+ job.schedule();
+ }
+ return null;
+ }
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/submodule/AddSubmoduleWizard.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/submodule/AddSubmoduleWizard.java
new file mode 100644
index 0000000000..3c8a09b865
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/submodule/AddSubmoduleWizard.java
@@ -0,0 +1,77 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * 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
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.ui.internal.submodule;
+
+import org.eclipse.egit.ui.UIIcons;
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.egit.ui.internal.SecureStoreUtils;
+import org.eclipse.egit.ui.internal.components.RepositorySelectionPage;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * Wizard to configure a new submodule
+ */
+public class AddSubmoduleWizard extends Wizard {
+
+ private final Repository repo;
+
+ private SubmodulePathWizardPage pathPage;
+
+ private RepositorySelectionPage uriPage;
+
+ /**
+ * Create wizard
+ *
+ * @param repo
+ */
+ public AddSubmoduleWizard(Repository repo) {
+ this.repo = repo;
+ setWindowTitle(UIText.AddSubmoduleWizard_WindowTitle);
+ setDefaultPageImageDescriptor(UIIcons.WIZBAN_IMPORT_REPO);
+ }
+
+ public void addPages() {
+ pathPage = new SubmodulePathWizardPage(repo);
+ addPage(pathPage);
+ uriPage = new RepositorySelectionPage(true, null);
+ uriPage.setPageComplete(false);
+ addPage(uriPage);
+ }
+
+ /**
+ * Get path of submodule
+ *
+ * @return path
+ */
+ public String getPath() {
+ return pathPage.getPath();
+ }
+
+ /**
+ * Get URI of submodule
+ *
+ * @return uri
+ */
+ public URIish getUri() {
+ return uriPage.getSelection().getURI();
+ }
+
+ public boolean performFinish() {
+ if (uriPage.getStoreInSecureStore()
+ && !SecureStoreUtils.storeCredentials(uriPage.getCredentials(),
+ uriPage.getSelection().getURI()))
+ return false;
+
+ return true;
+ }
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/submodule/SubmodulePathWizardPage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/submodule/SubmodulePathWizardPage.java
new file mode 100644
index 0000000000..f8d0df669d
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/submodule/SubmodulePathWizardPage.java
@@ -0,0 +1,108 @@
+/******************************************************************************
+ * Copyright (c) 2012 GitHub Inc.
+ * 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
+ *
+ * Contributors:
+ * Kevin Sawicki (GitHub Inc.) - initial API and implementation
+ *****************************************************************************/
+package org.eclipse.egit.ui.internal.submodule;
+
+import java.io.File;
+
+import org.eclipse.egit.ui.UIText;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * Wizard page to configure the path of a submodule
+ */
+public class SubmodulePathWizardPage extends WizardPage {
+
+ private final Repository repo;
+
+ private Text pathText;
+
+ private String path;
+
+ /**
+ * Create submodule path wizard page
+ *
+ * @param repo
+ */
+ public SubmodulePathWizardPage(Repository repo) {
+ super("pathPage"); //$NON-NLS-1$
+ this.repo = repo;
+ }
+
+ public void createControl(Composite parent) {
+ Composite displayArea = new Composite(parent, SWT.NONE);
+ GridLayoutFactory.swtDefaults().numColumns(2).equalWidth(false)
+ .applyTo(displayArea);
+ GridDataFactory.fillDefaults().grab(true, true).applyTo(displayArea);
+
+ new Label(displayArea, SWT.NONE)
+ .setText(UIText.SubmodulePathWizardPage_PathLabel);
+
+ pathText = new Text(displayArea, SWT.SINGLE | SWT.BORDER);
+ GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER)
+ .grab(true, false).applyTo(pathText);
+
+ pathText.addModifyListener(new ModifyListener() {
+
+ public void modifyText(ModifyEvent e) {
+ validate();
+ }
+ });
+
+ setControl(displayArea);
+ setTitle(UIText.SubmodulePathWizardPage_Title);
+ setMessage(UIText.SubmodulePathWizardPage_Message);
+ setPageComplete(false);
+ }
+
+ /**
+ * @return path
+ */
+ public String getPath() {
+ return path;
+ }
+
+ private void validate() {
+ final String currentPath = pathText.getText();
+ if (currentPath.length() == 0) {
+ setPageComplete(false);
+ return;
+ }
+
+ File file = new File(repo.getWorkTree(), currentPath);
+ if (file.isFile()) {
+ setErrorMessage(UIText.SubmodulePathWizardPage_ErrorPathMustBeEmpty);
+ setPageComplete(false);
+ return;
+ }
+
+ if (file.isDirectory()) {
+ String[] children = file.list();
+ if (children != null && children.length > 0) {
+ setErrorMessage(UIText.SubmodulePathWizardPage_ErrorPathMustBeEmpty);
+ setPageComplete(false);
+ return;
+ }
+ }
+
+ this.path = currentPath;
+ setErrorMessage(null);
+ setPageComplete(true);
+ }
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties
index 621e562902..10b4d28775 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/uitext.properties
@@ -35,6 +35,7 @@ AddConfigEntryDialog_KeyComponentsMessage=The key must have two or three compone
AddConfigEntryDialog_KeyLabel=&Key
AddConfigEntryDialog_MustEnterKeyMessage=Please enter a key
AddConfigEntryDialog_ValueLabel=&Value
+AddSubmoduleWizard_WindowTitle=Add Submodule
AddToIndexAction_addingFiles=Adding Files to Git Index
AddToIndexCommand_addingFilesFailed=Adding files failed
RemoveFromIndexAction_removingFiles=Removing file from Git Index
@@ -1470,6 +1471,16 @@ StagingView_exceptionTitle=Refresh Error
StagingView_exceptionMessage=Errors occurred while applying processing change notifications.
StagingView_UnstageItemMenuLabel=Remove from Git Index
StagingView_StageItemMenuLabel=Add to Git Index
+SubmoduleAddCommand_AddError=Submodule add error
+SubmoduleAddCommand_JobTitle=Creating submodule ''{0}'' from ''{1}''
+SubmodulePathWizardPage_ErrorPathMustBeEmpty=Path must be an empty or non-existent directory
+SubmodulePathWizardPage_Message=Enter relative path to submodule
+SubmodulePathWizardPage_PathLabel=Submodule Path:
+SubmodulePathWizardPage_Title=Submodule Path
+SubmoduleSyncCommand_SyncError=Submodule configuration sync error
+SubmoduleSyncCommand_Title=Synchronized submodule configuration
+SubmoduleUpdateCommand_Title=Updating submodules
+SubmoduleUpdateCommand_UpdateError=Submodule update error
SynchronizeWithMenu_custom=&Custom...
SynchronizeFetchJob_JobName=Fetching changes before synchronization launch

Back to the top