Search in sources :

Example 16 with CommitBuilder

use of org.locationtech.geogig.api.CommitBuilder in project GeoGig by boundlessgeo.

the class SquashOp method _call.

/**
     * Executes the squash operation.
     * 
     * @return the new head after modifying the history squashing commits
     * @see org.locationtech.geogig.api.AbstractGeoGigOp#call()
     */
@Override
protected ObjectId _call() {
    Preconditions.checkNotNull(since);
    Preconditions.checkNotNull(until);
    GraphDatabase graphDb = graphDatabase();
    Repository repository = repository();
    Platform platform = platform();
    final Optional<Ref> currHead = command(RefParse.class).setName(Ref.HEAD).call();
    Preconditions.checkState(currHead.isPresent(), "Repository has no HEAD, can't squash.");
    Preconditions.checkState(currHead.get() instanceof SymRef, "Can't squash from detached HEAD");
    final SymRef headRef = (SymRef) currHead.get();
    final String currentBranch = headRef.getTarget();
    Preconditions.checkState(index().isClean() && workingTree().isClean(), "You must have a clean working tree and index to perform a squash.");
    Optional<ObjectId> ancestor = command(FindCommonAncestor.class).setLeft(since).setRight(until).call();
    Preconditions.checkArgument(ancestor.isPresent(), "'since' and 'until' command do not have a common ancestor");
    Preconditions.checkArgument(ancestor.get().equals(since.getId()), "Commits provided in wrong order");
    Preconditions.checkArgument(!since.getParentIds().isEmpty(), "'since' commit has no parents");
    // we get a a list of commits to apply on top of the squashed commits
    List<RevCommit> commits = getCommitsAfterUntil();
    ImmutableSet<Ref> refs = command(ForEachRef.class).setPrefixFilter(Ref.HEADS_PREFIX).call();
    // we create a list of all parents of those squashed commits, in case they are
    // merge commits. The resulting commit will have all these parents
    //
    // While iterating the set of commits to squash, we check that there are no branch starting
    // points among them. Any commit with more than one child causes an exception to be thrown,
    // since the squash operation does not support squashing those commits
    Iterator<RevCommit> toSquash = command(LogOp.class).setSince(since.getParentIds().get(0)).setUntil(until.getId()).setFirstParentOnly(true).call();
    List<ObjectId> firstParents = Lists.newArrayList();
    List<ObjectId> secondaryParents = Lists.newArrayList();
    final List<ObjectId> squashedIds = Lists.newArrayList();
    RevCommit commitToSquash = until;
    while (toSquash.hasNext()) {
        commitToSquash = toSquash.next();
        squashedIds.add(commitToSquash.getId());
        Preconditions.checkArgument(graphDb.getChildren(commitToSquash.getId()).size() < 2, "The commits to squash include a branch starting point. Squashing that type of commit is not supported.");
        for (Ref ref : refs) {
            // In case a branch has been created but no commit has been made on it and the
            // starting commit has just one child
            Preconditions.checkArgument(!ref.getObjectId().equals(commitToSquash.getId()) || ref.getObjectId().equals(currHead.get().getObjectId()) || commitToSquash.getParentIds().size() > 1, "The commits to squash include a branch starting point. Squashing that type of commit is not supported.");
        }
        ImmutableList<ObjectId> parentIds = commitToSquash.getParentIds();
        for (int i = 1; i < parentIds.size(); i++) {
            secondaryParents.add(parentIds.get(i));
        }
        firstParents.add(parentIds.get(0));
    }
    Preconditions.checkArgument(since.equals(commitToSquash), "Cannot reach 'since' from 'until' commit through first parentage");
    // We do the same check in the children commits
    for (RevCommit commit : commits) {
        Preconditions.checkArgument(graphDb.getChildren(commit.getId()).size() < 2, "The commits after the ones to squash include a branch starting point. This scenario is not supported.");
        for (Ref ref : refs) {
            // In case a branch has been created but no commit has been made on it
            Preconditions.checkArgument(!ref.getObjectId().equals(commit.getId()) || ref.getObjectId().equals(currHead.get().getObjectId()) || commit.getParentIds().size() > 1, "The commits after the ones to squash include a branch starting point. This scenario is not supported.");
        }
    }
    ObjectId newHead;
    // rewind the head
    newHead = since.getParentIds().get(0);
    command(ResetOp.class).setCommit(Suppliers.ofInstance(newHead)).setMode(ResetMode.HARD).call();
    // add the current HEAD as first parent of the resulting commit
    // parents.add(0, newHead);
    // Create new commit
    List<ObjectId> parents = Lists.newArrayList();
    parents.addAll(firstParents);
    parents.addAll(secondaryParents);
    ObjectId endTree = until.getTreeId();
    CommitBuilder builder = new CommitBuilder(until);
    Collection<ObjectId> filteredParents = Collections2.filter(parents, new Predicate<ObjectId>() {

        @Override
        public boolean apply(@Nullable ObjectId id) {
            return !squashedIds.contains(id);
        }
    });
    builder.setParentIds(Lists.newArrayList(filteredParents));
    builder.setTreeId(endTree);
    if (message == null) {
        message = since.getMessage();
    }
    long timestamp = platform.currentTimeMillis();
    builder.setMessage(message);
    builder.setCommitter(resolveCommitter());
    builder.setCommitterEmail(resolveCommitterEmail());
    builder.setCommitterTimestamp(timestamp);
    builder.setCommitterTimeZoneOffset(platform.timeZoneOffset(timestamp));
    builder.setAuthorTimestamp(until.getAuthor().getTimestamp());
    RevCommit newCommit = builder.build();
    repository.objectDatabase().put(newCommit);
    newHead = newCommit.getId();
    ObjectId newTreeId = newCommit.getTreeId();
    command(UpdateRef.class).setName(currentBranch).setNewValue(newHead).call();
    command(UpdateSymRef.class).setName(Ref.HEAD).setNewValue(currentBranch).call();
    workingTree().updateWorkHead(newTreeId);
    index().updateStageHead(newTreeId);
    // now put the other commits after the squashed one
    newHead = addCommits(commits, currentBranch, newHead);
    return newHead;
}
Also used : Platform(org.locationtech.geogig.api.Platform) GraphDatabase(org.locationtech.geogig.storage.GraphDatabase) CommitBuilder(org.locationtech.geogig.api.CommitBuilder) UpdateSymRef(org.locationtech.geogig.api.plumbing.UpdateSymRef) UpdateSymRef(org.locationtech.geogig.api.plumbing.UpdateSymRef) SymRef(org.locationtech.geogig.api.SymRef) RevCommit(org.locationtech.geogig.api.RevCommit) ObjectId(org.locationtech.geogig.api.ObjectId) UpdateRef(org.locationtech.geogig.api.plumbing.UpdateRef) Repository(org.locationtech.geogig.repository.Repository) UpdateRef(org.locationtech.geogig.api.plumbing.UpdateRef) ForEachRef(org.locationtech.geogig.api.plumbing.ForEachRef) UpdateSymRef(org.locationtech.geogig.api.plumbing.UpdateSymRef) Ref(org.locationtech.geogig.api.Ref) SymRef(org.locationtech.geogig.api.SymRef) FindCommonAncestor(org.locationtech.geogig.api.plumbing.FindCommonAncestor)

Example 17 with CommitBuilder

use of org.locationtech.geogig.api.CommitBuilder in project GeoGig by boundlessgeo.

the class SquashOp method addCommits.

private ObjectId addCommits(List<RevCommit> commits, String currentBranch, final ObjectId squashedId) {
    final Platform platform = platform();
    final Map<ObjectId, ObjectId> replacedCommits = Maps.newHashMap();
    replacedCommits.put(until.getId(), squashedId);
    ObjectId head = squashedId;
    for (RevCommit commit : commits) {
        CommitBuilder builder = new CommitBuilder(commit);
        Collection<ObjectId> parents = Collections2.transform(commit.getParentIds(), new Function<ObjectId, ObjectId>() {

            @Override
            @Nullable
            public ObjectId apply(@Nullable ObjectId id) {
                if (replacedCommits.containsKey(id)) {
                    return replacedCommits.get(id);
                } else {
                    return id;
                }
            }
        });
        builder.setParentIds(Lists.newArrayList(parents));
        builder.setTreeId(commit.getTreeId());
        long timestamp = platform.currentTimeMillis();
        builder.setCommitterTimestamp(timestamp);
        builder.setCommitterTimeZoneOffset(platform.timeZoneOffset(timestamp));
        RevCommit newCommit = builder.build();
        replacedCommits.put(commit.getId(), newCommit.getId());
        objectDatabase().put(newCommit);
        head = newCommit.getId();
        ObjectId newTreeId = newCommit.getTreeId();
        command(UpdateRef.class).setName(currentBranch).setNewValue(head).call();
        command(UpdateSymRef.class).setName(Ref.HEAD).setNewValue(currentBranch).call();
        workingTree().updateWorkHead(newTreeId);
        index().updateStageHead(newTreeId);
    }
    return head;
}
Also used : UpdateSymRef(org.locationtech.geogig.api.plumbing.UpdateSymRef) Platform(org.locationtech.geogig.api.Platform) ObjectId(org.locationtech.geogig.api.ObjectId) CommitBuilder(org.locationtech.geogig.api.CommitBuilder) UpdateRef(org.locationtech.geogig.api.plumbing.UpdateRef) Nullable(javax.annotation.Nullable) RevCommit(org.locationtech.geogig.api.RevCommit)

Example 18 with CommitBuilder

use of org.locationtech.geogig.api.CommitBuilder in project GeoGig by boundlessgeo.

the class RevParseIntegrationTest method setUpInternal.

/**
     * <pre>
     * <code>
     *                                                                         HEAD
     *                                                                          /                                                                             
     * masterCommit1    masterCommit2   masterCommit3   mergeCommit   masterCommit4
     *    o----------------o----------------o-------------o--------------o 
     *                      \                            /
     *                       \                          /
     *                        o------------------------o
     *                 branchCommit1                 branchCommit2
     *                                                        \
     *                                                        BRANCH
     * </code>
     * </pre>
     */
@Override
protected void setUpInternal() throws Exception {
    geogig.command(InitOp.class).call();
    masterCommit1 = commit("masterCommit1");
    masterCommit2 = commit("masterCommit2");
    Ref branch = geogig.command(BranchCreateOp.class).setName("BRANCH").setSource(masterCommit2.getId().toString()).setAutoCheckout(true).call();
    assertEquals(masterCommit2.getId(), branch.getObjectId());
    branchCommit1 = commit("branchCommit1");
    branchCommit2 = commit("branchCommit2");
    geogig.command(CheckoutOp.class).setSource("master").call();
    masterCommit3 = commit("masterCommit3");
    // fake a merge until we have the merge op in place
    CommitBuilder cb = new CommitBuilder();
    cb.setParentIds(ImmutableList.of(masterCommit3.getId(), branchCommit2.getId()));
    cb.setMessage("mergeCommit");
    cb.setAuthor("groldan");
    cb.setCommitter("groldan");
    cb.setTreeId(masterCommit3.getTreeId());
    long now = System.currentTimeMillis();
    cb.setAuthorTimestamp(now);
    cb.setCommitterTimestamp(now);
    mergeCommit = cb.build();
    getRepository().objectDatabase().put(mergeCommit);
    geogig.command(UpdateRef.class).setName("refs/heads/master").setOldValue(masterCommit3.getId()).setNewValue(mergeCommit.getId()).call();
    // end faking up merge op
    masterCommit4 = commit("masterCommit4");
}
Also used : InitOp(org.locationtech.geogig.api.porcelain.InitOp) Ref(org.locationtech.geogig.api.Ref) UpdateRef(org.locationtech.geogig.api.plumbing.UpdateRef) CommitBuilder(org.locationtech.geogig.api.CommitBuilder)

Example 19 with CommitBuilder

use of org.locationtech.geogig.api.CommitBuilder in project GeoGig by boundlessgeo.

the class AbstractMappedRemoteRepo method fetchSparseCommit.

/**
     * This function takes all of the changes introduced by the specified commit and filters them
     * based on the repository filter. It then uses the filtered results to construct a new commit
     * that is the descendant of commits that the original's parents are mapped to.
     * 
     * @param commitId the commit id of the original, non-sparse commit
     * @param allowEmpty allow the function to create an empty sparse commit
     */
private void fetchSparseCommit(ObjectId commitId, boolean allowEmpty) {
    Optional<RevObject> object = getObject(commitId);
    if (object.isPresent() && object.get().getType().equals(TYPE.COMMIT)) {
        RevCommit commit = (RevCommit) object.get();
        FilteredDiffIterator changes = getFilteredChanges(commit);
        GraphDatabase graphDatabase = localRepository.graphDatabase();
        ObjectDatabase objectDatabase = localRepository.objectDatabase();
        graphDatabase.put(commit.getId(), commit.getParentIds());
        RevTree rootTree = RevTree.EMPTY;
        if (commit.getParentIds().size() > 0) {
            // Map this commit to the last "sparse" commit in my ancestry
            ObjectId mappedCommit = graphDatabase.getMapping(commit.getParentIds().get(0));
            graphDatabase.map(commit.getId(), mappedCommit);
            Optional<ObjectId> treeId = localRepository.command(ResolveTreeish.class).setTreeish(mappedCommit).call();
            if (treeId.isPresent()) {
                rootTree = localRepository.getTree(treeId.get());
            }
        } else {
            graphDatabase.map(commit.getId(), ObjectId.NULL);
        }
        Iterator<DiffEntry> it = Iterators.filter(changes, new Predicate<DiffEntry>() {

            @Override
            public boolean apply(DiffEntry e) {
                return true;
            }
        });
        if (it.hasNext()) {
            // Create new commit
            WriteTree writeTree = localRepository.command(WriteTree.class).setOldRoot(Suppliers.ofInstance(rootTree)).setDiffSupplier(Suppliers.ofInstance((Iterator<DiffEntry>) it));
            if (changes.isAutoIngesting()) {
                // the iterator already ingests objects into the ObjectDatabase
                writeTree.dontMoveObjects();
            }
            ObjectId newTreeId = writeTree.call();
            CommitBuilder builder = new CommitBuilder(commit);
            List<ObjectId> newParents = new LinkedList<ObjectId>();
            for (ObjectId parentCommitId : commit.getParentIds()) {
                newParents.add(graphDatabase.getMapping(parentCommitId));
            }
            builder.setParentIds(newParents);
            builder.setTreeId(newTreeId);
            RevCommit mapped = builder.build();
            objectDatabase.put(mapped);
            if (changes.wasFiltered()) {
                graphDatabase.setProperty(mapped.getId(), GraphDatabase.SPARSE_FLAG, "true");
            }
            graphDatabase.map(mapped.getId(), commit.getId());
            // Replace the old mapping with the new commit Id.
            graphDatabase.map(commit.getId(), mapped.getId());
        } else if (allowEmpty) {
            CommitBuilder builder = new CommitBuilder(commit);
            List<ObjectId> newParents = new LinkedList<ObjectId>();
            for (ObjectId parentCommitId : commit.getParentIds()) {
                newParents.add(graphDatabase.getMapping(parentCommitId));
            }
            builder.setParentIds(newParents);
            builder.setTreeId(rootTree.getId());
            builder.setMessage(PLACEHOLDER_COMMIT_MESSAGE);
            RevCommit mapped = builder.build();
            objectDatabase.put(mapped);
            graphDatabase.setProperty(mapped.getId(), GraphDatabase.SPARSE_FLAG, "true");
            graphDatabase.map(mapped.getId(), commit.getId());
            // Replace the old mapping with the new commit Id.
            graphDatabase.map(commit.getId(), mapped.getId());
        } else {
            // Mark the mapped commit as sparse, since it wont have these changes
            graphDatabase.setProperty(graphDatabase.getMapping(commit.getId()), GraphDatabase.SPARSE_FLAG, "true");
        }
    }
}
Also used : WriteTree(org.locationtech.geogig.api.plumbing.WriteTree) RevObject(org.locationtech.geogig.api.RevObject) GraphDatabase(org.locationtech.geogig.storage.GraphDatabase) ObjectId(org.locationtech.geogig.api.ObjectId) CommitBuilder(org.locationtech.geogig.api.CommitBuilder) LinkedList(java.util.LinkedList) ObjectDatabase(org.locationtech.geogig.storage.ObjectDatabase) Iterator(java.util.Iterator) ImmutableList(com.google.common.collect.ImmutableList) LinkedList(java.util.LinkedList) List(java.util.List) RevTree(org.locationtech.geogig.api.RevTree) RevCommit(org.locationtech.geogig.api.RevCommit) DiffEntry(org.locationtech.geogig.api.plumbing.diff.DiffEntry)

Example 20 with CommitBuilder

use of org.locationtech.geogig.api.CommitBuilder in project GeoGig by boundlessgeo.

the class LocalMappedRemoteRepo method pushSparseCommit.

/**
     * This function takes all of the changes introduced by a commit on the sparse repository and
     * creates a new commit on the full repository with those changes.
     * 
     * @param commitId the commit id of commit from the sparse repository
     * @param from the sparse repository
     * @param to the full repository
     */
protected void pushSparseCommit(ObjectId commitId) {
    Repository from = localRepository;
    Repository to = remoteGeoGig.getRepository();
    Optional<RevObject> object = from.command(RevObjectParse.class).setObjectId(commitId).call();
    if (object.isPresent() && object.get().getType().equals(TYPE.COMMIT)) {
        RevCommit commit = (RevCommit) object.get();
        ObjectId parent = ObjectId.NULL;
        List<ObjectId> newParents = new LinkedList<ObjectId>();
        for (int i = 0; i < commit.getParentIds().size(); i++) {
            ObjectId parentId = commit.getParentIds().get(i);
            if (i != 0) {
                Optional<ObjectId> commonAncestor = from.command(FindCommonAncestor.class).setLeftId(commit.getParentIds().get(0)).setRightId(parentId).call();
                if (commonAncestor.isPresent()) {
                    if (from.command(CheckSparsePath.class).setStart(parentId).setEnd(commonAncestor.get()).call()) {
                        // This should be the base commit to preserve the sparse changes that
                        // were filtered
                        // out.
                        newParents.add(0, from.graphDatabase().getMapping(parentId));
                        continue;
                    }
                }
            }
            newParents.add(from.graphDatabase().getMapping(parentId));
        }
        if (newParents.size() > 0) {
            parent = from.graphDatabase().getMapping(newParents.get(0));
        }
        Iterator<DiffEntry> diffIter = from.command(DiffOp.class).setNewVersion(commitId).setOldVersion(parent).setReportTrees(true).call();
        LocalCopyingDiffIterator changes = new LocalCopyingDiffIterator(diffIter, from, to);
        RevTree rootTree = RevTree.EMPTY;
        if (newParents.size() > 0) {
            ObjectId mappedCommit = newParents.get(0);
            Optional<ObjectId> treeId = to.command(ResolveTreeish.class).setTreeish(mappedCommit).call();
            if (treeId.isPresent()) {
                rootTree = to.getTree(treeId.get());
            }
        }
        // Create new commit
        ObjectId newTreeId = to.command(WriteTree.class).setOldRoot(Suppliers.ofInstance(rootTree)).setDiffSupplier(Suppliers.ofInstance((Iterator<DiffEntry>) changes)).call();
        CommitBuilder builder = new CommitBuilder(commit);
        builder.setParentIds(newParents);
        builder.setTreeId(newTreeId);
        RevCommit mapped = builder.build();
        to.objectDatabase().put(mapped);
        from.graphDatabase().map(commit.getId(), mapped.getId());
        from.graphDatabase().map(mapped.getId(), commit.getId());
    }
}
Also used : CheckSparsePath(org.locationtech.geogig.api.plumbing.CheckSparsePath) WriteTree(org.locationtech.geogig.api.plumbing.WriteTree) RevObject(org.locationtech.geogig.api.RevObject) ObjectId(org.locationtech.geogig.api.ObjectId) CommitBuilder(org.locationtech.geogig.api.CommitBuilder) LinkedList(java.util.LinkedList) Repository(org.locationtech.geogig.repository.Repository) FindCommonAncestor(org.locationtech.geogig.api.plumbing.FindCommonAncestor) RevTree(org.locationtech.geogig.api.RevTree) RevCommit(org.locationtech.geogig.api.RevCommit) DiffEntry(org.locationtech.geogig.api.plumbing.diff.DiffEntry)

Aggregations

CommitBuilder (org.locationtech.geogig.api.CommitBuilder)22 ObjectId (org.locationtech.geogig.api.ObjectId)18 RevCommit (org.locationtech.geogig.api.RevCommit)16 UpdateRef (org.locationtech.geogig.api.plumbing.UpdateRef)8 Test (org.junit.Test)7 RevTree (org.locationtech.geogig.api.RevTree)7 Ref (org.locationtech.geogig.api.Ref)6 UpdateSymRef (org.locationtech.geogig.api.plumbing.UpdateSymRef)6 SymRef (org.locationtech.geogig.api.SymRef)4 WriteTree2 (org.locationtech.geogig.api.plumbing.WriteTree2)4 Repository (org.locationtech.geogig.repository.Repository)4 Platform (org.locationtech.geogig.api.Platform)3 DiffEntry (org.locationtech.geogig.api.plumbing.diff.DiffEntry)3 Optional (com.google.common.base.Optional)2 ImmutableList (com.google.common.collect.ImmutableList)2 File (java.io.File)2 IOException (java.io.IOException)2 LinkedList (java.util.LinkedList)2 List (java.util.List)2 Context (org.locationtech.geogig.api.Context)2