James Moger
2014-03-06 9883341c1fbb35abcb7a1d09f93f6a9e805ea551
Revise push/mirror tickets branch triggering
1 files added
5 files modified
178 ■■■■ changed files
src/main/java/com/gitblit/git/GitblitReceivePack.java 6 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/git/PatchsetReceivePack.java 23 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/git/ReceiveCommandEvent.java 38 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/service/MirrorService.java 35 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/tickets/BranchTicketService.java 72 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/tickets/ITicketService.java 4 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/git/GitblitReceivePack.java
@@ -51,7 +51,6 @@
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.tickets.BranchTicketService;
import com.gitblit.tickets.BranchTicketService.TicketsBranchUpdated;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.ClientLogger;
import com.gitblit.utils.CommitCache;
@@ -344,12 +343,13 @@
        } catch (Exception e) {
            LOGGER.error(MessageFormat.format("Failed to update {0} pushlog", repository.name), e);
        }
        // check for updates pushed to the BranchTicketService branch
        // if the BranchTicketService is active it will reindex, as appropriate
        for (ReceiveCommand cmd : commands) {
            if (Result.OK.equals(cmd.getResult())
                    && BranchTicketService.BRANCH.equals(cmd.getRefName())) {
                rp.getRepository().fireEvent(new TicketsBranchUpdated(repository));
                rp.getRepository().fireEvent(new ReceiveCommandEvent(repository, cmd));
            }
        }
src/main/java/com/gitblit/git/PatchsetReceivePack.java
@@ -60,6 +60,7 @@
import com.gitblit.models.TicketModel.PatchsetType;
import com.gitblit.models.TicketModel.Status;
import com.gitblit.models.UserModel;
import com.gitblit.tickets.BranchTicketService;
import com.gitblit.tickets.ITicketService;
import com.gitblit.tickets.TicketMilestone;
import com.gitblit.tickets.TicketNotifier;
@@ -105,7 +106,7 @@
    protected final TicketNotifier ticketNotifier;
    private boolean requireCleanMerge;
    private boolean requireMergeablePatchset;
    public PatchsetReceivePack(IGitblit gitblit, Repository db, RepositoryModel repository, UserModel user) {
        super(gitblit, db, repository, user);
@@ -257,12 +258,26 @@
    /** Execute commands to update references. */
    @Override
    protected void executeCommands() {
        // we process patchsets unless the user is pushing something special
        boolean processPatchsets = true;
        for (ReceiveCommand cmd : filterCommands(Result.NOT_ATTEMPTED)) {
            if (ticketService instanceof BranchTicketService
                    && BranchTicketService.BRANCH.equals(cmd.getRefName())) {
                // the user is pushing an update to the BranchTicketService data
                processPatchsets = false;
            }
        }
        // workaround for JGit's awful scoping choices
        //
        // reset the patchset refs to NOT_ATTEMPTED (see validateCommands)
        for (ReceiveCommand cmd : filterCommands(Result.OK)) {
            if (isPatchsetRef(cmd.getRefName())) {
                cmd.setResult(Result.NOT_ATTEMPTED);
            } else if (ticketService instanceof BranchTicketService
                    && BranchTicketService.BRANCH.equals(cmd.getRefName())) {
                // the user is pushing an update to the BranchTicketService data
                processPatchsets = false;
            }
        }
@@ -292,7 +307,7 @@
                continue;
            }
            if (isPatchsetRef(cmd.getRefName())) {
            if (isPatchsetRef(cmd.getRefName()) && processPatchsets) {
                if (ticketService == null) {
                    sendRejection(cmd, "Sorry, the ticket service is unavailable and can not accept patchsets at this time.");
                    continue;
@@ -393,6 +408,8 @@
                for (ReceiveCommand cmd : toApply) {
                    if (cmd.getResult() == Result.NOT_ATTEMPTED) {
                        sendRejection(cmd, "lock error: {0}", err.getMessage());
                        LOGGER.error(MessageFormat.format("failed to lock {0}:{1}",
                                repository.name, cmd.getRefName()), err);
                    }
                }
            }
@@ -539,7 +556,7 @@
        case MERGEABLE:
            break;
        default:
            if (ticket == null || requireCleanMerge) {
            if (ticket == null || requireMergeablePatchset) {
                sendError("");
                sendError("Your patchset can not be cleanly merged into {0}.", forBranch);
                sendError("Please rebase your patchset and push again.");
src/main/java/com/gitblit/git/ReceiveCommandEvent.java
New file
@@ -0,0 +1,38 @@
/*
 * Copyright 2014 gitblit.com.
 *
 * 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.gitblit.git;
import org.eclipse.jgit.events.RefsChangedEvent;
import org.eclipse.jgit.transport.ReceiveCommand;
import com.gitblit.models.RepositoryModel;
/**
 * The event fired by other classes to allow this service to index tickets.
 *
 * @author James Moger
 */
public class ReceiveCommandEvent extends RefsChangedEvent {
    public final RepositoryModel model;
    public final ReceiveCommand cmd;
    public ReceiveCommandEvent(RepositoryModel model, ReceiveCommand cmd) {
        this.model = model;
        this.cmd = cmd;
    }
}
src/main/java/com/gitblit/service/MirrorService.java
@@ -28,6 +28,8 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.transport.ReceiveCommand.Type;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.TrackingRefUpdate;
import org.slf4j.Logger;
@@ -35,11 +37,11 @@
import com.gitblit.IStoredSettings;
import com.gitblit.Keys;
import com.gitblit.git.ReceiveCommandEvent;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.tickets.BranchTicketService;
import com.gitblit.tickets.BranchTicketService.TicketsBranchUpdated;
import com.gitblit.utils.JGitUtils;
/**
@@ -147,7 +149,7 @@
                FetchResult result = git.fetch().setRemote(mirror.getName()).setDryRun(testing).call();
                Collection<TrackingRefUpdate> refUpdates = result.getTrackingRefUpdates();
                if (refUpdates.size() > 0) {
                    boolean ticketBranchUpdated = false;
                    ReceiveCommand ticketBranchCmd = null;
                    for (TrackingRefUpdate ru : refUpdates) {
                        StringBuilder sb = new StringBuilder();
                        sb.append("updated mirror ");
@@ -164,14 +166,33 @@
                        sb.append("..");
                        sb.append(ru.getNewObjectId() == null ? "" : ru.getNewObjectId().abbreviate(7).name());
                        logger.info(sb.toString());
                        if (BranchTicketService.BRANCH.equals(ru.getLocalName())) {
                            ticketBranchUpdated = true;
                            ReceiveCommand.Type type = null;
                            switch (ru.getResult()) {
                            case NEW:
                                type = Type.CREATE;
                                break;
                            case FAST_FORWARD:
                                type = Type.UPDATE;
                                break;
                            case FORCED:
                                type = Type.UPDATE_NONFASTFORWARD;
                                break;
                            default:
                                type = null;
                                break;
                            }
                            if (type != null) {
                                ticketBranchCmd = new ReceiveCommand(ru.getOldObjectId(),
                                    ru.getNewObjectId(), ru.getLocalName(), type);
                            }
                        }
                    }
                    if (ticketBranchUpdated) {
                        repository.fireEvent(new TicketsBranchUpdated(model));
                    if (ticketBranchCmd != null) {
                        repository.fireEvent(new ReceiveCommandEvent(model, ticketBranchCmd));
                    }
                }
            } catch (Exception e) {
src/main/java/com/gitblit/tickets/BranchTicketService.java
@@ -27,6 +27,7 @@
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import javax.inject.Inject;
@@ -50,15 +51,18 @@
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.ReceiveCommand;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import com.gitblit.Constants;
import com.gitblit.git.ReceiveCommandEvent;
import com.gitblit.manager.INotificationManager;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.manager.IUserManager;
import com.gitblit.models.PathModel;
import com.gitblit.models.PathModel.PathChangeModel;
import com.gitblit.models.RefModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.TicketModel;
@@ -78,18 +82,6 @@
 */
public class BranchTicketService extends ITicketService implements RefsChangedListener {
    /**
     *  The event fired by other classes to allow this service to index tickets.
     */
    public static class TicketsBranchUpdated extends RefsChangedEvent {
        public final RepositoryModel model;
        public TicketsBranchUpdated(RepositoryModel model) {
            this.model = model;
        }
    }
    public static final String BRANCH = "refs/gitblit/tickets";
    private static final String JOURNAL = "journal.json";
@@ -111,7 +103,7 @@
                repositoryManager);
        lastAssignedId = new ConcurrentHashMap<String, AtomicLong>();
        // register the branch ticket service for repository ref changes
        Repository.getGlobalListenerList().addRefsChangedListener(this);
    }
@@ -138,16 +130,60 @@
    }
    /**
     * Listen for refs changed events and reindex that repository.
     * Listen for tickets branch changes and (re)index tickets, as appropriate
     */
    @Override
    public void onRefsChanged(RefsChangedEvent event) {
        if (!(event instanceof TicketsBranchUpdated)) {
    public synchronized void onRefsChanged(RefsChangedEvent event) {
        if (!(event instanceof ReceiveCommandEvent)) {
            return;
        }
        RepositoryModel repository = ((TicketsBranchUpdated) event).model;
        ReceiveCommandEvent branchUpdate = (ReceiveCommandEvent) event;
        RepositoryModel repository = branchUpdate.model;
        ReceiveCommand cmd = branchUpdate.cmd;
        try {
            reindex(repository);
            switch (cmd.getType()) {
            case CREATE:
            case UPDATE_NONFASTFORWARD:
                // reindex everything
                reindex(repository);
                break;
            case UPDATE:
                // incrementally index ticket updates
                resetCaches(repository);
                long start = System.nanoTime();
                log.info("incrementally indexing {} ticket branch due to received ref update", repository.name);
                Repository db = repositoryManager.getRepository(repository.name);
                try {
                    Set<Long> ids = new HashSet<Long>();
                    List<PathChangeModel> paths = JGitUtils.getFilesInRange(db,
                            cmd.getOldId().getName(), cmd.getNewId().getName());
                    for (PathChangeModel path : paths) {
                        String name = path.name.substring(path.name.lastIndexOf('/') + 1);
                        if (!JOURNAL.equals(name)) {
                            continue;
                        }
                        String tid = path.path.split("/")[2];
                        long ticketId = Long.parseLong(tid);
                        if (!ids.contains(ticketId)) {
                            ids.add(ticketId);
                            TicketModel ticket = getTicket(repository, ticketId);
                            log.info(MessageFormat.format("indexing ticket #{0,number,0}: {1}",
                                    ticketId, ticket.title));
                            indexer.index(ticket);
                        }
                    }
                    long end = System.nanoTime();
                    log.info("incremental indexing of {0} ticket(s) completed in {1} msecs",
                            ids.size(), TimeUnit.NANOSECONDS.toMillis(end - start));
                } finally {
                    db.close();
                }
                break;
            default:
                log.warn("Unexpected receive type {} in BranchTicketService.onRefsChanged" + cmd.getType());
                break;
            }
        } catch (Exception e) {
            log.error("failed to reindex " + repository.name, e);
        }
src/main/java/com/gitblit/tickets/ITicketService.java
@@ -897,6 +897,7 @@
    public boolean deleteAll(RepositoryModel repository) {
        boolean success = deleteAllImpl(repository);
        if (success) {
            log.info("Deleted all tickets for {}", repository.name);
            resetCaches(repository);
            indexer.deleteAll(repository);
        }
@@ -936,6 +937,8 @@
        TicketModel ticket = getTicket(repository, ticketId);
        boolean success = deleteTicketImpl(repository, ticket, deletedBy);
        if (success) {
            log.info(MessageFormat.format("Deleted {0} ticket #{1,number,0}: {2}",
                    repository.name, ticketId, ticket.title));
            ticketsCache.invalidate(new TicketKey(repository, ticketId));
            indexer.delete(ticket);
            return true;
@@ -1074,6 +1077,7 @@
        long end = System.nanoTime();
        long secs = TimeUnit.NANOSECONDS.toMillis(end - start);
        log.info("reindexing completed in {} msecs.", secs);
        resetCaches(repository);
    }
    /**