SubmoduleAddCommand.java

  1. /*
  2.  * Copyright (C) 2011, GitHub Inc. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */
  10. package org.eclipse.jgit.api;

  11. import java.io.File;
  12. import java.io.IOException;
  13. import java.text.MessageFormat;

  14. import org.eclipse.jgit.api.errors.GitAPIException;
  15. import org.eclipse.jgit.api.errors.JGitInternalException;
  16. import org.eclipse.jgit.api.errors.NoFilepatternException;
  17. import org.eclipse.jgit.errors.ConfigInvalidException;
  18. import org.eclipse.jgit.internal.JGitText;
  19. import org.eclipse.jgit.internal.submodule.SubmoduleValidator;
  20. import org.eclipse.jgit.lib.ConfigConstants;
  21. import org.eclipse.jgit.lib.Constants;
  22. import org.eclipse.jgit.lib.NullProgressMonitor;
  23. import org.eclipse.jgit.lib.ProgressMonitor;
  24. import org.eclipse.jgit.lib.Repository;
  25. import org.eclipse.jgit.lib.StoredConfig;
  26. import org.eclipse.jgit.storage.file.FileBasedConfig;
  27. import org.eclipse.jgit.submodule.SubmoduleWalk;
  28. import org.eclipse.jgit.treewalk.filter.PathFilter;
  29. import org.eclipse.jgit.treewalk.filter.TreeFilter;

  30. /**
  31.  * A class used to execute a submodule add command.
  32.  *
  33.  * This will clone the configured submodule, register the submodule in the
  34.  * .gitmodules file and the repository config file, and also add the submodule
  35.  * and .gitmodules file to the index.
  36.  *
  37.  * @see <a href=
  38.  *      "http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html"
  39.  *      >Git documentation about submodules</a>
  40.  */
  41. public class SubmoduleAddCommand extends
  42.         TransportCommand<SubmoduleAddCommand, Repository> {

  43.     private String name;

  44.     private String path;

  45.     private String uri;

  46.     private ProgressMonitor monitor;

  47.     /**
  48.      * Constructor for SubmoduleAddCommand.
  49.      *
  50.      * @param repo
  51.      *            a {@link org.eclipse.jgit.lib.Repository} object.
  52.      */
  53.     public SubmoduleAddCommand(Repository repo) {
  54.         super(repo);
  55.     }

  56.     /**
  57.      * Set the submodule name
  58.      *
  59.      * @param name
  60.      * @return this command
  61.      * @since 5.1
  62.      */
  63.     public SubmoduleAddCommand setName(String name) {
  64.         this.name = name;
  65.         return this;
  66.     }

  67.     /**
  68.      * Set repository-relative path of submodule
  69.      *
  70.      * @param path
  71.      *            (with <code>/</code> as separator)
  72.      * @return this command
  73.      */
  74.     public SubmoduleAddCommand setPath(String path) {
  75.         this.path = path;
  76.         return this;
  77.     }

  78.     /**
  79.      * Set URI to clone submodule from
  80.      *
  81.      * @param uri
  82.      *            a {@link java.lang.String} object.
  83.      * @return this command
  84.      */
  85.     public SubmoduleAddCommand setURI(String uri) {
  86.         this.uri = uri;
  87.         return this;
  88.     }

  89.     /**
  90.      * The progress monitor associated with the clone operation. By default,
  91.      * this is set to <code>NullProgressMonitor</code>
  92.      *
  93.      * @see NullProgressMonitor
  94.      * @param monitor
  95.      *            a {@link org.eclipse.jgit.lib.ProgressMonitor} object.
  96.      * @return this command
  97.      */
  98.     public SubmoduleAddCommand setProgressMonitor(ProgressMonitor monitor) {
  99.         this.monitor = monitor;
  100.         return this;
  101.     }

  102.     /**
  103.      * Is the configured already a submodule in the index?
  104.      *
  105.      * @return true if submodule exists in index, false otherwise
  106.      * @throws java.io.IOException
  107.      */
  108.     protected boolean submoduleExists() throws IOException {
  109.         TreeFilter filter = PathFilter.create(path);
  110.         try (SubmoduleWalk w = SubmoduleWalk.forIndex(repo)) {
  111.             return w.setFilter(filter).next();
  112.         }
  113.     }

  114.     /**
  115.      * {@inheritDoc}
  116.      * <p>
  117.      * Executes the {@code SubmoduleAddCommand}
  118.      *
  119.      * The {@code Repository} instance returned by this command needs to be
  120.      * closed by the caller to free resources held by the {@code Repository}
  121.      * instance. It is recommended to call this method as soon as you don't need
  122.      * a reference to this {@code Repository} instance anymore.
  123.      */
  124.     @Override
  125.     public Repository call() throws GitAPIException {
  126.         checkCallable();
  127.         if (path == null || path.length() == 0)
  128.             throw new IllegalArgumentException(JGitText.get().pathNotConfigured);
  129.         if (uri == null || uri.length() == 0)
  130.             throw new IllegalArgumentException(JGitText.get().uriNotConfigured);
  131.         if (name == null || name.length() == 0) {
  132.             // Use the path as the default.
  133.             name = path;
  134.         }

  135.         try {
  136.             SubmoduleValidator.assertValidSubmoduleName(name);
  137.             SubmoduleValidator.assertValidSubmodulePath(path);
  138.             SubmoduleValidator.assertValidSubmoduleUri(uri);
  139.         } catch (SubmoduleValidator.SubmoduleValidationException e) {
  140.             throw new IllegalArgumentException(e.getMessage());
  141.         }

  142.         try {
  143.             if (submoduleExists())
  144.                 throw new JGitInternalException(MessageFormat.format(
  145.                         JGitText.get().submoduleExists, path));
  146.         } catch (IOException e) {
  147.             throw new JGitInternalException(e.getMessage(), e);
  148.         }

  149.         final String resolvedUri;
  150.         try {
  151.             resolvedUri = SubmoduleWalk.getSubmoduleRemoteUrl(repo, uri);
  152.         } catch (IOException e) {
  153.             throw new JGitInternalException(e.getMessage(), e);
  154.         }
  155.         // Clone submodule repository
  156.         File moduleDirectory = SubmoduleWalk.getSubmoduleDirectory(repo, path);
  157.         CloneCommand clone = Git.cloneRepository();
  158.         configure(clone);
  159.         clone.setDirectory(moduleDirectory);
  160.         clone.setGitDir(new File(new File(repo.getDirectory(),
  161.                 Constants.MODULES), path));
  162.         clone.setURI(resolvedUri);
  163.         if (monitor != null)
  164.             clone.setProgressMonitor(monitor);
  165.         Repository subRepo = null;
  166.         try (Git git = clone.call()) {
  167.             subRepo = git.getRepository();
  168.             subRepo.incrementOpen();
  169.         }

  170.         // Save submodule URL to parent repository's config
  171.         StoredConfig config = repo.getConfig();
  172.         config.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION, name,
  173.                 ConfigConstants.CONFIG_KEY_URL, resolvedUri);
  174.         try {
  175.             config.save();
  176.         } catch (IOException e) {
  177.             throw new JGitInternalException(e.getMessage(), e);
  178.         }

  179.         // Save path and URL to parent repository's .gitmodules file
  180.         FileBasedConfig modulesConfig = new FileBasedConfig(new File(
  181.                 repo.getWorkTree(), Constants.DOT_GIT_MODULES), repo.getFS());
  182.         try {
  183.             modulesConfig.load();
  184.             modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
  185.                     name, ConfigConstants.CONFIG_KEY_PATH, path);
  186.             modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
  187.                     name, ConfigConstants.CONFIG_KEY_URL, uri);
  188.             modulesConfig.save();
  189.         } catch (IOException | ConfigInvalidException e) {
  190.             throw new JGitInternalException(e.getMessage(), e);
  191.         }

  192.         AddCommand add = new AddCommand(repo);
  193.         // Add .gitmodules file to parent repository's index
  194.         add.addFilepattern(Constants.DOT_GIT_MODULES);
  195.         // Add submodule directory to parent repository's index
  196.         add.addFilepattern(path);
  197.         try {
  198.             add.call();
  199.         } catch (NoFilepatternException e) {
  200.             throw new JGitInternalException(e.getMessage(), e);
  201.         }

  202.         return subRepo;
  203.     }
  204. }