SubmoduleAddCommand.java

  1. /*
  2.  * Copyright (C) 2011, GitHub Inc.
  3.  * and other copyright owners as documented in the project's IP log.
  4.  *
  5.  * This program and the accompanying materials are made available
  6.  * under the terms of the Eclipse Distribution License v1.0 which
  7.  * accompanies this distribution, is reproduced below, and is
  8.  * available at http://www.eclipse.org/org/documents/edl-v10.php
  9.  *
  10.  * All rights reserved.
  11.  *
  12.  * Redistribution and use in source and binary forms, with or
  13.  * without modification, are permitted provided that the following
  14.  * conditions are met:
  15.  *
  16.  * - Redistributions of source code must retain the above copyright
  17.  *   notice, this list of conditions and the following disclaimer.
  18.  *
  19.  * - Redistributions in binary form must reproduce the above
  20.  *   copyright notice, this list of conditions and the following
  21.  *   disclaimer in the documentation and/or other materials provided
  22.  *   with the distribution.
  23.  *
  24.  * - Neither the name of the Eclipse Foundation, Inc. nor the
  25.  *   names of its contributors may be used to endorse or promote
  26.  *   products derived from this software without specific prior
  27.  *   written permission.
  28.  *
  29.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30.  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41.  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42.  */
  43. package org.eclipse.jgit.api;

  44. import java.io.File;
  45. import java.io.IOException;
  46. import java.text.MessageFormat;

  47. import org.eclipse.jgit.api.errors.GitAPIException;
  48. import org.eclipse.jgit.api.errors.JGitInternalException;
  49. import org.eclipse.jgit.api.errors.NoFilepatternException;
  50. import org.eclipse.jgit.errors.ConfigInvalidException;
  51. import org.eclipse.jgit.internal.JGitText;
  52. import org.eclipse.jgit.internal.submodule.SubmoduleValidator;
  53. import org.eclipse.jgit.lib.ConfigConstants;
  54. import org.eclipse.jgit.lib.Constants;
  55. import org.eclipse.jgit.lib.NullProgressMonitor;
  56. import org.eclipse.jgit.lib.ProgressMonitor;
  57. import org.eclipse.jgit.lib.Repository;
  58. import org.eclipse.jgit.lib.StoredConfig;
  59. import org.eclipse.jgit.storage.file.FileBasedConfig;
  60. import org.eclipse.jgit.submodule.SubmoduleWalk;
  61. import org.eclipse.jgit.treewalk.filter.PathFilter;
  62. import org.eclipse.jgit.treewalk.filter.TreeFilter;

  63. /**
  64.  * A class used to execute a submodule add command.
  65.  *
  66.  * This will clone the configured submodule, register the submodule in the
  67.  * .gitmodules file and the repository config file, and also add the submodule
  68.  * and .gitmodules file to the index.
  69.  *
  70.  * @see <a href=
  71.  *      "http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html"
  72.  *      >Git documentation about submodules</a>
  73.  */
  74. public class SubmoduleAddCommand extends
  75.         TransportCommand<SubmoduleAddCommand, Repository> {

  76.     private String name;

  77.     private String path;

  78.     private String uri;

  79.     private ProgressMonitor monitor;

  80.     /**
  81.      * Constructor for SubmoduleAddCommand.
  82.      *
  83.      * @param repo
  84.      *            a {@link org.eclipse.jgit.lib.Repository} object.
  85.      */
  86.     public SubmoduleAddCommand(Repository repo) {
  87.         super(repo);
  88.     }

  89.     /**
  90.      * Set the submodule name
  91.      *
  92.      * @param name
  93.      * @return this command
  94.      * @since 5.1
  95.      */
  96.     public SubmoduleAddCommand setName(String name) {
  97.         this.name = name;
  98.         return this;
  99.     }

  100.     /**
  101.      * Set repository-relative path of submodule
  102.      *
  103.      * @param path
  104.      *            (with <code>/</code> as separator)
  105.      * @return this command
  106.      */
  107.     public SubmoduleAddCommand setPath(String path) {
  108.         this.path = path;
  109.         return this;
  110.     }

  111.     /**
  112.      * Set URI to clone submodule from
  113.      *
  114.      * @param uri
  115.      *            a {@link java.lang.String} object.
  116.      * @return this command
  117.      */
  118.     public SubmoduleAddCommand setURI(String uri) {
  119.         this.uri = uri;
  120.         return this;
  121.     }

  122.     /**
  123.      * The progress monitor associated with the clone operation. By default,
  124.      * this is set to <code>NullProgressMonitor</code>
  125.      *
  126.      * @see NullProgressMonitor
  127.      * @param monitor
  128.      *            a {@link org.eclipse.jgit.lib.ProgressMonitor} object.
  129.      * @return this command
  130.      */
  131.     public SubmoduleAddCommand setProgressMonitor(ProgressMonitor monitor) {
  132.         this.monitor = monitor;
  133.         return this;
  134.     }

  135.     /**
  136.      * Is the configured already a submodule in the index?
  137.      *
  138.      * @return true if submodule exists in index, false otherwise
  139.      * @throws java.io.IOException
  140.      */
  141.     protected boolean submoduleExists() throws IOException {
  142.         TreeFilter filter = PathFilter.create(path);
  143.         try (SubmoduleWalk w = SubmoduleWalk.forIndex(repo)) {
  144.             return w.setFilter(filter).next();
  145.         }
  146.     }

  147.     /**
  148.      * {@inheritDoc}
  149.      * <p>
  150.      * Executes the {@code SubmoduleAddCommand}
  151.      *
  152.      * The {@code Repository} instance returned by this command needs to be
  153.      * closed by the caller to free resources held by the {@code Repository}
  154.      * instance. It is recommended to call this method as soon as you don't need
  155.      * a reference to this {@code Repository} instance anymore.
  156.      */
  157.     @Override
  158.     public Repository call() throws GitAPIException {
  159.         checkCallable();
  160.         if (path == null || path.length() == 0)
  161.             throw new IllegalArgumentException(JGitText.get().pathNotConfigured);
  162.         if (uri == null || uri.length() == 0)
  163.             throw new IllegalArgumentException(JGitText.get().uriNotConfigured);
  164.         if (name == null || name.length() == 0) {
  165.             // Use the path as the default.
  166.             name = path;
  167.         }

  168.         try {
  169.             SubmoduleValidator.assertValidSubmoduleName(name);
  170.             SubmoduleValidator.assertValidSubmodulePath(path);
  171.             SubmoduleValidator.assertValidSubmoduleUri(uri);
  172.         } catch (SubmoduleValidator.SubmoduleValidationException e) {
  173.             throw new IllegalArgumentException(e.getMessage());
  174.         }

  175.         try {
  176.             if (submoduleExists())
  177.                 throw new JGitInternalException(MessageFormat.format(
  178.                         JGitText.get().submoduleExists, path));
  179.         } catch (IOException e) {
  180.             throw new JGitInternalException(e.getMessage(), e);
  181.         }

  182.         final String resolvedUri;
  183.         try {
  184.             resolvedUri = SubmoduleWalk.getSubmoduleRemoteUrl(repo, uri);
  185.         } catch (IOException e) {
  186.             throw new JGitInternalException(e.getMessage(), e);
  187.         }
  188.         // Clone submodule repository
  189.         File moduleDirectory = SubmoduleWalk.getSubmoduleDirectory(repo, path);
  190.         CloneCommand clone = Git.cloneRepository();
  191.         configure(clone);
  192.         clone.setDirectory(moduleDirectory);
  193.         clone.setGitDir(new File(new File(repo.getDirectory(),
  194.                 Constants.MODULES), path));
  195.         clone.setURI(resolvedUri);
  196.         if (monitor != null)
  197.             clone.setProgressMonitor(monitor);
  198.         Repository subRepo = null;
  199.         try (Git git = clone.call()) {
  200.             subRepo = git.getRepository();
  201.             subRepo.incrementOpen();
  202.         }

  203.         // Save submodule URL to parent repository's config
  204.         StoredConfig config = repo.getConfig();
  205.         config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, name,
  206.                 ConfigConstants.CONFIG_KEY_URL, resolvedUri);
  207.         try {
  208.             config.save();
  209.         } catch (IOException e) {
  210.             throw new JGitInternalException(e.getMessage(), e);
  211.         }

  212.         // Save path and URL to parent repository's .gitmodules file
  213.         FileBasedConfig modulesConfig = new FileBasedConfig(new File(
  214.                 repo.getWorkTree(), Constants.DOT_GIT_MODULES), repo.getFS());
  215.         try {
  216.             modulesConfig.load();
  217.             modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
  218.                     name, ConfigConstants.CONFIG_KEY_PATH, path);
  219.             modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
  220.                     name, ConfigConstants.CONFIG_KEY_URL, uri);
  221.             modulesConfig.save();
  222.         } catch (IOException | ConfigInvalidException e) {
  223.             throw new JGitInternalException(e.getMessage(), e);
  224.         }

  225.         AddCommand add = new AddCommand(repo);
  226.         // Add .gitmodules file to parent repository's index
  227.         add.addFilepattern(Constants.DOT_GIT_MODULES);
  228.         // Add submodule directory to parent repository's index
  229.         add.addFilepattern(path);
  230.         try {
  231.             add.call();
  232.         } catch (NoFilepatternException e) {
  233.             throw new JGitInternalException(e.getMessage(), e);
  234.         }

  235.         return subRepo;
  236.     }
  237. }