/* Copyright (c) 2013-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: * Victor Olaya (Boundless) - initial implementation */ package org.locationtech.geogig.test.integration; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.locationtech.geogig.api.ObjectId; import org.locationtech.geogig.api.Ref; import org.locationtech.geogig.api.RevCommit; import org.locationtech.geogig.api.plumbing.RefParse; import org.locationtech.geogig.api.porcelain.BranchCreateOp; import org.locationtech.geogig.api.porcelain.CheckoutOp; import org.locationtech.geogig.api.porcelain.CommitOp; import org.locationtech.geogig.api.porcelain.LogOp; import org.locationtech.geogig.api.porcelain.MergeOp; import org.locationtech.geogig.api.porcelain.MergeOp.MergeReport; import org.locationtech.geogig.api.porcelain.SquashOp; import org.opengis.feature.Feature; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; public class SquashOpTest extends RepositoryTestCase { @Rule public ExpectedException exception = ExpectedException.none(); @Test public void testSquash() throws Exception { List<Feature> features = Arrays.asList(points1, lines1, points2, lines2, points3, lines3); List<RevCommit> commits = Lists.newArrayList(); for (Feature f : features) { insertAndAdd(f); final RevCommit commit = geogig.command(CommitOp.class).call(); commits.add(commit); } geogig.command(SquashOp.class).setSince(commits.get(1)).setUntil(commits.get(4)).call(); Iterator<RevCommit> log = geogig.command(LogOp.class).call(); ArrayList<RevCommit> logentries = Lists.newArrayList(log); assertEquals(3, logentries.size()); RevCommit headCommit = logentries.get(0); RevCommit squashedCommit = logentries.get(1); RevCommit presquashCommit = logentries.get(2); assertEquals(commits.get(5).getTreeId(), headCommit.getTreeId()); assertEquals(commits.get(1).getMessage(), squashedCommit.getMessage()); assertEquals(commits.get(4).getAuthor().getTimestamp(), squashedCommit.getAuthor() .getTimestamp()); assertEquals(commits.get(0).getTreeId(), presquashCommit.getTreeId()); } @Test public void testSquashWithMessage() throws Exception { List<Feature> features = Arrays.asList(points1, lines1, points2, lines2, points3, lines3); List<RevCommit> commits = Lists.newArrayList(); for (Feature f : features) { insertAndAdd(f); final RevCommit commit = geogig.command(CommitOp.class).setMessage("Squashed").call(); commits.add(commit); } geogig.command(SquashOp.class).setSince(commits.get(1)).setUntil(commits.get(4)).call(); Iterator<RevCommit> log = geogig.command(LogOp.class).call(); ArrayList<RevCommit> logentries = Lists.newArrayList(log); assertEquals(3, logentries.size()); RevCommit headCommit = logentries.get(0); RevCommit squashedCommit = logentries.get(1); RevCommit presquashCommit = logentries.get(2); assertEquals(commits.get(5).getTreeId(), headCommit.getTreeId()); assertEquals("Squashed", squashedCommit.getMessage()); assertEquals(commits.get(4).getAuthor().getTimestamp(), squashedCommit.getAuthor() .getTimestamp()); assertEquals(commits.get(0).getTreeId(), presquashCommit.getTreeId()); } @Test public void testSquashAtBranchTip() throws Exception { List<Feature> features = Arrays.asList(points1, lines1, points2, lines2, points3, lines3); List<RevCommit> commits = Lists.newArrayList(); for (Feature f : features) { insertAndAdd(f); final RevCommit commit = geogig.command(CommitOp.class).call(); commits.add(commit); } geogig.command(SquashOp.class).setSince(commits.get(1)).setUntil(commits.get(5)).call(); Iterator<RevCommit> log = geogig.command(LogOp.class).call(); ArrayList<RevCommit> logentries = Lists.newArrayList(log); assertEquals(2, logentries.size()); RevCommit squashedCommit = logentries.get(0); RevCommit presquashCommit = logentries.get(1); assertEquals(commits.get(5).getTreeId(), squashedCommit.getTreeId()); assertEquals(commits.get(1).getMessage(), squashedCommit.getMessage()); assertEquals(commits.get(5).getAuthor().getTimestamp(), squashedCommit.getAuthor() .getTimestamp()); assertEquals(commits.get(0).getTreeId(), presquashCommit.getTreeId()); } @Test public void testSquashAtHistoryOrigin() throws Exception { List<Feature> features = Arrays.asList(points1, lines1, points2, lines2, points3, lines3); List<RevCommit> commits = Lists.newArrayList(); for (Feature f : features) { insertAndAdd(f); final RevCommit commit = geogig.command(CommitOp.class).call(); commits.add(commit); } try { geogig.command(SquashOp.class).setSince(commits.get(0)).setUntil(commits.get(4)).call(); fail(); } catch (IllegalArgumentException e) { assertTrue(e.getMessage().contains("no parents")); } } @Test public void testSquashwithSameSinceAndUntilCommit() throws Exception { List<Feature> features = Arrays.asList(points1, lines1, points2, lines2, points3, lines3); List<RevCommit> commits = Lists.newArrayList(); for (Feature f : features) { insertAndAdd(f); final RevCommit commit = geogig.command(CommitOp.class) .setMessage(f.getIdentifier().getID()).call(); commits.add(commit); } geogig.command(SquashOp.class).setSince(commits.get(1)).setUntil(commits.get(1)) .setMessage("squashed").call(); Iterator<RevCommit> log = geogig.command(LogOp.class).call(); ArrayList<RevCommit> logentries = Lists.newArrayList(log); assertEquals(6, logentries.size()); RevCommit squashedCommit = logentries.get(4); assertEquals(commits.get(1).getTreeId(), squashedCommit.getTreeId()); assertEquals("squashed", squashedCommit.getMessage()); } @Test public void testSquash2() throws Exception { List<Feature> features = Arrays.asList(points1, lines1, points2, lines2, points3, lines3); List<RevCommit> commits = Lists.newArrayList(); for (Feature f : features) { insertAndAdd(f); final RevCommit commit = geogig.command(CommitOp.class) .setMessage(f.getIdentifier().getID()).call(); commits.add(commit); } geogig.command(SquashOp.class).setSince(commits.get(1)).setUntil(commits.get(2)) .setMessage("squashed").call(); Iterator<RevCommit> log = geogig.command(LogOp.class).call(); ArrayList<RevCommit> logentries = Lists.newArrayList(log); assertEquals(5, logentries.size()); RevCommit squashedCommit = logentries.get(3); assertEquals(commits.get(2).getTreeId(), squashedCommit.getTreeId()); assertEquals("squashed", squashedCommit.getMessage()); } @Test public void testUncleanWorkingTree() throws Exception { insertAndAdd(points1); RevCommit c1 = geogig.command(CommitOp.class).setMessage("commit for " + idP1).call(); insert(points2); exception.expect(IllegalStateException.class); geogig.command(SquashOp.class).setSince(c1).setUntil(c1).call(); } @Test public void testUncleanIndex() throws Exception { insertAndAdd(points1); RevCommit c1 = geogig.command(CommitOp.class).setMessage("commit for " + idP1).call(); insertAndAdd(points2); exception.expect(IllegalStateException.class); geogig.command(SquashOp.class).setSince(c1).setUntil(c1).call(); } @Test public void testSquashWithMergedBranch() throws Exception { // Try to squash the commits marked (*) in this history // o // | // o - Points 1 added // |\ // | o - branch1 - Points 2 added // | | // o | - Points 3 added* // | | // o | - Lines 1 added* // |/ // o - master - HEAD - Merge commit insertAndAdd(points1); @SuppressWarnings("unused") final RevCommit c1 = geogig.command(CommitOp.class).setMessage("commit for " + idP1).call(); geogig.command(BranchCreateOp.class).setAutoCheckout(true).setName("branch1").call(); insertAndAdd(points2); final RevCommit c2 = geogig.command(CommitOp.class).setMessage("commit for " + idP2).call(); geogig.command(CheckoutOp.class).setSource("master").call(); insertAndAdd(points3); final RevCommit c3 = geogig.command(CommitOp.class).setMessage("commit for " + idP3).call(); insertAndAdd(lines1); final RevCommit c4 = geogig.command(CommitOp.class).setMessage("commit for " + idL1).call(); Ref branch1 = geogig.command(RefParse.class).setName("branch1").call().get(); geogig.command(MergeOp.class).addCommit(Suppliers.ofInstance(branch1.getObjectId())) .setMessage("My merge message.").call(); geogig.command(SquashOp.class).setSince(c3).setUntil(c4).setMessage("Squashed").call(); // check that the commit added after the squashed has all the parents ArrayList<RevCommit> log = Lists.newArrayList(geogig.command(LogOp.class) .setFirstParentOnly(true).call()); assertEquals(3, log.size()); ImmutableList<ObjectId> parents = log.get(0).getParentIds(); assertEquals(2, parents.size()); assertEquals("Squashed", log.get(1).getMessage()); assertEquals(log.get(1).getId(), parents.get(0)); assertEquals(c2.getId(), parents.get(1)); } @Test public void testSquashInMergedBranch() throws Exception { // Try to squash the commits marked (*) in this history // o // | // o - Points 1 added // |\ // | o - branch1 - Points 2 added* // | | // | o - Points 3 added* // | | // o | - Lines 1 added // |/ // o - master - HEAD - Merge commit* insertAndAdd(points1); @SuppressWarnings("unused") final RevCommit c1 = geogig.command(CommitOp.class).setMessage("commit for " + idP1).call(); geogig.command(BranchCreateOp.class).setAutoCheckout(true).setName("branch1").call(); insertAndAdd(points2); final RevCommit c2 = geogig.command(CommitOp.class).setMessage("commit for " + idP2).call(); insertAndAdd(points3); @SuppressWarnings("unused") final RevCommit c3 = geogig.command(CommitOp.class).setMessage("commit for " + idP3).call(); geogig.command(CheckoutOp.class).setSource("master").call(); insertAndAdd(lines1); @SuppressWarnings("unused") final RevCommit c4 = geogig.command(CommitOp.class).setMessage("commit for " + idL1).call(); Ref branch1 = geogig.command(RefParse.class).setName("branch1").call().get(); MergeReport mergeReport = geogig.command(MergeOp.class) .addCommit(Suppliers.ofInstance(branch1.getObjectId())) .setMessage("My merge message.").call(); try { geogig.command(SquashOp.class).setSince(c2).setUntil(mergeReport.getMergeCommit()) .setMessage("Squashed").call(); } catch (IllegalArgumentException e) { assertTrue(e.getMessage().equals( "Cannot reach 'since' from 'until' commit through first parentage")); } } @Test public void testSquashWithMergeCommit() throws Exception { // Try to squash the commits marked (*) in this history // o // | // o - Points 1 added // |\ // | o - branch1 - Points 2 added // | | // o | - Points 3 added* // | | // o | - Lines 1 added* // |/ // o - master - HEAD - Merge commit* insertAndAdd(points1); final RevCommit c1 = geogig.command(CommitOp.class).setMessage("commit for " + idP1).call(); geogig.command(BranchCreateOp.class).setAutoCheckout(true).setName("branch1").call(); insertAndAdd(points2); final RevCommit c2 = geogig.command(CommitOp.class).setMessage("commit for " + idP2).call(); geogig.command(CheckoutOp.class).setSource("master").call(); insertAndAdd(points3); final RevCommit c3 = geogig.command(CommitOp.class).setMessage("commit for " + idP3).call(); insertAndAdd(lines1); @SuppressWarnings("unused") final RevCommit c4 = geogig.command(CommitOp.class).setMessage("commit for " + idL1).call(); Ref branch1 = geogig.command(RefParse.class).setName("branch1").call().get(); MergeReport mergeReport = geogig.command(MergeOp.class) .addCommit(Suppliers.ofInstance(branch1.getObjectId())) .setMessage("My merge message.").call(); geogig.command(SquashOp.class).setSince(c3).setUntil(mergeReport.getMergeCommit()) .setMessage("Squashed").call(); ArrayList<RevCommit> log = Lists.newArrayList(geogig.command(LogOp.class) .setFirstParentOnly(true).call()); assertEquals(2, log.size()); ImmutableList<ObjectId> parents = log.get(0).getParentIds(); assertEquals(c1.getId(), parents.get(0)); assertEquals(c2.getId(), parents.get(1)); } @Test public void testSquashIncludingBranchStartingPoint() throws Exception { // Try to squash the commits marked (*) in this history // o // | // o - Lines 2 added // | // o - Points 1 added* // |\ // | o - branch1 - Points 2 added // | | // o | - Points 3 added* // | | // o | - Lines 1 added* // |/ // o - master - HEAD - Merge commit* insertAndAdd(lines2); @SuppressWarnings("unused") final RevCommit c0 = geogig.command(CommitOp.class).setMessage("commit for " + idL2).call(); insertAndAdd(points1); final RevCommit c1 = geogig.command(CommitOp.class).setMessage("commit for " + idP1).call(); geogig.command(BranchCreateOp.class).setAutoCheckout(true).setName("branch1").call(); insertAndAdd(points2); @SuppressWarnings("unused") final RevCommit c2 = geogig.command(CommitOp.class).setMessage("commit for " + idP2).call(); geogig.command(CheckoutOp.class).setSource("master").call(); insertAndAdd(points3); @SuppressWarnings("unused") final RevCommit c3 = geogig.command(CommitOp.class).setMessage("commit for " + idP3).call(); insertAndAdd(lines1); @SuppressWarnings("unused") final RevCommit c4 = geogig.command(CommitOp.class).setMessage("commit for " + idL1).call(); Ref branch1 = geogig.command(RefParse.class).setName("branch1").call().get(); MergeReport mergeReport = geogig.command(MergeOp.class) .addCommit(Suppliers.ofInstance(branch1.getObjectId())) .setMessage("My merge message.").call(); try { geogig.command(SquashOp.class).setSince(c1).setUntil(mergeReport.getMergeCommit()) .setMessage("Squashed").call(); fail(); } catch (IllegalArgumentException e) { assertTrue(e .getMessage() .equals("The commits to squash include a branch starting point. Squashing that type of commit is not supported.")); } } @Test public void testSquashwithBranchCreatedInChildren() throws Exception { // Try to squash the commits marked (*) in this history // o // | // o - Points 1 added // | // o - Points 2 added* // | // o - Points 3 added* // | // o - Lines 1 added // |\ // | o - Lines 2 added // | // o - Lines 3 added insertAndAdd(points1); @SuppressWarnings("unused") 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(points3); final RevCommit c3 = geogig.command(CommitOp.class).setMessage("commit for " + idP3).call(); insertAndAdd(lines1); @SuppressWarnings("unused") final RevCommit c4 = geogig.command(CommitOp.class).setMessage("commit for " + idL1).call(); geogig.command(BranchCreateOp.class).setName("branch1").setAutoCheckout(true).call(); insertAndAdd(lines2); @SuppressWarnings("unused") final RevCommit c5 = geogig.command(CommitOp.class).setMessage("commit for " + idL2).call(); geogig.command(CheckoutOp.class).setSource("master").call(); insertAndAdd(lines3); @SuppressWarnings("unused") final RevCommit c6 = geogig.command(CommitOp.class).setMessage("commit for " + idL3).call(); try { geogig.command(SquashOp.class).setSince(c2).setUntil(c3).setMessage("Squashed").call(); fail(); } catch (IllegalArgumentException e) { assertTrue(e .getMessage() .equals("The commits after the ones to squash include a branch starting point. This scenario is not supported.")); } } @Test public void testSquashwithBranchWithoutCommitsCreatedInChildren() throws Exception { insertAndAdd(points1); @SuppressWarnings("unused") 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(points3); final RevCommit c3 = geogig.command(CommitOp.class).setMessage("commit for " + idP3).call(); insertAndAdd(lines1); @SuppressWarnings("unused") final RevCommit c4 = geogig.command(CommitOp.class).setMessage("commit for " + idL1).call(); geogig.command(BranchCreateOp.class).setName("branch1").call(); insertAndAdd(lines2); @SuppressWarnings("unused") final RevCommit c5 = geogig.command(CommitOp.class).setMessage("commit for " + idL2).call(); try { geogig.command(SquashOp.class).setSince(c2).setUntil(c3).setMessage("Squashed").call(); fail(); } catch (IllegalArgumentException e) { assertTrue(e .getMessage() .equals("The commits after the ones to squash include a branch starting point. This scenario is not supported.")); } } @Override protected void setUpInternal() throws Exception { } }