blob: 1f4dfd1c239ab1dbe7f1501ee15e613166b00af3 [file] [log] [blame]
// Copyright (C) 2016 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.util;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.TagCache;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.io.IOException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
public class RefUpdater {
private static final FluentLogger log = FluentLogger.forEnclosingClass();
public class Args {
public final BranchNameKey branch;
public ObjectId expectedOldObjectId;
public ObjectId newObjectId;
public boolean isForceUpdate;
public PersonIdent refLogIdent;
public String refLogMessage;
public Args(BranchNameKey branch) {
this.branch = branch;
CurrentUser user = userProvider.get();
if (user instanceof IdentifiedUser) {
refLogIdent = ((IdentifiedUser) user).newRefLogIdent();
} else {
refLogIdent = gerrit;
}
}
}
protected final Provider<CurrentUser> userProvider;
protected final @GerritPersonIdent PersonIdent gerrit;
protected final GitRepositoryManager repoManager;
protected final GitReferenceUpdated gitRefUpdated;
protected final TagCache tagCache;
protected final AccountCache accountCache;
@Inject
RefUpdater(
AccountCache accountCache,
Provider<CurrentUser> userProvider,
@GerritPersonIdent PersonIdent gerrit,
GitRepositoryManager repoManager,
TagCache tagCache,
GitReferenceUpdated gitRefUpdated) {
this.accountCache = accountCache;
this.userProvider = userProvider;
this.gerrit = gerrit;
this.repoManager = repoManager;
this.tagCache = tagCache;
this.gitRefUpdated = gitRefUpdated;
}
public void update(BranchNameKey branch, ObjectId oldRefId, ObjectId newRefId)
throws IOException, NoSuchProjectException {
this.update(branch, oldRefId, newRefId, null);
}
public void update(
BranchNameKey branch, ObjectId oldRefId, ObjectId newRefId, String refLogMessage)
throws IOException, NoSuchProjectException {
Args args = new Args(branch);
args.expectedOldObjectId = oldRefId;
args.newObjectId = newRefId;
args.refLogMessage = refLogMessage;
this.update(args);
}
public void forceUpdate(BranchNameKey branch, ObjectId newRefId)
throws IOException, NoSuchProjectException {
this.forceUpdate(branch, newRefId, null);
}
public void forceUpdate(BranchNameKey branch, ObjectId newRefId, String refLogMessage)
throws IOException, NoSuchProjectException {
Args args = new Args(branch);
args.newObjectId = newRefId;
args.isForceUpdate = true;
args.refLogMessage = refLogMessage;
update(args);
}
public void delete(BranchNameKey branch) throws IOException, NoSuchProjectException {
Args args = new Args(branch);
args.newObjectId = ObjectId.zeroId();
args.isForceUpdate = true;
update(args);
}
public void update(Args args) throws IOException, NoSuchProjectException {
new Update(args).update();
}
protected class Update {
protected Repository repo;
protected Args args;
protected RefUpdate update;
protected BranchNameKey branch;
protected Project.NameKey project;
protected boolean delete;
protected Update(Args args) {
this.args = args;
branch = args.branch;
project = branch.project();
delete = args.newObjectId.equals(ObjectId.zeroId());
}
protected void update() throws IOException, NoSuchProjectException {
try {
repo = repoManager.openRepository(project);
try {
initUpdate();
handleResult(runUpdate());
} catch (IOException err) {
log.atSevere().withCause(err).log(
"RefUpdate failed: branch not updated: %s", branch.branch());
throw err;
} finally {
repo.close();
repo = null;
}
} catch (RepositoryNotFoundException e) {
throw new NoSuchProjectException(project);
}
}
protected void initUpdate() throws IOException {
update = repo.updateRef(branch.branch());
update.setExpectedOldObjectId(args.expectedOldObjectId);
update.setNewObjectId(args.newObjectId);
update.setRefLogIdent(args.refLogIdent);
update.setForceUpdate(args.isForceUpdate);
if (args.refLogMessage != null) {
update.setRefLogMessage(args.refLogMessage, true);
}
}
protected RefUpdate.Result runUpdate() throws IOException {
if (delete) {
return update.delete();
}
return update.update();
}
protected void handleResult(RefUpdate.Result result) throws IOException {
switch (result) {
case FORCED:
if (!delete && !args.isForceUpdate) {
throw new IOException(result.name());
}
// $FALL-THROUGH$
case FAST_FORWARD:
case NEW:
case NO_CHANGE:
onUpdated(update, args);
break;
case IO_FAILURE:
case LOCK_FAILURE:
case NOT_ATTEMPTED:
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case REJECTED_MISSING_OBJECT:
case REJECTED_OTHER_REASON:
case RENAMED:
default:
throw new IOException(result.name());
}
}
protected void onUpdated(RefUpdate update, Args args) {
if (update.getResult() == RefUpdate.Result.FAST_FORWARD) {
tagCache.updateFastForward(
project, update.getName(), update.getOldObjectId(), args.newObjectId);
}
if (userProvider.get().isIdentifiedUser()) {
AccountState accountState = accountCache.get(userProvider.get().getAccountId()).get();
gitRefUpdated.fire(project, update, accountState);
}
}
}
}