Merge "Enable streaming large files put to an edit"
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java
index 7c18cb2..126250e 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/edit/ChangeEditIT.java
@@ -125,7 +125,7 @@
assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW);
assertThat(
modifier.modifyFile(editUtil.byChange(change).get(), FILE_NAME,
- CONTENT_NEW)).isEqualTo(RefUpdate.Result.FORCED);
+ RestSession.newRawInput(CONTENT_NEW))).isEqualTo(RefUpdate.Result.FORCED);
editUtil.delete(editUtil.byChange(change).get());
assertThat(editUtil.byChange(change).isPresent()).isFalse();
}
@@ -136,7 +136,7 @@
.isEqualTo(RefUpdate.Result.NEW);
assertThat(
modifier.modifyFile(editUtil.byChange(change).get(), FILE_NAME,
- CONTENT_NEW2)).isEqualTo(RefUpdate.Result.FORCED);
+ RestSession.newRawInput(CONTENT_NEW2))).isEqualTo(RefUpdate.Result.FORCED);
editUtil.publish(editUtil.byChange(change).get());
assertThat(editUtil.byChange(change).isPresent()).isFalse();
}
@@ -148,7 +148,7 @@
RefUpdate.Result.NEW);
assertThat(
modifier.modifyFile(editUtil.byChange(change).get(), FILE_NAME,
- CONTENT_NEW)).isEqualTo(RefUpdate.Result.FORCED);
+ RestSession.newRawInput(CONTENT_NEW))).isEqualTo(RefUpdate.Result.FORCED);
Optional<ChangeEdit> edit = editUtil.byChange(change);
RestResponse r = adminSession.post(urlPublish());
assertThat(r.getStatusCode()).isEqualTo(SC_NO_CONTENT);
@@ -164,7 +164,7 @@
assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW);
assertThat(
modifier.modifyFile(editUtil.byChange(change).get(), FILE_NAME,
- CONTENT_NEW)).isEqualTo(RefUpdate.Result.FORCED);
+ RestSession.newRawInput(CONTENT_NEW))).isEqualTo(RefUpdate.Result.FORCED);
Optional<ChangeEdit> edit = editUtil.byChange(change);
RestResponse r = adminSession.delete(urlEdit());
assertThat(r.getStatusCode()).isEqualTo(SC_NO_CONTENT);
@@ -177,7 +177,7 @@
assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW);
assertThat(
modifier.modifyFile(editUtil.byChange(change).get(), FILE_NAME,
- CONTENT_NEW)).isEqualTo(RefUpdate.Result.FORCED);
+ RestSession.newRawInput(CONTENT_NEW))).isEqualTo(RefUpdate.Result.FORCED);
ChangeEdit edit = editUtil.byChange(change).get();
PatchSet current = getCurrentPatchSet(changeId);
assertThat(edit.getBasePatchSet().getPatchSetId()).isEqualTo(
@@ -200,7 +200,7 @@
assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW);
assertThat(
modifier.modifyFile(editUtil.byChange(change).get(), FILE_NAME,
- CONTENT_NEW)).isEqualTo(RefUpdate.Result.FORCED);
+ RestSession.newRawInput(CONTENT_NEW))).isEqualTo(RefUpdate.Result.FORCED);
ChangeEdit edit = editUtil.byChange(change).get();
PatchSet current = getCurrentPatchSet(changeId);
assertThat(edit.getBasePatchSet().getPatchSetId()).isEqualTo(
@@ -223,7 +223,7 @@
public void updateExistingFile() throws Exception {
assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW);
Optional<ChangeEdit> edit = editUtil.byChange(change);
- assertThat(modifier.modifyFile(edit.get(), FILE_NAME, CONTENT_NEW))
+ assertThat(modifier.modifyFile(edit.get(), FILE_NAME, RestSession.newRawInput(CONTENT_NEW)))
.isEqualTo(RefUpdate.Result.FORCED);
edit = editUtil.byChange(change);
assertByteArray(
@@ -240,7 +240,7 @@
assertThat(r.getStatusCode()).isEqualTo(SC_NO_CONTENT);
assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW);
Optional<ChangeEdit> edit = editUtil.byChange(change);
- assertThat(modifier.modifyFile(edit.get(), FILE_NAME, CONTENT_NEW))
+ assertThat(modifier.modifyFile(edit.get(), FILE_NAME, RestSession.newRawInput(CONTENT_NEW)))
.isEqualTo(RefUpdate.Result.FORCED);
edit = editUtil.byChange(change);
EditInfo info = toEditInfo(false);
@@ -258,7 +258,7 @@
public void retrieveFilesInEdit() throws Exception {
assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW);
Optional<ChangeEdit> edit = editUtil.byChange(change);
- assertThat(modifier.modifyFile(edit.get(), FILE_NAME, CONTENT_NEW))
+ assertThat(modifier.modifyFile(edit.get(), FILE_NAME, RestSession.newRawInput(CONTENT_NEW)))
.isEqualTo(RefUpdate.Result.FORCED);
EditInfo info = toEditInfo(true);
@@ -345,13 +345,13 @@
public void amendExistingFile() throws Exception {
assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW);
Optional<ChangeEdit> edit = editUtil.byChange(change);
- assertThat(modifier.modifyFile(edit.get(), FILE_NAME, CONTENT_NEW))
+ assertThat(modifier.modifyFile(edit.get(), FILE_NAME, RestSession.newRawInput(CONTENT_NEW)))
.isEqualTo(RefUpdate.Result.FORCED);
edit = editUtil.byChange(change);
assertByteArray(
fileUtil.getContent(edit.get().getChange().getProject(), edit.get()
.getRevision().get(), FILE_NAME), CONTENT_NEW);
- assertThat(modifier.modifyFile(edit.get(), FILE_NAME, CONTENT_NEW2))
+ assertThat(modifier.modifyFile(edit.get(), FILE_NAME, RestSession.newRawInput(CONTENT_NEW2)))
.isEqualTo(RefUpdate.Result.FORCED);
edit = editUtil.byChange(change);
assertByteArray(
@@ -419,7 +419,7 @@
assertThat(adminSession.putRaw(urlEditFile(), in.content).getStatusCode())
.isEqualTo(SC_NO_CONTENT);
Optional<ChangeEdit> edit = editUtil.byChange(change);
- assertThat(modifier.modifyFile(edit.get(), FILE_NAME, CONTENT_NEW2))
+ assertThat(modifier.modifyFile(edit.get(), FILE_NAME, RestSession.newRawInput(CONTENT_NEW2)))
.isEqualTo(RefUpdate.Result.FORCED);
edit = editUtil.byChange(change);
RestResponse r = adminSession.get(urlEditFile());
@@ -449,7 +449,7 @@
public void addNewFile() throws Exception {
assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW);
Optional<ChangeEdit> edit = editUtil.byChange(change);
- assertThat(modifier.modifyFile(edit.get(), FILE_NAME2, CONTENT_NEW))
+ assertThat(modifier.modifyFile(edit.get(), FILE_NAME2, RestSession.newRawInput(CONTENT_NEW)))
.isEqualTo(RefUpdate.Result.FORCED);
edit = editUtil.byChange(change);
assertByteArray(
@@ -461,13 +461,13 @@
public void addNewFileAndAmend() throws Exception {
assertThat(modifier.createEdit(change, ps)).isEqualTo(RefUpdate.Result.NEW);
Optional<ChangeEdit> edit = editUtil.byChange(change);
- assertThat(modifier.modifyFile(edit.get(), FILE_NAME2, CONTENT_NEW))
+ assertThat(modifier.modifyFile(edit.get(), FILE_NAME2, RestSession.newRawInput(CONTENT_NEW)))
.isEqualTo(RefUpdate.Result.FORCED);
edit = editUtil.byChange(change);
assertByteArray(
fileUtil.getContent(edit.get().getChange().getProject(), edit.get()
.getRevision().get(), FILE_NAME2), CONTENT_NEW);
- assertThat(modifier.modifyFile(edit.get(), FILE_NAME2, CONTENT_NEW2))
+ assertThat(modifier.modifyFile(edit.get(), FILE_NAME2, RestSession.newRawInput(CONTENT_NEW2)))
.isEqualTo(RefUpdate.Result.FORCED);
edit = editUtil.byChange(change);
assertByteArray(
@@ -482,7 +482,7 @@
modifier.modifyFile(
editUtil.byChange(change).get(),
FILE_NAME,
- CONTENT_OLD);
+ RestSession.newRawInput(CONTENT_OLD));
fail();
} catch (InvalidChangeOperationException e) {
assertThat(e.getMessage()).isEqualTo("no changes were made");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java
index 7d823c3..d3c07e0 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeEdits.java
@@ -16,7 +16,6 @@
import com.google.common.base.Optional;
import com.google.common.base.Strings;
-import com.google.common.io.ByteStreams;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.common.EditInfo;
import com.google.gerrit.extensions.registration.DynamicMap;
@@ -371,8 +370,10 @@
public Response<?> apply(ChangeEditResource rsrc, Input input)
throws AuthException, ResourceConflictException, IOException {
try {
- editModifier.modifyFile(rsrc.getChangeEdit(), rsrc.getPath(),
- ByteStreams.toByteArray(input.content.getInputStream()));
+ editModifier.modifyFile(
+ rsrc.getChangeEdit(),
+ rsrc.getPath(),
+ input.content);
} catch(InvalidChangeOperationException | IOException e) {
throw new ResourceConflictException(e.getMessage());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java
index ad59093..a06e4e6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditModifier.java
@@ -14,11 +14,16 @@
package com.google.gerrit.server.edit;
+import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.gerrit.server.edit.ChangeEditUtil.editRefName;
import static com.google.gerrit.server.edit.ChangeEditUtil.editRefPrefix;
+import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
+import com.google.common.io.ByteStreams;
+import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.RawInput;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
@@ -39,7 +44,6 @@
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.lib.BatchRefUpdate;
import org.eclipse.jgit.lib.CommitBuilder;
-import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.ObjectId;
@@ -57,6 +61,7 @@
import org.eclipse.jgit.treewalk.TreeWalk;
import java.io.IOException;
+import java.io.InputStream;
import java.util.Map;
import java.util.TimeZone;
@@ -223,7 +228,7 @@
* @throws IOException
*/
public RefUpdate.Result modifyFile(ChangeEdit edit,
- String file, byte[] content) throws AuthException,
+ String file, RawInput content) throws AuthException,
InvalidChangeOperationException, IOException {
return modify(TreeOperation.CHANGE_ENTRY, edit, file, content);
}
@@ -261,7 +266,7 @@
}
private RefUpdate.Result modify(TreeOperation op,
- ChangeEdit edit, String file, byte[] content)
+ ChangeEdit edit, String file, @Nullable RawInput content)
throws AuthException, IOException, InvalidChangeOperationException {
if (!currentUser.get().isIdentifiedUser()) {
throw new AuthException("Authentication required");
@@ -283,7 +288,7 @@
RevCommit base = prevEdit.getParent(0);
base = rw.parseCommit(base);
ObjectId newTree = writeNewTree(op, rw, inserter,
- prevEdit, reader, file, content, base);
+ prevEdit, reader, file, toBlob(inserter, content), base);
if (ObjectId.equals(newTree, prevEdit.getTree())) {
throw new InvalidChangeOperationException("no changes were made");
}
@@ -301,6 +306,20 @@
}
}
+ private static ObjectId toBlob(ObjectInserter ins, @Nullable RawInput content)
+ throws IOException {
+ if (content == null) {
+ return null;
+ }
+
+ long len = content.getContentLength();
+ InputStream in = content.getInputStream();
+ if (len < 0) {
+ return ins.insert(OBJ_BLOB, ByteStreams.toByteArray(in));
+ }
+ return ins.insert(OBJ_BLOB, len, in);
+ }
+
private ObjectId createCommit(IdentifiedUser me, ObjectInserter inserter,
RevCommit prevEdit, RevCommit base, ObjectId tree) throws IOException {
CommitBuilder builder = new CommitBuilder();
@@ -330,75 +349,66 @@
private static ObjectId writeNewTree(TreeOperation op, RevWalk rw,
ObjectInserter ins, RevCommit prevEdit, ObjectReader reader,
- String fileName, byte[] content, RevCommit base)
+ String fileName, final @Nullable ObjectId content, RevCommit base)
throws IOException, InvalidChangeOperationException {
- DirCache newTree = createTree(reader, prevEdit);
- editTree(
- op,
- rw,
- base,
- newTree.editor(),
- ins,
- fileName,
- content);
- return newTree.writeTree(ins);
- }
-
- private static void editTree(TreeOperation op, RevWalk rw, RevCommit base,
- DirCacheEditor dce, ObjectInserter ins, String path, byte[] content)
- throws IOException, InvalidChangeOperationException {
+ DirCache newTree = readTree(reader, prevEdit);
+ DirCacheEditor dce = newTree.editor();
switch (op) {
case DELETE_ENTRY:
- dce.add(new DeletePath(path));
+ dce.add(new DeletePath(fileName));
break;
+
case CHANGE_ENTRY:
+ checkNotNull("new content required", content);
+ dce.add(new PathEdit(fileName) {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ if (ent.getRawMode() == 0) {
+ ent.setFileMode(FileMode.REGULAR_FILE);
+ }
+ ent.setObjectId(content);
+ }
+ });
+ break;
+
case RESTORE_ENTRY:
- dce.add(getPathEdit(op, rw, base, path, ins, content));
+ TreeWalk tw = TreeWalk.forPath(
+ rw.getObjectReader(),
+ fileName,
+ base.getTree().getId());
+
+ // If the file does not exist in the base commit, try to restore it
+ // from the base's parent commit.
+ if (tw == null && base.getParentCount() == 1) {
+ tw = TreeWalk.forPath(rw.getObjectReader(), fileName,
+ rw.parseCommit(base.getParent(0)).getTree());
+ }
+ if (tw == null) {
+ throw new InvalidChangeOperationException(String.format(
+ "cannot restore path %s: missing in base revision %s",
+ fileName, base.abbreviate(8).name()));
+ }
+
+ final FileMode mode = tw.getFileMode(0);
+ final ObjectId oid = tw.getObjectId(0);
+ dce.add(new PathEdit(fileName) {
+ @Override
+ public void apply(DirCacheEntry ent) {
+ ent.setFileMode(mode);
+ ent.setObjectId(oid);
+ }
+ });
break;
}
dce.finish();
+ return newTree.writeTree(ins);
}
- private static PathEdit getPathEdit(TreeOperation op, RevWalk rw,
- RevCommit base, String path, ObjectInserter ins, byte[] content)
- throws IOException, InvalidChangeOperationException {
- final ObjectId oid = op == TreeOperation.CHANGE_ENTRY
- ? ins.insert(Constants.OBJ_BLOB, content)
- : getObjectIdForRestoreOperation(rw, base, path);
- return new PathEdit(path) {
- @Override
- public void apply(DirCacheEntry ent) {
- ent.setFileMode(FileMode.REGULAR_FILE);
- ent.setObjectId(oid);
- }
- };
- }
-
- private static ObjectId getObjectIdForRestoreOperation(RevWalk rw,
- RevCommit base, String path)
- throws IOException, InvalidChangeOperationException {
- TreeWalk tw = TreeWalk.forPath(rw.getObjectReader(), path,
- base.getTree().getId());
- // If the file does not exist in the base commit, try to restore it
- // from the base's parent commit.
- if (tw == null && base.getParentCount() == 1) {
- tw = TreeWalk.forPath(rw.getObjectReader(), path,
- rw.parseCommit(base.getParent(0)).getTree().getId());
- }
- if (tw == null) {
- throw new InvalidChangeOperationException(String.format(
- "cannot restore path %s: missing in base revision %s",
- path, base.abbreviate(8).name()));
- }
- return tw.getObjectId(0);
- }
-
- private static DirCache createTree(ObjectReader reader, RevCommit prevEdit)
+ private static DirCache readTree(ObjectReader reader, RevCommit prevEdit)
throws IOException {
DirCache dc = DirCache.newInCore();
DirCacheBuilder b = dc.builder();
- b.addTree(new byte[0], DirCacheEntry.STAGE_0, reader, prevEdit.getTree()
- .getId());
+ b.addTree(new byte[0], DirCacheEntry.STAGE_0, reader, prevEdit.getTree());
b.finish();
return dc;
}