diff options
author | Kevin Sawicki <kevin@github.com> | 2011-12-19 09:38:11 -0800 |
---|---|---|
committer | Chris Aniszczyk <zx@twitter.com> | 2011-12-28 10:33:14 -0600 |
commit | 92c6f2f97bb478ac0d4731cacafaef52ac0d08d7 (patch) | |
tree | 211b11ae74bf4116cc12b30db6d80e256e671cd0 /org.eclipse.jgit/src/org/eclipse | |
parent | 3c7dceec1a7dc367a3e716331bacc37b74e3f6e0 (diff) | |
download | jgit-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')
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; + } +} |