use of org.eclipse.jgit.transport.ReceiveCommand in project gerrit by GerritCodeReview.
the class VersionedMetaData method openUpdate.
/**
* Open a batch of updates to the same metadata ref.
*
* <p>This allows making multiple commits to a single metadata ref, at the end of which is a
* single ref update. For batching together updates to multiple refs (each consisting of one or
* more commits against their respective refs), create the {@link MetaDataUpdate} with a {@link
* BatchRefUpdate}.
*
* <p>A ref update produced by this {@link BatchMetaDataUpdate} is only committed if there is no
* associated {@link BatchRefUpdate}. As a result, the configured ref updated event is not fired
* if there is an associated batch.
*
* <p>If object inserter, reader and revwalk are provided, then the updates are not flushed,
* allowing callers the flexibility to flush only once after several updates.
*
* @param update helper info about the update.
* @param objInserter Shared object inserter.
* @param objReader Shared object reader.
* @param revWalk Shared rev walk.
* @throws IOException if the update failed.
*/
public BatchMetaDataUpdate openUpdate(MetaDataUpdate update, ObjectInserter objInserter, ObjectReader objReader, RevWalk revWalk) throws IOException {
final Repository db = update.getRepository();
inserter = objInserter == null ? db.newObjectInserter() : objInserter;
reader = objReader == null ? inserter.newReader() : objReader;
final RevWalk rw = revWalk == null ? new RevWalk(reader) : revWalk;
final RevTree tree = revision != null ? rw.parseTree(revision) : null;
newTree = readTree(tree);
return new BatchMetaDataUpdate() {
RevCommit src = revision;
AnyObjectId srcTree = tree;
@Override
public void write(CommitBuilder commit) throws IOException {
write(VersionedMetaData.this, commit);
}
private boolean doSave(VersionedMetaData config, CommitBuilder commit) throws IOException {
DirCache nt = config.newTree;
ObjectReader r = config.reader;
ObjectInserter i = config.inserter;
RevCommit c = config.revision;
try {
config.newTree = newTree;
config.reader = reader;
config.inserter = inserter;
config.revision = src;
return config.onSave(commit);
} catch (ConfigInvalidException e) {
throw new IOException("Cannot update " + getRefName() + " in " + db.getDirectory() + ": " + e.getMessage(), e);
} finally {
config.newTree = nt;
config.reader = r;
config.inserter = i;
config.revision = c;
}
}
@Override
public void write(VersionedMetaData config, CommitBuilder commit) throws IOException {
checkSameRef(config);
if (!doSave(config, commit)) {
return;
}
ObjectId res = newTree.writeTree(inserter);
if (res.equals(srcTree) && !update.allowEmpty() && (commit.getTreeId() == null)) {
// If there are no changes to the content, don't create the commit.
return;
}
// the tree for the updated DirCache.
if (commit.getTreeId() == null) {
commit.setTreeId(res);
} else {
// In this case, the caller populated the tree without using DirCache.
res = commit.getTreeId();
}
if (src != null) {
commit.addParentId(src);
}
if (update.insertChangeId()) {
commit.setMessage(ChangeIdUtil.insertId(commit.getMessage(), CommitMessageUtil.generateChangeId()));
}
src = rw.parseCommit(inserter.insert(commit));
srcTree = res;
}
private void checkSameRef(VersionedMetaData other) {
String thisRef = VersionedMetaData.this.getRefName();
String otherRef = other.getRefName();
checkArgument(otherRef.equals(thisRef), "cannot add %s for %s to %s on %s", other.getClass().getSimpleName(), otherRef, BatchMetaDataUpdate.class.getSimpleName(), thisRef);
}
@Override
public RevCommit createRef(String refName) throws IOException {
if (Objects.equals(src, revision)) {
return revision;
}
return updateRef(ObjectId.zeroId(), src, refName);
}
@Override
public RevCommit commit() throws IOException {
return commitAt(revision);
}
@Override
public RevCommit commitAt(ObjectId expected) throws IOException {
if (Objects.equals(src, expected)) {
return revision;
}
return updateRef(MoreObjects.firstNonNull(expected, ObjectId.zeroId()), src, getRefName());
}
@Override
public void close() {
newTree = null;
if (revWalk == null) {
rw.close();
}
if (objInserter == null && inserter != null) {
inserter.close();
inserter = null;
}
if (objReader == null && reader != null) {
reader.close();
reader = null;
}
}
private RevCommit updateRef(AnyObjectId oldId, AnyObjectId newId, String refName) throws IOException {
BatchRefUpdate bru = update.getBatch();
if (bru != null) {
bru.addCommand(new ReceiveCommand(oldId.toObjectId(), newId.toObjectId(), refName));
if (objInserter == null) {
inserter.flush();
}
revision = rw.parseCommit(newId);
return revision;
}
RefUpdate ru = db.updateRef(refName);
ru.setExpectedOldObjectId(oldId);
ru.setNewObjectId(newId);
ru.setRefLogIdent(update.getCommitBuilder().getAuthor());
String message = update.getCommitBuilder().getMessage();
if (message == null) {
message = "meta data update";
}
try (BufferedReader reader = new BufferedReader(new StringReader(message))) {
// read the subject line and use it as reflog message
ru.setRefLogMessage("commit: " + reader.readLine(), true);
}
logger.atFine().log("Saving commit '%s' on project '%s'", message.trim(), projectName);
inserter.flush();
RefUpdate.Result result = ru.update();
switch(result) {
case NEW:
case FAST_FORWARD:
revision = rw.parseCommit(ru.getNewObjectId());
update.fireGitRefUpdatedEvent(ru);
logger.atFine().log("Saved commit '%s' as revision '%s' on project '%s'", message.trim(), revision.name(), projectName);
return revision;
case LOCK_FAILURE:
throw new LockFailureException(errorMsg(ru, db.getDirectory()), ru);
case FORCED:
case IO_FAILURE:
case NOT_ATTEMPTED:
case NO_CHANGE:
case REJECTED:
case REJECTED_CURRENT_BRANCH:
case RENAMED:
case REJECTED_MISSING_OBJECT:
case REJECTED_OTHER_REASON:
default:
throw new GitUpdateFailureException(errorMsg(ru, db.getDirectory()), ru);
}
}
};
}
use of org.eclipse.jgit.transport.ReceiveCommand in project gerrit by GerritCodeReview.
the class SubmitStrategyOp method updateRepo.
@Override
public final void updateRepo(RepoContext ctx) throws Exception {
logger.atFine().log("%s#updateRepo for change %s", getClass().getSimpleName(), toMerge.change().getId());
checkState(ctx.getRevWalk() == args.rw, "SubmitStrategyOp requires callers to call BatchUpdate#setRepository with exactly the same" + " CodeReviewRevWalk instance from the SubmitStrategy.Arguments: %s != %s", ctx.getRevWalk(), args.rw);
// Run the submit strategy implementation and record the merge tip state so
// we can create the ref update.
CodeReviewCommit tipBefore = args.mergeTip.getCurrentTip();
alreadyMergedCommit = getAlreadyMergedCommit(ctx);
if (alreadyMergedCommit == null) {
updateRepoImpl(ctx);
} else {
logger.atFine().log("Already merged as %s", alreadyMergedCommit.name());
}
CodeReviewCommit tipAfter = args.mergeTip.getCurrentTip();
if (Objects.equals(tipBefore, tipAfter)) {
logger.atFine().log("Did not move tip");
return;
} else if (tipAfter == null) {
logger.atFine().log("No merge tip, no update to perform");
return;
}
logger.atFine().log("Moved tip from %s to %s", tipBefore, tipAfter);
checkProjectConfig(ctx, tipAfter);
// Needed by postUpdate, at which point mergeTip will have advanced further,
// so it's easier to just snapshot the command.
command = new ReceiveCommand(firstNonNull(tipBefore, ObjectId.zeroId()), tipAfter, getDest().branch());
ctx.addRefUpdate(command);
args.submoduleCommits.addBranchTip(getDest(), tipAfter);
}
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 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 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);
}
}
}
Aggregations