diff options
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java')
-rw-r--r-- | gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java | 304 |
1 files changed, 0 insertions, 304 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java deleted file mode 100644 index d1d72fa579..0000000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/edit/ChangeEditUtil.java +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright (C) 2014 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.edit; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.collect.ListMultimap; -import com.google.gerrit.common.TimeUtil; -import com.google.gerrit.extensions.api.changes.NotifyHandling; -import com.google.gerrit.extensions.api.changes.RecipientType; -import com.google.gerrit.extensions.client.ChangeKind; -import com.google.gerrit.extensions.restapi.AuthException; -import com.google.gerrit.extensions.restapi.ResourceConflictException; -import com.google.gerrit.extensions.restapi.RestApiException; -import com.google.gerrit.reviewdb.client.Account; -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.server.ReviewDb; -import com.google.gerrit.server.ChangeUtil; -import com.google.gerrit.server.CurrentUser; -import com.google.gerrit.server.IdentifiedUser; -import com.google.gerrit.server.PatchSetUtil; -import com.google.gerrit.server.change.ChangeKindCache; -import com.google.gerrit.server.change.PatchSetInserter; -import com.google.gerrit.server.git.GitRepositoryManager; -import com.google.gerrit.server.index.change.ChangeIndexer; -import com.google.gerrit.server.notedb.ChangeNotes; -import com.google.gerrit.server.update.BatchUpdate; -import com.google.gerrit.server.update.BatchUpdateOp; -import com.google.gerrit.server.update.RepoContext; -import com.google.gerrit.server.update.UpdateException; -import com.google.gwtorm.server.OrmException; -import com.google.inject.Inject; -import com.google.inject.Provider; -import com.google.inject.Singleton; -import java.io.IOException; -import java.util.Optional; -import org.eclipse.jgit.lib.CommitBuilder; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.ObjectInserter; -import org.eclipse.jgit.lib.ObjectReader; -import org.eclipse.jgit.lib.Ref; -import org.eclipse.jgit.lib.RefUpdate; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.revwalk.RevCommit; -import org.eclipse.jgit.revwalk.RevWalk; - -/** - * Utility functions to manipulate change edits. - * - * <p>This class contains methods to retrieve, publish and delete edits. For changing edits see - * {@link ChangeEditModifier}. - */ -@Singleton -public class ChangeEditUtil { - private final GitRepositoryManager gitManager; - private final PatchSetInserter.Factory patchSetInserterFactory; - private final ChangeIndexer indexer; - private final Provider<ReviewDb> db; - private final Provider<CurrentUser> userProvider; - private final ChangeKindCache changeKindCache; - private final PatchSetUtil psUtil; - - @Inject - ChangeEditUtil( - GitRepositoryManager gitManager, - PatchSetInserter.Factory patchSetInserterFactory, - ChangeIndexer indexer, - Provider<ReviewDb> db, - Provider<CurrentUser> userProvider, - ChangeKindCache changeKindCache, - PatchSetUtil psUtil) { - this.gitManager = gitManager; - this.patchSetInserterFactory = patchSetInserterFactory; - this.indexer = indexer; - this.db = db; - this.userProvider = userProvider; - this.changeKindCache = changeKindCache; - this.psUtil = psUtil; - } - - /** - * Retrieve edit for a given change. - * - * <p>At most one change edit can exist per user and change. - * - * @param notes change notes of change to retrieve change edits for. - * @return edit for this change for this user, if present. - * @throws AuthException if this is not a logged-in user. - * @throws IOException if an error occurs. - */ - public Optional<ChangeEdit> byChange(ChangeNotes notes) throws AuthException, IOException { - return byChange(notes, userProvider.get()); - } - - /** - * Retrieve edit for a change and the given user. - * - * <p>At most one change edit can exist per user and change. - * - * @param notes change notes of change to retrieve change edits for. - * @param user user to retrieve edits as. - * @return edit for this change for this user, if present. - * @throws AuthException if this is not a logged-in user. - * @throws IOException if an error occurs. - */ - public Optional<ChangeEdit> byChange(ChangeNotes notes, CurrentUser user) - throws AuthException, IOException { - if (!user.isIdentifiedUser()) { - throw new AuthException("Authentication required"); - } - IdentifiedUser u = user.asIdentifiedUser(); - Change change = notes.getChange(); - try (Repository repo = gitManager.openRepository(change.getProject())) { - int n = change.currentPatchSetId().get(); - String[] refNames = new String[n]; - for (int i = n; i > 0; i--) { - refNames[i - 1] = - RefNames.refsEdit(u.getAccountId(), change.getId(), new PatchSet.Id(change.getId(), i)); - } - Ref ref = repo.getRefDatabase().firstExactRef(refNames); - if (ref == null) { - return Optional.empty(); - } - try (RevWalk rw = new RevWalk(repo)) { - RevCommit commit = rw.parseCommit(ref.getObjectId()); - PatchSet basePs = getBasePatchSet(notes, ref); - return Optional.of(new ChangeEdit(change, ref.getName(), commit, basePs)); - } - } - } - - /** - * Promote change edit to patch set, by squashing the edit into its parent. - * - * @param updateFactory factory for creating updates. - * @param notes the {@code ChangeNotes} of the change to which the change edit belongs - * @param user the current user - * @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, - ChangeNotes notes, - CurrentUser user, - 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(notes, 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(), user, TimeUtil.nowTs())) { - bu.setRepository(repo, rw, oi); - bu.addOp(change.getId(), inserter.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(); - } - } - } - - /** - * Delete change edit. - * - * @param edit change edit to delete - * @throws IOException - * @throws OrmException - */ - public void delete(ChangeEdit edit) throws IOException, OrmException { - Change change = edit.getChange(); - try (Repository repo = gitManager.openRepository(change.getProject())) { - deleteRef(repo, edit); - } - indexer.index(db.get(), change); - } - - private PatchSet getBasePatchSet(ChangeNotes notes, Ref ref) throws IOException { - try { - int pos = ref.getName().lastIndexOf("/"); - checkArgument(pos > 0, "invalid edit ref: %s", ref.getName()); - String psId = ref.getName().substring(pos + 1); - return psUtil.get( - db.get(), notes, new PatchSet.Id(notes.getChange().getId(), Integer.parseInt(psId))); - } catch (OrmException | NumberFormatException e) { - throw new IOException(e); - } - } - - private RevCommit squashEdit( - 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()) - && edit.getFullMessage().equals(parent.getFullMessage())) { - throw new ResourceConflictException("identical tree and message"); - } - return writeSquashedCommit(rw, inserter, parent, edit); - } - - private static void deleteRef(Repository repo, ChangeEdit edit) throws IOException { - String refName = edit.getRefName(); - RefUpdate ru = repo.updateRef(refName, true); - ru.setExpectedOldObjectId(edit.getEditCommit()); - ru.setForceUpdate(true); - RefUpdate.Result result = ru.delete(); - switch (result) { - case FORCED: - case NEW: - case NO_CHANGE: - break; - case FAST_FORWARD: - case IO_FAILURE: - case LOCK_FAILURE: - case NOT_ATTEMPTED: - case REJECTED: - case REJECTED_CURRENT_BRANCH: - case RENAMED: - case REJECTED_MISSING_OBJECT: - case REJECTED_OTHER_REASON: - 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(); - for (int i = 0; i < parent.getParentCount(); i++) { - mergeCommit.addParentId(parent.getParent(i)); - } - mergeCommit.setAuthor(parent.getAuthorIdent()); - mergeCommit.setMessage(edit.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; - } -} |