Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

FetchAndPullCommandsRecurseSubmodulesTest.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. /*
  2. * Copyright (C) 2017 David Pursehouse <david.pursehouse@gmail.com> 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 static org.junit.Assert.assertEquals;
  12. import static org.junit.Assert.assertNotNull;
  13. import static org.junit.Assert.assertTrue;
  14. import java.io.File;
  15. import org.eclipse.jgit.api.ResetCommand.ResetType;
  16. import org.eclipse.jgit.junit.JGitTestUtil;
  17. import org.eclipse.jgit.junit.RepositoryTestCase;
  18. import org.eclipse.jgit.lib.ConfigConstants;
  19. import org.eclipse.jgit.lib.Constants;
  20. import org.eclipse.jgit.lib.ObjectId;
  21. import org.eclipse.jgit.lib.Repository;
  22. import org.eclipse.jgit.lib.StoredConfig;
  23. import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode;
  24. import org.eclipse.jgit.revwalk.RevCommit;
  25. import org.eclipse.jgit.submodule.SubmoduleStatus;
  26. import org.eclipse.jgit.submodule.SubmoduleStatusType;
  27. import org.eclipse.jgit.submodule.SubmoduleWalk;
  28. import org.eclipse.jgit.transport.FetchResult;
  29. import org.eclipse.jgit.transport.RefSpec;
  30. import org.junit.Before;
  31. import org.junit.experimental.theories.DataPoints;
  32. import org.junit.experimental.theories.Theories;
  33. import org.junit.experimental.theories.Theory;
  34. import org.junit.runner.RunWith;
  35. @RunWith(Theories.class)
  36. public class FetchAndPullCommandsRecurseSubmodulesTest extends RepositoryTestCase {
  37. @DataPoints
  38. public static boolean[] useFetch = { true, false };
  39. private Git git;
  40. private Git git2;
  41. private Git sub1Git;
  42. private Git sub2Git;
  43. private RevCommit commit1;
  44. private RevCommit commit2;
  45. private ObjectId submodule1Head;
  46. private ObjectId submodule2Head;
  47. private final RefSpec REFSPEC = new RefSpec("refs/heads/master");
  48. private final String REMOTE = "origin";
  49. private final String PATH = "sub";
  50. @Before
  51. public void setUpSubmodules() throws Exception {
  52. git = new Git(db);
  53. // Create submodule 1
  54. File submodule1 = createTempDirectory(
  55. "testCloneRepositoryWithNestedSubmodules1");
  56. sub1Git = Git.init().setDirectory(submodule1).call();
  57. assertNotNull(sub1Git);
  58. Repository sub1 = sub1Git.getRepository();
  59. assertNotNull(sub1);
  60. addRepoToClose(sub1);
  61. String file = "file.txt";
  62. write(new File(sub1.getWorkTree(), file), "content");
  63. sub1Git.add().addFilepattern(file).call();
  64. RevCommit commit = sub1Git.commit().setMessage("create file").call();
  65. assertNotNull(commit);
  66. // Create submodule 2
  67. File submodule2 = createTempDirectory(
  68. "testCloneRepositoryWithNestedSubmodules2");
  69. sub2Git = Git.init().setDirectory(submodule2).call();
  70. assertNotNull(sub2Git);
  71. Repository sub2 = sub2Git.getRepository();
  72. assertNotNull(sub2);
  73. addRepoToClose(sub2);
  74. write(new File(sub2.getWorkTree(), file), "content");
  75. sub2Git.add().addFilepattern(file).call();
  76. RevCommit sub2Head = sub2Git.commit().setMessage("create file").call();
  77. assertNotNull(sub2Head);
  78. // Add submodule 2 to submodule 1
  79. Repository r2 = sub1Git.submoduleAdd().setPath(PATH)
  80. .setURI(sub2.getDirectory().toURI().toString()).call();
  81. assertNotNull(r2);
  82. addRepoToClose(r2);
  83. RevCommit sub1Head = sub1Git.commit().setAll(true)
  84. .setMessage("Adding submodule").call();
  85. assertNotNull(sub1Head);
  86. // Add submodule 1 to default repository
  87. Repository r1 = git.submoduleAdd().setPath(PATH)
  88. .setURI(sub1.getDirectory().toURI().toString()).call();
  89. assertNotNull(r1);
  90. addRepoToClose(r1);
  91. assertNotNull(git.commit().setAll(true).setMessage("Adding submodule")
  92. .call());
  93. // Clone default repository and include submodules
  94. File directory = createTempDirectory(
  95. "testCloneRepositoryWithNestedSubmodules");
  96. CloneCommand clone = Git.cloneRepository();
  97. clone.setDirectory(directory);
  98. clone.setCloneSubmodules(true);
  99. clone.setURI(git.getRepository().getDirectory().toURI().toString());
  100. git2 = clone.call();
  101. addRepoToClose(git2.getRepository());
  102. assertNotNull(git2);
  103. // Record current FETCH_HEAD of submodules
  104. try (SubmoduleWalk walk = SubmoduleWalk
  105. .forIndex(git2.getRepository())) {
  106. assertTrue(walk.next());
  107. Repository r = walk.getRepository();
  108. submodule1Head = r.resolve(Constants.FETCH_HEAD);
  109. try (SubmoduleWalk walk2 = SubmoduleWalk.forIndex(r)) {
  110. assertTrue(walk2.next());
  111. submodule2Head = walk2.getRepository()
  112. .resolve(Constants.FETCH_HEAD);
  113. }
  114. }
  115. // Commit in submodule 1
  116. JGitTestUtil.writeTrashFile(r1, "f1.txt", "test");
  117. sub1Git.add().addFilepattern("f1.txt").call();
  118. commit1 = sub1Git.commit().setMessage("new commit").call();
  119. // Commit in submodule 2
  120. JGitTestUtil.writeTrashFile(r2, "f2.txt", "test");
  121. sub2Git.add().addFilepattern("f2.txt").call();
  122. commit2 = sub2Git.commit().setMessage("new commit").call();
  123. }
  124. @Theory
  125. public void shouldNotFetchSubmodulesWhenNo(boolean fetch) throws Exception {
  126. FetchResult result = execute(FetchRecurseSubmodulesMode.NO, fetch);
  127. assertTrue(result.submoduleResults().isEmpty());
  128. assertSubmoduleFetchHeads(submodule1Head, submodule2Head);
  129. }
  130. @Theory
  131. public void shouldFetchSubmodulesWhenYes(boolean fetch) throws Exception {
  132. FetchResult result = execute(FetchRecurseSubmodulesMode.YES, fetch);
  133. assertTrue(result.submoduleResults().containsKey("sub"));
  134. FetchResult subResult = result.submoduleResults().get("sub");
  135. assertTrue(subResult.submoduleResults().containsKey("sub"));
  136. assertSubmoduleFetchHeads(commit1, commit2);
  137. }
  138. @Theory
  139. public void shouldFetchSubmodulesWhenOnDemandAndRevisionChanged(
  140. boolean fetch) throws Exception {
  141. RevCommit update = updateSubmoduleRevision();
  142. FetchResult result = execute(FetchRecurseSubmodulesMode.ON_DEMAND,
  143. fetch);
  144. // The first submodule should have been updated
  145. assertTrue(result.submoduleResults().containsKey("sub"));
  146. FetchResult subResult = result.submoduleResults().get("sub");
  147. // The second submodule should not get updated
  148. assertTrue(subResult.submoduleResults().isEmpty());
  149. assertSubmoduleFetchHeads(commit1, submodule2Head);
  150. // After fetch the parent repo's fetch head should be the commit
  151. // that updated the submodule.
  152. assertEquals(update,
  153. git2.getRepository().resolve(Constants.FETCH_HEAD));
  154. }
  155. @Theory
  156. public void shouldNotFetchSubmodulesWhenOnDemandAndRevisionNotChanged(
  157. boolean fetch) throws Exception {
  158. FetchResult result = execute(FetchRecurseSubmodulesMode.ON_DEMAND,
  159. fetch);
  160. assertTrue(result.submoduleResults().isEmpty());
  161. assertSubmoduleFetchHeads(submodule1Head, submodule2Head);
  162. }
  163. @Theory
  164. public void shouldNotFetchSubmodulesWhenSubmoduleConfigurationSetToNo(
  165. boolean fetch) throws Exception {
  166. StoredConfig config = git2.getRepository().getConfig();
  167. config.setEnum(ConfigConstants.CONFIG_SUBMODULE_SECTION, PATH,
  168. ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES,
  169. FetchRecurseSubmodulesMode.NO);
  170. config.save();
  171. updateSubmoduleRevision();
  172. FetchResult result = execute(null, fetch);
  173. assertTrue(result.submoduleResults().isEmpty());
  174. assertSubmoduleFetchHeads(submodule1Head, submodule2Head);
  175. }
  176. @Theory
  177. public void shouldFetchSubmodulesWhenSubmoduleConfigurationSetToYes(
  178. boolean fetch) throws Exception {
  179. StoredConfig config = git2.getRepository().getConfig();
  180. config.setEnum(ConfigConstants.CONFIG_SUBMODULE_SECTION, PATH,
  181. ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES,
  182. FetchRecurseSubmodulesMode.YES);
  183. config.save();
  184. FetchResult result = execute(null, fetch);
  185. assertTrue(result.submoduleResults().containsKey("sub"));
  186. FetchResult subResult = result.submoduleResults().get("sub");
  187. assertTrue(subResult.submoduleResults().containsKey("sub"));
  188. assertSubmoduleFetchHeads(commit1, commit2);
  189. }
  190. @Theory
  191. public void shouldNotFetchSubmodulesWhenFetchConfigurationSetToNo(
  192. boolean fetch) throws Exception {
  193. StoredConfig config = git2.getRepository().getConfig();
  194. config.setEnum(ConfigConstants.CONFIG_FETCH_SECTION, null,
  195. ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES,
  196. FetchRecurseSubmodulesMode.NO);
  197. config.save();
  198. updateSubmoduleRevision();
  199. FetchResult result = execute(null, fetch);
  200. assertTrue(result.submoduleResults().isEmpty());
  201. assertSubmoduleFetchHeads(submodule1Head, submodule2Head);
  202. }
  203. @Theory
  204. public void shouldFetchSubmodulesWhenFetchConfigurationSetToYes(
  205. boolean fetch) throws Exception {
  206. StoredConfig config = git2.getRepository().getConfig();
  207. config.setEnum(ConfigConstants.CONFIG_FETCH_SECTION, null,
  208. ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES,
  209. FetchRecurseSubmodulesMode.YES);
  210. config.save();
  211. FetchResult result = execute(null, fetch);
  212. assertTrue(result.submoduleResults().containsKey("sub"));
  213. FetchResult subResult = result.submoduleResults().get("sub");
  214. assertTrue(subResult.submoduleResults().containsKey("sub"));
  215. assertSubmoduleFetchHeads(commit1, commit2);
  216. }
  217. private RevCommit updateSubmoduleRevision() throws Exception {
  218. // Fetch the submodule in the original git and reset it to
  219. // the commit that was created
  220. try (SubmoduleWalk w = SubmoduleWalk.forIndex(git.getRepository())) {
  221. assertTrue(w.next());
  222. try (Git g = new Git(w.getRepository())) {
  223. g.fetch().setRemote(REMOTE).setRefSpecs(REFSPEC).call();
  224. g.reset().setMode(ResetType.HARD).setRef(commit1.name()).call();
  225. }
  226. }
  227. // Submodule index Id should be same as before, but head Id should be
  228. // updated to the new commit, and status should be "checked out".
  229. SubmoduleStatus subStatus = git.submoduleStatus().call().get("sub");
  230. assertEquals(submodule1Head, subStatus.getIndexId());
  231. assertEquals(commit1, subStatus.getHeadId());
  232. assertEquals(SubmoduleStatusType.REV_CHECKED_OUT, subStatus.getType());
  233. // Add and commit the submodule status
  234. git.add().addFilepattern("sub").call();
  235. RevCommit update = git.commit().setMessage("update sub").call();
  236. // Both submodule index and head should now be at the new commit, and
  237. // the status should be "initialized".
  238. subStatus = git.submoduleStatus().call().get("sub");
  239. assertEquals(commit1, subStatus.getIndexId());
  240. assertEquals(commit1, subStatus.getHeadId());
  241. assertEquals(SubmoduleStatusType.INITIALIZED, subStatus.getType());
  242. return update;
  243. }
  244. private FetchResult execute(FetchRecurseSubmodulesMode mode, boolean fetch)
  245. throws Exception {
  246. FetchResult result;
  247. if (fetch) {
  248. result = git2.fetch().setRemote(REMOTE).setRefSpecs(REFSPEC)
  249. .setRecurseSubmodules(mode).call();
  250. } else {
  251. // For the purposes of this test we don't need to care about the
  252. // pull result, or the result of pull with merge. We are only
  253. // interested in checking whether or not the submodules were updated
  254. // as expected. Setting to rebase makes it easier to assert about
  255. // the state of the parent repository head, i.e. we know it should
  256. // be at the submodule update commit, and don't need to consider a
  257. // merge commit created by the pull.
  258. result = git2.pull().setRemote(REMOTE).setRebase(true)
  259. .setRecurseSubmodules(mode).call().getFetchResult();
  260. }
  261. assertNotNull(result);
  262. return result;
  263. }
  264. private void assertSubmoduleFetchHeads(ObjectId expectedHead1,
  265. ObjectId expectedHead2) throws Exception {
  266. Object newHead1 = null;
  267. ObjectId newHead2 = null;
  268. try (SubmoduleWalk walk = SubmoduleWalk
  269. .forIndex(git2.getRepository())) {
  270. assertTrue(walk.next());
  271. try (Repository r = walk.getRepository()) {
  272. newHead1 = r.resolve(Constants.FETCH_HEAD);
  273. try (SubmoduleWalk walk2 = SubmoduleWalk.forIndex(r)) {
  274. assertTrue(walk2.next());
  275. try (Repository r2 = walk2.getRepository()) {
  276. newHead2 = r2.resolve(Constants.FETCH_HEAD);
  277. }
  278. }
  279. }
  280. }
  281. assertEquals(expectedHead1, newHead1);
  282. assertEquals(expectedHead2, newHead2);
  283. }
  284. }