/* Copyright (c) 2012-2014 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Gabriel Roldan (Boundless) - initial implementation
*/
package org.locationtech.geogig.test.integration;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.locationtech.geogig.api.Node;
import org.locationtech.geogig.api.NodeRef;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.Ref;
import org.locationtech.geogig.api.RevCommit;
import org.locationtech.geogig.api.RevTree;
import org.locationtech.geogig.api.SymRef;
import org.locationtech.geogig.api.plumbing.FindTreeChild;
import org.locationtech.geogig.api.plumbing.RefParse;
import org.locationtech.geogig.api.plumbing.RevObjectParse;
import org.locationtech.geogig.api.plumbing.RevParse;
import org.locationtech.geogig.api.porcelain.BranchCreateOp;
import org.locationtech.geogig.api.porcelain.CheckoutException;
import org.locationtech.geogig.api.porcelain.CheckoutOp;
import org.locationtech.geogig.api.porcelain.CheckoutResult;
import org.locationtech.geogig.api.porcelain.CommitOp;
import org.locationtech.geogig.api.porcelain.ConfigOp;
import org.locationtech.geogig.api.porcelain.ConfigOp.ConfigAction;
import org.locationtech.geogig.api.porcelain.MergeConflictsException;
import org.locationtech.geogig.api.porcelain.MergeOp;
import org.locationtech.geogig.api.porcelain.RemoveOp;
import org.opengis.feature.Feature;
import com.google.common.base.Optional;
import com.google.common.base.Suppliers;
public class CheckoutOpTest extends RepositoryTestCase {
@Rule
public ExpectedException exception = ExpectedException.none();
private Feature points1ModifiedB;
private Feature points1Modified;
@Override
protected void setUpInternal() throws Exception {
// These values should be used during a commit to set author/committer
// TODO: author/committer roles need to be defined better, but for
// now they are the same thing.
repo.command(ConfigOp.class).setAction(ConfigAction.CONFIG_SET).setName("user.name")
.setValue("groldan").call();
repo.command(ConfigOp.class).setAction(ConfigAction.CONFIG_SET).setName("user.email")
.setValue("groldan@boundlessgeo.com").call();
points1ModifiedB = feature(pointsType, idP1, "StringProp1_3", new Integer(2000),
"POINT(1 1)");
points1Modified = feature(pointsType, idP1, "StringProp1_2", new Integer(1000),
"POINT(1 1)");
}
@Test
public void testAddPaths() throws Exception {
ObjectId oID1 = insertAndAdd(points1);
insertAndAdd(points2);
insertAndAdd(points3);
ObjectId oID2 = insertAndAdd(lines1);
geogig.command(CommitOp.class).setMessage("commit for all features").call();
geogig.command(BranchCreateOp.class).setAutoCheckout(true).setName("testBranch").call();
ObjectId oID1Modified = insertAndAdd(points1_modified);
ObjectId oID3 = insertAndAdd(lines2);
ObjectId oID4 = insertAndAdd(lines3);
geogig.command(CommitOp.class).setMessage("commit for modified points1").call();
List<String> paths = Arrays.asList(
NodeRef.appendChild(pointsName, points1.getIdentifier().getID()),
NodeRef.appendChild(linesName, lines1.getIdentifier().getID()));
RevTree root = repo.workingTree().getTree();
Optional<Node> featureBlob1 = repo.getTreeChild(root, paths.get(0));
assertEquals(oID1Modified, featureBlob1.get().getObjectId());
Optional<Node> featureBlob2 = repo.getTreeChild(root, paths.get(1));
assertEquals(oID2, featureBlob2.get().getObjectId());
Optional<Node> featureBlob3 = repo.getTreeChild(root,
NodeRef.appendChild(linesName, lines2.getIdentifier().getID()));
assertEquals(oID3, featureBlob3.get().getObjectId());
Optional<Node> featureBlob4 = repo.getTreeChild(root,
NodeRef.appendChild(linesName, lines3.getIdentifier().getID()));
assertEquals(oID4, featureBlob4.get().getObjectId());
geogig.command(CheckoutOp.class).setSource("master").addPaths(paths).call();
root = repo.workingTree().getTree();
featureBlob1 = repo.getTreeChild(root, paths.get(0));
assertEquals(oID1, featureBlob1.get().getObjectId());
featureBlob2 = repo.getTreeChild(root, paths.get(1));
assertEquals(oID2, featureBlob2.get().getObjectId());
featureBlob3 = repo.getTreeChild(root,
NodeRef.appendChild(linesName, lines2.getIdentifier().getID()));
assertEquals(oID3, featureBlob3.get().getObjectId());
featureBlob4 = repo.getTreeChild(root,
NodeRef.appendChild(linesName, lines3.getIdentifier().getID()));
assertEquals(oID4, featureBlob4.get().getObjectId());
}
@Test
public void testCheckoutCommitDettachedHead() throws Exception {
insertAndAdd(points1);
final RevCommit c1 = geogig.command(CommitOp.class).setMessage("commit for " + idP1).call();
insertAndAdd(points2);
final RevCommit c2 = geogig.command(CommitOp.class).setMessage("commit for " + idP2).call();
insertAndAdd(lines1);
final RevCommit c3 = geogig.command(CommitOp.class).setMessage("commit for " + idL2).call();
CheckoutResult result;
result = geogig.command(CheckoutOp.class).setSource(c1.getId().toString()).call();
assertEquals(c1.getTreeId(), result.getNewTree());
assertFalse(geogig.command(RefParse.class).setName(Ref.HEAD).call().get() instanceof SymRef);
assertTrue(geogig.command(RefParse.class).setName(Ref.HEAD).call().get() instanceof Ref);
result = geogig.command(CheckoutOp.class).setSource(c2.getId().toString()).call();
assertEquals(c2.getTreeId(), result.getNewTree());
result = geogig.command(CheckoutOp.class).setSource(c3.getId().toString()).call();
assertEquals(c3.getTreeId(), result.getNewTree());
}
@Test
public void testCheckoutBranch() throws Exception {
insertAndAdd(points1);
final RevCommit c1 = geogig.command(CommitOp.class).setMessage("commit for " + idP1).call();
final Ref branch1 = geogig.command(BranchCreateOp.class).setName("branch1").call();
insertAndAdd(points2);
final RevCommit c2 = geogig.command(CommitOp.class).setMessage("commit for " + idP2).call();
final Ref branch2 = geogig.command(BranchCreateOp.class).setName("branch2").call();
insertAndAdd(lines1);
final RevCommit c3 = geogig.command(CommitOp.class).setMessage("commit for " + idL2).call();
final Ref branch3 = geogig.command(BranchCreateOp.class).setName("branch3").call();
CheckoutResult result;
result = geogig.command(CheckoutOp.class).setSource("branch1").call();
assertEquals(c1.getTreeId(), result.getNewTree());
assertTrue(geogig.command(RefParse.class).setName(Ref.HEAD).call().get() instanceof SymRef);
assertEquals(branch1.getName(), ((SymRef) geogig.command(RefParse.class).setName(Ref.HEAD)
.call().get()).getTarget());
result = geogig.command(CheckoutOp.class).setSource("branch2").call();
assertEquals(c2.getTreeId(), result.getNewTree());
assertTrue(geogig.command(RefParse.class).setName(Ref.HEAD).call().get() instanceof SymRef);
assertEquals(branch2.getName(), ((SymRef) geogig.command(RefParse.class).setName(Ref.HEAD)
.call().get()).getTarget());
result = geogig.command(CheckoutOp.class).setSource("branch3").call();
assertEquals(c3.getTreeId(), result.getNewTree());
assertTrue(geogig.command(RefParse.class).setName(Ref.HEAD).call().get() instanceof SymRef);
assertEquals(branch3.getName(), ((SymRef) geogig.command(RefParse.class).setName(Ref.HEAD)
.call().get()).getTarget());
}
@Test
public void testCheckoutPathFilter() throws Exception {
ObjectId points1Id = insertAndAdd(points1);
geogig.command(CommitOp.class).setMessage("commit for " + idP1).call();
insert(points1_modified);
CheckoutResult result = geogig.command(CheckoutOp.class).addPath("Points/Points.1").call();
Optional<RevTree> workTree = geogig.command(RevObjectParse.class)
.setObjectId(result.getNewTree()).call(RevTree.class);
Optional<NodeRef> nodeRef = geogig.command(FindTreeChild.class).setParent(workTree.get())
.setChildPath("Points/Points.1").call();
assertEquals(points1Id, nodeRef.get().getNode().getObjectId());
}
@Test
public void testCheckoutPathFilterWithNothingInIndex() throws Exception {
insertAndAdd(points2);
insert(points1_modified);
exception.expect(IllegalStateException.class);
geogig.command(CheckoutOp.class).addPath("Points/Points.1").call();
}
@Test
public void testCheckoutPathFilterWithMultiplePaths() throws Exception {
ObjectId points1Id = insertAndAdd(points1);
ObjectId lines1Id = insertAndAdd(lines1);
geogig.command(CommitOp.class).setMessage("commit 1").call();
insert(points1_modified);
insert(lines2);
Collection<String> paths = Arrays.asList("Points/Points.1", "Lines");
CheckoutResult result = geogig.command(CheckoutOp.class).addPaths(paths).call();
Optional<RevTree> workTree = geogig.command(RevObjectParse.class)
.setObjectId(result.getNewTree()).call(RevTree.class);
Optional<NodeRef> nodeRef = geogig.command(FindTreeChild.class).setParent(workTree.get())
.setChildPath("Points/Points.1").call();
assertEquals(points1Id, nodeRef.get().getNode().getObjectId());
nodeRef = geogig.command(FindTreeChild.class).setParent(workTree.get())
.setChildPath("Lines/Lines.1").call();
assertEquals(lines1Id, nodeRef.get().getNode().getObjectId());
nodeRef = geogig.command(FindTreeChild.class).setParent(workTree.get())
.setChildPath("Lines/Lines.2").call();
assertFalse(nodeRef.isPresent());
}
@Test
public void testCheckoutPathFilterWithTreeOtherThanIndex() throws Exception {
ObjectId points1Id = insertAndAdd(points1);
geogig.command(CommitOp.class).setMessage("commit 1").call();
ObjectId points2Id = insertAndAdd(points2);
RevCommit c2 = geogig.command(CommitOp.class).setMessage("commit 2").call();
insertAndAdd(points3);
geogig.command(CommitOp.class).setMessage("commit 3").call();
insert(points1_modified);
CheckoutResult result = geogig.command(CheckoutOp.class)
.setSource(c2.getTreeId().toString()).addPath("Points").call();
Optional<RevTree> workTree = geogig.command(RevObjectParse.class)
.setObjectId(result.getNewTree()).call(RevTree.class);
Optional<NodeRef> nodeRef = geogig.command(FindTreeChild.class).setParent(workTree.get())
.setChildPath("Points/Points.1").call();
assertEquals(points1Id, nodeRef.get().getNode().getObjectId());
nodeRef = geogig.command(FindTreeChild.class).setParent(workTree.get())
.setChildPath("Points/Points.2").call();
assertEquals(points2Id, nodeRef.get().getNode().getObjectId());
nodeRef = geogig.command(FindTreeChild.class).setParent(workTree.get())
.setChildPath("Points/Points.3").call();
assertFalse(nodeRef.isPresent());
}
@Test
public void testCheckoutNoParametersSet() {
exception.expect(IllegalStateException.class);
geogig.command(CheckoutOp.class).call();
}
@Test
public void testCheckoutBranchWithChangesInTheIndex() throws Exception {
insertAndAdd(points1);
geogig.command(CommitOp.class).setMessage("commit for " + idP1).call();
geogig.command(BranchCreateOp.class).setName("branch1").call();
insertAndAdd(points2);
exception.expect(CheckoutException.class);
geogig.command(CheckoutOp.class).setSource("branch1").call();
}
@Test
public void testCheckoutBranchWithChangesInTheWorkTree() throws Exception {
insertAndAdd(points1);
geogig.command(CommitOp.class).setMessage("commit for " + idP1).call();
geogig.command(BranchCreateOp.class).setName("branch1").call();
insert(points2);
exception.expect(CheckoutException.class);
geogig.command(CheckoutOp.class).setSource("branch1").call();
}
@Test
public void testCheckoutBranchWithForceOptionAndChangesInTheIndex() throws Exception {
insertAndAdd(points1);
RevCommit c1 = geogig.command(CommitOp.class).setMessage("commit for " + idP1).call();
Ref branch1 = geogig.command(BranchCreateOp.class).setName("branch1").call();
insertAndAdd(points2);
CheckoutResult result = geogig.command(CheckoutOp.class).setSource("branch1")
.setForce(true).call();
assertEquals(c1.getTreeId(), result.getNewTree());
assertTrue(geogig.command(RefParse.class).setName(Ref.HEAD).call().get() instanceof SymRef);
assertEquals(branch1.getName(), ((SymRef) geogig.command(RefParse.class).setName(Ref.HEAD)
.call().get()).getTarget());
}
@Test
public void testCheckoutPathFilterToUpdatePathThatIsntInIndex() throws Exception {
insertAndAdd(points1);
geogig.command(CommitOp.class).setMessage("commit 1").call();
insertAndAdd(points2);
geogig.command(CommitOp.class).setMessage("commit 2").call();
insertAndAdd(points3);
geogig.command(CommitOp.class).setMessage("commit 3").call();
geogig.command(BranchCreateOp.class).setAutoCheckout(true).setName("branch1").call();
insertAndAdd(lines1);
geogig.command(CommitOp.class).setMessage("commit 4").call();
insertAndAdd(lines2);
geogig.command(CommitOp.class).setMessage("commit 5").call();
insertAndAdd(lines3);
geogig.command(CommitOp.class).setMessage("commit 6").call();
geogig.command(CheckoutOp.class).setSource("master").call();
CheckoutResult result = geogig.command(CheckoutOp.class).setSource("branch1")
.addPath("Lines/Lines.1").call();
Optional<RevTree> workTree = geogig.command(RevObjectParse.class)
.setObjectId(result.getNewTree()).call(RevTree.class);
Optional<NodeRef> nodeRef = geogig.command(FindTreeChild.class).setParent(workTree.get())
.setChildPath("Points/Points.1").call();
assertTrue(nodeRef.isPresent());
nodeRef = geogig.command(FindTreeChild.class).setParent(workTree.get())
.setChildPath("Points/Points.2").call();
assertTrue(nodeRef.isPresent());
nodeRef = geogig.command(FindTreeChild.class).setParent(workTree.get())
.setChildPath("Points/Points.3").call();
assertTrue(nodeRef.isPresent());
nodeRef = geogig.command(FindTreeChild.class).setParent(workTree.get())
.setChildPath("Lines/Lines.1").call();
assertTrue(nodeRef.isPresent());
nodeRef = geogig.command(FindTreeChild.class).setParent(workTree.get())
.setChildPath("Lines/Lines.2").call();
assertFalse(nodeRef.isPresent());
nodeRef = geogig.command(FindTreeChild.class).setParent(workTree.get())
.setChildPath("Lines/Lines.3").call();
assertFalse(nodeRef.isPresent());
}
@Test
public void testCheckoutPathDuringConflict() throws Exception {
createConflictedState();
String path = NodeRef.appendChild(pointsName, idP1);
try {
geogig.command(CheckoutOp.class).addPath(path).call();
} catch (CheckoutException e) {
assertTrue(true);
}
}
@Test
public void testCheckoutBranchDuringConflict() throws Exception {
createConflictedState();
try {
geogig.command(CheckoutOp.class).setSource("TestBranch").call();
} catch (CheckoutException e) {
assertTrue(true);
}
}
@Test
public void testCheckoutOursAndBranchDuringConflict() throws Exception {
createConflictedState();
try {
geogig.command(CheckoutOp.class).setSource("TestBranch").setOurs(true).call();
} catch (IllegalArgumentException e) {
assertTrue(true);
}
}
@Test
public void testCheckoutForceDuringConflict() throws Exception {
createConflictedState();
String path = NodeRef.appendChild(pointsName, idP1);
String path2 = NodeRef.appendChild(pointsName, idP1);
geogig.command(CheckoutOp.class).addPath(path).addPath(path2).setForce(true).call();
}
@Test
public void testCheckoutOursAndTheirs() throws Exception {
try {
geogig.command(CheckoutOp.class).setOurs(true).setTheirs(true).addPath("dummypath")
.call();
fail();
} catch (IllegalArgumentException e) {
assertTrue(true);
}
}
@Test
public void testCheckoutOurs() throws Exception {
createConflictedState();
String path = NodeRef.appendChild(pointsName, idP1);
geogig.command(CheckoutOp.class).addPath(path).setOurs(true).call();
Optional<Node> node = geogig.getRepository().workingTree().findUnstaged(path);
String headPath = Ref.HEAD + ":" + path;
Optional<ObjectId> id = geogig.command(RevParse.class).setRefSpec(headPath).call();
assertEquals(id.get(), node.get().getObjectId());
}
@Test
public void testCheckoutOursDeleted() throws Exception {
createDeleteOursConflictedState();
String path = NodeRef.appendChild(pointsName, idP1);
geogig.command(CheckoutOp.class).addPath(path).setOurs(true).call();
Optional<Node> node = geogig.getRepository().index().findStaged(path);
assertFalse(node.isPresent());
String headPath = Ref.HEAD + ":" + path;
Optional<ObjectId> id = geogig.command(RevParse.class).setRefSpec(headPath).call();
assertFalse(id.isPresent());
}
@Test
public void testCheckoutTheirs() throws Exception {
createConflictedState();
String path = NodeRef.appendChild(pointsName, idP1);
geogig.command(CheckoutOp.class).addPath(path).setTheirs(true).call();
Optional<Node> node = geogig.getRepository().workingTree().findUnstaged(path);
String headPath = Ref.MERGE_HEAD + ":" + path;
Optional<ObjectId> id = geogig.command(RevParse.class).setRefSpec(headPath).call();
assertEquals(id.get(), node.get().getObjectId());
}
@Test
public void testCheckoutTheirsDeleted() throws Exception {
createDeleteTheirsConflictedState();
String path = NodeRef.appendChild(pointsName, idP1);
geogig.command(CheckoutOp.class).addPath(path).setTheirs(true).call();
Optional<Node> node = geogig.getRepository().index().findStaged(path);
assertFalse(node.isPresent());
String headPath = Ref.MERGE_HEAD + ":" + path;
Optional<ObjectId> id = geogig.command(RevParse.class).setRefSpec(headPath).call();
assertFalse(id.isPresent());
}
private void createConflictedState() throws Exception {
// Create the following revision graph
// o
// |
// o - Points 1 added
// |\
// | o - TestBranch - Points 1 modified and points 2 added
// |
// o - master - HEAD - Points 1 modifiedB
insertAndAdd(points1);
geogig.command(CommitOp.class).call();
geogig.command(BranchCreateOp.class).setName("TestBranch").call();
insertAndAdd(points1Modified);
geogig.command(CommitOp.class).call();
geogig.command(CheckoutOp.class).setSource("TestBranch").call();
insertAndAdd(points1ModifiedB);
insertAndAdd(points2);
geogig.command(CommitOp.class).call();
geogig.command(CheckoutOp.class).setSource("master").call();
Ref branch = geogig.command(RefParse.class).setName("TestBranch").call().get();
try {
geogig.command(MergeOp.class).addCommit(Suppliers.ofInstance(branch.getObjectId()))
.call();
fail();
} catch (MergeConflictsException e) {
assertTrue(e.getMessage().contains("conflict"));
}
}
private void createDeleteTheirsConflictedState() throws Exception {
// Create the following revision graph
// o
// |
// o - Points 1 added
// |\
// | o - TestBranch - Points 1 deleted and points 2 added
// |
// o - master - HEAD - Points 1 modified
insertAndAdd(points1);
geogig.command(CommitOp.class).call();
geogig.command(BranchCreateOp.class).setName("TestBranch").call();
insertAndAdd(points1Modified);
geogig.command(CommitOp.class).call();
geogig.command(CheckoutOp.class).setSource("TestBranch").call();
geogig.command(RemoveOp.class).addPathToRemove(NodeRef.appendChild(pointsName, idP1))
.call();
insertAndAdd(points2);
geogig.command(CommitOp.class).call();
geogig.command(CheckoutOp.class).setSource("master").call();
Ref branch = geogig.command(RefParse.class).setName("TestBranch").call().get();
try {
geogig.command(MergeOp.class).addCommit(Suppliers.ofInstance(branch.getObjectId()))
.call();
fail();
} catch (MergeConflictsException e) {
assertTrue(e.getMessage().contains("conflict"));
}
}
private void createDeleteOursConflictedState() throws Exception {
// Create the following revision graph
// o
// |
// o - Points 1 added
// |\
// | o - TestBranch - Points 1 deleted and points 2 added
// |
// o - master - HEAD - Points 1 modified
insertAndAdd(points1);
geogig.command(CommitOp.class).call();
geogig.command(BranchCreateOp.class).setName("TestBranch").call();
geogig.command(RemoveOp.class).addPathToRemove(NodeRef.appendChild(pointsName, idP1))
.call();
geogig.command(CommitOp.class).call();
geogig.command(CheckoutOp.class).setSource("TestBranch").call();
insertAndAdd(points1ModifiedB);
insertAndAdd(points2);
geogig.command(CommitOp.class).call();
geogig.command(CheckoutOp.class).setSource("master").call();
Ref branch = geogig.command(RefParse.class).setName("TestBranch").call().get();
try {
geogig.command(MergeOp.class).addCommit(Suppliers.ofInstance(branch.getObjectId()))
.call();
fail();
} catch (MergeConflictsException e) {
assertTrue(e.getMessage().contains("conflict"));
}
}
}