123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- /*
- * 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);
- }
-
- }
|