aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.test/tst/org/eclipse/jgit
diff options
context:
space:
mode:
authorMathias Kinzler <mathias.kinzler@sap.com>2010-10-08 14:01:55 +0200
committerChris Aniszczyk <caniszczyk@gmail.com>2010-10-10 15:38:49 -0500
commit7bdef4583bd9aad6e4cb9e51543bdc3c0fb26db4 (patch)
tree8482e545d3af94fb61c23c3397183be5bfee3589 /org.eclipse.jgit.test/tst/org/eclipse/jgit
parentbe2ddff6a72cff3d28b87d038a96194a6e2bd272 (diff)
downloadjgit-7bdef4583bd9aad6e4cb9e51543bdc3c0fb26db4.tar.gz
jgit-7bdef4583bd9aad6e4cb9e51543bdc3c0fb26db4.zip
Add "Branch" command
The need for branching becomes more pressing with pull support: we need to make sure the upstream configuration entries are written correctly when creating and renaming branches (and of course are cleaned up when deleting them). This adds support for listing, adding, deleting and renaming branches including the more common options. Bug: 326938 Change-Id: I00bcc19476e835d6fd78fd188acde64946c1505c Signed-off-by: Mathias Kinzler <mathias.kinzler@sap.com> Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
Diffstat (limited to 'org.eclipse.jgit.test/tst/org/eclipse/jgit')
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java416
1 files changed, 416 insertions, 0 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java
new file mode 100644
index 0000000000..07dc560030
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/api/BranchCommandTest.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2010, Mathias Kinzler <mathias.kinzler@sap.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 java.util.List;
+
+import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode;
+import org.eclipse.jgit.api.ListBranchCommand.ListMode;
+import org.eclipse.jgit.api.errors.CannotDeleteCurrentBranchException;
+import org.eclipse.jgit.api.errors.DetachedHeadException;
+import org.eclipse.jgit.api.errors.InvalidRefNameException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.api.errors.NotMergedException;
+import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
+import org.eclipse.jgit.api.errors.RefNotFoundException;
+import org.eclipse.jgit.errors.AmbiguousObjectException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryTestCase;
+import org.eclipse.jgit.lib.StoredConfig;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.transport.FetchResult;
+import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.transport.RemoteConfig;
+import org.eclipse.jgit.transport.URIish;
+
+public class BranchCommandTest extends RepositoryTestCase {
+ private Git git;
+
+ RevCommit initialCommit;
+
+ RevCommit secondCommit;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ git = new Git(db);
+ // checkout master
+ git.commit().setMessage("initial commit").call();
+ // commit something
+ writeTrashFile("Test.txt", "Hello world");
+ git.add().addFilepattern("Test.txt").call();
+ initialCommit = git.commit().setMessage("Initial commit").call();
+ writeTrashFile("Test.txt", "Some change");
+ git.add().addFilepattern("Test.txt").call();
+ secondCommit = git.commit().setMessage("Second commit").call();
+ // create a master branch
+ RefUpdate rup = db.updateRef("refs/heads/master");
+ rup.setNewObjectId(initialCommit.getId());
+ rup.setForceUpdate(true);
+ rup.update();
+ }
+
+ private Git setUpRepoWithRemote() throws Exception {
+ Repository remoteRepository = createWorkRepository();
+ Git remoteGit = new Git(remoteRepository);
+ // commit something
+ writeTrashFile("Test.txt", "Hello world");
+ remoteGit.add().addFilepattern("Test.txt").call();
+ initialCommit = remoteGit.commit().setMessage("Initial commit").call();
+ writeTrashFile("Test.txt", "Some change");
+ remoteGit.add().addFilepattern("Test.txt").call();
+ secondCommit = remoteGit.commit().setMessage("Second commit").call();
+ // create a master branch
+ RefUpdate rup = remoteRepository.updateRef("refs/heads/master");
+ rup.setNewObjectId(initialCommit.getId());
+ rup.forceUpdate();
+
+ Repository localRepository = createWorkRepository();
+ Git localGit = new Git(localRepository);
+ StoredConfig config = localRepository.getConfig();
+ RemoteConfig rc = new RemoteConfig(config, "origin");
+ rc.addURI(new URIish(remoteRepository.getDirectory().getPath()));
+ rc.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/origin/*"));
+ rc.update(config);
+ config.save();
+ FetchResult res = localGit.fetch().setRemote("origin").call();
+ assertFalse(res.getTrackingRefUpdates().isEmpty());
+ rup = localRepository.updateRef("refs/heads/master");
+ rup.setNewObjectId(initialCommit.getId());
+ rup.forceUpdate();
+ rup = localRepository.updateRef(Constants.HEAD);
+ rup.link("refs/heads/master");
+ rup.setNewObjectId(initialCommit.getId());
+ rup.update();
+ return localGit;
+ }
+
+ public void testCreateAndList() throws Exception {
+ int localBefore;
+ int remoteBefore;
+ int allBefore;
+
+ // invalid name not allowed
+ try {
+ git.branchCreate().setName("In va lid").call();
+ fail("Create branch with invalid ref name should fail");
+ } catch (InvalidRefNameException e) {
+ // expected
+ }
+ // existing name not allowed w/o force
+ try {
+ git.branchCreate().setName("master").call();
+ fail("Create branch with existing ref name should fail");
+ } catch (RefAlreadyExistsException e) {
+ // expected
+ }
+
+ localBefore = git.branchList().call().size();
+ remoteBefore = git.branchList().setListMode(ListMode.REMOTE).call()
+ .size();
+ allBefore = git.branchList().setListMode(ListMode.ALL).call().size();
+
+ assertEquals(localBefore + remoteBefore, allBefore);
+ Ref newBranch = createBranch(git, "NewForTestList", false, "master",
+ null);
+ assertEquals("refs/heads/NewForTestList", newBranch.getName());
+
+ assertEquals(1, git.branchList().call().size() - localBefore);
+ assertEquals(0, git.branchList().setListMode(ListMode.REMOTE).call()
+ .size()
+ - remoteBefore);
+ assertEquals(1, git.branchList().setListMode(ListMode.ALL).call()
+ .size()
+ - allBefore);
+ // we can only create local branches
+ newBranch = createBranch(git,
+ "refs/remotes/origin/NewRemoteForTestList", false, "master",
+ null);
+ assertEquals("refs/heads/refs/remotes/origin/NewRemoteForTestList",
+ newBranch.getName());
+ assertEquals(2, git.branchList().call().size() - localBefore);
+ assertEquals(0, git.branchList().setListMode(ListMode.REMOTE).call()
+ .size()
+ - remoteBefore);
+ assertEquals(2, git.branchList().setListMode(ListMode.ALL).call()
+ .size()
+ - allBefore);
+ }
+
+ public void testCreateFromCommit() throws Exception {
+ Ref branch = git.branchCreate().setName("FromInitial").setStartPoint(
+ initialCommit).call();
+ assertEquals(initialCommit.getId(), branch.getObjectId());
+ branch = git.branchCreate().setName("FromInitial2").setStartPoint(
+ initialCommit.getId().name()).call();
+ assertEquals(initialCommit.getId(), branch.getObjectId());
+ try {
+ git.branchCreate().setName("FromInitial").setStartPoint(
+ secondCommit).call();
+ } catch (RefAlreadyExistsException e) {
+ // expected
+ }
+ branch = git.branchCreate().setName("FromInitial").setStartPoint(
+ secondCommit).setForce(true).call();
+ assertEquals(secondCommit.getId(), branch.getObjectId());
+ }
+
+ public void testCreateForce() throws Exception {
+ // using commits
+ Ref newBranch = createBranch(git, "NewForce", false, secondCommit
+ .getId().name(), null);
+ assertEquals(newBranch.getTarget().getObjectId(), secondCommit.getId());
+ try {
+ newBranch = createBranch(git, "NewForce", false, initialCommit
+ .getId().name(), null);
+ fail("Should have failed");
+ } catch (RefAlreadyExistsException e) {
+ // expected
+ }
+ newBranch = createBranch(git, "NewForce", true, initialCommit.getId()
+ .name(), null);
+ assertEquals(newBranch.getTarget().getObjectId(), initialCommit.getId());
+ git.branchDelete().setBranchNames("NewForce").call();
+ // using names
+
+ git.branchCreate().setName("NewForce").setStartPoint("master").call();
+ assertEquals(newBranch.getTarget().getObjectId(), initialCommit.getId());
+ try {
+ git.branchCreate().setName("NewForce").setStartPoint("master")
+ .call();
+ fail("Should have failed");
+ } catch (RefAlreadyExistsException e) {
+ // expected
+ }
+ git.branchCreate().setName("NewForce").setStartPoint("master")
+ .setForce(true).call();
+ assertEquals(newBranch.getTarget().getObjectId(), initialCommit.getId());
+ }
+
+ public void testDelete() throws Exception {
+ createBranch(git, "ForDelete", false, "master", null);
+ git.branchDelete().setBranchNames("ForDelete").call();
+ // now point the branch to a non-merged commit
+ createBranch(git, "ForDelete", false, secondCommit.getId().name(), null);
+ try {
+ git.branchDelete().setBranchNames("ForDelete").call();
+ fail("Deletion of a non-merged branch without force should have failed");
+ } catch (NotMergedException e) {
+ // expected
+ }
+ List<String> deleted = git.branchDelete().setBranchNames("ForDelete")
+ .setForce(true).call();
+ assertEquals(1, deleted.size());
+ assertEquals(Constants.R_HEADS + "ForDelete", deleted.get(0));
+ createBranch(git, "ForDelete", false, "master", null);
+ try {
+ createBranch(git, "ForDelete", false, "master", null);
+ fail("Repeated creation of same branch without force should fail");
+ } catch (RefAlreadyExistsException e) {
+ // expected
+ }
+ // change starting point
+ Ref newBranch = createBranch(git, "ForDelete", true, initialCommit
+ .name(), null);
+ assertEquals(newBranch.getTarget().getObjectId(), initialCommit.getId());
+ newBranch = createBranch(git, "ForDelete", true, secondCommit.name(),
+ null);
+ assertEquals(newBranch.getTarget().getObjectId(), secondCommit.getId());
+ git.branchDelete().setBranchNames("ForDelete").setForce(true);
+ try {
+ git.branchDelete().setBranchNames("master").call();
+ fail("Deletion of checked out branch without force should have failed");
+ } catch (CannotDeleteCurrentBranchException e) {
+ // expected
+ }
+ try {
+ git.branchDelete().setBranchNames("master").setForce(true).call();
+ fail("Deletion of checked out branch with force should have failed");
+ } catch (CannotDeleteCurrentBranchException e) {
+ // expected
+ }
+ }
+
+ public void testPullConfigRemoteBranch() throws Exception {
+ Git localGit = setUpRepoWithRemote();
+ Ref remote = localGit.branchList().setListMode(ListMode.REMOTE).call()
+ .get(0);
+ assertEquals("refs/remotes/origin/master", remote.getName());
+ // by default, we should create pull configuration
+ createBranch(localGit, "newFromRemote", false, remote.getName(), null);
+ assertEquals("origin", localGit.getRepository().getConfig().getString(
+ "branch", "newFromRemote", "remote"));
+ localGit.branchDelete().setBranchNames("newFromRemote").call();
+ // the pull configuration should be gone after deletion
+ assertNull(localGit.getRepository().getConfig().getString("branch",
+ "newFromRemote", "remote"));
+ // use --no-track
+ createBranch(localGit, "newFromRemote", false, remote.getName(),
+ SetupUpstreamMode.NOTRACK);
+ assertNull(localGit.getRepository().getConfig().getString("branch",
+ "newFromRemote", "remote"));
+ localGit.branchDelete().setBranchNames("newFromRemote").call();
+ }
+
+ public void testPullConfigLocalBranch() throws Exception {
+ Git localGit = setUpRepoWithRemote();
+ // by default, we should not create pull configuration
+ createBranch(localGit, "newFromMaster", false, "master", null);
+ assertNull(localGit.getRepository().getConfig().getString("branch",
+ "newFromMaster", "remote"));
+ localGit.branchDelete().setBranchNames("newFromMaster").call();
+ // use --track
+ createBranch(localGit, "newFromMaster", false, "master",
+ SetupUpstreamMode.TRACK);
+ assertEquals(".", localGit.getRepository().getConfig().getString(
+ "branch", "newFromMaster", "remote"));
+ localGit.branchDelete().setBranchNames("newFromMaster").call();
+ // the pull configuration should be gone after deletion
+ assertNull(localGit.getRepository().getConfig().getString("branch",
+ "newFromRemote", "remote"));
+ }
+
+ public void testPullConfigRenameLocalBranch() throws Exception {
+ Git localGit = setUpRepoWithRemote();
+ // by default, we should not create pull configuration
+ createBranch(localGit, "newFromMaster", false, "master", null);
+ assertNull(localGit.getRepository().getConfig().getString("branch",
+ "newFromMaster", "remote"));
+ localGit.branchDelete().setBranchNames("newFromMaster").call();
+ // use --track
+ createBranch(localGit, "newFromMaster", false, "master",
+ SetupUpstreamMode.TRACK);
+ assertEquals(".", localGit.getRepository().getConfig().getString(
+ "branch", "newFromMaster", "remote"));
+ localGit.branchRename().setOldName("newFromMaster").setNewName(
+ "renamed").call();
+ assertNull(".", localGit.getRepository().getConfig().getString(
+ "branch", "newFromMaster", "remote"));
+ assertEquals(".", localGit.getRepository().getConfig().getString(
+ "branch", "renamed", "remote"));
+ localGit.branchDelete().setBranchNames("renamed").call();
+ // the pull configuration should be gone after deletion
+ assertNull(localGit.getRepository().getConfig().getString("branch",
+ "newFromRemote", "remote"));
+ }
+
+ public void testRenameLocalBranch() throws Exception {
+ // null newName not allowed
+ try {
+ git.branchRename().call();
+ } catch (InvalidRefNameException e) {
+ // expected
+ }
+ // invalid newName not allowed
+ try {
+ git.branchRename().setNewName("In va lid").call();
+ } catch (InvalidRefNameException e) {
+ // expected
+ }
+ // not existing name not allowed
+ try {
+ git.branchRename().setOldName("notexistingbranch").setNewName(
+ "newname").call();
+ } catch (RefNotFoundException e) {
+ // expected
+ }
+ // create some branch
+ createBranch(git, "existing", false, "master", null);
+ // a local branch
+ Ref branch = createBranch(git, "fromMasterForRename", false, "master",
+ null);
+ assertEquals(Constants.R_HEADS + "fromMasterForRename", branch
+ .getName());
+ Ref renamed = git.branchRename().setOldName("fromMasterForRename")
+ .setNewName("newName").call();
+ assertEquals(Constants.R_HEADS + "newName", renamed.getName());
+ try {
+ git.branchRename().setOldName(renamed.getName()).setNewName(
+ "existing").call();
+ fail("Should have failed");
+ } catch (RefAlreadyExistsException e) {
+ // expected
+ }
+ try {
+ git.branchRename().setNewName("In va lid").call();
+ fail("Rename with invalid ref name should fail");
+ } catch (InvalidRefNameException e) {
+ // expected
+ }
+ // rename without old name and detached head not allowed
+ RefUpdate rup = git.getRepository().updateRef(Constants.HEAD, true);
+ rup.setNewObjectId(initialCommit);
+ rup.forceUpdate();
+ try {
+ git.branchRename().setNewName("detached").call();
+ } catch (DetachedHeadException e) {
+ // expected
+ }
+ }
+
+ public void testRenameRemoteTrackingBranch() throws Exception {
+ Git localGit = setUpRepoWithRemote();
+ Ref remoteBranch = localGit.branchList().setListMode(ListMode.REMOTE)
+ .call().get(0);
+ Ref renamed = localGit.branchRename()
+ .setOldName(remoteBranch.getName()).setNewName("newRemote")
+ .call();
+ assertEquals(Constants.R_REMOTES + "newRemote", renamed.getName());
+ }
+
+ public Ref createBranch(Git actGit, String name, boolean force,
+ String startPoint, SetupUpstreamMode mode)
+ throws JGitInternalException, RefAlreadyExistsException,
+ AmbiguousObjectException, RefNotFoundException,
+ InvalidRefNameException {
+ CreateBranchCommand cmd = actGit.branchCreate();
+ cmd.setName(name);
+ cmd.setForce(force);
+ cmd.setStartPoint(startPoint);
+ cmd.setUpstreamMode(mode);
+ return cmd.call();
+ }
+}