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();
|
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);
|
return new PatchSet.Id(id.getParentKey(), id.get() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,35 +15,63 @@
|
|||||||
package com.google.gerrit.server.edit;
|
package com.google.gerrit.server.edit;
|
||||||
|
|
||||||
import com.google.common.base.Optional;
|
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.AuthException;
|
||||||
|
import com.google.gerrit.extensions.restapi.ResourceConflictException;
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.Change;
|
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.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.IdentifiedUser;
|
||||||
|
import com.google.gerrit.server.change.PatchSetInserter;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
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.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
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.Ref;
|
||||||
|
import org.eclipse.jgit.lib.RefUpdate;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility functions to manipulate change edits.
|
* Utility functions to manipulate change edits.
|
||||||
* <p>
|
* <p>
|
||||||
* This class contains method to retrieve edits.
|
* This class contains methods to retrieve and publish edits.
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ChangeEditUtil {
|
public class ChangeEditUtil {
|
||||||
private final GitRepositoryManager gitManager;
|
private final GitRepositoryManager gitManager;
|
||||||
|
private final PatchSetInserter.Factory patchSetInserterFactory;
|
||||||
|
private final ChangeControl.GenericFactory changeControlFactory;
|
||||||
|
private final Provider<ReviewDb> db;
|
||||||
private final Provider<IdentifiedUser> user;
|
private final Provider<IdentifiedUser> user;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ChangeEditUtil(GitRepositoryManager gitManager,
|
ChangeEditUtil(GitRepositoryManager gitManager,
|
||||||
|
PatchSetInserter.Factory patchSetInserterFactory,
|
||||||
|
ChangeControl.GenericFactory changeControlFactory,
|
||||||
|
Provider<ReviewDb> db,
|
||||||
Provider<IdentifiedUser> user) {
|
Provider<IdentifiedUser> user) {
|
||||||
this.gitManager = gitManager;
|
this.gitManager = gitManager;
|
||||||
|
this.patchSetInserterFactory = patchSetInserterFactory;
|
||||||
|
this.changeControlFactory = changeControlFactory;
|
||||||
|
this.db = db;
|
||||||
this.user = user;
|
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:
|
* Returns reference for this change edit with sharded user and change number:
|
||||||
* refs/users/UU/UUUU/edit-CCCC.
|
* refs/users/UU/UUUU/edit-CCCC.
|
||||||
@@ -88,4 +231,75 @@ public class ChangeEditUtil {
|
|||||||
RefNames.refsUsers(accountId),
|
RefNames.refsUsers(accountId),
|
||||||
changeId.get());
|
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