use of org.eclipse.jgit.transport.ReceiveCommand in project gitblit by gitblit.
the class GitblitReceivePack method executeCommands.
/** Execute commands to update references. */
@Override
protected void executeCommands() {
List<ReceiveCommand> toApply = filterCommands(Result.NOT_ATTEMPTED);
if (toApply.isEmpty()) {
return;
}
ProgressMonitor updating = NullProgressMonitor.INSTANCE;
boolean sideBand = isCapabilityEnabled(CAPABILITY_SIDE_BAND_64K);
if (sideBand) {
SideBandProgressMonitor pm = new SideBandProgressMonitor(msgOut);
pm.setDelayStart(250, TimeUnit.MILLISECONDS);
updating = pm;
}
BatchRefUpdate batch = getRepository().getRefDatabase().newBatchUpdate();
batch.setAllowNonFastForwards(isAllowNonFastForwards());
batch.setRefLogIdent(getRefLogIdent());
batch.setRefLogMessage("push", true);
for (ReceiveCommand cmd : toApply) {
if (Result.NOT_ATTEMPTED != cmd.getResult()) {
// Already rejected by the core receive process.
continue;
}
batch.addCommand(cmd);
}
if (!batch.getCommands().isEmpty()) {
try {
batch.execute(getRevWalk(), updating);
} catch (IOException err) {
for (ReceiveCommand cmd : toApply) {
if (cmd.getResult() == Result.NOT_ATTEMPTED) {
sendRejection(cmd, "lock error: {0}", err.getMessage());
}
}
}
}
//
if (ticketService != null) {
List<ReceiveCommand> allUpdates = ReceiveCommand.filter(batch.getCommands(), Result.OK);
if (!allUpdates.isEmpty()) {
int ticketsProcessed = 0;
for (ReceiveCommand cmd : allUpdates) {
switch(cmd.getType()) {
case CREATE:
case UPDATE:
if (cmd.getRefName().startsWith(Constants.R_HEADS)) {
Collection<TicketModel> tickets = processReferencedTickets(cmd);
ticketsProcessed += tickets.size();
for (TicketModel ticket : tickets) {
ticketNotifier.queueMailing(ticket);
}
}
break;
case UPDATE_NONFASTFORWARD:
if (cmd.getRefName().startsWith(Constants.R_HEADS)) {
String base = JGitUtils.getMergeBase(getRepository(), cmd.getOldId(), cmd.getNewId());
List<TicketLink> deletedRefs = JGitUtils.identifyTicketsBetweenCommits(getRepository(), settings, base, cmd.getOldId().name());
for (TicketLink link : deletedRefs) {
link.isDelete = true;
}
Change deletion = new Change(user.username);
deletion.pendingLinks = deletedRefs;
ticketService.updateTicket(repository, 0, deletion);
Collection<TicketModel> tickets = processReferencedTickets(cmd);
ticketsProcessed += tickets.size();
for (TicketModel ticket : tickets) {
ticketNotifier.queueMailing(ticket);
}
}
break;
case DELETE:
//Identify if the branch has been merged
SortedMap<Integer, String> bases = new TreeMap<Integer, String>();
try {
ObjectId dObj = cmd.getOldId();
Collection<Ref> tips = getRepository().getRefDatabase().getRefs(Constants.R_HEADS).values();
for (Ref ref : tips) {
ObjectId iObj = ref.getObjectId();
String mergeBase = JGitUtils.getMergeBase(getRepository(), dObj, iObj);
if (mergeBase != null) {
int d = JGitUtils.countCommits(getRepository(), getRevWalk(), mergeBase, dObj.name());
bases.put(d, mergeBase);
//All commits have been merged into some other branch
if (d == 0) {
break;
}
}
}
if (bases.isEmpty()) {
//TODO: Handle orphan branch case
} else {
if (bases.firstKey() > 0) {
//Delete references from the remaining commits that haven't been merged
String mergeBase = bases.get(bases.firstKey());
List<TicketLink> deletedRefs = JGitUtils.identifyTicketsBetweenCommits(getRepository(), settings, mergeBase, dObj.name());
for (TicketLink link : deletedRefs) {
link.isDelete = true;
}
Change deletion = new Change(user.username);
deletion.pendingLinks = deletedRefs;
ticketService.updateTicket(repository, 0, deletion);
}
}
} catch (IOException e) {
LOGGER.error(null, e);
}
break;
default:
break;
}
}
if (ticketsProcessed == 1) {
sendInfo("1 ticket updated");
} else if (ticketsProcessed > 1) {
sendInfo("{0} tickets updated", ticketsProcessed);
}
}
// reset the ticket caches for the repository
ticketService.resetCaches(repository);
}
}
use of org.eclipse.jgit.transport.ReceiveCommand in project gitblit by gitblit.
the class GitblitReceivePack method updateIncrementalPushTags.
/**
* Optionally update the incremental push tags.
*
* @param commands
*/
protected void updateIncrementalPushTags(Collection<ReceiveCommand> commands) {
if (!repository.useIncrementalPushTags) {
return;
}
// tag each pushed branch tip
String emailAddress = user.emailAddress == null ? getRefLogIdent().getEmailAddress() : user.emailAddress;
PersonIdent userIdent = new PersonIdent(user.getDisplayName(), emailAddress);
for (ReceiveCommand cmd : commands) {
if (!cmd.getRefName().startsWith(Constants.R_HEADS)) {
// only tag branch ref changes
continue;
}
if (!ReceiveCommand.Type.DELETE.equals(cmd.getType()) && ReceiveCommand.Result.OK.equals(cmd.getResult())) {
String objectId = cmd.getNewId().getName();
String branch = cmd.getRefName().substring(Constants.R_HEADS.length());
// get translation based on the server's locale setting
String template = Translation.get("gb.incrementalPushTagMessage");
String msg = MessageFormat.format(template, branch);
String prefix;
if (StringUtils.isEmpty(repository.incrementalPushTagPrefix)) {
prefix = settings.getString(Keys.git.defaultIncrementalPushTagPrefix, "r");
} else {
prefix = repository.incrementalPushTagPrefix;
}
JGitUtils.createIncrementalRevisionTag(getRepository(), objectId, userIdent, prefix, "0", msg);
}
}
}
use of org.eclipse.jgit.transport.ReceiveCommand in project gitblit by gitblit.
the class GitblitReceivePack method onPreReceive.
/**
* Instrumentation point where the incoming push event has been parsed,
* validated, objects created BUT refs have not been updated. You might
* use this to enforce a branch-write permissions model.
*/
@Override
public void onPreReceive(ReceivePack rp, Collection<ReceiveCommand> commands) {
if (commands.size() == 0) {
// no receive commands to process
// this can happen if receive pack subclasses intercept and filter
// the commands
LOGGER.debug("skipping pre-receive processing, no refs created, updated, or removed");
return;
}
if (repository.isMirror) {
// repository is a mirror
for (ReceiveCommand cmd : commands) {
sendRejection(cmd, "Gitblit does not allow pushes to \"{0}\" because it is a mirror!", repository.name);
}
return;
}
if (repository.isFrozen) {
// repository is frozen/readonly
for (ReceiveCommand cmd : commands) {
sendRejection(cmd, "Gitblit does not allow pushes to \"{0}\" because it is frozen!", repository.name);
}
return;
}
if (!repository.isBare) {
// repository has a working copy
for (ReceiveCommand cmd : commands) {
sendRejection(cmd, "Gitblit does not allow pushes to \"{0}\" because it has a working copy!", repository.name);
}
return;
}
if (!canPush(commands)) {
// user does not have push permissions
for (ReceiveCommand cmd : commands) {
sendRejection(cmd, "User \"{0}\" does not have push permissions for \"{1}\"!", user.username, repository.name);
}
return;
}
if (repository.accessRestriction.atLeast(AccessRestrictionType.PUSH) && repository.verifyCommitter) {
// enforce committer verification
if (StringUtils.isEmpty(user.emailAddress)) {
// reject the push because the pushing account does not have an email address
for (ReceiveCommand cmd : commands) {
sendRejection(cmd, "Sorry, the account \"{0}\" does not have an email address set for committer verification!", user.username);
}
return;
}
// Optionally enforce that the committer of first parent chain
// match the account being used to push the commits.
//
// This requires all merge commits are executed with the "--no-ff"
// option to force a merge commit even if fast-forward is possible.
// This ensures that the chain first parents has the commit
// identity of the merging user.
boolean allRejected = false;
for (ReceiveCommand cmd : commands) {
String firstParent = null;
try {
List<RevCommit> commits = JGitUtils.getRevLog(rp.getRepository(), cmd.getOldId().name(), cmd.getNewId().name());
for (RevCommit commit : commits) {
if (firstParent != null) {
if (!commit.getName().equals(firstParent)) {
// ignore: commit is right-descendant of a merge
continue;
}
}
// update expected next commit id
if (commit.getParentCount() == 0) {
firstParent = null;
} else {
firstParent = commit.getParents()[0].getId().getName();
}
PersonIdent committer = commit.getCommitterIdent();
if (!user.is(committer.getName(), committer.getEmailAddress())) {
// verification failed
String reason = MessageFormat.format("{0} by {1} <{2}> was not committed by {3} ({4}) <{5}>", commit.getId().name(), committer.getName(), StringUtils.isEmpty(committer.getEmailAddress()) ? "?" : committer.getEmailAddress(), user.getDisplayName(), user.username, user.emailAddress);
LOGGER.warn(reason);
cmd.setResult(Result.REJECTED_OTHER_REASON, reason);
allRejected &= true;
break;
} else {
allRejected = false;
}
}
} catch (Exception e) {
LOGGER.error("Failed to verify commits were made by pushing user", e);
}
}
if (allRejected) {
// all ref updates rejected, abort
return;
}
}
for (ReceiveCommand cmd : commands) {
String ref = cmd.getRefName();
if (ref.startsWith(Constants.R_HEADS)) {
switch(cmd.getType()) {
case UPDATE_NONFASTFORWARD:
case DELETE:
// reset branch commit cache on REWIND and DELETE
CommitCache.instance().clear(repository.name, ref);
break;
default:
break;
}
} else if (ref.equals(BranchTicketService.BRANCH)) {
// ensure pushing user is an administrator OR an owner
// i.e. prevent ticket tampering
boolean permitted = user.canAdmin() || repository.isOwner(user.username);
if (!permitted) {
sendRejection(cmd, "{0} is not permitted to push to {1}", user.username, ref);
}
} else if (ref.startsWith(Constants.R_FOR)) {
// prevent accidental push to refs/for
sendRejection(cmd, "{0} is not configured to receive patchsets", repository.name);
}
}
// call pre-receive plugins
for (ReceiveHook hook : gitblit.getExtensions(ReceiveHook.class)) {
try {
hook.onPreReceive(this, commands);
} catch (Exception e) {
LOGGER.error("Failed to execute extension", e);
}
}
Set<String> scripts = new LinkedHashSet<String>();
scripts.addAll(gitblit.getPreReceiveScriptsInherited(repository));
if (!ArrayUtils.isEmpty(repository.preReceiveScripts)) {
scripts.addAll(repository.preReceiveScripts);
}
runGroovy(commands, scripts);
for (ReceiveCommand cmd : commands) {
if (!Result.NOT_ATTEMPTED.equals(cmd.getResult())) {
LOGGER.warn(MessageFormat.format("{0} {1} because \"{2}\"", cmd.getNewId().getName(), cmd.getResult(), cmd.getMessage()));
}
}
}
use of org.eclipse.jgit.transport.ReceiveCommand in project gitblit by gitblit.
the class PatchsetReceivePack method processPatchset.
/**
* Creates or updates an ticket with the specified patchset.
*
* @param cmd
* @return a ticket if the creation or update was successful
*/
private TicketModel processPatchset(PatchsetCommand cmd) {
Change change = cmd.getChange();
if (cmd.isNewTicket()) {
// create the ticket object
TicketModel ticket = ticketService.createTicket(repository, cmd.getTicketId(), change);
if (ticket != null) {
sendInfo("");
sendHeader("#{0,number,0}: {1}", ticket.number, StringUtils.trimString(ticket.title, Constants.LEN_SHORTLOG));
sendInfo("created proposal ticket from patchset");
sendInfo(ticketService.getTicketUrl(ticket));
sendInfo("");
// log the new patch ref
RefLogUtils.updateRefLog(user, getRepository(), Arrays.asList(new ReceiveCommand(cmd.getOldId(), cmd.getNewId(), cmd.getRefName())));
// call any patchset hooks
for (PatchsetHook hook : gitblit.getExtensions(PatchsetHook.class)) {
try {
hook.onNewPatchset(ticket);
} catch (Exception e) {
LOGGER.error("Failed to execute extension", e);
}
}
return ticket;
} else {
sendError("FAILED to create ticket");
}
} else {
// update an existing ticket
TicketModel ticket = ticketService.updateTicket(repository, cmd.getTicketId(), change);
if (ticket != null) {
sendInfo("");
sendHeader("#{0,number,0}: {1}", ticket.number, StringUtils.trimString(ticket.title, Constants.LEN_SHORTLOG));
if (change.patchset.rev == 1) {
// new patchset
sendInfo("uploaded patchset {0} ({1})", change.patchset.number, change.patchset.type.toString());
} else {
// updated patchset
sendInfo("added {0} {1} to patchset {2}", change.patchset.added, change.patchset.added == 1 ? "commit" : "commits", change.patchset.number);
}
sendInfo(ticketService.getTicketUrl(ticket));
sendInfo("");
// log the new patchset ref
RefLogUtils.updateRefLog(user, getRepository(), Arrays.asList(new ReceiveCommand(cmd.getOldId(), cmd.getNewId(), cmd.getRefName())));
// call any patchset hooks
final boolean isNewPatchset = change.patchset.rev == 1;
for (PatchsetHook hook : gitblit.getExtensions(PatchsetHook.class)) {
try {
if (isNewPatchset) {
hook.onNewPatchset(ticket);
} else {
hook.onUpdatePatchset(ticket);
}
} catch (Exception e) {
LOGGER.error("Failed to execute extension", e);
}
}
// return the updated ticket
return ticket;
} else {
sendError("FAILED to upload {0} for ticket {1,number,0}", change.patchset, cmd.getTicketId());
}
}
return null;
}
use of org.eclipse.jgit.transport.ReceiveCommand in project gitblit by gitblit.
the class PatchsetReceivePack method executeCommands.
/** 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;
}
}
// 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;
}
}
List<ReceiveCommand> toApply = filterCommands(Result.NOT_ATTEMPTED);
if (toApply.isEmpty()) {
return;
}
ProgressMonitor updating = NullProgressMonitor.INSTANCE;
boolean sideBand = isCapabilityEnabled(CAPABILITY_SIDE_BAND_64K);
if (sideBand) {
SideBandProgressMonitor pm = new SideBandProgressMonitor(msgOut);
pm.setDelayStart(250, TimeUnit.MILLISECONDS);
updating = pm;
}
BatchRefUpdate batch = getRepository().getRefDatabase().newBatchUpdate();
batch.setAllowNonFastForwards(isAllowNonFastForwards());
batch.setRefLogIdent(getRefLogIdent());
batch.setRefLogMessage("push", true);
ReceiveCommand patchsetRefCmd = null;
PatchsetCommand patchsetCmd = null;
for (ReceiveCommand cmd : toApply) {
if (Result.NOT_ATTEMPTED != cmd.getResult()) {
// Already rejected by the core receive process.
continue;
}
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;
}
if (!ticketService.isReady()) {
sendRejection(cmd, "Sorry, the ticket service can not accept patchsets at this time.");
continue;
}
if (UserModel.ANONYMOUS.equals(user)) {
// server allows anonymous pushes, but anonymous patchset
// contributions are prohibited by design
sendRejection(cmd, "Sorry, anonymous patchset contributions are prohibited.");
continue;
}
final Matcher m = NEW_PATCHSET.matcher(cmd.getRefName());
if (m.matches()) {
// prohibit pushing directly to a patchset ref
long id = getTicketId(cmd.getRefName());
sendError("You may not directly push directly to a patchset ref!");
sendError("Instead, please push to one the following:");
sendError(" - {0}{1,number,0}", Constants.R_FOR, id);
sendError(" - {0}{1,number,0}", Constants.R_TICKET, id);
sendRejection(cmd, "protected ref");
continue;
}
if (hasRefNamespace(Constants.R_FOR)) {
// the refs/for/ namespace exists and it must not
LOGGER.error("{} already has refs in the {} namespace", repository.name, Constants.R_FOR);
sendRejection(cmd, "Sorry, a repository administrator will have to remove the {} namespace", Constants.R_FOR);
continue;
}
if (cmd.getNewId().equals(ObjectId.zeroId())) {
// ref deletion request
if (cmd.getRefName().startsWith(Constants.R_TICKET)) {
if (user.canDeleteRef(repository)) {
batch.addCommand(cmd);
} else {
sendRejection(cmd, "Sorry, you do not have permission to delete {}", cmd.getRefName());
}
} else {
sendRejection(cmd, "Sorry, you can not delete {}", cmd.getRefName());
}
continue;
}
if (patchsetRefCmd != null) {
sendRejection(cmd, "You may only push one patchset at a time.");
continue;
}
LOGGER.info(MessageFormat.format("Verifying {0} push ref \"{1}\" received from {2}", repository.name, cmd.getRefName(), user.username));
// responsible verification
String responsible = PatchsetCommand.getSingleOption(cmd, PatchsetCommand.RESPONSIBLE);
if (!StringUtils.isEmpty(responsible)) {
UserModel assignee = gitblit.getUserModel(responsible);
if (assignee == null) {
// no account by this name
sendRejection(cmd, "{0} can not be assigned any tickets because there is no user account by that name", responsible);
continue;
} else if (!assignee.canPush(repository)) {
// account does not have RW permissions
sendRejection(cmd, "{0} ({1}) can not be assigned any tickets because the user does not have RW permissions for {2}", assignee.getDisplayName(), assignee.username, repository.name);
continue;
}
}
// milestone verification
String milestone = PatchsetCommand.getSingleOption(cmd, PatchsetCommand.MILESTONE);
if (!StringUtils.isEmpty(milestone)) {
TicketMilestone milestoneModel = ticketService.getMilestone(repository, milestone);
if (milestoneModel == null) {
// milestone does not exist
sendRejection(cmd, "Sorry, \"{0}\" is not a valid milestone!", milestone);
continue;
}
}
// watcher verification
List<String> watchers = PatchsetCommand.getOptions(cmd, PatchsetCommand.WATCH);
if (!ArrayUtils.isEmpty(watchers)) {
boolean verified = true;
for (String watcher : watchers) {
UserModel user = gitblit.getUserModel(watcher);
if (user == null) {
// watcher does not exist
sendRejection(cmd, "Sorry, \"{0}\" is not a valid username for the watch list!", watcher);
verified = false;
break;
}
}
if (!verified) {
continue;
}
}
patchsetRefCmd = cmd;
patchsetCmd = preparePatchset(cmd);
if (patchsetCmd != null) {
batch.addCommand(patchsetCmd);
}
continue;
}
batch.addCommand(cmd);
}
if (!batch.getCommands().isEmpty()) {
try {
batch.execute(getRevWalk(), updating);
} catch (IOException err) {
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);
}
}
}
}
//
if (patchsetRefCmd != null && patchsetCmd != null) {
if (!patchsetCmd.getResult().equals(Result.OK)) {
// patchset command failed!
LOGGER.error(patchsetCmd.getType() + " " + patchsetCmd.getRefName() + " " + patchsetCmd.getResult());
patchsetRefCmd.setResult(patchsetCmd.getResult(), patchsetCmd.getMessage());
} else {
// all patchset commands were applied
patchsetRefCmd.setResult(Result.OK);
// update the ticket branch ref
RefUpdate ru = updateRef(patchsetCmd.getTicketBranch(), patchsetCmd.getNewId(), patchsetCmd.getPatchsetType());
updateReflog(ru);
TicketModel ticket = processPatchset(patchsetCmd);
if (ticket != null) {
ticketNotifier.queueMailing(ticket);
}
}
}
//
// if there are standard ref update receive commands that were
// successfully processed, process referenced tickets, if any
//
List<ReceiveCommand> allUpdates = ReceiveCommand.filter(batch.getCommands(), Result.OK);
List<ReceiveCommand> refUpdates = excludePatchsetCommands(allUpdates);
List<ReceiveCommand> stdUpdates = excludeTicketCommands(refUpdates);
if (!stdUpdates.isEmpty()) {
int ticketsProcessed = 0;
for (ReceiveCommand cmd : stdUpdates) {
switch(cmd.getType()) {
case CREATE:
case UPDATE:
if (cmd.getRefName().startsWith(Constants.R_HEADS)) {
Collection<TicketModel> tickets = processReferencedTickets(cmd);
ticketsProcessed += tickets.size();
for (TicketModel ticket : tickets) {
ticketNotifier.queueMailing(ticket);
}
}
break;
case UPDATE_NONFASTFORWARD:
if (cmd.getRefName().startsWith(Constants.R_HEADS)) {
String base = JGitUtils.getMergeBase(getRepository(), cmd.getOldId(), cmd.getNewId());
List<TicketLink> deletedRefs = JGitUtils.identifyTicketsBetweenCommits(getRepository(), settings, base, cmd.getOldId().name());
for (TicketLink link : deletedRefs) {
link.isDelete = true;
}
Change deletion = new Change(user.username);
deletion.pendingLinks = deletedRefs;
ticketService.updateTicket(repository, 0, deletion);
Collection<TicketModel> tickets = processReferencedTickets(cmd);
ticketsProcessed += tickets.size();
for (TicketModel ticket : tickets) {
ticketNotifier.queueMailing(ticket);
}
}
break;
default:
break;
}
}
if (ticketsProcessed == 1) {
sendInfo("1 ticket updated");
} else if (ticketsProcessed > 1) {
sendInfo("{0} tickets updated", ticketsProcessed);
}
}
// reset the ticket caches for the repository
ticketService.resetCaches(repository);
}
Aggregations