com.googlesource.gerrit.plugins.refprotection.BackupRef.java Source code

Java tutorial

Introduction

Here is the source code for com.googlesource.gerrit.plugins.refprotection.BackupRef.java

Source

/*
 *  The MIT License
 *
 *  Copyright 2015 Sony Mobile Communications AB. All rights reserved.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */
package com.googlesource.gerrit.plugins.refprotection;

import com.google.gerrit.extensions.annotations.PluginName;
import com.google.gerrit.extensions.api.projects.BranchInput;
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.server.config.PluginConfigFactory;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.RefUpdateAttribute;
import com.google.gerrit.server.events.RefUpdatedEvent;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.CreateBranch;
import com.google.gerrit.server.project.ProjectResource;
import com.google.inject.Inject;

import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.TagBuilder;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.eclipse.jgit.lib.Constants.R_REFS;
import static org.eclipse.jgit.lib.Constants.R_TAGS;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class BackupRef {
    public static final String R_BACKUPS = R_REFS + "backups/";
    private static final Logger log = LoggerFactory.getLogger(BackupRef.class);
    private final CreateBranch.Factory createBranchFactory;
    @Inject
    private static PluginConfigFactory cfg;
    @Inject
    private static GitRepositoryManager repoManager;
    @Inject
    @PluginName
    private static String pluginName;

    @Inject
    BackupRef(CreateBranch.Factory createBranchFactory) {
        this.createBranchFactory = createBranchFactory;
    }

    public void createBackup(RefUpdatedEvent event, ProjectResource project) {
        String refName = event.getRefName();

        try (Repository git = repoManager.openRepository(project.getNameKey())) {
            String backupRef = get(project, refName);

            // No-op if the backup branch name is same as the original
            if (backupRef.equals(refName)) {
                return;
            }

            try (RevWalk revWalk = new RevWalk(git)) {
                RefUpdateAttribute refUpdate = event.refUpdate.get();
                if (cfg.getFromGerritConfig(pluginName).getBoolean("createTag", false)) {
                    TagBuilder tag = new TagBuilder();
                    AccountAttribute submitter = event.submitter.get();
                    tag.setTagger(new PersonIdent(submitter.name, submitter.email));
                    tag.setObjectId(revWalk.parseCommit(ObjectId.fromString(refUpdate.oldRev)));
                    String update = "Non-fast-forward update to";
                    if (refUpdate.newRev.equals(ObjectId.zeroId().getName())) {
                        update = "Deleted";
                    }
                    String type = "branch";
                    String fullMessage = "";
                    if (refUpdate.refName.startsWith(R_TAGS)) {
                        type = "tag";
                        try {
                            RevTag origTag = revWalk.parseTag(ObjectId.fromString(refUpdate.oldRev));
                            SimpleDateFormat format = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy ZZZZ");
                            PersonIdent taggerIdent = origTag.getTaggerIdent();
                            String tagger = String.format("Tagger: %s <%s>\nDate:   %s", taggerIdent.getName(),
                                    taggerIdent.getEmailAddress(), format.format(taggerIdent.getWhen()));
                            fullMessage = "\n\nOriginal tag:\n" + tagger + "\n\n" + origTag.getFullMessage();
                        } catch (MissingObjectException e) {
                            log.warn("Original tag does not exist", e);
                        } catch (IncorrectObjectTypeException e) {
                            log.warn("Original tag was not a tag", e);
                        } catch (IOException e) {
                            log.warn("Unable to read original tag details", e);
                        }
                    }
                    tag.setMessage(update + " " + type + " " + refUpdate.refName + fullMessage);
                    tag.setTag(backupRef);

                    ObjectInserter inserter = git.newObjectInserter();
                    ObjectId tagId = inserter.insert(tag);
                    inserter.flush();
                    RefUpdate tagRef = git.updateRef(tag.getTag());
                    tagRef.setNewObjectId(tagId);
                    tagRef.setRefLogMessage("tagged deleted branch/tag " + tag.getTag(), false);
                    tagRef.setForceUpdate(false);
                    Result result = tagRef.update();
                    switch (result) {
                    case NEW:
                    case FORCED:
                        log.debug("Successfully created backup tag");
                        break;

                    case LOCK_FAILURE:
                        log.error("Failed to lock repository while creating backup tag");
                        break;

                    case REJECTED:
                        log.error("Tag already exists while creating backup tag");
                        break;

                    default:
                        log.error("Unknown error while creating backup tag");
                    }
                } else {
                    BranchInput input = new BranchInput();
                    input.ref = backupRef;
                    // We need to parse the commit to ensure if it's a tag, we get the
                    // commit the tag points to!
                    input.revision = ObjectId
                            .toString(revWalk.parseCommit(ObjectId.fromString(refUpdate.oldRev)).getId());

                    try {
                        createBranchFactory.create(backupRef).apply(project, input);
                    } catch (BadRequestException | AuthException | ResourceConflictException | IOException e) {
                        log.error(e.getMessage(), e);
                    }
                }
            }
        } catch (RepositoryNotFoundException e) {
            log.error("Repository does not exist", e);
        } catch (IOException e) {
            log.error("Could not open repository", e);
        }
    }

    static String get(ProjectResource project, String refName) {
        if (cfg.getFromGerritConfig(pluginName).getBoolean("useTimestamp", true)) {
            return getTimestampBranch(refName);
        } else {
            return getSequentialBranch(project, refName);
        }
    }

    static String getTimestampBranch(String refName) {
        if (refName.startsWith(R_HEADS) || refName.startsWith(R_TAGS)) {
            return String.format("%s-%s", R_BACKUPS + refName.replaceFirst(R_REFS, ""),
                    new SimpleDateFormat("YYYYMMdd-HHmmss").format(new Date()));
        }

        return refName;
    }

    private static String getSequentialBranch(ProjectResource project, String branchName) {
        Integer rev = 1;
        String deletedName = branchName.replaceFirst(R_REFS, "");
        try (Repository git = repoManager.openRepository(project.getNameKey())) {
            for (Ref ref : git.getAllRefs().values()) {
                String name = ref.getName();
                if (name.startsWith(R_BACKUPS + deletedName + "/")) {
                    Integer thisNum = Integer.parseInt(name.substring(name.lastIndexOf('/') + 1));
                    if (thisNum >= rev) {
                        rev = thisNum + 1;
                    }
                }
            }
        } catch (RepositoryNotFoundException e) {
            log.error("Repository does not exist", e);
        } catch (IOException e) {
            log.error("Could not determine latest revision of deleted branch", e);
        }

        return R_BACKUPS + deletedName + "/" + rev;
    }
}