use of com.google.gerrit.server.update.RepoContext in project gerrit by GerritCodeReview.
the class SubmitStrategyOp method getAlreadyMergedCommit.
private CodeReviewCommit getAlreadyMergedCommit(RepoContext ctx) throws IOException {
CodeReviewCommit tip = args.mergeTip.getInitialTip();
if (tip == null) {
return null;
}
CodeReviewRevWalk rw = (CodeReviewRevWalk) ctx.getRevWalk();
Change.Id id = getId();
String refPrefix = id.toRefPrefix();
Map<String, ObjectId> refs = ctx.getRepoView().getRefs(refPrefix);
List<CodeReviewCommit> commits = new ArrayList<>(refs.size());
for (Map.Entry<String, ObjectId> e : refs.entrySet()) {
PatchSet.Id psId = PatchSet.Id.fromRef(refPrefix + e.getKey());
if (psId == null) {
continue;
}
try {
CodeReviewCommit c = rw.parseCommit(e.getValue());
c.setPatchsetId(psId);
commits.add(c);
} catch (MissingObjectException | IncorrectObjectTypeException ex) {
// Bogus ref, can't be merged into tip so we don't care.
continue;
}
}
Collections.sort(commits, ReviewDbUtil.intKeyOrdering().reverse().onResultOf(c -> c.getPatchsetId()));
CodeReviewCommit result = MergeUtil.findAnyMergedInto(rw, commits, tip);
if (result == null) {
return null;
}
// Some patch set of this change is actually merged into the target
// branch, most likely because a previous run of MergeOp failed after
// updateRepo, during updateChange.
//
// Do the best we can to clean this up: mark the change as merged and set
// the current patch set. Don't touch the dest branch at all. This can
// lead to some odd situations like another change in the set merging in
// a different patch set of this change, but that's unavoidable at this
// point. At least the change will end up in the right state.
//
// TODO(dborowitz): Consider deleting later junk patch set refs. They
// presumably don't have PatchSets pointing to them.
rw.parseBody(result);
result.add(args.canMergeFlag);
PatchSet.Id psId = result.getPatchsetId();
result.copyFrom(toMerge);
// Got overwriten by copyFrom.
result.setPatchsetId(psId);
result.setStatusCode(CommitMergeStatus.ALREADY_MERGED);
args.commitStatus.put(result);
return result;
}
use of com.google.gerrit.server.update.RepoContext in project gerrit by GerritCodeReview.
the class NoteDbOnlyIT method updateChangeFailureRollsBackRefUpdate.
@Test
public void updateChangeFailureRollsBackRefUpdate() throws Exception {
assume().that(notesMigration.fuseUpdates()).isTrue();
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
String master = "refs/heads/master";
String backup = "refs/backup/master";
ObjectId master1 = getRef(master).get();
assertThat(getRef(backup)).isEmpty();
// Toy op that copies the value of refs/heads/master to refs/backup/master.
BatchUpdateOp backupMasterOp = new BatchUpdateOp() {
ObjectId newId;
@Override
public void updateRepo(RepoContext ctx) throws IOException {
ObjectId oldId = ctx.getRepoView().getRef(backup).orElse(ObjectId.zeroId());
newId = ctx.getRepoView().getRef(master).get();
ctx.addRefUpdate(oldId, newId, backup);
}
@Override
public boolean updateChange(ChangeContext ctx) {
ctx.getUpdate(ctx.getChange().currentPatchSetId()).setChangeMessage("Backed up master branch to " + newId.name());
return true;
}
};
try (BatchUpdate bu = newBatchUpdate(batchUpdateFactory)) {
bu.addOp(id, backupMasterOp);
bu.execute();
}
// Ensure backupMasterOp worked.
assertThat(getRef(backup)).hasValue(master1);
assertThat(getMessages(id)).contains("Backed up master branch to " + master1.name());
// Advance master by submitting the change.
gApi.changes().id(id.get()).current().review(ReviewInput.approve());
gApi.changes().id(id.get()).current().submit();
ObjectId master2 = getRef(master).get();
assertThat(master2).isNotEqualTo(master1);
int msgCount = getMessages(id).size();
try (BatchUpdate bu = newBatchUpdate(batchUpdateFactory)) {
// This time, we attempt to back up master, but we fail during updateChange.
bu.addOp(id, backupMasterOp);
String msg = "Change is bad";
bu.addOp(id, new BatchUpdateOp() {
@Override
public boolean updateChange(ChangeContext ctx) throws ResourceConflictException {
throw new ResourceConflictException(msg);
}
});
try {
bu.execute();
assert_().fail("expected ResourceConflictException");
} catch (ResourceConflictException e) {
assertThat(e).hasMessageThat().isEqualTo(msg);
}
}
// If updateChange hadn't failed, backup would have been updated to master2.
assertThat(getRef(backup)).hasValue(master1);
assertThat(getMessages(id)).hasSize(msgCount);
}
use of com.google.gerrit.server.update.RepoContext in project gerrit by GerritCodeReview.
the class ConsistencyCheckerIT method mergeChange.
private ChangeControl mergeChange(ChangeControl ctl) throws Exception {
final ObjectId oldId = getDestRef(ctl);
final ObjectId newId = ObjectId.fromString(psUtil.current(db, ctl.getNotes()).getRevision().get());
final String dest = ctl.getChange().getDest().get();
try (BatchUpdate bu = newUpdate(adminId)) {
bu.addOp(ctl.getId(), new BatchUpdateOp() {
@Override
public void updateRepo(RepoContext ctx) throws IOException {
ctx.addRefUpdate(oldId, newId, dest);
}
@Override
public boolean updateChange(ChangeContext ctx) throws OrmException {
ctx.getChange().setStatus(Change.Status.MERGED);
ctx.getUpdate(ctx.getChange().currentPatchSetId()).fixStatus(Change.Status.MERGED);
return true;
}
});
bu.execute();
}
return reload(ctl);
}
use of com.google.gerrit.server.update.RepoContext in project gerrit by GerritCodeReview.
the class ConsistencyChecker method insertMergedPatchSet.
private void insertMergedPatchSet(final RevCommit commit, @Nullable final PatchSet.Id psIdToDelete, boolean reuseOldPsId) {
ProblemInfo notFound = problem("No patch set found for merged commit " + commit.name());
if (!user.get().isIdentifiedUser()) {
notFound.status = Status.FIX_FAILED;
notFound.outcome = "Must be called by an identified user to insert new patch set";
return;
}
ProblemInfo insertPatchSetProblem;
ProblemInfo deleteOldPatchSetProblem;
if (psIdToDelete == null) {
insertPatchSetProblem = problem(String.format("Expected merged commit %s has no associated patch set", commit.name()));
deleteOldPatchSetProblem = null;
} else {
String msg = String.format("Expected merge commit %s corresponds to patch set %s," + " not the current patch set %s", commit.name(), psIdToDelete.get(), change().currentPatchSetId().get());
// Maybe an identical problem, but different fix.
deleteOldPatchSetProblem = reuseOldPsId ? null : problem(msg);
insertPatchSetProblem = problem(msg);
}
List<ProblemInfo> currProblems = new ArrayList<>(3);
currProblems.add(notFound);
if (deleteOldPatchSetProblem != null) {
currProblems.add(insertPatchSetProblem);
}
currProblems.add(insertPatchSetProblem);
try {
PatchSet.Id psId = (psIdToDelete != null && reuseOldPsId) ? psIdToDelete : ChangeUtil.nextPatchSetId(repo, change().currentPatchSetId());
PatchSetInserter inserter = patchSetInserterFactory.create(ctl, psId, commit);
try (BatchUpdate bu = newBatchUpdate()) {
bu.setRepository(repo, rw, oi);
if (psIdToDelete != null) {
// Delete the given patch set ref. If reuseOldPsId is true,
// PatchSetInserter will reinsert the same ref, making it a no-op.
bu.addOp(ctl.getId(), new BatchUpdateOp() {
@Override
public void updateRepo(RepoContext ctx) throws IOException {
ctx.addRefUpdate(commit, ObjectId.zeroId(), psIdToDelete.toRefName());
}
});
if (!reuseOldPsId) {
bu.addOp(ctl.getId(), new DeletePatchSetFromDbOp(checkNotNull(deleteOldPatchSetProblem), psIdToDelete));
}
}
bu.addOp(ctl.getId(), inserter.setValidate(false).setFireRevisionCreated(false).setNotify(NotifyHandling.NONE).setAllowClosed(true).setMessage("Patch set for merged commit inserted by consistency checker"));
bu.addOp(ctl.getId(), new FixMergedOp(notFound));
bu.execute();
}
ctl = changeControlFactory.controlFor(db.get(), inserter.getChange(), ctl.getUser());
insertPatchSetProblem.status = Status.FIXED;
insertPatchSetProblem.outcome = "Inserted as patch set " + psId.get();
} catch (OrmException | IOException | UpdateException | RestApiException e) {
warn(e);
for (ProblemInfo pi : currProblems) {
pi.status = Status.FIX_FAILED;
pi.outcome = "Error inserting merged patch set";
}
return;
}
}
use of com.google.gerrit.server.update.RepoContext in project gerrit by GerritCodeReview.
the class ChangeEditUtil method publish.
/**
* Promote change edit to patch set, by squashing the edit into its parent.
*
* @param updateFactory factory for creating updates.
* @param ctl the {@code ChangeControl} of the change to which the change edit belongs
* @param edit change edit to publish
* @param notify Notify handling that defines to whom email notifications should be sent after the
* change edit is published.
* @param accountsToNotify Accounts that should be notified after the change edit is published.
* @throws IOException
* @throws OrmException
* @throws UpdateException
* @throws RestApiException
*/
public void publish(BatchUpdate.Factory updateFactory, ChangeControl ctl, final ChangeEdit edit, NotifyHandling notify, ListMultimap<RecipientType, Account.Id> accountsToNotify) throws IOException, OrmException, RestApiException, UpdateException {
Change change = edit.getChange();
try (Repository repo = gitManager.openRepository(change.getProject());
ObjectInserter oi = repo.newObjectInserter();
ObjectReader reader = oi.newReader();
RevWalk rw = new RevWalk(reader)) {
PatchSet basePatchSet = edit.getBasePatchSet();
if (!basePatchSet.getId().equals(change.currentPatchSetId())) {
throw new ResourceConflictException("only edit for current patch set can be published");
}
RevCommit squashed = squashEdit(rw, oi, edit.getEditCommit(), basePatchSet);
PatchSet.Id psId = ChangeUtil.nextPatchSetId(repo, change.currentPatchSetId());
PatchSetInserter inserter = patchSetInserterFactory.create(ctl, psId, squashed).setNotify(notify).setAccountsToNotify(accountsToNotify);
StringBuilder message = new StringBuilder("Patch Set ").append(inserter.getPatchSetId().get()).append(": ");
// Previously checked that the base patch set is the current patch set.
ObjectId prior = ObjectId.fromString(basePatchSet.getRevision().get());
ChangeKind kind = changeKindCache.getChangeKind(change.getProject(), rw, repo.getConfig(), prior, squashed);
if (kind == ChangeKind.NO_CODE_CHANGE) {
message.append("Commit message was updated.");
inserter.setDescription("Edit commit message");
} else {
message.append("Published edit on patch set ").append(basePatchSet.getPatchSetId()).append(".");
}
try (BatchUpdate bu = updateFactory.create(db.get(), change.getProject(), ctl.getUser(), TimeUtil.nowTs())) {
bu.setRepository(repo, rw, oi);
bu.addOp(change.getId(), inserter.setDraft(change.getStatus() == Status.DRAFT || basePatchSet.isDraft()).setMessage(message.toString()));
bu.addOp(change.getId(), new BatchUpdateOp() {
@Override
public void updateRepo(RepoContext ctx) throws Exception {
ctx.addRefUpdate(edit.getEditCommit().copy(), ObjectId.zeroId(), edit.getRefName());
}
});
bu.execute();
}
indexer.index(db.get(), inserter.getChange());
}
}
Aggregations