com.gitblit.git.PatchsetCommand.java Source code

Java tutorial

Introduction

Here is the source code for com.gitblit.git.PatchsetCommand.java

Source

/*
 * Copyright 2013 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 java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.ReceiveCommand;

import com.gitblit.Constants;
import com.gitblit.models.TicketModel;
import com.gitblit.models.TicketModel.Change;
import com.gitblit.models.TicketModel.Field;
import com.gitblit.models.TicketModel.Patchset;
import com.gitblit.models.TicketModel.PatchsetType;
import com.gitblit.models.TicketModel.Status;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.StringUtils;

/**
 *
 * A subclass of ReceiveCommand which constructs a ticket change based on a
 * patchset and data derived from the push ref.
 *
 * @author James Moger
 *
 */
public class PatchsetCommand extends ReceiveCommand {

    public static final String TOPIC = "t=";

    public static final String RESPONSIBLE = "r=";

    public static final String WATCH = "cc=";

    public static final String MILESTONE = "m=";

    protected final Change change;

    protected boolean isNew;

    protected long ticketId;

    public static String getBasePatchsetBranch(long ticketNumber) {
        StringBuilder sb = new StringBuilder();
        sb.append(Constants.R_TICKETS_PATCHSETS);
        long m = ticketNumber % 100L;
        if (m < 10) {
            sb.append('0');
        }
        sb.append(m);
        sb.append('/');
        sb.append(ticketNumber);
        sb.append('/');
        return sb.toString();
    }

    public static String getTicketBranch(long ticketNumber) {
        return Constants.R_TICKET + ticketNumber;
    }

    public static String getReviewBranch(long ticketNumber) {
        return "ticket-" + ticketNumber;
    }

    public static String getPatchsetBranch(long ticketId, long patchset) {
        return getBasePatchsetBranch(ticketId) + patchset;
    }

    public static long getTicketNumber(String ref) {
        if (ref.startsWith(Constants.R_TICKETS_PATCHSETS)) {
            // patchset revision

            // strip changes ref
            String p = ref.substring(Constants.R_TICKETS_PATCHSETS.length());
            // strip shard id
            p = p.substring(p.indexOf('/') + 1);
            // strip revision
            p = p.substring(0, p.indexOf('/'));
            // parse ticket number
            return Long.parseLong(p);
        } else if (ref.startsWith(Constants.R_TICKET)) {
            String p = ref.substring(Constants.R_TICKET.length());
            // parse ticket number
            return Long.parseLong(p);
        }
        return 0L;
    }

    public PatchsetCommand(String username, Patchset patchset) {
        super(patchset.isFF() ? ObjectId.fromString(patchset.parent) : ObjectId.zeroId(),
                ObjectId.fromString(patchset.tip), null);
        this.change = new Change(username);
        this.change.patchset = patchset;
    }

    public PatchsetType getPatchsetType() {
        return change.patchset.type;
    }

    public boolean isNewTicket() {
        return isNew;
    }

    public long getTicketId() {
        return ticketId;
    }

    public Change getChange() {
        return change;
    }

    /**
     * Creates a "new ticket" change for the proposal.
     *
     * @param commit
     * @param mergeTo
     * @param ticketId
     * @parem pushRef
     */
    public void newTicket(RevCommit commit, String mergeTo, long ticketId, String pushRef) {
        this.ticketId = ticketId;
        isNew = true;
        change.setField(Field.title, getTitle(commit));
        change.setField(Field.body, getBody(commit));
        change.setField(Field.status, Status.New);
        change.setField(Field.mergeTo, mergeTo);
        change.setField(Field.type, TicketModel.Type.Proposal);

        Set<String> watchSet = new TreeSet<String>();
        watchSet.add(change.author);

        // identify parameters passed in the push ref
        if (!StringUtils.isEmpty(pushRef)) {
            List<String> watchers = getOptions(pushRef, WATCH);
            if (!ArrayUtils.isEmpty(watchers)) {
                for (String cc : watchers) {
                    watchSet.add(cc.toLowerCase());
                }
            }

            String milestone = getSingleOption(pushRef, MILESTONE);
            if (!StringUtils.isEmpty(milestone)) {
                // user provided milestone
                change.setField(Field.milestone, milestone);
            }

            String responsible = getSingleOption(pushRef, RESPONSIBLE);
            if (!StringUtils.isEmpty(responsible)) {
                // user provided responsible
                change.setField(Field.responsible, responsible);
                watchSet.add(responsible);
            }

            String topic = getSingleOption(pushRef, TOPIC);
            if (!StringUtils.isEmpty(topic)) {
                // user provided topic
                change.setField(Field.topic, topic);
            }
        }

        // set the watchers
        change.watch(watchSet.toArray(new String[watchSet.size()]));
    }

    /**
     *
     * @param commit
     * @param mergeTo
     * @param ticket
     * @param pushRef
     */
    public void updateTicket(RevCommit commit, String mergeTo, TicketModel ticket, String pushRef) {

        this.ticketId = ticket.number;

        if (ticket.isClosed()) {
            // re-opening a closed ticket
            change.setField(Field.status, Status.Open);
        }

        // ticket may or may not already have an integration branch
        if (StringUtils.isEmpty(ticket.mergeTo) || !ticket.mergeTo.equals(mergeTo)) {
            change.setField(Field.mergeTo, mergeTo);
        }

        if (ticket.isProposal() && change.patchset.commits == 1 && change.patchset.type.isRewrite()) {

            // Gerrit-style title and description updates from the commit
            // message
            String title = getTitle(commit);
            String body = getBody(commit);

            if (!ticket.title.equals(title)) {
                // title changed
                change.setField(Field.title, title);
            }

            if (!ticket.body.equals(body)) {
                // description changed
                change.setField(Field.body, body);
            }
        }

        Set<String> watchSet = new TreeSet<String>();
        watchSet.add(change.author);

        // update the patchset command metadata
        if (!StringUtils.isEmpty(pushRef)) {
            List<String> watchers = getOptions(pushRef, WATCH);
            if (!ArrayUtils.isEmpty(watchers)) {
                for (String cc : watchers) {
                    watchSet.add(cc.toLowerCase());
                }
            }

            String milestone = getSingleOption(pushRef, MILESTONE);
            if (!StringUtils.isEmpty(milestone) && !milestone.equals(ticket.milestone)) {
                // user specified a (different) milestone
                change.setField(Field.milestone, milestone);
            }

            String responsible = getSingleOption(pushRef, RESPONSIBLE);
            if (!StringUtils.isEmpty(responsible) && !responsible.equals(ticket.responsible)) {
                // user specified a (different) responsible
                change.setField(Field.responsible, responsible);
                watchSet.add(responsible);
            }

            String topic = getSingleOption(pushRef, TOPIC);
            if (!StringUtils.isEmpty(topic) && !topic.equals(ticket.topic)) {
                // user specified a (different) topic
                change.setField(Field.topic, topic);
            }
        }

        // update the watchers
        watchSet.removeAll(ticket.getWatchers());
        if (!watchSet.isEmpty()) {
            change.watch(watchSet.toArray(new String[watchSet.size()]));
        }
    }

    @Override
    public String getRefName() {
        return getPatchsetBranch();
    }

    public String getPatchsetBranch() {
        return getBasePatchsetBranch(ticketId) + change.patchset.number;
    }

    public String getTicketBranch() {
        return getTicketBranch(ticketId);
    }

    private String getTitle(RevCommit commit) {
        String title = commit.getShortMessage();
        return title;
    }

    /**
     * Returns the body of the commit message
     *
     * @return
     */
    private String getBody(RevCommit commit) {
        String body = commit.getFullMessage().substring(commit.getShortMessage().length()).trim();
        return body;
    }

    /** Extracts a ticket field from the ref name */
    private static List<String> getOptions(String refName, String token) {
        if (refName.indexOf('%') > -1) {
            List<String> list = new ArrayList<String>();
            String[] strings = refName.substring(refName.indexOf('%') + 1).split(",");
            for (String str : strings) {
                if (str.toLowerCase().startsWith(token)) {
                    String val = str.substring(token.length());
                    list.add(val);
                }
            }
            return list;
        }
        return null;
    }

    /** Extracts a ticket field from the ref name */
    private static String getSingleOption(String refName, String token) {
        List<String> list = getOptions(refName, token);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    /** Extracts a ticket field from the ref name */
    public static String getSingleOption(ReceiveCommand cmd, String token) {
        return getSingleOption(cmd.getRefName(), token);
    }

    /** Extracts a ticket field from the ref name */
    public static List<String> getOptions(ReceiveCommand cmd, String token) {
        return getOptions(cmd.getRefName(), token);
    }

}