final Change.Id changeId = patchSetId.getParentKey();
final ChangeControl changeControl =
changeControlFactory.validateFor(changeId);
if (!changeControl.canRebase()) {
throw new InvalidChangeOperationException(
"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;
}