/* Copyright (c) 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.rest.repository; import static org.locationtech.geogig.rest.repository.RESTUtils.getGeogig; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.locationtech.geogig.api.CommitBuilder; import org.locationtech.geogig.api.GeoGIG; import org.locationtech.geogig.api.ObjectId; import org.locationtech.geogig.api.RevCommit; import org.locationtech.geogig.api.RevTree; import org.locationtech.geogig.api.plumbing.ResolveTreeish; import org.locationtech.geogig.api.plumbing.WriteTree; import org.locationtech.geogig.api.plumbing.diff.DiffEntry; import org.locationtech.geogig.remote.BinaryPackedChanges; import org.locationtech.geogig.remote.HttpFilteredDiffIterator; import org.locationtech.geogig.repository.Repository; import org.locationtech.geogig.storage.ObjectReader; import org.locationtech.geogig.storage.ObjectSerializingFactory; import org.locationtech.geogig.storage.datastream.DataStreamSerializationFactoryV1; import org.restlet.Context; import org.restlet.Finder; import org.restlet.data.MediaType; import org.restlet.data.Request; import org.restlet.data.Response; import org.restlet.resource.Representation; import org.restlet.resource.Resource; import org.restlet.resource.StringRepresentation; import com.google.common.base.Optional; import com.google.common.base.Suppliers; /** * Creates a new commit on the server with the changes provided by the client. */ public class ApplyChangesResource extends Finder { @Override public Resource findTarget(Request request, Response response) { return new ChangesResource(getContext(), request, response); } private class ChangesResource extends Resource { public ChangesResource(// Context context, // Request request, // Response response) // { super(context, request, response); } @Override public boolean allowPost() { return true; } @Override public void post(Representation entity) { InputStream input = null; ObjectId newCommitId = ObjectId.NULL; try { input = getRequest().getEntity().getStream(); final GeoGIG ggit = getGeogig(getRequest()).get(); final Repository repository = ggit.getRepository(); // read in commit object final ObjectSerializingFactory factory = DataStreamSerializationFactoryV1.INSTANCE; ObjectReader<RevCommit> reader = factory.createCommitReader(); RevCommit commit = reader.read(ObjectId.NULL, input); // I don't need to know the // original ObjectId // read in parents List<ObjectId> newParents = new LinkedList<ObjectId>(); int numParents = input.read(); for (int i = 0; i < numParents; i++) { ObjectId parentId = readObjectId(input); newParents.add(parentId); } // read in the changes BinaryPackedChanges unpacker = new BinaryPackedChanges(repository); Iterator<DiffEntry> changes = new HttpFilteredDiffIterator(input, unpacker); RevTree rootTree = RevTree.EMPTY; if (newParents.size() > 0) { ObjectId mappedCommit = newParents.get(0); Optional<ObjectId> treeId = repository.command(ResolveTreeish.class) .setTreeish(mappedCommit).call(); if (treeId.isPresent()) { rootTree = repository.getTree(treeId.get()); } } // Create new commit ObjectId newTreeId = repository.command(WriteTree.class) .setOldRoot(Suppliers.ofInstance(rootTree)) .setDiffSupplier(Suppliers.ofInstance((Iterator<DiffEntry>) changes)) .dontMoveObjects()// HttpFilteredDiffIterator is "auto ingesting" .call(); CommitBuilder builder = new CommitBuilder(commit); builder.setParentIds(newParents); builder.setTreeId(newTreeId); RevCommit mapped = builder.build(); repository.objectDatabase().put(mapped); newCommitId = mapped.getId(); } catch (IOException e) { throw new RuntimeException(e); } getResponse().setEntity( new StringRepresentation(newCommitId.toString(), MediaType.TEXT_PLAIN)); } private ObjectId readObjectId(final InputStream in) throws IOException { byte[] rawBytes = new byte[20]; int amount = 0; int len = 20; int offset = 0; while ((amount = in.read(rawBytes, offset, len - offset)) != 0) { if (amount < 0) throw new EOFException("Came to end of input"); offset += amount; if (offset == len) break; } ObjectId id = ObjectId.createNoClone(rawBytes); return id; } } }