123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- /*
- * Copyright (C) 2017 David Pursehouse <david.pursehouse@gmail.com>
- * 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 static org.junit.Assert.assertEquals;
- import static org.junit.Assert.assertNotNull;
- import static org.junit.Assert.assertTrue;
-
- import java.io.File;
-
- import org.eclipse.jgit.api.ResetCommand.ResetType;
- import org.eclipse.jgit.junit.JGitTestUtil;
- import org.eclipse.jgit.junit.RepositoryTestCase;
- import org.eclipse.jgit.lib.ConfigConstants;
- import org.eclipse.jgit.lib.Constants;
- import org.eclipse.jgit.lib.ObjectId;
- import org.eclipse.jgit.lib.Repository;
- import org.eclipse.jgit.lib.StoredConfig;
- import org.eclipse.jgit.lib.SubmoduleConfig.FetchRecurseSubmodulesMode;
- import org.eclipse.jgit.revwalk.RevCommit;
- import org.eclipse.jgit.submodule.SubmoduleStatus;
- import org.eclipse.jgit.submodule.SubmoduleStatusType;
- import org.eclipse.jgit.submodule.SubmoduleWalk;
- import org.eclipse.jgit.transport.FetchResult;
- import org.eclipse.jgit.transport.RefSpec;
- import org.junit.Before;
- import org.junit.experimental.theories.DataPoints;
- import org.junit.experimental.theories.Theories;
- import org.junit.experimental.theories.Theory;
- import org.junit.runner.RunWith;
-
- @RunWith(Theories.class)
- public class FetchAndPullCommandsRecurseSubmodulesTest extends RepositoryTestCase {
- @DataPoints
- public static boolean[] useFetch = { true, false };
-
- private Git git;
-
- private Git git2;
-
- private Git sub1Git;
-
- private Git sub2Git;
-
- private RevCommit commit1;
-
- private RevCommit commit2;
-
- private ObjectId submodule1Head;
-
- private ObjectId submodule2Head;
-
- private final RefSpec REFSPEC = new RefSpec("refs/heads/master");
-
- private final String REMOTE = "origin";
-
- private final String PATH = "sub";
-
- @Before
- public void setUpSubmodules() throws Exception {
- git = new Git(db);
-
- // Create submodule 1
- File submodule1 = createTempDirectory(
- "testCloneRepositoryWithNestedSubmodules1");
- sub1Git = Git.init().setDirectory(submodule1).call();
- assertNotNull(sub1Git);
- Repository sub1 = sub1Git.getRepository();
- assertNotNull(sub1);
- addRepoToClose(sub1);
-
- String file = "file.txt";
-
- write(new File(sub1.getWorkTree(), file), "content");
- sub1Git.add().addFilepattern(file).call();
- RevCommit commit = sub1Git.commit().setMessage("create file").call();
- assertNotNull(commit);
-
- // Create submodule 2
- File submodule2 = createTempDirectory(
- "testCloneRepositoryWithNestedSubmodules2");
- sub2Git = Git.init().setDirectory(submodule2).call();
- assertNotNull(sub2Git);
- Repository sub2 = sub2Git.getRepository();
- assertNotNull(sub2);
- addRepoToClose(sub2);
-
- write(new File(sub2.getWorkTree(), file), "content");
- sub2Git.add().addFilepattern(file).call();
- RevCommit sub2Head = sub2Git.commit().setMessage("create file").call();
- assertNotNull(sub2Head);
-
- // Add submodule 2 to submodule 1
- Repository r2 = sub1Git.submoduleAdd().setPath(PATH)
- .setURI(sub2.getDirectory().toURI().toString()).call();
- assertNotNull(r2);
- addRepoToClose(r2);
- RevCommit sub1Head = sub1Git.commit().setAll(true)
- .setMessage("Adding submodule").call();
- assertNotNull(sub1Head);
-
- // Add submodule 1 to default repository
- Repository r1 = git.submoduleAdd().setPath(PATH)
- .setURI(sub1.getDirectory().toURI().toString()).call();
- assertNotNull(r1);
- addRepoToClose(r1);
- assertNotNull(git.commit().setAll(true).setMessage("Adding submodule")
- .call());
-
- // Clone default repository and include submodules
- File directory = createTempDirectory(
- "testCloneRepositoryWithNestedSubmodules");
- CloneCommand clone = Git.cloneRepository();
- clone.setDirectory(directory);
- clone.setCloneSubmodules(true);
- clone.setURI(git.getRepository().getDirectory().toURI().toString());
- git2 = clone.call();
- addRepoToClose(git2.getRepository());
- assertNotNull(git2);
-
- // Record current FETCH_HEAD of submodules
- try (SubmoduleWalk walk = SubmoduleWalk
- .forIndex(git2.getRepository())) {
- assertTrue(walk.next());
- Repository r = walk.getRepository();
- submodule1Head = r.resolve(Constants.FETCH_HEAD);
-
- try (SubmoduleWalk walk2 = SubmoduleWalk.forIndex(r)) {
- assertTrue(walk2.next());
- submodule2Head = walk2.getRepository()
- .resolve(Constants.FETCH_HEAD);
- }
- }
-
- // Commit in submodule 1
- JGitTestUtil.writeTrashFile(r1, "f1.txt", "test");
- sub1Git.add().addFilepattern("f1.txt").call();
- commit1 = sub1Git.commit().setMessage("new commit").call();
-
- // Commit in submodule 2
- JGitTestUtil.writeTrashFile(r2, "f2.txt", "test");
- sub2Git.add().addFilepattern("f2.txt").call();
- commit2 = sub2Git.commit().setMessage("new commit").call();
- }
-
- @Theory
- public void shouldNotFetchSubmodulesWhenNo(boolean fetch) throws Exception {
- FetchResult result = execute(FetchRecurseSubmodulesMode.NO, fetch);
- assertTrue(result.submoduleResults().isEmpty());
- assertSubmoduleFetchHeads(submodule1Head, submodule2Head);
- }
-
- @Theory
- public void shouldFetchSubmodulesWhenYes(boolean fetch) throws Exception {
- FetchResult result = execute(FetchRecurseSubmodulesMode.YES, fetch);
- assertTrue(result.submoduleResults().containsKey("sub"));
- FetchResult subResult = result.submoduleResults().get("sub");
- assertTrue(subResult.submoduleResults().containsKey("sub"));
- assertSubmoduleFetchHeads(commit1, commit2);
- }
-
- @Theory
- public void shouldFetchSubmodulesWhenOnDemandAndRevisionChanged(
- boolean fetch) throws Exception {
- RevCommit update = updateSubmoduleRevision();
- FetchResult result = execute(FetchRecurseSubmodulesMode.ON_DEMAND,
- fetch);
-
- // The first submodule should have been updated
- assertTrue(result.submoduleResults().containsKey("sub"));
- FetchResult subResult = result.submoduleResults().get("sub");
-
- // The second submodule should not get updated
- assertTrue(subResult.submoduleResults().isEmpty());
- assertSubmoduleFetchHeads(commit1, submodule2Head);
-
- // After fetch the parent repo's fetch head should be the commit
- // that updated the submodule.
- assertEquals(update,
- git2.getRepository().resolve(Constants.FETCH_HEAD));
- }
-
- @Theory
- public void shouldNotFetchSubmodulesWhenOnDemandAndRevisionNotChanged(
- boolean fetch) throws Exception {
- FetchResult result = execute(FetchRecurseSubmodulesMode.ON_DEMAND,
- fetch);
- assertTrue(result.submoduleResults().isEmpty());
- assertSubmoduleFetchHeads(submodule1Head, submodule2Head);
- }
-
- @Theory
- public void shouldNotFetchSubmodulesWhenSubmoduleConfigurationSetToNo(
- boolean fetch) throws Exception {
- StoredConfig config = git2.getRepository().getConfig();
- config.setEnum(ConfigConstants.CONFIG_SUBMODULE_SECTION, PATH,
- ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES,
- FetchRecurseSubmodulesMode.NO);
- config.save();
- updateSubmoduleRevision();
- FetchResult result = execute(null, fetch);
- assertTrue(result.submoduleResults().isEmpty());
- assertSubmoduleFetchHeads(submodule1Head, submodule2Head);
- }
-
- @Theory
- public void shouldFetchSubmodulesWhenSubmoduleConfigurationSetToYes(
- boolean fetch) throws Exception {
- StoredConfig config = git2.getRepository().getConfig();
- config.setEnum(ConfigConstants.CONFIG_SUBMODULE_SECTION, PATH,
- ConfigConstants.CONFIG_KEY_FETCH_RECURSE_SUBMODULES,
- FetchRecurseSubmodulesMode.YES);
- config.save();
- FetchResult result = execute(null, fetch);
- assertTrue(result.submoduleResults().containsKey("sub"));
- FetchResult subResult = result.submoduleResults().get("sub");
- assertTrue(subResult.submoduleResults().containsKey("sub"));
- assertSubmoduleFetchHeads(commit1, commit2);
- }
-
- @Theory
- public void shouldNotFetchSubmodulesWhenFetchConfigurationSetToNo(
- boolean fetch) throws Exception {
- StoredConfig config = git2.getRepository().getConfig();
- config.setEnum(ConfigConstants.CONFIG_FETCH_SECTION, null,
- ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES,
- FetchRecurseSubmodulesMode.NO);
- config.save();
- updateSubmoduleRevision();
- FetchResult result = execute(null, fetch);
- assertTrue(result.submoduleResults().isEmpty());
- assertSubmoduleFetchHeads(submodule1Head, submodule2Head);
- }
-
- @Theory
- public void shouldFetchSubmodulesWhenFetchConfigurationSetToYes(
- boolean fetch) throws Exception {
- StoredConfig config = git2.getRepository().getConfig();
- config.setEnum(ConfigConstants.CONFIG_FETCH_SECTION, null,
- ConfigConstants.CONFIG_KEY_RECURSE_SUBMODULES,
- FetchRecurseSubmodulesMode.YES);
- config.save();
- FetchResult result = execute(null, fetch);
- assertTrue(result.submoduleResults().containsKey("sub"));
- FetchResult subResult = result.submoduleResults().get("sub");
- assertTrue(subResult.submoduleResults().containsKey("sub"));
- assertSubmoduleFetchHeads(commit1, commit2);
- }
-
- private RevCommit updateSubmoduleRevision() throws Exception {
- // Fetch the submodule in the original git and reset it to
- // the commit that was created
- try (SubmoduleWalk w = SubmoduleWalk.forIndex(git.getRepository())) {
- assertTrue(w.next());
- try (Git g = new Git(w.getRepository())) {
- g.fetch().setRemote(REMOTE).setRefSpecs(REFSPEC).call();
- g.reset().setMode(ResetType.HARD).setRef(commit1.name()).call();
- }
- }
-
- // Submodule index Id should be same as before, but head Id should be
- // updated to the new commit, and status should be "checked out".
- SubmoduleStatus subStatus = git.submoduleStatus().call().get("sub");
- assertEquals(submodule1Head, subStatus.getIndexId());
- assertEquals(commit1, subStatus.getHeadId());
- assertEquals(SubmoduleStatusType.REV_CHECKED_OUT, subStatus.getType());
-
- // Add and commit the submodule status
- git.add().addFilepattern("sub").call();
- RevCommit update = git.commit().setMessage("update sub").call();
-
- // Both submodule index and head should now be at the new commit, and
- // the status should be "initialized".
- subStatus = git.submoduleStatus().call().get("sub");
- assertEquals(commit1, subStatus.getIndexId());
- assertEquals(commit1, subStatus.getHeadId());
- assertEquals(SubmoduleStatusType.INITIALIZED, subStatus.getType());
-
- return update;
- }
-
- private FetchResult execute(FetchRecurseSubmodulesMode mode, boolean fetch)
- throws Exception {
- FetchResult result;
-
- if (fetch) {
- result = git2.fetch().setRemote(REMOTE).setRefSpecs(REFSPEC)
- .setRecurseSubmodules(mode).call();
- } else {
- // For the purposes of this test we don't need to care about the
- // pull result, or the result of pull with merge. We are only
- // interested in checking whether or not the submodules were updated
- // as expected. Setting to rebase makes it easier to assert about
- // the state of the parent repository head, i.e. we know it should
- // be at the submodule update commit, and don't need to consider a
- // merge commit created by the pull.
- result = git2.pull().setRemote(REMOTE).setRebase(true)
- .setRecurseSubmodules(mode).call().getFetchResult();
- }
- assertNotNull(result);
- return result;
- }
-
- private void assertSubmoduleFetchHeads(ObjectId expectedHead1,
- ObjectId expectedHead2) throws Exception {
- try (SubmoduleWalk walk = SubmoduleWalk
- .forIndex(git2.getRepository())) {
- assertTrue(walk.next());
- Repository r = walk.getRepository();
- ObjectId newHead1 = r.resolve(Constants.FETCH_HEAD);
- ObjectId newHead2;
- try (SubmoduleWalk walk2 = SubmoduleWalk.forIndex(r)) {
- assertTrue(walk2.next());
- newHead2 = walk2.getRepository().resolve(Constants.FETCH_HEAD);
- }
-
- assertEquals(expectedHead1, newHead1);
- assertEquals(expectedHead2, newHead2);
- }
- }
- }
|