InlineEdit: Add method to publish edits
Change-Id: I7f73c716d71a0cf335d99d90bf96452ed3798ad6
This commit is contained in:

committed by
David Ostrovsky

parent
0034fabb30
commit
a570dbfb06
@@ -520,7 +520,7 @@ public class ChangeUtil {
|
||||
return (IdentifiedUser) userProvider.get();
|
||||
}
|
||||
|
||||
private static PatchSet.Id nextPatchSetId(PatchSet.Id id) {
|
||||
public static PatchSet.Id nextPatchSetId(PatchSet.Id id) {
|
||||
return new PatchSet.Id(id.getParentKey(), id.get() + 1);
|
||||
}
|
||||
|
||||
|
@@ -15,35 +15,63 @@
|
||||
package com.google.gerrit.server.edit;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gerrit.extensions.restapi.AuthException;
|
||||
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||
import com.google.gerrit.reviewdb.client.Account;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||
import com.google.gerrit.reviewdb.client.RefNames;
|
||||
import com.google.gerrit.reviewdb.client.RevId;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.ChangeUtil;
|
||||
import com.google.gerrit.server.IdentifiedUser;
|
||||
import com.google.gerrit.server.change.PatchSetInserter;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.project.ChangeControl;
|
||||
import com.google.gerrit.server.project.InvalidChangeOperationException;
|
||||
import com.google.gerrit.server.project.NoSuchChangeException;
|
||||
import com.google.gerrit.server.util.TimeUtil;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.eclipse.jgit.lib.CommitBuilder;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectInserter;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.RefUpdate;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility functions to manipulate change edits.
|
||||
* <p>
|
||||
* This class contains method to retrieve edits.
|
||||
* This class contains methods to retrieve and publish edits.
|
||||
*/
|
||||
@Singleton
|
||||
public class ChangeEditUtil {
|
||||
private final GitRepositoryManager gitManager;
|
||||
private final PatchSetInserter.Factory patchSetInserterFactory;
|
||||
private final ChangeControl.GenericFactory changeControlFactory;
|
||||
private final Provider<ReviewDb> db;
|
||||
private final Provider<IdentifiedUser> user;
|
||||
|
||||
@Inject
|
||||
ChangeEditUtil(GitRepositoryManager gitManager,
|
||||
PatchSetInserter.Factory patchSetInserterFactory,
|
||||
ChangeControl.GenericFactory changeControlFactory,
|
||||
Provider<ReviewDb> db,
|
||||
Provider<IdentifiedUser> user) {
|
||||
this.gitManager = gitManager;
|
||||
this.patchSetInserterFactory = patchSetInserterFactory;
|
||||
this.changeControlFactory = changeControlFactory;
|
||||
this.db = db;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
@@ -75,6 +103,121 @@ public class ChangeEditUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Promote change edit to patch set, by squashing the edit into
|
||||
* its parent.
|
||||
*
|
||||
* @param edit change edit to publish
|
||||
* @throws AuthException
|
||||
* @throws NoSuchChangeException
|
||||
* @throws IOException
|
||||
* @throws InvalidChangeOperationException
|
||||
* @throws OrmException
|
||||
* @throws ResourceConflictException
|
||||
*/
|
||||
public void publish(ChangeEdit edit) throws AuthException,
|
||||
NoSuchChangeException, IOException, InvalidChangeOperationException,
|
||||
OrmException, ResourceConflictException {
|
||||
Change change = edit.getChange();
|
||||
Repository repo = gitManager.openRepository(change.getProject());
|
||||
try {
|
||||
RevWalk rw = new RevWalk(repo);
|
||||
ObjectInserter inserter = repo.newObjectInserter();
|
||||
try {
|
||||
RevCommit editCommit = rw.parseCommit(edit.getRef().getObjectId());
|
||||
if (editCommit == null) {
|
||||
throw new NoSuchChangeException(change.getId());
|
||||
}
|
||||
|
||||
PatchSet basePatchSet = getBasePatchSet(edit, editCommit);
|
||||
if (!basePatchSet.getId().equals(change.currentPatchSetId())) {
|
||||
throw new ResourceConflictException(
|
||||
"only edit for current patch set can be published");
|
||||
}
|
||||
|
||||
insertPatchSet(edit, change, repo, rw, basePatchSet,
|
||||
squashEdit(repo, rw, inserter, editCommit, basePatchSet));
|
||||
} finally {
|
||||
inserter.release();
|
||||
rw.release();
|
||||
}
|
||||
|
||||
// TODO(davido): This should happen in the same BatchRefUpdate.
|
||||
deleteRef(repo, edit);
|
||||
} finally {
|
||||
repo.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve base patch set the edit was created on.
|
||||
*
|
||||
* @param edit change edit to retrieve base patch set for
|
||||
* @return parent patch set of the edit
|
||||
* @throws IOException
|
||||
* @throws InvalidChangeOperationException
|
||||
*/
|
||||
public PatchSet getBasePatchSet(ChangeEdit edit) throws IOException,
|
||||
InvalidChangeOperationException {
|
||||
Change change = edit.getChange();
|
||||
Repository repo = gitManager.openRepository(change.getProject());
|
||||
try {
|
||||
RevWalk rw = new RevWalk(repo);
|
||||
try {
|
||||
return getBasePatchSet(edit, rw.parseCommit(
|
||||
edit.getRef().getObjectId()));
|
||||
} finally {
|
||||
rw.release();
|
||||
}
|
||||
} finally {
|
||||
repo.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve base patch set the edit was created on.
|
||||
*
|
||||
* @param edit change edit to retrieve base patch set for
|
||||
* @param commit change edit commit
|
||||
* @return parent patch set of the edit
|
||||
* @throws IOException
|
||||
* @throws InvalidChangeOperationException
|
||||
*/
|
||||
public PatchSet getBasePatchSet(ChangeEdit edit, RevCommit commit)
|
||||
throws IOException, InvalidChangeOperationException {
|
||||
if (commit.getParentCount() != 1) {
|
||||
throw new InvalidChangeOperationException(
|
||||
"change edit commit has multiple parents");
|
||||
}
|
||||
RevCommit parentCommit = commit.getParent(0);
|
||||
ObjectId rev = parentCommit.getId();
|
||||
RevId parentRev = new RevId(ObjectId.toString(rev));
|
||||
try {
|
||||
List<PatchSet> r = db.get().patchSets().byRevision(parentRev).toList();
|
||||
if (r.isEmpty()) {
|
||||
throw new InvalidChangeOperationException(String.format(
|
||||
"patch set %s change edit is based on doesn't exist",
|
||||
rev.abbreviate(8)));
|
||||
}
|
||||
if (r.size() > 1) {
|
||||
throw new InvalidChangeOperationException(String.format(
|
||||
"multiple patch sets for change edit parent %s",
|
||||
rev.abbreviate(8)));
|
||||
}
|
||||
PatchSet parentPatchSet = Iterables.getOnlyElement(r);
|
||||
if (!edit.getChange().getId().equals(
|
||||
parentPatchSet.getId().getParentKey())) {
|
||||
throw new InvalidChangeOperationException(String.format(
|
||||
"different change edit ID %d and its parent patch set %d",
|
||||
edit.getChange().getId().get(),
|
||||
parentPatchSet.getId().getParentKey().get()));
|
||||
}
|
||||
return parentPatchSet;
|
||||
} catch (OrmException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns reference for this change edit with sharded user and change number:
|
||||
* refs/users/UU/UUUU/edit-CCCC.
|
||||
@@ -88,4 +231,75 @@ public class ChangeEditUtil {
|
||||
RefNames.refsUsers(accountId),
|
||||
changeId.get());
|
||||
}
|
||||
|
||||
private RevCommit squashEdit(Repository repo, RevWalk rw,
|
||||
ObjectInserter inserter, RevCommit edit, PatchSet basePatchSet)
|
||||
throws IOException, ResourceConflictException {
|
||||
RevCommit parent = rw.parseCommit(ObjectId.fromString(
|
||||
basePatchSet.getRevision().get()));
|
||||
if (parent.getTree().equals(edit.getTree())) {
|
||||
throw new ResourceConflictException("identical tree");
|
||||
}
|
||||
return writeSquashedCommit(rw, inserter, parent, edit);
|
||||
}
|
||||
|
||||
private void insertPatchSet(ChangeEdit edit, Change change,
|
||||
Repository repo, RevWalk rw, PatchSet basePatchSet, RevCommit squashed)
|
||||
throws NoSuchChangeException, InvalidChangeOperationException,
|
||||
OrmException, IOException {
|
||||
PatchSet ps = new PatchSet(
|
||||
ChangeUtil.nextPatchSetId(change.currentPatchSetId()));
|
||||
ps.setRevision(new RevId(ObjectId.toString(squashed)));
|
||||
ps.setUploader(edit.getUser().getAccountId());
|
||||
ps.setCreatedOn(TimeUtil.nowTs());
|
||||
|
||||
PatchSetInserter insr =
|
||||
patchSetInserterFactory.create(repo, rw,
|
||||
changeControlFactory.controlFor(change, edit.getUser()),
|
||||
squashed);
|
||||
insr.setPatchSet(ps)
|
||||
.setMessage(
|
||||
String.format("Patch Set %d: Published edit on patch set %d",
|
||||
ps.getPatchSetId(),
|
||||
basePatchSet.getPatchSetId()))
|
||||
.insert();
|
||||
}
|
||||
|
||||
private static void deleteRef(Repository repo, ChangeEdit edit)
|
||||
throws IOException {
|
||||
String refName = edit.getRefName();
|
||||
RefUpdate ru = repo.updateRef(refName, true);
|
||||
ru.setExpectedOldObjectId(edit.getRef().getObjectId());
|
||||
ru.setForceUpdate(true);
|
||||
RefUpdate.Result result = ru.delete();
|
||||
switch (result) {
|
||||
case FORCED:
|
||||
case NEW:
|
||||
case NO_CHANGE:
|
||||
break;
|
||||
default:
|
||||
throw new IOException(String.format("Failed to delete ref %s: %s",
|
||||
refName, result));
|
||||
}
|
||||
}
|
||||
|
||||
private static RevCommit writeSquashedCommit(RevWalk rw,
|
||||
ObjectInserter inserter, RevCommit parent, RevCommit edit)
|
||||
throws IOException {
|
||||
CommitBuilder mergeCommit = new CommitBuilder();
|
||||
mergeCommit.setParentIds(parent.getParent(0));
|
||||
mergeCommit.setAuthor(parent.getAuthorIdent());
|
||||
mergeCommit.setMessage(parent.getFullMessage());
|
||||
mergeCommit.setCommitter(edit.getCommitterIdent());
|
||||
mergeCommit.setTreeId(edit.getTree());
|
||||
|
||||
return rw.parseCommit(commit(inserter, mergeCommit));
|
||||
}
|
||||
|
||||
private static ObjectId commit(ObjectInserter inserter,
|
||||
CommitBuilder mergeCommit) throws IOException {
|
||||
ObjectId id = inserter.insert(mergeCommit);
|
||||
inserter.flush();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user