Merge "Fix rebase patch set and revert change to update Git first"

This commit is contained in:
Shawn O. Pearce
2012-08-09 13:58:36 -07:00
committed by gerrit code review

View File

@@ -14,6 +14,7 @@
package com.google.gerrit.server; package com.google.gerrit.server;
import com.google.common.collect.Sets;
import com.google.gerrit.common.ChangeHookRunner; import com.google.gerrit.common.ChangeHookRunner;
import com.google.gerrit.common.ChangeHooks; import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
@@ -38,7 +39,6 @@ import com.google.gerrit.server.mail.ReplacePatchSetSender;
import com.google.gerrit.server.mail.ReplyToChangeSender; import com.google.gerrit.server.mail.ReplyToChangeSender;
import com.google.gerrit.server.mail.RevertedSender; import com.google.gerrit.server.mail.RevertedSender;
import com.google.gerrit.server.patch.PatchSetInfoFactory; import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.InvalidChangeOperationException; import com.google.gerrit.server.project.InvalidChangeOperationException;
import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.project.NoSuchChangeException;
@@ -68,6 +68,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@@ -196,13 +197,15 @@ public class ChangeUtil {
* Rebases a commit * Rebases a commit
* *
* @param git Repository to find commits in * @param git Repository to find commits in
* @param inserter inserter to handle new trees and blobs.
* @param original The commit to rebase * @param original The commit to rebase
* @param base Base to rebase against * @param base Base to rebase against
* @return CommitBuilder the newly rebased commit * @return CommitBuilder the newly rebased commit
* @throws IOException Merged failed * @throws IOException Merged failed
*/ */
public static CommitBuilder rebaseCommit(Repository git, RevCommit original, public static CommitBuilder rebaseCommit(Repository git,
RevCommit base, PersonIdent committerIdent) throws IOException { final ObjectInserter inserter, RevCommit original, RevCommit base,
PersonIdent committerIdent) throws IOException {
if (original.getParentCount() == 0) { if (original.getParentCount() == 0) {
throw new IOException( throw new IOException(
@@ -222,6 +225,20 @@ public class ChangeUtil {
} }
final ThreeWayMerger merger = MergeStrategy.RESOLVE.newMerger(git, true); final ThreeWayMerger merger = MergeStrategy.RESOLVE.newMerger(git, true);
merger.setObjectInserter(new ObjectInserter.Filter() {
@Override
protected ObjectInserter delegate() {
return inserter;
}
@Override
public void flush() {
}
@Override
public void release() {
}
});
merger.setBase(parentCommit); merger.setBase(parentCommit);
merger.merge(original, base); merger.merge(original, base);
@@ -251,7 +268,7 @@ public class ChangeUtil {
final ApprovalsUtil approvalsUtil) throws NoSuchChangeException, final ApprovalsUtil approvalsUtil) throws NoSuchChangeException,
EmailException, OrmException, MissingObjectException, EmailException, OrmException, MissingObjectException,
IncorrectObjectTypeException, IOException, IncorrectObjectTypeException, IOException,
PatchSetInfoNotAvailableException, InvalidChangeOperationException { InvalidChangeOperationException {
final Change.Id changeId = patchSetId.getParentKey(); final Change.Id changeId = patchSetId.getParentKey();
final ChangeControl changeControl = final ChangeControl changeControl =
@@ -319,82 +336,92 @@ public class ChangeUtil {
branchTipCommit = revWalk.parseCommit(destRef.getObjectId()); branchTipCommit = revWalk.parseCommit(destRef.getObjectId());
} }
final RevCommit originalCommit = final RevCommit rebasedCommit;
revWalk.parseCommit(ObjectId.fromString(originalPatchSet
.getRevision().get()));
CommitBuilder rebasedCommitBuilder =
rebaseCommit(git, originalCommit, branchTipCommit, myIdent);
final ObjectInserter oi = git.newObjectInserter(); final ObjectInserter oi = git.newObjectInserter();
final ObjectId rebasedCommitId;
try { try {
rebasedCommitId = oi.insert(rebasedCommitBuilder); ObjectId oldId = ObjectId.fromString(originalPatchSet.getRevision().get());
ObjectId newId = oi.insert(rebaseCommit(
git, oi, revWalk.parseCommit(oldId), branchTipCommit, myIdent));
oi.flush(); oi.flush();
rebasedCommit = revWalk.parseCommit(newId);
} finally { } finally {
oi.release(); oi.release();
} }
Change updatedChange = change.nextPatchSetId();
db.changes().atomicUpdate(changeId, new AtomicUpdate<Change>() { final PatchSet newPatchSet = new PatchSet(change.currPatchSetId());
newPatchSet.setCreatedOn(new Timestamp(System.currentTimeMillis()));
newPatchSet.setUploader(user.getAccountId());
newPatchSet.setRevision(new RevId(rebasedCommit.name()));
final PatchSetInfo info =
patchSetInfoFactory.get(rebasedCommit, newPatchSet.getId());
RefUpdate ru = git.updateRef(newPatchSet.getRefName());
ru.setExpectedOldObjectId(ObjectId.zeroId());
ru.setNewObjectId(rebasedCommit);
ru.disableRefLog();
if (ru.update(revWalk) != RefUpdate.Result.NEW) {
throw new IOException(String.format(
"Failed to create ref %s in %s: %s", newPatchSet.getRefName(),
change.getDest().getParentKey().get(), ru.getResult()));
}
replication.fire(change.getProject(), ru.getName());
final Set<Account.Id> oldReviewers = Sets.newHashSet();
final Set<Account.Id> oldCC = Sets.newHashSet();
db.changes().beginTransaction(change.getId());
try {
Change updatedChange;
updatedChange = db.changes().atomicUpdate(changeId,
new AtomicUpdate<Change>() {
@Override @Override
public Change update(Change change) { public Change update(Change change) {
if (change.getStatus().isOpen()) { if (change.getStatus().isOpen()) {
change.nextPatchSetId(); change.updateNumberOfPatchSets(newPatchSet.getPatchSetId());
return change; return change;
} else { } else {
return null; return null;
} }
} }
}); });
if (updatedChange != null) {
if (updatedChange == null) {
throw new InvalidChangeOperationException("Change is closed: "
+ change.toString());
} else {
change = updatedChange; change = updatedChange;
} else {
throw new InvalidChangeOperationException(
String.format("Change %s is closed", change.getId()));
} }
final PatchSet rebasedPatchSet = new PatchSet(change.currPatchSetId()); insertAncestors(db, newPatchSet.getId(), rebasedCommit);
rebasedPatchSet.setCreatedOn(change.getCreatedOn()); db.patchSets().insert(Collections.singleton(newPatchSet));
rebasedPatchSet.setUploader(user.getAccountId()); updatedChange = db.changes().atomicUpdate(changeId,
rebasedPatchSet.setRevision(new RevId(rebasedCommitId.getName()));
insertAncestors(db, rebasedPatchSet.getId(),
revWalk.parseCommit(rebasedCommitId));
db.patchSets().insert(Collections.singleton(rebasedPatchSet));
final PatchSetInfo info =
patchSetInfoFactory.get(db, rebasedPatchSet.getId());
change =
db.changes().atomicUpdate(change.getId(),
new AtomicUpdate<Change>() { new AtomicUpdate<Change>() {
@Override @Override
public Change update(Change change) { public Change update(Change change) {
if (change.getStatus().isClosed()) {
return null;
}
if (!change.currentPatchSetId().equals(patchSetId)) {
return null;
}
if (change.getStatus() != Change.Status.DRAFT) {
change.setStatus(Change.Status.NEW);
}
change.setLastSha1MergeTested(null);
change.setCurrentPatchSet(info); change.setCurrentPatchSet(info);
ChangeUtil.updated(change); ChangeUtil.updated(change);
return change; return change;
} }
}); });
if (updatedChange != null) {
final RefUpdate ru = git.updateRef(rebasedPatchSet.getRefName()); change = updatedChange;
ru.setNewObjectId(rebasedCommitId); } else {
ru.disableRefLog(); throw new InvalidChangeOperationException(
if (ru.update(revWalk) != RefUpdate.Result.NEW) { String.format("Change %s was modified", change.getId()));
throw new IOException("Failed to create ref "
+ rebasedPatchSet.getRefName() + " in " + git.getDirectory()
+ ": " + ru.getResult());
} }
replication.fire(change.getProject(), ru.getName()); for (PatchSetApproval a : approvalsUtil.copyVetosToLatestPatchSet(change)) {
List<PatchSetApproval> patchSetApprovals = approvalsUtil.copyVetosToLatestPatchSet(change);
final Set<Account.Id> oldReviewers = new HashSet<Account.Id>();
final Set<Account.Id> oldCC = new HashSet<Account.Id>();
for (PatchSetApproval a : patchSetApprovals) {
if (a.getValue() != 0) { if (a.getValue() != 0) {
oldReviewers.add(a.getAccountId()); oldReviewers.add(a.getAccountId());
} else { } else {
@@ -407,16 +434,20 @@ public class ChangeUtil {
ChangeUtil.messageUUID(db)), user.getAccountId(), patchSetId); ChangeUtil.messageUUID(db)), user.getAccountId(), patchSetId);
cmsg.setMessage("Patch Set " + patchSetId.get() + ": Rebased"); cmsg.setMessage("Patch Set " + patchSetId.get() + ": Rebased");
db.changeMessages().insert(Collections.singleton(cmsg)); db.changeMessages().insert(Collections.singleton(cmsg));
db.commit();
} finally {
db.rollback();
}
final ReplacePatchSetSender cm = final ReplacePatchSetSender cm =
rebasedPatchSetSenderFactory.create(change); rebasedPatchSetSenderFactory.create(change);
cm.setFrom(user.getAccountId()); cm.setFrom(user.getAccountId());
cm.setPatchSet(rebasedPatchSet); cm.setPatchSet(newPatchSet);
cm.addReviewers(oldReviewers); cm.addReviewers(oldReviewers);
cm.addExtraCC(oldCC); cm.addExtraCC(oldCC);
cm.send(); cm.send();
hooks.doPatchsetCreatedHook(change, rebasedPatchSet, db); hooks.doPatchsetCreatedHook(change, newPatchSet, db);
} finally { } finally {
revWalk.release(); revWalk.release();
} }
@@ -432,9 +463,7 @@ public class ChangeUtil {
final PatchSetInfoFactory patchSetInfoFactory, final PatchSetInfoFactory patchSetInfoFactory,
final GitReferenceUpdated replication, PersonIdent myIdent) final GitReferenceUpdated replication, PersonIdent myIdent)
throws NoSuchChangeException, EmailException, OrmException, throws NoSuchChangeException, EmailException, OrmException,
MissingObjectException, IncorrectObjectTypeException, IOException, MissingObjectException, IncorrectObjectTypeException, IOException {
PatchSetInfoNotAvailableException {
final Change.Id changeId = patchSetId.getParentKey(); final Change.Id changeId = patchSetId.getParentKey();
final PatchSet patch = db.patchSets().get(patchSetId); final PatchSet patch = db.patchSets().get(patchSetId);
if (patch == null) { if (patch == null) {
@@ -446,7 +475,7 @@ public class ChangeUtil {
git = gitManager.openRepository(db.changes().get(changeId).getProject()); git = gitManager.openRepository(db.changes().get(changeId).getProject());
} catch (RepositoryNotFoundException e) { } catch (RepositoryNotFoundException e) {
throw new NoSuchChangeException(changeId, e); throw new NoSuchChangeException(changeId, e);
}; }
final RevWalk revWalk = new RevWalk(git); final RevWalk revWalk = new RevWalk(git);
try { try {
@@ -459,54 +488,62 @@ public class ChangeUtil {
RevCommit parentToCommitToRevert = commitToRevert.getParent(0); RevCommit parentToCommitToRevert = commitToRevert.getParent(0);
revWalk.parseHeaders(parentToCommitToRevert); revWalk.parseHeaders(parentToCommitToRevert);
CommitBuilder revertCommit = new CommitBuilder(); CommitBuilder revertCommitBuilder = new CommitBuilder();
revertCommit.addParentId(commitToRevert); revertCommitBuilder.addParentId(commitToRevert);
revertCommit.setTreeId(parentToCommitToRevert.getTree()); revertCommitBuilder.setTreeId(parentToCommitToRevert.getTree());
revertCommit.setAuthor(authorIdent); revertCommitBuilder.setAuthor(authorIdent);
revertCommit.setCommitter(myIdent); revertCommitBuilder.setCommitter(myIdent);
final ObjectId computedChangeId = final ObjectId computedChangeId =
ChangeIdUtil.computeChangeId(parentToCommitToRevert.getTree(), ChangeIdUtil.computeChangeId(parentToCommitToRevert.getTree(),
commitToRevert, authorIdent, myIdent, message); commitToRevert, authorIdent, myIdent, message);
revertCommit.setMessage(ChangeIdUtil.insertId(message, computedChangeId, true)); revertCommitBuilder.setMessage(ChangeIdUtil.insertId(message, computedChangeId, true));
final ObjectInserter oi = git.newObjectInserter();; RevCommit revertCommit;
ObjectId id; final ObjectInserter oi = git.newObjectInserter();
try { try {
id = oi.insert(revertCommit); ObjectId id = oi.insert(revertCommitBuilder);
oi.flush(); oi.flush();
revertCommit = revWalk.parseCommit(id);
} finally { } finally {
oi.release(); oi.release();
} }
Change.Key changeKey = new Change.Key("I" + computedChangeId.name()); final Change change = new Change(
final Change change = new Change.Key("I" + computedChangeId.name()),
new Change(changeKey, new Change.Id(db.nextChangeId()), new Change.Id(db.nextChangeId()),
user.getAccountId(), db.changes().get(changeId).getDest()); user.getAccountId(),
db.changes().get(changeId).getDest());
change.nextPatchSetId(); change.nextPatchSetId();
final PatchSet ps = new PatchSet(change.currPatchSetId()); final PatchSet ps = new PatchSet(change.currPatchSetId());
ps.setCreatedOn(change.getCreatedOn()); ps.setCreatedOn(change.getCreatedOn());
ps.setUploader(user.getAccountId()); ps.setUploader(change.getOwner());
ps.setRevision(new RevId(id.getName())); ps.setRevision(new RevId(revertCommit.name()));
db.patchSets().insert(Collections.singleton(ps)); change.setCurrentPatchSet(patchSetInfoFactory.get(revertCommit, ps.getId()));
final PatchSetInfo info =
patchSetInfoFactory.get(revWalk.parseCommit(id), ps.getId());
change.setCurrentPatchSet(info);
ChangeUtil.updated(change); ChangeUtil.updated(change);
db.changes().insert(Collections.singleton(change));
final RefUpdate ru = git.updateRef(ps.getRefName()); final RefUpdate ru = git.updateRef(ps.getRefName());
ru.setNewObjectId(id); ru.setExpectedOldObjectId(ObjectId.zeroId());
ru.setNewObjectId(revertCommit);
ru.disableRefLog(); ru.disableRefLog();
if (ru.update(revWalk) != RefUpdate.Result.NEW) { if (ru.update(revWalk) != RefUpdate.Result.NEW) {
throw new IOException("Failed to create ref " + ps.getRefName() throw new IOException(String.format(
+ " in " + git.getDirectory() + ": " + ru.getResult()); "Failed to create ref %s in %s: %s", ps.getRefName(),
change.getDest().getParentKey().get(), ru.getResult()));
}
replication.fire(change.getProject(), ru.getName());
db.changes().beginTransaction(change.getId());
try {
insertAncestors(db, ps.getId(), revertCommit);
db.patchSets().insert(Collections.singleton(ps));
db.changes().insert(Collections.singleton(change));
db.commit();
} finally {
db.rollback();
} }
replication.fire(db.changes().get(changeId).getProject(),
ru.getName());
final ChangeMessage cmsg = final ChangeMessage cmsg =
new ChangeMessage(new ChangeMessage.Key(changeId, new ChangeMessage(new ChangeMessage.Key(changeId,
@@ -514,7 +551,7 @@ public class ChangeUtil {
final StringBuilder msgBuf = final StringBuilder msgBuf =
new StringBuilder("Patch Set " + patchSetId.get() + ": Reverted"); new StringBuilder("Patch Set " + patchSetId.get() + ": Reverted");
msgBuf.append("\n\n"); msgBuf.append("\n\n");
msgBuf.append("This patchset was reverted in change: " + changeKey.get()); msgBuf.append("This patchset was reverted in change: " + change.getKey().get());
cmsg.setMessage(msgBuf.toString()); cmsg.setMessage(msgBuf.toString());
db.changeMessages().insert(Collections.singleton(cmsg)); db.changeMessages().insert(Collections.singleton(cmsg));
@@ -596,7 +633,7 @@ public class ChangeUtil {
public static <T extends ReplyToChangeSender> void updatedChange( public static <T extends ReplyToChangeSender> void updatedChange(
final ReviewDb db, final IdentifiedUser user, final Change change, final ReviewDb db, final IdentifiedUser user, final Change change,
final ChangeMessage cmsg, ReplyToChangeSender.Factory<T> senderFactory) final ChangeMessage cmsg, ReplyToChangeSender.Factory<T> senderFactory)
throws NoSuchChangeException, EmailException, OrmException { throws OrmException {
db.changeMessages().insert(Collections.singleton(cmsg)); db.changeMessages().insert(Collections.singleton(cmsg));
new ApprovalsUtil(db, null).syncChangeStatus(change); new ApprovalsUtil(db, null).syncChangeStatus(change);