"Cannot rebase: New patch sets are not allowed to be added to change: "
+ changeId.toString());
}
Change change = changeControl.getChange();
final Repository git = gitManager.openRepository(change.getProject());
try {
final RevWalk revWalk = new RevWalk(git);
try {
final PatchSet originalPatchSet = db.patchSets().get(patchSetId);
RevCommit branchTipCommit = null;
List<PatchSetAncestor> patchSetAncestors =
db.patchSetAncestors().ancestorsOf(patchSetId).toList();
if (patchSetAncestors.size() > 1) {
throw new IOException(
"The patch set you are trying to rebase is dependent on several other patch sets: "
+ patchSetAncestors.toString());
}
if (patchSetAncestors.size() == 1) {
List<PatchSet> depPatchSetList = db.patchSets()
.byRevision(patchSetAncestors.get(0).getAncestorRevision())
.toList();
if (!depPatchSetList.isEmpty()) {
PatchSet depPatchSet = depPatchSetList.get(0);
Change.Id depChangeId = depPatchSet.getId().getParentKey();
Change depChange = db.changes().get(depChangeId);
if (depChange.getStatus() == Status.ABANDONED) {
throw new IOException("Cannot rebase against an abandoned change: "
+ depChange.getKey().toString());
}
if (depChange.getStatus().isOpen()) {
PatchSet latestDepPatchSet =
db.patchSets().get(depChange.currentPatchSetId());
if (!depPatchSet.getId().equals(depChange.currentPatchSetId())) {
branchTipCommit =
revWalk.parseCommit(ObjectId
.fromString(latestDepPatchSet.getRevision().get()));
} else {
throw new IOException(
"Change is already based on the latest patch set of the dependent change.");
}
}
}
}
if (branchTipCommit == null) {
// We are dependent on a merged PatchSet or have no PatchSet
// dependencies at all.
Ref destRef = git.getRef(change.getDest().get());
if (destRef == null) {
throw new IOException(
"The destination branch does not exist: "
+ change.getDest().get());
}
branchTipCommit = revWalk.parseCommit(destRef.getObjectId());
}
final RevCommit originalCommit =
revWalk.parseCommit(ObjectId.fromString(originalPatchSet
.getRevision().get()));
CommitBuilder rebasedCommitBuilder =
rebaseCommit(git, originalCommit, branchTipCommit, myIdent);
final ObjectInserter oi = git.newObjectInserter();
final ObjectId rebasedCommitId;
try {
rebasedCommitId = oi.insert(rebasedCommitBuilder);
oi.flush();
} finally {
oi.release();
}
Change updatedChange =
db.changes().atomicUpdate(changeId, new AtomicUpdate<Change>() {
@Override
public Change update(Change change) {
if (change.getStatus().isOpen()) {
change.nextPatchSetId();
return change;
} else {
return null;
}
}
});
if (updatedChange == null) {
throw new InvalidChangeOperationException("Change is closed: "
+ change.toString());
} else {
change = updatedChange;
}
final PatchSet rebasedPatchSet = new PatchSet(change.currPatchSetId());
rebasedPatchSet.setCreatedOn(change.getCreatedOn());
rebasedPatchSet.setUploader(user.getAccountId());
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>() {
@Override
public Change update(Change change) {
change.setCurrentPatchSet(info);
ChangeUtil.updated(change);
return change;
}
});
final RefUpdate ru = git.updateRef(rebasedPatchSet.getRefName());
ru.setNewObjectId(rebasedCommitId);
ru.disableRefLog();
if (ru.update(revWalk) != RefUpdate.Result.NEW) {
throw new IOException("Failed to create ref "
+ rebasedPatchSet.getRefName() + " in " + git.getDirectory()
+ ": " + ru.getResult());
}
replication.fire(change.getProject(), ru.getName());
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) {
oldReviewers.add(a.getAccountId());
} else {
oldCC.add(a.getAccountId());
}
}
final ChangeMessage cmsg =
new ChangeMessage(new ChangeMessage.Key(changeId,
ChangeUtil.messageUUID(db)), user.getAccountId(), patchSetId);
cmsg.setMessage("Patch Set " + patchSetId.get() + ": Rebased");
db.changeMessages().insert(Collections.singleton(cmsg));
final ReplacePatchSetSender cm =
rebasedPatchSetSenderFactory.create(change);
cm.setFrom(user.getAccountId());
cm.setPatchSet(rebasedPatchSet);
cm.addReviewers(oldReviewers);
cm.addExtraCC(oldCC);
cm.send();
hooks.doPatchsetCreatedHook(change, rebasedPatchSet, db);
} finally {
revWalk.release();
}
} finally {
git.close();
}
}