blob: 7a67960a9f0e38a4be0cff5b6ba7f92f6a49d28b [file] [log] [blame]
package com.googlesource.gerrit.plugins.refprotection;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.CreateBranch;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
class RefUpdateListener implements GitReferenceUpdatedListener {
private static final Logger log = LoggerFactory
.getLogger(RefUpdateListener.class);
private final CreateBranch.Factory createBranchFactory;
private final ProjectControl.GenericFactory projectControl;
private final CurrentUser user;
private final GitRepositoryManager repoManager;
@Inject
RefUpdateListener(CreateBranch.Factory createBranchFactory,
ProjectControl.GenericFactory p, CurrentUser user,
GitRepositoryManager repoManager) {
this.createBranchFactory = createBranchFactory;
this.projectControl = p;
this.user = user;
this.repoManager = repoManager;
}
@Override
public void onGitReferenceUpdated(final Event event) {
if (isRelevantRef(event)) {
Project.NameKey nameKey = new Project.NameKey(event.getProjectName());
try {
ProjectResource project =
new ProjectResource(projectControl.controlFor(nameKey, user));
if (isRefDeleted(event) || isNonFastForwardUpdate(event, project)) {
createBackupBranch(event, project);
}
} catch (NoSuchProjectException | IOException e) {
log.error(e.getMessage(), e);
}
}
}
/**
* Create a backup branch for the given ref.
*
* @param event the Event
*/
private void createBackupBranch(Event event, ProjectResource project) {
String branchName = event.getRefName().replaceFirst(R_HEADS, "");
try {
String branchPrefix = "";
int n = branchName.lastIndexOf("/");
if (n != -1) {
branchPrefix = branchName.substring(0, n + 1);
branchName = branchName.substring(n + 1);
}
String ref =
String.format("%sbackup-%s-%s", branchPrefix, branchName,
new SimpleDateFormat("YYYYMMdd-HHmmss").format(new Date()));
CreateBranch.Input input = new CreateBranch.Input();
input.ref = ref;
input.revision = event.getOldObjectId();
createBranchFactory.create(ref).apply(project, input);
} catch (BadRequestException | AuthException | ResourceConflictException
| IOException e) {
log.error(e.getMessage(), e);
}
}
/**
* Is the event on a relevant ref?
*
* @param event the Event
* @return True if relevant, otherwise False.
*/
private boolean isRelevantRef(Event event) {
return (!isNewRef(event)) &&
(event.getRefName().startsWith(R_HEADS)
|| event.getRefName().startsWith(R_TAGS));
}
/**
* Is the event a new ref?
*
* @param event the Event
* @return True if a new ref, otherwise False.
*/
private boolean isNewRef(Event event) {
return event.getOldObjectId().equals(ObjectId.zeroId().getName());
}
/**
* Is the event a ref deletion?
*
* @param event the Event
* @return True if a ref deletion, otherwise False.
*/
private boolean isRefDeleted(Event event) {
if (event.getNewObjectId().equals(ObjectId.zeroId().getName())) {
log.info(String.format(
"Ref Deleted: project [%s] refname [%s] old object id [%s]",
event.getProjectName(), event.getRefName(), event.getOldObjectId()));
return true;
}
return false;
}
/**
* Is the event a non-fast-forward update?
*
* @param event the Event
* @return True if a non-fast-forward update, otherwise False.
*/
private boolean isNonFastForwardUpdate(Event event, ProjectResource project)
throws RepositoryNotFoundException, IOException {
Repository repo = null;
RevWalk walk = null;
try {
repo = repoManager.openRepository(project.getNameKey());
walk = new RevWalk(repo);
RevCommit oldCommit =
walk.parseCommit(repo.resolve(event.getOldObjectId()));
RevCommit newCommit =
walk.parseCommit(repo.resolve(event.getNewObjectId()));
return !walk.isMergedInto(oldCommit, newCommit);
} finally {
if (walk != null) {
walk.release();
}
if (repo != null) {
repo.close();
}
}
}
}