use of org.eclipse.jgit.transport.ReceiveCommand in project gerrit by GerritCodeReview.
the class DeleteRef method createDeleteCommand.
private ReceiveCommand createDeleteCommand(ProjectState projectState, Repository r, String refName) throws IOException, ResourceConflictException, PermissionBackendException {
Ref ref = r.getRefDatabase().exactRef(refName);
ReceiveCommand command;
if (ref == null) {
command = new ReceiveCommand(ObjectId.zeroId(), ObjectId.zeroId(), refName);
command.setResult(Result.REJECTED_OTHER_REASON, "it doesn't exist or you do not have permission to delete it");
return command;
}
command = new ReceiveCommand(ref.getObjectId(), ObjectId.zeroId(), ref.getName());
if (isConfigRef(refName)) {
// Never allow to delete the meta config branch.
command.setResult(Result.REJECTED_OTHER_REASON, "not allowed to delete branch " + refName);
} else {
try {
permissionBackend.currentUser().project(projectState.getNameKey()).ref(refName).check(RefPermission.DELETE);
} catch (AuthException denied) {
command.setResult(Result.REJECTED_OTHER_REASON, "it doesn't exist or you do not have permission to delete it");
}
}
if (!projectState.statePermitsWrite()) {
command.setResult(Result.REJECTED_OTHER_REASON, "project state does not permit write");
}
if (!refName.startsWith(R_TAGS)) {
BranchNameKey branchKey = BranchNameKey.create(projectState.getNameKey(), ref.getName());
if (!queryProvider.get().setLimit(1).byBranchOpen(branchKey).isEmpty()) {
command.setResult(Result.REJECTED_OTHER_REASON, "it has open changes");
}
}
RefUpdate u = r.updateRef(refName);
u.setForceUpdate(true);
u.setExpectedOldObjectId(r.exactRef(refName).getObjectId());
u.setNewObjectId(ObjectId.zeroId());
refDeletionValidator.validateRefOperation(projectState.getName(), identifiedUser.get(), u, /* pushOptions */
ImmutableListMultimap.of());
return command;
}
use of org.eclipse.jgit.transport.ReceiveCommand in project gerrit by GerritCodeReview.
the class ReceiveCommits method autoCloseChanges.
private void autoCloseChanges(ReceiveCommand cmd, Task progress) {
try (TraceTimer traceTimer = newTimer("autoCloseChanges")) {
logger.atFine().log("Starting auto-closing of changes");
String refName = cmd.getRefName();
Set<Change.Id> ids = new HashSet<>();
// handleRegularCommands
try {
retryHelper.changeUpdate("autoCloseChanges", updateFactory -> {
try (BatchUpdate bu = updateFactory.create(projectState.getNameKey(), user, TimeUtil.now());
ObjectInserter ins = repo.newObjectInserter();
ObjectReader reader = ins.newReader();
RevWalk rw = new RevWalk(reader)) {
if (ObjectId.zeroId().equals(cmd.getOldId())) {
// potentially expensive computation that loop over all commits.
return null;
}
bu.setRepository(repo, rw, ins);
// TODO(dborowitz): Teach BatchUpdate to ignore missing changes.
RevCommit newTip = rw.parseCommit(cmd.getNewId());
BranchNameKey branch = BranchNameKey.create(project.getNameKey(), refName);
rw.reset();
rw.sort(RevSort.REVERSE);
rw.markStart(newTip);
rw.markUninteresting(rw.parseCommit(cmd.getOldId()));
Map<Change.Key, ChangeNotes> byKey = null;
List<ReplaceRequest> replaceAndClose = new ArrayList<>();
int existingPatchSets = 0;
int newPatchSets = 0;
SubmissionId submissionId = null;
COMMIT: for (RevCommit c; (c = rw.next()) != null; ) {
rw.parseBody(c);
// refs pointing to this commit.
for (PatchSet.Id psId : receivePackRefCache.patchSetIdsFromObjectId(c.copy())) {
Optional<ChangeNotes> notes = getChangeNotes(psId.changeId());
if (notes.isPresent() && notes.get().getChange().getDest().equals(branch)) {
if (submissionId == null) {
submissionId = new SubmissionId(notes.get().getChange());
}
existingPatchSets++;
bu.addOp(notes.get().getChangeId(), setPrivateOpFactory.create(false, null));
bu.addOp(psId.changeId(), mergedByPushOpFactory.create(requestScopePropagator, psId, submissionId, refName, newTip.getId().getName()));
continue COMMIT;
}
}
for (String changeId : ChangeUtil.getChangeIdsFromFooter(c, urlFormatter.get())) {
if (byKey == null) {
byKey = retryHelper.changeIndexQuery("queryOpenChangesByKeyByBranch", q -> openChangesByKeyByBranch(q, branch)).call();
}
ChangeNotes onto = byKey.get(Change.key(changeId.trim()));
if (onto != null) {
newPatchSets++;
// Hold onto this until we're done with the walk, as the call to
// req.validate below calls isMergedInto which resets the walk.
ReplaceRequest req = new ReplaceRequest(onto.getChangeId(), c, cmd, false);
req.notes = onto;
replaceAndClose.add(req);
continue COMMIT;
}
}
}
for (ReplaceRequest req : replaceAndClose) {
Change.Id id = req.notes.getChangeId();
if (!req.validateNewPatchSetForAutoClose()) {
logger.atFine().log("Not closing %s because validation failed", id);
continue;
}
if (submissionId == null) {
submissionId = new SubmissionId(req.notes.getChange());
}
req.addOps(bu, null);
bu.addOp(id, setPrivateOpFactory.create(false, null));
bu.addOp(id, mergedByPushOpFactory.create(requestScopePropagator, req.psId, submissionId, refName, newTip.getId().getName()).setPatchSetProvider(req.replaceOp::getPatchSet));
bu.addOp(id, new ChangeProgressOp(progress));
ids.add(id);
}
logger.atFine().log("Auto-closing %d changes with existing patch sets and %d with new patch" + " sets", existingPatchSets, newPatchSets);
bu.execute();
} catch (IOException | StorageException | PermissionBackendException e) {
throw new StorageException("Failed to auto-close changes", e);
}
// If we are here, we didn't throw UpdateException. Record the result.
// The ordering is indeterminate due to the HashSet; unfortunately, Change.Id
// doesn't
// fit into TreeSet.
ids.stream().forEach(id -> result.addChange(ReceiveCommitsResult.ChangeStatus.AUTOCLOSED, id));
return null;
}).defaultTimeoutMultiplier(5).call();
} catch (RestApiException e) {
logger.atSevere().withCause(e).log("Can't insert patchset");
} catch (UpdateException e) {
logger.atSevere().withCause(e).log("Failed to auto-close changes");
} finally {
logger.atFine().log("Done auto-closing changes");
}
}
}
use of org.eclipse.jgit.transport.ReceiveCommand in project gerrit by GerritCodeReview.
the class ReceiveCommits method insertChangesAndPatchSets.
private void insertChangesAndPatchSets(ImmutableList<CreateRequest> newChanges, Task replaceProgress) {
try (TraceTimer traceTimer = newTimer("insertChangesAndPatchSets", Metadata.builder().resourceCount(newChanges.size()))) {
ReceiveCommand magicBranchCmd = magicBranch != null ? magicBranch.cmd : null;
if (magicBranchCmd != null && magicBranchCmd.getResult() != NOT_ATTEMPTED) {
logger.atWarning().log("Skipping change updates on %s because ref update failed: %s %s", project.getName(), magicBranchCmd.getResult(), Strings.nullToEmpty(magicBranchCmd.getMessage()));
return;
}
try (BatchUpdate bu = batchUpdateFactory.create(project.getNameKey(), user.materializedCopy(), TimeUtil.now());
ObjectInserter ins = repo.newObjectInserter();
ObjectReader reader = ins.newReader();
RevWalk rw = new RevWalk(reader)) {
bu.setRepository(repo, rw, ins);
bu.setRefLogMessage("push");
if (magicBranch != null) {
bu.setNotify(magicBranch.getNotifyForNewChange());
}
logger.atFine().log("Adding %d replace requests", newChanges.size());
for (ReplaceRequest replace : replaceByChange.values()) {
replace.addOps(bu, replaceProgress);
if (magicBranch != null) {
bu.setNotifyHandling(replace.ontoChange, magicBranch.getNotifyHandling(replace.notes));
if (magicBranch.shouldPublishComments()) {
bu.addOp(replace.notes.getChangeId(), publishCommentsOp.create(replace.psId, project.getNameKey()));
Optional<ChangeNotes> changeNotes = getChangeNotes(replace.notes.getChangeId());
if (!changeNotes.isPresent()) {
// If not present, no need to update attention set here since this is a new change.
continue;
}
List<HumanComment> drafts = commentsUtil.draftByChangeAuthor(changeNotes.get(), user.getAccountId());
if (drafts.isEmpty()) {
// If no comments, attention set shouldn't update since the user didn't reply.
continue;
}
replyAttentionSetUpdates.processAutomaticAttentionSetRulesOnReply(bu, changeNotes.get(), isReadyForReview(changeNotes.get()), user, drafts);
}
}
}
logger.atFine().log("Adding %d create requests", newChanges.size());
for (CreateRequest create : newChanges) {
create.addOps(bu);
}
logger.atFine().log("Adding %d group update requests", newChanges.size());
updateGroups.forEach(r -> r.addOps(bu));
logger.atFine().log("Executing batch");
try {
bu.execute();
} catch (UpdateException e) {
throw asRestApiException(e);
}
replaceByChange.values().stream().forEach(req -> result.addChange(ReceiveCommitsResult.ChangeStatus.REPLACED, req.ontoChange));
newChanges.stream().forEach(req -> result.addChange(ReceiveCommitsResult.ChangeStatus.CREATED, req.changeId));
if (magicBranchCmd != null) {
magicBranchCmd.setResult(OK);
}
for (ReplaceRequest replace : replaceByChange.values()) {
String rejectMessage = replace.getRejectMessage();
if (rejectMessage == null) {
if (replace.inputCommand.getResult() == NOT_ATTEMPTED) {
// Not necessarily the magic branch, so need to set OK on the original value.
replace.inputCommand.setResult(OK);
}
} else {
logger.atFine().log("Rejecting due to message from ReplaceOp");
reject(replace.inputCommand, rejectMessage);
}
}
} catch (ResourceConflictException e) {
addError(e.getMessage());
reject(magicBranchCmd, "conflict");
} catch (BadRequestException | UnprocessableEntityException | AuthException e) {
logger.atFine().withCause(e).log("Rejecting due to client error");
reject(magicBranchCmd, e.getMessage());
} catch (RestApiException | IOException e) {
throw new StorageException("Can't insert change/patch set for " + project.getName(), e);
}
if (magicBranch != null && magicBranch.submit) {
try {
submit(newChanges, replaceByChange.values());
} catch (ResourceConflictException e) {
addError(e.getMessage());
reject(magicBranchCmd, "conflict");
} catch (RestApiException | StorageException | UpdateException | IOException | ConfigInvalidException | PermissionBackendException e) {
logger.atSevere().withCause(e).log("Error submitting changes to %s", project.getName());
reject(magicBranchCmd, "error during submit");
}
}
}
}
use of org.eclipse.jgit.transport.ReceiveCommand in project gerrit by GerritCodeReview.
the class ReceiveCommits method parseMagicBranch.
/**
* Parse the magic branch data (refs/for/BRANCH/OPTIONALTOPIC%OPTIONS) into the magicBranch
* member.
*
* <p>Assumes we are handling a magic branch here.
*/
private void parseMagicBranch(ReceiveCommand cmd) throws PermissionBackendException, IOException {
try (TraceTimer traceTimer = newTimer("parseMagicBranch")) {
logger.atFine().log("Found magic branch %s", cmd.getRefName());
MagicBranchInput magicBranch = new MagicBranchInput(user, projectState, cmd, labelTypes);
String ref;
magicBranch.cmdLineParser = optionParserFactory.create(magicBranch);
// Filter out plugin push options, as the parser would reject them as unknown.
ImmutableListMultimap<String, String> pushOptionsToParse = pushOptions.entries().stream().filter(e -> !isPluginPushOption(e.getKey())).collect(toImmutableListMultimap(e -> e.getKey(), e -> e.getValue()));
try {
ref = magicBranch.parse(pushOptionsToParse);
} catch (CmdLineException e) {
if (!magicBranch.cmdLineParser.wasHelpRequestedByOption()) {
logger.atFine().log("Invalid branch syntax");
reject(cmd, e.getMessage());
return;
}
// never happens
ref = null;
}
if (magicBranch.skipValidation) {
reject(cmd, String.format("\"--%s\" option is only supported for direct push", PUSH_OPTION_SKIP_VALIDATION));
return;
}
if (magicBranch.topic != null && magicBranch.topic.length() > ChangeUtil.TOPIC_MAX_LENGTH) {
reject(cmd, String.format("topic length exceeds the limit (%d)", ChangeUtil.TOPIC_MAX_LENGTH));
}
if (magicBranch.cmdLineParser.wasHelpRequestedByOption()) {
StringWriter w = new StringWriter();
w.write("\nHelp for refs/for/branch:\n\n");
magicBranch.cmdLineParser.printUsage(w, null);
String pluginPushOptionsHelp = StreamSupport.stream(pluginPushOptions.entries().spliterator(), /* parallel= */
false).map(e -> String.format("-o %s~%s: %s", e.getPluginName(), e.get().getName(), e.get().getDescription())).sorted().collect(joining("\n"));
if (!pluginPushOptionsHelp.isEmpty()) {
w.write("\nPlugin push options:\n" + pluginPushOptionsHelp);
}
addMessage(w.toString());
reject(cmd, "see help");
return;
}
if (projectState.isAllUsers() && RefNames.REFS_USERS_SELF.equals(ref)) {
logger.atFine().log("Handling %s", RefNames.REFS_USERS_SELF);
ref = RefNames.refsUsers(user.getAccountId());
}
// configuration.
if (receivePackRefCache.exactRef(ref) == null && !ref.equals(readHEAD(repo)) && !ref.equals(RefNames.REFS_CONFIG)) {
logger.atFine().log("Ref %s not found", ref);
if (ref.startsWith(Constants.R_HEADS)) {
String n = ref.substring(Constants.R_HEADS.length());
reject(cmd, "branch " + n + " not found");
} else {
reject(cmd, ref + " not found");
}
return;
}
magicBranch.dest = BranchNameKey.create(project.getNameKey(), ref);
magicBranch.perm = permissions.ref(ref);
Optional<AuthException> err = checkRefPermission(magicBranch.perm, RefPermission.READ).map(Optional::of).orElse(checkRefPermission(magicBranch.perm, RefPermission.CREATE_CHANGE));
if (err.isPresent()) {
rejectProhibited(cmd, err.get());
return;
}
if (magicBranch.isPrivate && magicBranch.removePrivate) {
reject(cmd, "the options 'private' and 'remove-private' are mutually exclusive");
return;
}
boolean privateByDefault = projectCache.get(project.getNameKey()).orElseThrow(illegalState(project.getNameKey())).is(BooleanProjectConfig.PRIVATE_BY_DEFAULT);
setChangeAsPrivate = magicBranch.isPrivate || (privateByDefault && !magicBranch.removePrivate);
if (receiveConfig.disablePrivateChanges && setChangeAsPrivate) {
reject(cmd, "private changes are disabled");
return;
}
if (magicBranch.workInProgress && magicBranch.ready) {
reject(cmd, "the options 'wip' and 'ready' are mutually exclusive");
return;
}
if (magicBranch.publishComments && magicBranch.noPublishComments) {
reject(cmd, "the options 'publish-comments' and 'no-publish-comments' are mutually exclusive");
return;
}
if (magicBranch.submit) {
err = checkRefPermission(magicBranch.perm, RefPermission.UPDATE_BY_SUBMIT);
if (err.isPresent()) {
rejectProhibited(cmd, err.get());
return;
}
}
RevWalk walk = receivePack.getRevWalk();
RevCommit tip;
try {
tip = walk.parseCommit(magicBranch.cmd.getNewId());
logger.atFine().log("Tip of push: %s", tip.name());
} catch (IOException ex) {
magicBranch.cmd.setResult(REJECTED_MISSING_OBJECT);
logger.atSevere().withCause(ex).log("Invalid pack upload; one or more objects weren't sent");
return;
}
String destBranch = magicBranch.dest.branch();
try {
if (magicBranch.merged) {
if (magicBranch.base != null) {
reject(cmd, "cannot use merged with base");
return;
}
Ref refTip = receivePackRefCache.exactRef(magicBranch.dest.branch());
if (refTip == null) {
reject(cmd, magicBranch.dest.branch() + " not found");
return;
}
RevCommit branchTip = receivePack.getRevWalk().parseCommit(refTip.getObjectId());
if (!walk.isMergedInto(tip, branchTip)) {
reject(cmd, "not merged into branch");
return;
}
}
// if %base or %merged was specified, ignore newChangeForAllNotInTarget.
if (tip.getParentCount() > 1 || magicBranch.base != null || magicBranch.merged || tip.getParentCount() == 0) {
logger.atFine().log("Forcing newChangeForAllNotInTarget = false");
newChangeForAllNotInTarget = false;
}
if (magicBranch.base != null) {
logger.atFine().log("Handling %%base: %s", magicBranch.base);
magicBranch.baseCommit = Lists.newArrayListWithCapacity(magicBranch.base.size());
for (ObjectId id : magicBranch.base) {
try {
magicBranch.baseCommit.add(walk.parseCommit(id));
} catch (IncorrectObjectTypeException notCommit) {
reject(cmd, "base must be a commit");
return;
} catch (MissingObjectException e) {
reject(cmd, "base not found");
return;
} catch (IOException e) {
throw new StorageException(String.format("Project %s cannot read %s", project.getName(), id.name()), e);
}
}
} else if (newChangeForAllNotInTarget) {
Ref refTip = receivePackRefCache.exactRef(magicBranch.dest.branch());
if (refTip != null) {
RevCommit branchTip = receivePack.getRevWalk().parseCommit(refTip.getObjectId());
magicBranch.baseCommit = Collections.singletonList(branchTip);
logger.atFine().log("Set baseCommit = %s", magicBranch.baseCommit.get(0).name());
} else {
// repository and to review an initial project configuration.
if (!ref.equals(readHEAD(repo)) && !ref.equals(RefNames.REFS_CONFIG)) {
reject(cmd, magicBranch.dest.branch() + " not found");
return;
}
}
}
} catch (IOException e) {
throw new StorageException(String.format("Error walking to %s in project %s", destBranch, project.getName()), e);
}
if (validateConnected(magicBranch.cmd, magicBranch.dest, tip)) {
this.magicBranch = magicBranch;
this.result.magicPush(true);
}
}
}
use of org.eclipse.jgit.transport.ReceiveCommand in project gerrit by GerritCodeReview.
the class ReceiveCommits method handleRegularCommands.
private void handleRegularCommands(List<ReceiveCommand> cmds, MultiProgressMonitor progress) throws PermissionBackendException, IOException, NoSuchProjectException {
try (TraceTimer traceTimer = newTimer("handleRegularCommands", Metadata.builder().resourceCount(cmds.size()))) {
result.magicPush(false);
for (ReceiveCommand cmd : cmds) {
parseRegularCommand(cmd);
}
Map<BranchNameKey, ReceiveCommand> branches;
try (BatchUpdate bu = batchUpdateFactory.create(project.getNameKey(), user.materializedCopy(), TimeUtil.now());
ObjectInserter ins = repo.newObjectInserter();
ObjectReader reader = ins.newReader();
RevWalk rw = new RevWalk(reader);
MergeOpRepoManager orm = ormProvider.get()) {
bu.setRepository(repo, rw, ins);
bu.setRefLogMessage("push");
int added = 0;
for (ReceiveCommand cmd : cmds) {
if (cmd.getResult() == NOT_ATTEMPTED) {
bu.addRepoOnlyOp(new UpdateOneRefOp(cmd));
added++;
}
}
logger.atFine().log("Added %d additional ref updates", added);
SubmissionExecutor submissionExecutor = new SubmissionExecutor(false, superprojectUpdateSubmissionListeners);
submissionExecutor.execute(ImmutableList.of(bu));
orm.setContext(TimeUtil.now(), user, NotifyResolver.Result.none());
submissionExecutor.afterExecutions(orm);
branches = bu.getSuccessfullyUpdatedBranches(false);
} catch (UpdateException | RestApiException e) {
throw new StorageException(e);
}
// This could be moved into a SubmissionListener
branches.values().stream().filter(c -> isHead(c) || isConfig(c)).forEach(c -> {
// BatchUpdate because they involve kicking off an additional BatchUpdate.
switch(c.getType()) {
case CREATE:
case UPDATE:
case UPDATE_NONFASTFORWARD:
Task closeProgress = progress.beginSubTask("closed", UNKNOWN);
autoCloseChanges(c, closeProgress);
closeProgress.end();
break;
case DELETE:
break;
}
});
}
}
Aggregations