aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse
diff options
context:
space:
mode:
authorKevin Sawicki <kevin@github.com>2011-12-19 09:38:11 -0800
committerChris Aniszczyk <zx@twitter.com>2011-12-28 10:33:14 -0600
commit92c6f2f97bb478ac0d4731cacafaef52ac0d08d7 (patch)
tree211b11ae74bf4116cc12b30db6d80e256e671cd0 /org.eclipse.jgit/src/org/eclipse
parent3c7dceec1a7dc367a3e716331bacc37b74e3f6e0 (diff)
downloadjgit-92c6f2f97bb478ac0d4731cacafaef52ac0d08d7.tar.gz
jgit-92c6f2f97bb478ac0d4731cacafaef52ac0d08d7.zip
Add comand support for git-submodule
Adds the following commands: - Add - Init - Status - Sync - Update This also updates AddCommand so that file patterns added that are submodules can be staged in the index. Change-Id: Ie5112aa26430e5a2a3acd65a7b0e1d76067dc545 Signed-off-by: Kevin Sawicki <kevin@github.com> Signed-off-by: Chris Aniszczyk <zx@twitter.com>
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java43
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java211
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java130
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java148
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java157
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java190
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java9
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java3
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleStatus.java115
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleStatusType.java64
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java435
12 files changed, 1495 insertions, 13 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
index ced5681718..e04c7edb4b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
@@ -407,6 +407,7 @@ public class JGitText extends TranslationBundle {
/***/ public String packingCancelledDuringObjectsWriting;
/***/ public String packWriterStatistics;
/***/ public String pathIsNotInWorkingDir;
+ /***/ public String pathNotConfigured;
/***/ public String peeledLineBeforeRef;
/***/ public String peerDidNotSupplyACompleteObjectGraph;
/***/ public String prefixRemote;
@@ -481,6 +482,7 @@ public class JGitText extends TranslationBundle {
/***/ public String staleRevFlagsOn;
/***/ public String startingReadStageWithoutWrittenRequestDataPendingIsNotSupported;
/***/ public String statelessRPCRequiresOptionToBeEnabled;
+ /***/ public String submoduleExists;
/***/ public String submodulesNotSupported;
/***/ public String symlinkCannotBeWrittenAsTheLinkTarget;
/***/ public String systemConfigFileInvalid;
@@ -540,6 +542,7 @@ public class JGitText extends TranslationBundle {
/***/ public String unsupportedPackVersion;
/***/ public String updatingReferences;
/***/ public String updatingRefFailed;
+ /***/ public String uriNotConfigured;
/***/ public String uriNotFound;
/***/ public String userConfigFileInvalid;
/***/ public String walkFailure;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
index fcc58d1791..7e670304d9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/AddCommand.java
@@ -43,6 +43,7 @@
*/
package org.eclipse.jgit.api;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
@@ -57,6 +58,8 @@ import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.FileTreeIterator;
@@ -168,20 +171,34 @@ public class AddCommand extends GitCommand<DirCache> {
DirCacheEntry entry = new DirCacheEntry(path);
if (c == null || c.getDirCacheEntry() == null
|| !c.getDirCacheEntry().isAssumeValid()) {
- entry.setLength(sz);
- entry.setLastModified(f.getEntryLastModified());
- entry.setFileMode(f.getEntryFileMode());
-
- InputStream in = f.openEntryStream();
- try {
- entry.setObjectId(inserter.insert(
- Constants.OBJ_BLOB, sz, in));
- } finally {
- in.close();
+ FileMode mode = f.getEntryFileMode();
+ entry.setFileMode(mode);
+
+ if (FileMode.GITLINK != mode) {
+ entry.setLength(sz);
+ entry.setLastModified(f
+ .getEntryLastModified());
+ InputStream in = f.openEntryStream();
+ try {
+ entry.setObjectId(inserter.insert(
+ Constants.OBJ_BLOB, sz, in));
+ } finally {
+ in.close();
+ }
+ builder.add(entry);
+ lastAddedFile = path;
+ } else {
+ Repository subRepo = Git.open(
+ new File(repo.getWorkTree(), path))
+ .getRepository();
+ ObjectId subRepoHead = subRepo
+ .resolve(Constants.HEAD);
+ if (subRepoHead != null) {
+ entry.setObjectId(subRepoHead);
+ builder.add(entry);
+ lastAddedFile = path;
+ }
}
-
- builder.add(entry);
- lastAddedFile = path;
} else {
builder.add(c.getDirCacheEntry());
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
new file mode 100644
index 0000000000..0b5200481b
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleAddCommand.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2011, GitHub Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.JGitText;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.api.errors.NoFilepatternException;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.submodule.SubmoduleWalk;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
+
+/**
+ * A class used to execute a submodule add command.
+ *
+ * This will clone the configured submodule, register the submodule in the
+ * .gitmodules file and the repository config file, and also add the submodule
+ * and .gitmodules file to the index.
+ *
+ * @see <a
+ * href="http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html"
+ * >Git documentation about submodules</a>
+ */
+public class SubmoduleAddCommand extends GitCommand<Repository> {
+
+ private String path;
+
+ private String uri;
+
+ private ProgressMonitor monitor;
+
+ private CredentialsProvider credentialsProvider;
+
+ /**
+ * @param repo
+ */
+ public SubmoduleAddCommand(final Repository repo) {
+ super(repo);
+ }
+
+ /**
+ * Set repository-relative path of submodule
+ *
+ * @param path
+ * @return this command
+ */
+ public SubmoduleAddCommand setPath(final String path) {
+ this.path = path;
+ return this;
+ }
+
+ /**
+ * Set URI to clone submodule from
+ *
+ * @param uri
+ * @return this command
+ */
+ public SubmoduleAddCommand setURI(final String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ /**
+ * The progress monitor associated with the clone operation. By default,
+ * this is set to <code>NullProgressMonitor</code>
+ *
+ * @see NullProgressMonitor
+ * @param monitor
+ * @return this command
+ */
+ public SubmoduleAddCommand setProgressMonitor(final ProgressMonitor monitor) {
+ this.monitor = monitor;
+ return this;
+ }
+
+ /**
+ * @param credentialsProvider
+ * the {@link CredentialsProvider} to use
+ * @return this command
+ */
+ public SubmoduleAddCommand setCredentialsProvider(
+ final CredentialsProvider credentialsProvider) {
+ this.credentialsProvider = credentialsProvider;
+ return this;
+ }
+
+ /**
+ * Is the configured already a submodule in the index?
+ *
+ * @return true if submodule exists in index, false otherwise
+ * @throws IOException
+ */
+ protected boolean submoduleExists() throws IOException {
+ TreeFilter filter = PathFilter.create(path);
+ return SubmoduleWalk.forIndex(repo).setFilter(filter).next();
+ }
+
+ public Repository call() throws JGitInternalException {
+ checkCallable();
+ if (path == null || path.length() == 0)
+ throw new IllegalArgumentException(JGitText.get().pathNotConfigured);
+ if (uri == null || uri.length() == 0)
+ throw new IllegalArgumentException(JGitText.get().uriNotConfigured);
+
+ try {
+ if (submoduleExists())
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().submoduleExists, path));
+ } catch (IOException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+
+ // Clone submodule repository
+ File moduleDirectory = SubmoduleWalk.getSubmoduleDirectory(repo, path);
+ CloneCommand clone = Git.cloneRepository();
+ clone.setDirectory(moduleDirectory);
+ clone.setURI(uri);
+ if (monitor != null)
+ clone.setProgressMonitor(monitor);
+ if (credentialsProvider != null)
+ clone.setCredentialsProvider(credentialsProvider);
+ Repository subRepo = clone.call().getRepository();
+
+ // Save submodule URL to parent repository's config
+ StoredConfig config = repo.getConfig();
+ config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_URL, uri);
+ try {
+ config.save();
+ } catch (IOException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+
+ // Save path and URL to parent repository's .gitmodules file
+ FileBasedConfig modulesConfig = new FileBasedConfig(new File(
+ repo.getWorkTree(), Constants.DOT_GIT_MODULES), repo.getFS());
+ modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_PATH, path);
+ modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_URL, uri);
+ try {
+ modulesConfig.save();
+ } catch (IOException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+
+ AddCommand add = new AddCommand(repo);
+ // Add .gitmodules file to parent repository's index
+ add.addFilepattern(Constants.DOT_GIT_MODULES);
+ // Add submodule directory to parent repository's index
+ add.addFilepattern(path);
+ try {
+ add.call();
+ } catch (NoFilepatternException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+
+ return subRepo;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java
new file mode 100644
index 0000000000..ad8f02e47f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleInitCommand.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2011, GitHub Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.submodule.SubmoduleWalk;
+import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+
+/**
+ * A class used to execute a submodule init command.
+ *
+ * This will copy the 'url' and 'update' fields from the working tree
+ * .gitmodules file to a repository's config file for each submodule not
+ * currently present in the repository's config file.
+ *
+ * @see <a
+ * href="http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html"
+ * >Git documentation about submodules</a>
+ */
+public class SubmoduleInitCommand extends GitCommand<Collection<String>> {
+
+ private final Collection<String> paths;
+
+ /**
+ * @param repo
+ */
+ public SubmoduleInitCommand(final Repository repo) {
+ super(repo);
+ paths = new ArrayList<String>();
+ }
+
+ /**
+ * Add repository-relative submodule path to initialize
+ *
+ * @param path
+ * @return this command
+ */
+ public SubmoduleInitCommand addPath(final String path) {
+ paths.add(path);
+ return this;
+ }
+
+ public Collection<String> call() throws JGitInternalException {
+ checkCallable();
+
+ try {
+ SubmoduleWalk generator = SubmoduleWalk.forIndex(repo);
+ if (!paths.isEmpty())
+ generator.setFilter(PathFilterGroup.createFromStrings(paths));
+ StoredConfig config = repo.getConfig();
+ List<String> initialized = new ArrayList<String>();
+ while (generator.next()) {
+ // Ignore entry if URL is already present in config file
+ if (generator.getConfigUrl() != null)
+ continue;
+
+ String path = generator.getPath();
+ // Copy 'url' and 'update' fields from .gitmodules to config
+ // file
+ String url = generator.getModulesUrl();
+ String update = generator.getModulesUpdate();
+ if (url != null)
+ config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
+ path, ConfigConstants.CONFIG_KEY_URL, url);
+ if (update != null)
+ config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
+ path, ConfigConstants.CONFIG_KEY_UPDATE, update);
+ if (url != null || update != null)
+ initialized.add(path);
+ }
+ // Save repository config if any values were updated
+ if (!initialized.isEmpty())
+ config.save();
+ return initialized;
+ } catch (IOException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ } catch (ConfigInvalidException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java
new file mode 100644
index 0000000000..0542583785
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleStatusCommand.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2011, GitHub Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.submodule.SubmoduleWalk;
+import org.eclipse.jgit.submodule.SubmoduleStatus;
+import org.eclipse.jgit.submodule.SubmoduleStatusType;
+import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+
+/**
+ * A class used to execute a submodule status command.
+ *
+ * @see <a
+ * href="http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html"
+ * >Git documentation about submodules</a>
+ */
+public class SubmoduleStatusCommand extends
+ GitCommand<Map<String, SubmoduleStatus>> {
+
+ private final Collection<String> paths;
+
+ /**
+ * @param repo
+ */
+ public SubmoduleStatusCommand(final Repository repo) {
+ super(repo);
+ paths = new ArrayList<String>();
+ }
+
+ /**
+ * Add repository-relative submodule path to limit status reporting to
+ *
+ * @param path
+ * @return this command
+ */
+ public SubmoduleStatusCommand addPath(final String path) {
+ paths.add(path);
+ return this;
+ }
+
+ public Map<String, SubmoduleStatus> call() throws JGitInternalException {
+ checkCallable();
+
+ try {
+ SubmoduleWalk generator = SubmoduleWalk.forIndex(repo);
+ if (!paths.isEmpty())
+ generator.setFilter(PathFilterGroup.createFromStrings(paths));
+ Map<String, SubmoduleStatus> statuses = new HashMap<String, SubmoduleStatus>();
+ while (generator.next()) {
+ SubmoduleStatus status = getStatus(generator);
+ statuses.put(status.getPath(), status);
+ }
+ return statuses;
+ } catch (IOException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ } catch (ConfigInvalidException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+
+ private SubmoduleStatus getStatus(SubmoduleWalk generator)
+ throws IOException, ConfigInvalidException {
+ ObjectId id = generator.getObjectId();
+ String path = generator.getPath();
+
+ // Report missing if no path in .gitmodules file
+ if (generator.getModulesPath() == null)
+ return new SubmoduleStatus(SubmoduleStatusType.MISSING, path, id);
+
+ // Report uninitialized if no URL in config file
+ if (generator.getConfigUrl() == null)
+ return new SubmoduleStatus(SubmoduleStatusType.UNINITIALIZED, path,
+ id);
+
+ // Report uninitialized if no submodule repository
+ Repository subRepo = generator.getRepository();
+ if (subRepo == null)
+ return new SubmoduleStatus(SubmoduleStatusType.UNINITIALIZED, path,
+ id);
+
+ ObjectId headId = subRepo.resolve(Constants.HEAD);
+
+ // Report uninitialized if no HEAD commit in submodule repository
+ if (headId == null)
+ return new SubmoduleStatus(SubmoduleStatusType.UNINITIALIZED, path,
+ id, headId);
+
+ // Report checked out if HEAD commit is different than index commit
+ if (!headId.equals(id))
+ return new SubmoduleStatus(SubmoduleStatusType.REV_CHECKED_OUT,
+ path, id, headId);
+
+ // Report initialized if HEAD commit is the same as the index commit
+ return new SubmoduleStatus(SubmoduleStatusType.INITIALIZED, path, id,
+ headId);
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java
new file mode 100644
index 0000000000..43647a0c69
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleSyncCommand.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2011, GitHub Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.submodule.SubmoduleWalk;
+import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+
+/**
+ * A class used to execute a submodule sync command.
+ *
+ * This will set the remote URL in a submodule's repository to the current value
+ * in the .gitmodules file.
+ *
+ * @see <a
+ * href="http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html"
+ * >Git documentation about submodules</a>
+ */
+public class SubmoduleSyncCommand extends GitCommand<Map<String, String>> {
+
+ private final Collection<String> paths;
+
+ /**
+ * @param repo
+ */
+ public SubmoduleSyncCommand(final Repository repo) {
+ super(repo);
+ paths = new ArrayList<String>();
+ }
+
+ /**
+ * Add repository-relative submodule path to synchronize
+ *
+ * @param path
+ * @return this command
+ */
+ public SubmoduleSyncCommand addPath(final String path) {
+ paths.add(path);
+ return this;
+ }
+
+ /**
+ * Get branch that HEAD currently points to
+ *
+ * @param subRepo
+ * @return shortened branch name, null on failures
+ * @throws IOException
+ */
+ protected String getHeadBranch(final Repository subRepo) throws IOException {
+ Ref head = subRepo.getRef(Constants.HEAD);
+ if (head != null && head.isSymbolic())
+ return Repository.shortenRefName(head.getLeaf().getName());
+ else
+ return null;
+ }
+
+ public Map<String, String> call() throws JGitInternalException {
+ checkCallable();
+
+ try {
+ SubmoduleWalk generator = SubmoduleWalk.forIndex(repo);
+ if (!paths.isEmpty())
+ generator.setFilter(PathFilterGroup.createFromStrings(paths));
+ Map<String, String> synced = new HashMap<String, String>();
+ StoredConfig config = repo.getConfig();
+ while (generator.next()) {
+ String remoteUrl = generator.getModulesUrl();
+ if (remoteUrl == null)
+ continue;
+
+ String path = generator.getPath();
+ config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
+ path, ConfigConstants.CONFIG_KEY_URL, remoteUrl);
+ synced.put(path, remoteUrl);
+
+ Repository subRepo = generator.getRepository();
+ if (subRepo == null)
+ continue;
+
+ StoredConfig subConfig = subRepo.getConfig();
+ // Get name of remote associated with current branch and
+ // fall back to default remote name as last resort
+ String branch = getHeadBranch(subRepo);
+ String remote = null;
+ if (branch != null)
+ remote = subConfig.getString(
+ ConfigConstants.CONFIG_BRANCH_SECTION, branch,
+ ConfigConstants.CONFIG_KEY_REMOTE);
+ if (remote == null)
+ remote = Constants.DEFAULT_REMOTE_NAME;
+
+ subConfig.setString(ConfigConstants.CONFIG_REMOTE_SECTION,
+ remote, ConfigConstants.CONFIG_KEY_URL, remoteUrl);
+ subConfig.save();
+ }
+ if (!synced.isEmpty())
+ config.save();
+ return synced;
+ } catch (IOException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ } catch (ConfigInvalidException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
new file mode 100644
index 0000000000..72daa489eb
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/SubmoduleUpdateCommand.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2011, GitHub Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.dircache.DirCacheCheckout;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
+import org.eclipse.jgit.lib.ProgressMonitor;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.submodule.SubmoduleWalk;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
+
+/**
+ * A class used to execute a submodule update command.
+ *
+ * @see <a
+ * href="http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html"
+ * >Git documentation about submodules</a>
+ */
+public class SubmoduleUpdateCommand extends GitCommand<Collection<String>> {
+
+ private ProgressMonitor monitor;
+
+ private CredentialsProvider credentialsProvider;
+
+ private final Collection<String> paths;
+
+ /**
+ * @param repo
+ */
+ public SubmoduleUpdateCommand(final Repository repo) {
+ super(repo);
+ paths = new ArrayList<String>();
+ }
+
+ /**
+ * The progress monitor associated with the clone operation. By default,
+ * this is set to <code>NullProgressMonitor</code>
+ *
+ * @see NullProgressMonitor
+ * @param monitor
+ * @return this command
+ */
+ public SubmoduleUpdateCommand setProgressMonitor(
+ final ProgressMonitor monitor) {
+ this.monitor = monitor;
+ return this;
+ }
+
+ /**
+ * @param credentialsProvider
+ * the {@link CredentialsProvider} to use
+ * @return this command
+ */
+ public SubmoduleUpdateCommand setCredentialsProvider(
+ final CredentialsProvider credentialsProvider) {
+ this.credentialsProvider = credentialsProvider;
+ return this;
+ }
+
+ /**
+ * Add repository-relative submodule path to initialize
+ *
+ * @param path
+ * @return this command
+ */
+ public SubmoduleUpdateCommand addPath(final String path) {
+ paths.add(path);
+ return this;
+ }
+
+ public Collection<String> call() throws JGitInternalException {
+ checkCallable();
+
+ try {
+ SubmoduleWalk generator = SubmoduleWalk.forIndex(repo);
+ if (!paths.isEmpty())
+ generator.setFilter(PathFilterGroup.createFromStrings(paths));
+ List<String> updated = new ArrayList<String>();
+ while (generator.next()) {
+ // Skip submodules not registered in .gitmodules file
+ if (generator.getModulesPath() == null)
+ continue;
+ // Skip submodules not registered in parent repository's config
+ String url = generator.getConfigUrl();
+ if (url == null)
+ continue;
+
+ Repository submoduleRepo = generator.getRepository();
+ // Clone repository is not present
+ if (submoduleRepo == null) {
+ CloneCommand clone = Git.cloneRepository();
+ clone.setURI(url);
+ clone.setDirectory(generator.getDirectory());
+ if (monitor != null)
+ clone.setProgressMonitor(monitor);
+ if (credentialsProvider != null)
+ clone.setCredentialsProvider(credentialsProvider);
+ submoduleRepo = clone.call().getRepository();
+ }
+
+ RevWalk walk = new RevWalk(submoduleRepo);
+ RevCommit commit = walk.parseCommit(generator.getObjectId());
+
+ String update = generator.getConfigUpdate();
+ if (ConfigConstants.CONFIG_KEY_MERGE.equals(update)) {
+ MergeCommand merge = new MergeCommand(submoduleRepo);
+ merge.include(commit);
+ merge.call();
+ } else if (ConfigConstants.CONFIG_KEY_REBASE.equals(update)) {
+ RebaseCommand rebase = new RebaseCommand(submoduleRepo);
+ rebase.setUpstream(commit);
+ rebase.call();
+ } else {
+ // Checkout commit referenced in parent repository's index
+ // as a detached HEAD
+ DirCacheCheckout co = new DirCacheCheckout(submoduleRepo,
+ submoduleRepo.lockDirCache(), commit.getTree());
+ co.setFailOnConflict(true);
+ co.checkout();
+ RefUpdate refUpdate = submoduleRepo.updateRef(
+ Constants.HEAD, true);
+ refUpdate.setNewObjectId(commit);
+ refUpdate.forceUpdate();
+ }
+ updated.add(generator.getPath());
+ }
+ return updated;
+ } catch (IOException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ } catch (ConfigInvalidException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ } catch (GitAPIException e) {
+ throw new JGitInternalException(e.getMessage(), e);
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index 41862c7e41..ce11e66630 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -72,6 +72,9 @@ public class ConfigConstants {
/** The "workflow" section */
public static final String CONFIG_WORKFLOW_SECTION = "workflow";
+ /** The "submodule" section */
+ public static final String CONFIG_SUBMODULE_SECTION = "submodule";
+
/** The "algorithm" key */
public static final String CONFIG_KEY_ALGORITHM = "algorithm";
@@ -160,4 +163,10 @@ public class ConfigConstants {
/** The "defaultsourceref" key */
public static final String CONFIG_KEY_DEFBRANCHSTARTPOINT = "defbranchstartpoint";
+
+ /** The "path" key */
+ public static final String CONFIG_KEY_PATH = "path";
+
+ /** The "update" key */
+ public static final String CONFIG_KEY_UPDATE = "update";
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index 1d77cc174f..6621e54c5a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -328,6 +328,9 @@ public final class Constants {
/** Name of the ignore file */
public static final String DOT_GIT_IGNORE = ".gitignore";
+ /** Name of the submodules file */
+ public static final String DOT_GIT_MODULES = ".gitmodules";
+
/**
* Create a new digest function for objects.
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleStatus.java b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleStatus.java
new file mode 100644
index 0000000000..2298f4b17c
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleStatus.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2011, GitHub Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.submodule;
+
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * Status class containing the type, path, and commit id of the submodule.
+ */
+public class SubmoduleStatus {
+
+ private final SubmoduleStatusType type;
+
+ private final String path;
+
+ private final ObjectId indexId;
+
+ private final ObjectId headId;
+
+ /**
+ * Create submodule status
+ *
+ * @param type
+ * @param path
+ * @param indexId
+ */
+ public SubmoduleStatus(final SubmoduleStatusType type, final String path,
+ final ObjectId indexId) {
+ this(type, path, indexId, null);
+ }
+
+ /**
+ * Create submodule status
+ *
+ * @param type
+ * @param path
+ * @param indexId
+ * @param headId
+ */
+ public SubmoduleStatus(final SubmoduleStatusType type, final String path,
+ final ObjectId indexId, final ObjectId headId) {
+ this.type = type;
+ this.path = path;
+ this.indexId = indexId;
+ this.headId = headId;
+ }
+
+ /**
+ * @return type
+ */
+ public SubmoduleStatusType getType() {
+ return type;
+ }
+
+ /**
+ * @return path
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * @return index object id
+ */
+ public ObjectId getIndexId() {
+ return indexId;
+ }
+
+ /**
+ * @return HEAD object id
+ */
+ public ObjectId getHeadId() {
+ return headId;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleStatusType.java b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleStatusType.java
new file mode 100644
index 0000000000..be145f3d60
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleStatusType.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011, GitHub Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.submodule;
+
+/**
+ * Enumeration of different statuses that a submodule can be in
+ */
+public enum SubmoduleStatusType {
+
+ /** Submodule's configuration is missing */
+ MISSING,
+
+ /** Submodule's Git repository is not initialized */
+ UNINITIALIZED,
+
+ /** Submodule's Git repository is initialized */
+ INITIALIZED,
+
+ /**
+ * Submodule commit checked out is different than the commit referenced in
+ * the index tree
+ */
+ REV_CHECKED_OUT;
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
new file mode 100644
index 0000000000..ebe994826e
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/submodule/SubmoduleWalk.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2011, GitHub Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.submodule;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.jgit.dircache.DirCacheIterator;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ConfigConstants;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryBuilder;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.treewalk.AbstractTreeIterator;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.PathFilter;
+import org.eclipse.jgit.treewalk.filter.TreeFilter;
+import org.eclipse.jgit.util.FS;
+
+/**
+ * Walker that visits all submodule entries found in a tree
+ */
+public class SubmoduleWalk {
+
+ /**
+ * Create a generator to walk over the submodule entries currently in the
+ * index
+ *
+ * @param repository
+ * @return generator over submodule index entries
+ * @throws IOException
+ */
+ public static SubmoduleWalk forIndex(Repository repository)
+ throws IOException {
+ SubmoduleWalk generator = new SubmoduleWalk(repository);
+ generator.setTree(new DirCacheIterator(repository.readDirCache()));
+ return generator;
+ }
+
+ /**
+ * Create a generator and advance it to the submodule entry at the given
+ * path
+ *
+ * @param repository
+ * @param treeId
+ * @param path
+ * @return generator at given path, null if no submodule at given path
+ * @throws IOException
+ */
+ public static SubmoduleWalk forPath(Repository repository,
+ AnyObjectId treeId, String path) throws IOException {
+ SubmoduleWalk generator = new SubmoduleWalk(repository);
+ generator.setTree(treeId);
+ PathFilter filter = PathFilter.create(path);
+ generator.setFilter(filter);
+ while (generator.next())
+ if (filter.isDone(generator.walk))
+ return generator;
+ return null;
+ }
+
+ /**
+ * Create a generator and advance it to the submodule entry at the given
+ * path
+ *
+ * @param repository
+ * @param iterator
+ * @param path
+ * @return generator at given path, null if no submodule at given path
+ * @throws IOException
+ */
+ public static SubmoduleWalk forPath(Repository repository,
+ AbstractTreeIterator iterator, String path) throws IOException {
+ SubmoduleWalk generator = new SubmoduleWalk(repository);
+ generator.setTree(iterator);
+ PathFilter filter = PathFilter.create(path);
+ generator.setFilter(filter);
+ while (generator.next())
+ if (filter.isDone(generator.walk))
+ return generator;
+ return null;
+ }
+
+ /**
+ * Get submodule directory
+ *
+ * @param parent
+ * @param path
+ * @return directory
+ */
+ public static File getSubmoduleDirectory(final Repository parent,
+ final String path) {
+ return new File(parent.getWorkTree(), path);
+ }
+
+ /**
+ * Get submodule repository
+ *
+ * @param parent
+ * @param path
+ * @return repository or null if repository doesn't exist
+ * @throws IOException
+ */
+ public static Repository getSubmoduleRepository(final Repository parent,
+ final String path) throws IOException {
+ File directory = getSubmoduleGitDirectory(parent, path);
+ if (!directory.isDirectory())
+ return null;
+ try {
+ return new RepositoryBuilder().setMustExist(true)
+ .setFS(FS.DETECTED).setGitDir(directory).build();
+ } catch (RepositoryNotFoundException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Get the .git directory for a repository submodule path
+ *
+ * @param parent
+ * @param path
+ * @return .git for submodule repository
+ */
+ public static File getSubmoduleGitDirectory(final Repository parent,
+ final String path) {
+ return new File(getSubmoduleDirectory(parent, path), Constants.DOT_GIT);
+ }
+
+ private final Repository repository;
+
+ private final TreeWalk walk;
+
+ private StoredConfig repoConfig;
+
+ private FileBasedConfig modulesConfig;
+
+ private String path;
+
+ /**
+ * Create submodule generator
+ *
+ * @param repository
+ * @throws IOException
+ */
+ public SubmoduleWalk(final Repository repository) throws IOException {
+ this.repository = repository;
+ repoConfig = repository.getConfig();
+ walk = new TreeWalk(repository);
+ walk.setRecursive(true);
+ }
+
+ private void loadModulesConfig() throws IOException, ConfigInvalidException {
+ if (modulesConfig == null) {
+ File modulesFile = new File(repository.getWorkTree(),
+ Constants.DOT_GIT_MODULES);
+ FileBasedConfig config = new FileBasedConfig(modulesFile,
+ repository.getFS());
+ config.load();
+ modulesConfig = config;
+ }
+ }
+
+ /**
+ * Set tree filter
+ *
+ * @param filter
+ * @return this generator
+ */
+ public SubmoduleWalk setFilter(TreeFilter filter) {
+ walk.setFilter(filter);
+ return this;
+ }
+
+ /**
+ * Set the tree iterator used for finding submodule entries
+ *
+ * @param iterator
+ * @return this generator
+ * @throws CorruptObjectException
+ */
+ public SubmoduleWalk setTree(final AbstractTreeIterator iterator)
+ throws CorruptObjectException {
+ walk.addTree(iterator);
+ return this;
+ }
+
+ /**
+ * Set the tree used for finding submodule entries
+ *
+ * @param treeId
+ * @return this generator
+ * @throws IOException
+ * @throws IncorrectObjectTypeException
+ * @throws MissingObjectException
+ */
+ public SubmoduleWalk setTree(final AnyObjectId treeId) throws IOException {
+ walk.addTree(treeId);
+ return this;
+ }
+
+ /**
+ * Reset generator and start new submodule walk
+ *
+ * @return this generator
+ */
+ public SubmoduleWalk reset() {
+ repoConfig = repository.getConfig();
+ modulesConfig = null;
+ walk.reset();
+ return this;
+ }
+
+ /**
+ * Get directory that will be the root of the submodule's local repository
+ *
+ * @return submodule repository directory
+ */
+ public File getDirectory() {
+ return getSubmoduleDirectory(repository, path);
+ }
+
+ /**
+ * Get the .git directory for the current submodule entry
+ *
+ * @return .git for submodule repository
+ */
+ public File getGitDirectory() {
+ return getSubmoduleGitDirectory(repository, path);
+ }
+
+ /**
+ * Advance to next submodule in the index tree.
+ *
+ * The object id and path of the next entry can be obtained by calling
+ * {@link #getObjectId()} and {@link #getPath()}.
+ *
+ * @return true if entry found, false otherwise
+ * @throws IOException
+ */
+ public boolean next() throws IOException {
+ while (walk.next()) {
+ if (FileMode.GITLINK != walk.getFileMode(0))
+ continue;
+ path = walk.getPathString();
+ return true;
+ }
+ path = null;
+ return false;
+ }
+
+ /**
+ * Get path of current submodule entry
+ *
+ * @return path
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * Get object id of current submodule entry
+ *
+ * @return object id
+ */
+ public ObjectId getObjectId() {
+ return walk.getObjectId(0);
+ }
+
+ /**
+ * Get the configured path for current entry. This will be the value from
+ * the .gitmodules file in the current repository's working tree.
+ *
+ * @return configured path
+ * @throws ConfigInvalidException
+ * @throws IOException
+ */
+ public String getModulesPath() throws IOException, ConfigInvalidException {
+ loadModulesConfig();
+ return modulesConfig.getString(
+ ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_PATH);
+ }
+
+ /**
+ * Get the configured remote URL for current entry. This will be the value
+ * from the repository's config.
+ *
+ * @return configured URL
+ * @throws ConfigInvalidException
+ * @throws IOException
+ */
+ public String getConfigUrl() throws IOException, ConfigInvalidException {
+ return repoConfig.getString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
+ path, ConfigConstants.CONFIG_KEY_URL);
+ }
+
+ /**
+ * Get the configured remote URL for current entry. This will be the value
+ * from the .gitmodules file in the current repository's working tree.
+ *
+ * @return configured URL
+ * @throws ConfigInvalidException
+ * @throws IOException
+ */
+ public String getModulesUrl() throws IOException, ConfigInvalidException {
+ loadModulesConfig();
+ return modulesConfig.getString(
+ ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_URL);
+ }
+
+ /**
+ * Get the configured update field for current entry. This will be the value
+ * from the repository's config.
+ *
+ * @return update value
+ * @throws ConfigInvalidException
+ * @throws IOException
+ */
+ public String getConfigUpdate() throws IOException, ConfigInvalidException {
+ return repoConfig.getString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
+ path, ConfigConstants.CONFIG_KEY_UPDATE);
+ }
+
+ /**
+ * Get the configured update field for current entry. This will be the value
+ * from the .gitmodules file in the current repository's working tree.
+ *
+ * @return update value
+ * @throws ConfigInvalidException
+ * @throws IOException
+ */
+ public String getModulesUpdate() throws IOException, ConfigInvalidException {
+ loadModulesConfig();
+ return modulesConfig.getString(
+ ConfigConstants.CONFIG_SUBMODULE_SECTION, path,
+ ConfigConstants.CONFIG_KEY_UPDATE);
+ }
+
+ /**
+ * Does the current submodule entry have a .git directory in the parent
+ * repository's working tree?
+ *
+ * @return true if .git directory exists, false otherwise
+ */
+ public boolean hasGitDirectory() {
+ return getGitDirectory().isDirectory();
+ }
+
+ /**
+ * Get repository for current submodule entry
+ *
+ * @see #hasGitDirectory()
+ * @return repository or null if non-existent
+ * @throws IOException
+ */
+ public Repository getRepository() throws IOException {
+ return getSubmoduleRepository(repository, path);
+ }
+
+ /**
+ * Get commit id that HEAD points to in the current submodule's repository
+ *
+ * @return object id of HEAD reference
+ * @throws IOException
+ */
+ public ObjectId getHead() throws IOException {
+ Repository subRepo = getRepository();
+ return subRepo != null ? subRepo.resolve(Constants.HEAD) : null;
+ }
+
+ /**
+ * Get ref that HEAD points to in the current submodule's repository
+ *
+ * @return ref name, null on failures
+ * @throws IOException
+ */
+ public String getHeadRef() throws IOException {
+ Repository subRepo = getRepository();
+ if (subRepo == null)
+ return null;
+ Ref head = subRepo.getRef(Constants.HEAD);
+ return head != null ? head.getLeaf().getName() : null;
+ }
+}