You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SubmoduleWalk.java 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  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.submodule;
  11. import java.io.File;
  12. import java.io.IOException;
  13. import java.text.MessageFormat;
  14. import java.util.HashMap;
  15. import java.util.Map;
  16. import org.eclipse.jgit.dircache.DirCache;
  17. import org.eclipse.jgit.dircache.DirCacheIterator;
  18. import org.eclipse.jgit.errors.ConfigInvalidException;
  19. import org.eclipse.jgit.errors.CorruptObjectException;
  20. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  21. import org.eclipse.jgit.errors.MissingObjectException;
  22. import org.eclipse.jgit.errors.RepositoryNotFoundException;
  23. import org.eclipse.jgit.internal.JGitText;
  24. import org.eclipse.jgit.lib.AnyObjectId;
  25. import org.eclipse.jgit.lib.BaseRepositoryBuilder;
  26. import org.eclipse.jgit.lib.BlobBasedConfig;
  27. import org.eclipse.jgit.lib.Config;
  28. import org.eclipse.jgit.lib.ConfigConstants;
  29. import org.eclipse.jgit.lib.Constants;
  30. import org.eclipse.jgit.lib.FileMode;
  31. import org.eclipse.jgit.lib.ObjectId;
  32. import org.eclipse.jgit.lib.Ref;
  33. import org.eclipse.jgit.lib.Repository;
  34. import org.eclipse.jgit.lib.RepositoryBuilder;
  35. import org.eclipse.jgit.lib.RepositoryBuilderFactory;
  36. import org.eclipse.jgit.lib.StoredConfig;
  37. import org.eclipse.jgit.storage.file.FileBasedConfig;
  38. import org.eclipse.jgit.treewalk.AbstractTreeIterator;
  39. import org.eclipse.jgit.treewalk.CanonicalTreeParser;
  40. import org.eclipse.jgit.treewalk.TreeWalk;
  41. import org.eclipse.jgit.treewalk.filter.PathFilter;
  42. import org.eclipse.jgit.treewalk.filter.TreeFilter;
  43. import org.eclipse.jgit.util.FS;
  44. /**
  45. * Walker that visits all submodule entries found in a tree
  46. */
  47. public class SubmoduleWalk implements AutoCloseable {
  48. /**
  49. * The values for the config parameter submodule.<name>.ignore
  50. *
  51. * @since 3.6
  52. */
  53. public enum IgnoreSubmoduleMode {
  54. /**
  55. * Ignore all modifications to submodules
  56. */
  57. ALL,
  58. /**
  59. * Ignore changes to the working tree of a submodule
  60. */
  61. DIRTY,
  62. /**
  63. * Ignore changes to untracked files in the working tree of a submodule
  64. */
  65. UNTRACKED,
  66. /**
  67. * Ignore nothing. That's the default
  68. */
  69. NONE;
  70. }
  71. /**
  72. * Create a generator to walk over the submodule entries currently in the
  73. * index
  74. *
  75. * The {@code .gitmodules} file is read from the index.
  76. *
  77. * @param repository
  78. * a {@link org.eclipse.jgit.lib.Repository} object.
  79. * @return generator over submodule index entries. The caller is responsible
  80. * for calling {@link #close()}.
  81. * @throws java.io.IOException
  82. */
  83. public static SubmoduleWalk forIndex(Repository repository)
  84. throws IOException {
  85. @SuppressWarnings("resource") // The caller closes it
  86. SubmoduleWalk generator = new SubmoduleWalk(repository);
  87. try {
  88. DirCache index = repository.readDirCache();
  89. generator.setTree(new DirCacheIterator(index));
  90. } catch (IOException e) {
  91. generator.close();
  92. throw e;
  93. }
  94. return generator;
  95. }
  96. /**
  97. * Create a generator and advance it to the submodule entry at the given
  98. * path
  99. *
  100. * @param repository
  101. * a {@link org.eclipse.jgit.lib.Repository} object.
  102. * @param treeId
  103. * the root of a tree containing both a submodule at the given
  104. * path and .gitmodules at the root.
  105. * @param path
  106. * a {@link java.lang.String} object.
  107. * @return generator at given path. The caller is responsible for calling
  108. * {@link #close()}. Null if no submodule at given path.
  109. * @throws java.io.IOException
  110. */
  111. public static SubmoduleWalk forPath(Repository repository,
  112. AnyObjectId treeId, String path) throws IOException {
  113. SubmoduleWalk generator = new SubmoduleWalk(repository);
  114. try {
  115. generator.setTree(treeId);
  116. PathFilter filter = PathFilter.create(path);
  117. generator.setFilter(filter);
  118. generator.setRootTree(treeId);
  119. while (generator.next())
  120. if (filter.isDone(generator.walk))
  121. return generator;
  122. } catch (IOException e) {
  123. generator.close();
  124. throw e;
  125. }
  126. generator.close();
  127. return null;
  128. }
  129. /**
  130. * Create a generator and advance it to the submodule entry at the given
  131. * path
  132. *
  133. * @param repository
  134. * a {@link org.eclipse.jgit.lib.Repository} object.
  135. * @param iterator
  136. * the root of a tree containing both a submodule at the given
  137. * path and .gitmodules at the root.
  138. * @param path
  139. * a {@link java.lang.String} object.
  140. * @return generator at given path. The caller is responsible for calling
  141. * {@link #close()}. Null if no submodule at given path.
  142. * @throws java.io.IOException
  143. */
  144. public static SubmoduleWalk forPath(Repository repository,
  145. AbstractTreeIterator iterator, String path) throws IOException {
  146. SubmoduleWalk generator = new SubmoduleWalk(repository);
  147. try {
  148. generator.setTree(iterator);
  149. PathFilter filter = PathFilter.create(path);
  150. generator.setFilter(filter);
  151. generator.setRootTree(iterator);
  152. while (generator.next())
  153. if (filter.isDone(generator.walk))
  154. return generator;
  155. } catch (IOException e) {
  156. generator.close();
  157. throw e;
  158. }
  159. generator.close();
  160. return null;
  161. }
  162. /**
  163. * Get submodule directory
  164. *
  165. * @param parent
  166. * the {@link org.eclipse.jgit.lib.Repository}.
  167. * @param path
  168. * submodule path
  169. * @return directory
  170. */
  171. public static File getSubmoduleDirectory(final Repository parent,
  172. final String path) {
  173. return new File(parent.getWorkTree(), path);
  174. }
  175. /**
  176. * Get submodule repository
  177. *
  178. * @param parent
  179. * the {@link org.eclipse.jgit.lib.Repository}.
  180. * @param path
  181. * submodule path
  182. * @return repository or null if repository doesn't exist
  183. * @throws java.io.IOException
  184. */
  185. public static Repository getSubmoduleRepository(final Repository parent,
  186. final String path) throws IOException {
  187. return getSubmoduleRepository(parent.getWorkTree(), path,
  188. parent.getFS());
  189. }
  190. /**
  191. * Get submodule repository at path
  192. *
  193. * @param parent
  194. * the parent
  195. * @param path
  196. * submodule path
  197. * @return repository or null if repository doesn't exist
  198. * @throws java.io.IOException
  199. */
  200. public static Repository getSubmoduleRepository(final File parent,
  201. final String path) throws IOException {
  202. return getSubmoduleRepository(parent, path, FS.DETECTED);
  203. }
  204. /**
  205. * Get submodule repository at path, using the specified file system
  206. * abstraction
  207. *
  208. * @param parent
  209. * @param path
  210. * @param fs
  211. * the file system abstraction to be used
  212. * @return repository or null if repository doesn't exist
  213. * @throws IOException
  214. * @since 4.10
  215. */
  216. public static Repository getSubmoduleRepository(final File parent,
  217. final String path, FS fs) throws IOException {
  218. return getSubmoduleRepository(parent, path, fs,
  219. new RepositoryBuilder());
  220. }
  221. /**
  222. * Get submodule repository at path, using the specified file system
  223. * abstraction and the specified builder
  224. *
  225. * @param parent
  226. * {@link Repository} that contains the submodule
  227. * @param path
  228. * of the working tree of the submodule
  229. * @param fs
  230. * {@link FS} to use
  231. * @param builder
  232. * {@link BaseRepositoryBuilder} to use to build the submodule
  233. * repository
  234. * @return the {@link Repository} of the submodule, or {@code null} if it
  235. * doesn't exist
  236. * @throws IOException
  237. * on errors
  238. * @since 5.6
  239. */
  240. public static Repository getSubmoduleRepository(File parent, String path,
  241. FS fs, BaseRepositoryBuilder<?, ? extends Repository> builder)
  242. throws IOException {
  243. File subWorkTree = new File(parent, path);
  244. if (!subWorkTree.isDirectory()) {
  245. return null;
  246. }
  247. try {
  248. return builder //
  249. .setMustExist(true) //
  250. .setFS(fs) //
  251. .setWorkTree(subWorkTree) //
  252. .build();
  253. } catch (RepositoryNotFoundException e) {
  254. return null;
  255. }
  256. }
  257. /**
  258. * Resolve submodule repository URL.
  259. * <p>
  260. * This handles relative URLs that are typically specified in the
  261. * '.gitmodules' file by resolving them against the remote URL of the parent
  262. * repository.
  263. * <p>
  264. * Relative URLs will be resolved against the parent repository's working
  265. * directory if the parent repository has no configured remote URL.
  266. *
  267. * @param parent
  268. * parent repository
  269. * @param url
  270. * absolute or relative URL of the submodule repository
  271. * @return resolved URL
  272. * @throws java.io.IOException
  273. */
  274. public static String getSubmoduleRemoteUrl(final Repository parent,
  275. final String url) throws IOException {
  276. if (!url.startsWith("./") && !url.startsWith("../")) //$NON-NLS-1$ //$NON-NLS-2$
  277. return url;
  278. String remoteName = null;
  279. // Look up remote URL associated wit HEAD ref
  280. Ref ref = parent.exactRef(Constants.HEAD);
  281. if (ref != null) {
  282. if (ref.isSymbolic())
  283. ref = ref.getLeaf();
  284. remoteName = parent.getConfig().getString(
  285. ConfigConstants.CONFIG_BRANCH_SECTION,
  286. Repository.shortenRefName(ref.getName()),
  287. ConfigConstants.CONFIG_KEY_REMOTE);
  288. }
  289. // Fall back to 'origin' if current HEAD ref has no remote URL
  290. if (remoteName == null)
  291. remoteName = Constants.DEFAULT_REMOTE_NAME;
  292. String remoteUrl = parent.getConfig().getString(
  293. ConfigConstants.CONFIG_REMOTE_SECTION, remoteName,
  294. ConfigConstants.CONFIG_KEY_URL);
  295. // Fall back to parent repository's working directory if no remote URL
  296. if (remoteUrl == null) {
  297. remoteUrl = parent.getWorkTree().getAbsolutePath();
  298. // Normalize slashes to '/'
  299. if ('\\' == File.separatorChar)
  300. remoteUrl = remoteUrl.replace('\\', '/');
  301. }
  302. // Remove trailing '/'
  303. if (remoteUrl.charAt(remoteUrl.length() - 1) == '/')
  304. remoteUrl = remoteUrl.substring(0, remoteUrl.length() - 1);
  305. char separator = '/';
  306. String submoduleUrl = url;
  307. while (submoduleUrl.length() > 0) {
  308. if (submoduleUrl.startsWith("./")) //$NON-NLS-1$
  309. submoduleUrl = submoduleUrl.substring(2);
  310. else if (submoduleUrl.startsWith("../")) { //$NON-NLS-1$
  311. int lastSeparator = remoteUrl.lastIndexOf('/');
  312. if (lastSeparator < 1) {
  313. lastSeparator = remoteUrl.lastIndexOf(':');
  314. separator = ':';
  315. }
  316. if (lastSeparator < 1)
  317. throw new IOException(MessageFormat.format(
  318. JGitText.get().submoduleParentRemoteUrlInvalid,
  319. remoteUrl));
  320. remoteUrl = remoteUrl.substring(0, lastSeparator);
  321. submoduleUrl = submoduleUrl.substring(3);
  322. } else
  323. break;
  324. }
  325. return remoteUrl + separator + submoduleUrl;
  326. }
  327. private final Repository repository;
  328. private final TreeWalk walk;
  329. private StoredConfig repoConfig;
  330. private AbstractTreeIterator rootTree;
  331. private Config modulesConfig;
  332. private String path;
  333. private Map<String, String> pathToName;
  334. private RepositoryBuilderFactory factory;
  335. /**
  336. * Create submodule generator
  337. *
  338. * @param repository
  339. * the {@link org.eclipse.jgit.lib.Repository}.
  340. * @throws java.io.IOException
  341. */
  342. public SubmoduleWalk(Repository repository) throws IOException {
  343. this.repository = repository;
  344. repoConfig = repository.getConfig();
  345. walk = new TreeWalk(repository);
  346. walk.setRecursive(true);
  347. }
  348. /**
  349. * Set the config used by this walk.
  350. *
  351. * This method need only be called if constructing a walk manually instead of
  352. * with one of the static factory methods above.
  353. *
  354. * @param config
  355. * .gitmodules config object
  356. * @return this generator
  357. */
  358. public SubmoduleWalk setModulesConfig(Config config) {
  359. modulesConfig = config;
  360. loadPathNames();
  361. return this;
  362. }
  363. /**
  364. * Set the tree used by this walk for finding {@code .gitmodules}.
  365. * <p>
  366. * The root tree is not read until the first submodule is encountered by the
  367. * walk.
  368. * <p>
  369. * This method need only be called if constructing a walk manually instead of
  370. * with one of the static factory methods above.
  371. *
  372. * @param tree
  373. * tree containing .gitmodules
  374. * @return this generator
  375. */
  376. public SubmoduleWalk setRootTree(AbstractTreeIterator tree) {
  377. rootTree = tree;
  378. modulesConfig = null;
  379. pathToName = null;
  380. return this;
  381. }
  382. /**
  383. * Set the tree used by this walk for finding {@code .gitmodules}.
  384. * <p>
  385. * The root tree is not read until the first submodule is encountered by the
  386. * walk.
  387. * <p>
  388. * This method need only be called if constructing a walk manually instead of
  389. * with one of the static factory methods above.
  390. *
  391. * @param id
  392. * ID of a tree containing .gitmodules
  393. * @return this generator
  394. * @throws java.io.IOException
  395. */
  396. public SubmoduleWalk setRootTree(AnyObjectId id) throws IOException {
  397. final CanonicalTreeParser p = new CanonicalTreeParser();
  398. p.reset(walk.getObjectReader(), id);
  399. rootTree = p;
  400. modulesConfig = null;
  401. pathToName = null;
  402. return this;
  403. }
  404. /**
  405. * Load the config for this walk from {@code .gitmodules}.
  406. * <p>
  407. * Uses the root tree if {@link #setRootTree(AbstractTreeIterator)} was
  408. * previously called, otherwise uses the working tree.
  409. * <p>
  410. * If no submodule config is found, loads an empty config.
  411. *
  412. * @return this generator
  413. * @throws java.io.IOException
  414. * if an error occurred, or if the repository is bare
  415. * @throws org.eclipse.jgit.errors.ConfigInvalidException
  416. */
  417. public SubmoduleWalk loadModulesConfig() throws IOException, ConfigInvalidException {
  418. if (rootTree == null) {
  419. File modulesFile = new File(repository.getWorkTree(),
  420. Constants.DOT_GIT_MODULES);
  421. FileBasedConfig config = new FileBasedConfig(modulesFile,
  422. repository.getFS());
  423. config.load();
  424. modulesConfig = config;
  425. loadPathNames();
  426. } else {
  427. try (TreeWalk configWalk = new TreeWalk(repository)) {
  428. configWalk.addTree(rootTree);
  429. // The root tree may be part of the submodule walk, so we need to revert
  430. // it after this walk.
  431. int idx;
  432. for (idx = 0; !rootTree.first(); idx++) {
  433. rootTree.back(1);
  434. }
  435. try {
  436. configWalk.setRecursive(false);
  437. PathFilter filter = PathFilter.create(Constants.DOT_GIT_MODULES);
  438. configWalk.setFilter(filter);
  439. while (configWalk.next()) {
  440. if (filter.isDone(configWalk)) {
  441. modulesConfig = new BlobBasedConfig(null, repository,
  442. configWalk.getObjectId(0));
  443. loadPathNames();
  444. return this;
  445. }
  446. }
  447. modulesConfig = new Config();
  448. pathToName = null;
  449. } finally {
  450. if (idx > 0)
  451. rootTree.next(idx);
  452. }
  453. }
  454. }
  455. return this;
  456. }
  457. private void loadPathNames() {
  458. pathToName = null;
  459. if (modulesConfig != null) {
  460. HashMap<String, String> pathNames = new HashMap<>();
  461. for (String name : modulesConfig
  462. .getSubsections(ConfigConstants.CONFIG_SUBMODULE_SECTION)) {
  463. pathNames.put(modulesConfig.getString(
  464. ConfigConstants.CONFIG_SUBMODULE_SECTION, name,
  465. ConfigConstants.CONFIG_KEY_PATH), name);
  466. }
  467. pathToName = pathNames;
  468. }
  469. }
  470. /**
  471. * Checks whether the working tree contains a .gitmodules file. That's a
  472. * hint that the repo contains submodules.
  473. *
  474. * @param repository
  475. * the repository to check
  476. * @return <code>true</code> if the working tree contains a .gitmodules file,
  477. * <code>false</code> otherwise. Always returns <code>false</code>
  478. * for bare repositories.
  479. * @throws java.io.IOException
  480. * @throws CorruptObjectException if any.
  481. * @since 3.6
  482. */
  483. public static boolean containsGitModulesFile(Repository repository)
  484. throws IOException {
  485. if (repository.isBare()) {
  486. return false;
  487. }
  488. File modulesFile = new File(repository.getWorkTree(),
  489. Constants.DOT_GIT_MODULES);
  490. return (modulesFile.exists());
  491. }
  492. private void lazyLoadModulesConfig() throws IOException, ConfigInvalidException {
  493. if (modulesConfig == null) {
  494. loadModulesConfig();
  495. }
  496. }
  497. private String getModuleName(String modulePath) {
  498. String name = pathToName != null ? pathToName.get(modulePath) : null;
  499. return name != null ? name : modulePath;
  500. }
  501. /**
  502. * Set tree filter
  503. *
  504. * @param filter
  505. * a {@link org.eclipse.jgit.treewalk.filter.TreeFilter} object.
  506. * @return this generator
  507. */
  508. public SubmoduleWalk setFilter(TreeFilter filter) {
  509. walk.setFilter(filter);
  510. return this;
  511. }
  512. /**
  513. * Set the tree iterator used for finding submodule entries
  514. *
  515. * @param iterator
  516. * an {@link org.eclipse.jgit.treewalk.AbstractTreeIterator}
  517. * object.
  518. * @return this generator
  519. * @throws org.eclipse.jgit.errors.CorruptObjectException
  520. */
  521. public SubmoduleWalk setTree(AbstractTreeIterator iterator)
  522. throws CorruptObjectException {
  523. walk.addTree(iterator);
  524. return this;
  525. }
  526. /**
  527. * Set the tree used for finding submodule entries
  528. *
  529. * @param treeId
  530. * an {@link org.eclipse.jgit.lib.AnyObjectId} object.
  531. * @return this generator
  532. * @throws java.io.IOException
  533. * @throws IncorrectObjectTypeException
  534. * if any.
  535. * @throws MissingObjectException
  536. * if any.
  537. */
  538. public SubmoduleWalk setTree(AnyObjectId treeId) throws IOException {
  539. walk.addTree(treeId);
  540. return this;
  541. }
  542. /**
  543. * Reset generator and start new submodule walk
  544. *
  545. * @return this generator
  546. */
  547. public SubmoduleWalk reset() {
  548. repoConfig = repository.getConfig();
  549. modulesConfig = null;
  550. pathToName = null;
  551. walk.reset();
  552. return this;
  553. }
  554. /**
  555. * Get directory that will be the root of the submodule's local repository
  556. *
  557. * @return submodule repository directory
  558. */
  559. public File getDirectory() {
  560. return getSubmoduleDirectory(repository, path);
  561. }
  562. /**
  563. * Advance to next submodule in the index tree.
  564. *
  565. * The object id and path of the next entry can be obtained by calling
  566. * {@link #getObjectId()} and {@link #getPath()}.
  567. *
  568. * @return true if entry found, false otherwise
  569. * @throws java.io.IOException
  570. */
  571. public boolean next() throws IOException {
  572. while (walk.next()) {
  573. if (FileMode.GITLINK != walk.getFileMode(0))
  574. continue;
  575. path = walk.getPathString();
  576. return true;
  577. }
  578. path = null;
  579. return false;
  580. }
  581. /**
  582. * Get path of current submodule entry
  583. *
  584. * @return path
  585. */
  586. public String getPath() {
  587. return path;
  588. }
  589. /**
  590. * Sets the {@link RepositoryBuilderFactory} to use for creating submodule
  591. * repositories. If none is set, a plain {@link RepositoryBuilder} is used.
  592. *
  593. * @param factory
  594. * to set
  595. * @since 5.6
  596. */
  597. public void setBuilderFactory(RepositoryBuilderFactory factory) {
  598. this.factory = factory;
  599. }
  600. private BaseRepositoryBuilder<?, ? extends Repository> getBuilder() {
  601. return factory != null ? factory.get() : new RepositoryBuilder();
  602. }
  603. /**
  604. * The module name for the current submodule entry (used for the section
  605. * name of .git/config)
  606. *
  607. * @since 4.10
  608. * @return name
  609. * @throws ConfigInvalidException
  610. * @throws IOException
  611. */
  612. public String getModuleName() throws IOException, ConfigInvalidException {
  613. lazyLoadModulesConfig();
  614. return getModuleName(path);
  615. }
  616. /**
  617. * Get object id of current submodule entry
  618. *
  619. * @return object id
  620. */
  621. public ObjectId getObjectId() {
  622. return walk.getObjectId(0);
  623. }
  624. /**
  625. * Get the configured path for current entry. This will be the value from
  626. * the .gitmodules file in the current repository's working tree.
  627. *
  628. * @return configured path
  629. * @throws org.eclipse.jgit.errors.ConfigInvalidException
  630. * @throws java.io.IOException
  631. */
  632. public String getModulesPath() throws IOException, ConfigInvalidException {
  633. lazyLoadModulesConfig();
  634. return modulesConfig.getString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
  635. getModuleName(), ConfigConstants.CONFIG_KEY_PATH);
  636. }
  637. /**
  638. * Get the configured remote URL for current entry. This will be the value
  639. * from the repository's config.
  640. *
  641. * @return configured URL
  642. * @throws org.eclipse.jgit.errors.ConfigInvalidException
  643. * @throws java.io.IOException
  644. */
  645. public String getConfigUrl() throws IOException, ConfigInvalidException {
  646. return repoConfig.getString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
  647. getModuleName(), ConfigConstants.CONFIG_KEY_URL);
  648. }
  649. /**
  650. * Get the configured remote URL for current entry. This will be the value
  651. * from the .gitmodules file in the current repository's working tree.
  652. *
  653. * @return configured URL
  654. * @throws org.eclipse.jgit.errors.ConfigInvalidException
  655. * @throws java.io.IOException
  656. */
  657. public String getModulesUrl() throws IOException, ConfigInvalidException {
  658. lazyLoadModulesConfig();
  659. return modulesConfig.getString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
  660. getModuleName(), ConfigConstants.CONFIG_KEY_URL);
  661. }
  662. /**
  663. * Get the configured update field for current entry. This will be the value
  664. * from the repository's config.
  665. *
  666. * @return update value
  667. * @throws org.eclipse.jgit.errors.ConfigInvalidException
  668. * @throws java.io.IOException
  669. */
  670. public String getConfigUpdate() throws IOException, ConfigInvalidException {
  671. return repoConfig.getString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
  672. getModuleName(), ConfigConstants.CONFIG_KEY_UPDATE);
  673. }
  674. /**
  675. * Get the configured update field for current entry. This will be the value
  676. * from the .gitmodules file in the current repository's working tree.
  677. *
  678. * @return update value
  679. * @throws org.eclipse.jgit.errors.ConfigInvalidException
  680. * @throws java.io.IOException
  681. */
  682. public String getModulesUpdate() throws IOException, ConfigInvalidException {
  683. lazyLoadModulesConfig();
  684. return modulesConfig.getString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
  685. getModuleName(), ConfigConstants.CONFIG_KEY_UPDATE);
  686. }
  687. /**
  688. * Get the configured ignore field for the current entry. This will be the
  689. * value from the .gitmodules file in the current repository's working tree.
  690. *
  691. * @return ignore value
  692. * @throws org.eclipse.jgit.errors.ConfigInvalidException
  693. * @throws java.io.IOException
  694. * @since 3.6
  695. */
  696. public IgnoreSubmoduleMode getModulesIgnore() throws IOException,
  697. ConfigInvalidException {
  698. IgnoreSubmoduleMode mode = repoConfig.getEnum(
  699. IgnoreSubmoduleMode.values(),
  700. ConfigConstants.CONFIG_SUBMODULE_SECTION, getModuleName(),
  701. ConfigConstants.CONFIG_KEY_IGNORE, null);
  702. if (mode != null) {
  703. return mode;
  704. }
  705. lazyLoadModulesConfig();
  706. return modulesConfig.getEnum(IgnoreSubmoduleMode.values(),
  707. ConfigConstants.CONFIG_SUBMODULE_SECTION, getModuleName(),
  708. ConfigConstants.CONFIG_KEY_IGNORE, IgnoreSubmoduleMode.NONE);
  709. }
  710. /**
  711. * Get repository for current submodule entry
  712. *
  713. * @return repository or null if non-existent
  714. * @throws java.io.IOException
  715. */
  716. public Repository getRepository() throws IOException {
  717. return getSubmoduleRepository(repository.getWorkTree(), path,
  718. repository.getFS(), getBuilder());
  719. }
  720. /**
  721. * Get commit id that HEAD points to in the current submodule's repository
  722. *
  723. * @return object id of HEAD reference
  724. * @throws java.io.IOException
  725. */
  726. public ObjectId getHead() throws IOException {
  727. try (Repository subRepo = getRepository()) {
  728. if (subRepo == null) {
  729. return null;
  730. }
  731. return subRepo.resolve(Constants.HEAD);
  732. }
  733. }
  734. /**
  735. * Get ref that HEAD points to in the current submodule's repository
  736. *
  737. * @return ref name, null on failures
  738. * @throws java.io.IOException
  739. */
  740. public String getHeadRef() throws IOException {
  741. try (Repository subRepo = getRepository()) {
  742. if (subRepo == null) {
  743. return null;
  744. }
  745. Ref head = subRepo.exactRef(Constants.HEAD);
  746. return head != null ? head.getLeaf().getName() : null;
  747. }
  748. }
  749. /**
  750. * Get the resolved remote URL for the current submodule.
  751. * <p>
  752. * This method resolves the value of {@link #getModulesUrl()} to an absolute
  753. * URL
  754. *
  755. * @return resolved remote URL
  756. * @throws java.io.IOException
  757. * @throws org.eclipse.jgit.errors.ConfigInvalidException
  758. */
  759. public String getRemoteUrl() throws IOException, ConfigInvalidException {
  760. String url = getModulesUrl();
  761. return url != null ? getSubmoduleRemoteUrl(repository, url) : null;
  762. }
  763. /**
  764. * {@inheritDoc}
  765. * <p>
  766. * Release any resources used by this walker's reader.
  767. *
  768. * @since 4.0
  769. */
  770. @Override
  771. public void close() {
  772. walk.close();
  773. }
  774. }