use of com.google.gerrit.server.logging.TraceContext.TraceTimer in project gerrit by GerritCodeReview.
the class ReceiveCommits method parseDelete.
private void parseDelete(ReceiveCommand cmd) throws PermissionBackendException {
try (TraceTimer traceTimer = newTimer("parseDelete")) {
logger.atFine().log("Deleting %s", cmd);
if (cmd.getRefName().startsWith(REFS_CHANGES)) {
errors.put(CANNOT_DELETE_CHANGES, cmd.getRefName());
reject(cmd, "cannot delete changes");
} else if (isConfigRef(cmd.getRefName())) {
errors.put(CANNOT_DELETE_CONFIG, cmd.getRefName());
reject(cmd, "cannot delete project configuration");
}
Optional<AuthException> err = checkRefPermission(cmd, RefPermission.DELETE);
if (!err.isPresent()) {
validRefOperation(cmd);
} else {
rejectProhibited(cmd, err.get());
}
}
}
use of com.google.gerrit.server.logging.TraceContext.TraceTimer in project gerrit by GerritCodeReview.
the class ReceiveCommits method parseUpdate.
private void parseUpdate(ReceiveCommand cmd) throws PermissionBackendException {
try (TraceTimer traceTimer = TraceContext.newTimer("parseUpdate")) {
logger.atFine().log("Updating %s", cmd);
Optional<AuthException> err = checkRefPermission(cmd, RefPermission.UPDATE);
if (!err.isPresent()) {
if (isHead(cmd) && !isCommit(cmd)) {
reject(cmd, "head must point to commit");
return;
}
if (validRefOperation(cmd)) {
validateRegularPushCommits(BranchNameKey.create(project.getNameKey(), cmd.getRefName()), cmd);
}
} else {
rejectProhibited(cmd, err.get());
}
}
}
use of com.google.gerrit.server.logging.TraceContext.TraceTimer in project gerrit by GerritCodeReview.
the class ReceiveCommits method selectNewAndReplacedChangesFromMagicBranch.
private ImmutableList<CreateRequest> selectNewAndReplacedChangesFromMagicBranch(Task newProgress) throws IOException {
try (TraceTimer traceTimer = newTimer("selectNewAndReplacedChangesFromMagicBranch")) {
logger.atFine().log("Finding new and replaced changes");
List<CreateRequest> newChanges = new ArrayList<>();
GroupCollector groupCollector = GroupCollector.create(receivePackRefCache, psUtil, notesFactory, project.getNameKey());
BranchCommitValidator validator = commitValidatorFactory.create(projectState, magicBranch.dest, user);
try {
RevCommit start = setUpWalkForSelectingChanges();
if (start == null) {
return ImmutableList.of();
}
LinkedHashMap<RevCommit, ChangeLookup> pending = new LinkedHashMap<>();
Set<Change.Key> newChangeIds = new HashSet<>();
int maxBatchChanges = receiveConfig.getEffectiveMaxBatchChangesLimit(user);
int total = 0;
int alreadyTracked = 0;
boolean rejectImplicitMerges = start.getParentCount() == 1 && projectCache.get(project.getNameKey()).orElseThrow(illegalState(project.getNameKey())).is(BooleanProjectConfig.REJECT_IMPLICIT_MERGES) && // late.
!magicBranch.merged;
Set<RevCommit> mergedParents;
if (rejectImplicitMerges) {
mergedParents = new HashSet<>();
} else {
mergedParents = null;
}
for (; ; ) {
RevCommit c = receivePack.getRevWalk().next();
if (c == null) {
break;
}
total++;
receivePack.getRevWalk().parseBody(c);
String name = c.name();
groupCollector.visit(c);
Collection<PatchSet.Id> existingPatchSets = receivePackRefCache.patchSetIdsFromObjectId(c);
if (rejectImplicitMerges) {
Collections.addAll(mergedParents, c.getParents());
mergedParents.remove(c);
}
boolean commitAlreadyTracked = !existingPatchSets.isEmpty();
if (commitAlreadyTracked) {
alreadyTracked++;
// Corner cases where an existing commit might need a new group:
// A) Existing commit has a null group; wasn't assigned during schema
// upgrade, or schema upgrade is performed on a running server.
// B) Let A<-B<-C, then:
// 1. Push A to refs/heads/master
// 2. Push B to refs/for/master
// 3. Force push A~ to refs/heads/master
// 4. Push C to refs/for/master.
// B will be in existing so we aren't replacing the patch set. It
// used to have its own group, but now needs to to be changed to
// A's group.
// C) Commit is a PatchSet of a pre-existing change uploaded with a
// different target branch.
existingPatchSets.stream().forEach(i -> updateGroups.add(new UpdateGroupsRequest(i, c)));
if (!(newChangeForAllNotInTarget || magicBranch.base != null)) {
continue;
}
}
List<String> idList = ChangeUtil.getChangeIdsFromFooter(c, urlFormatter.get());
if (!idList.isEmpty()) {
pending.put(c, lookupByChangeKey(c, Change.key(idList.get(idList.size() - 1).trim())));
} else {
pending.put(c, lookupByCommit(c));
}
int n = pending.size() + newChanges.size();
if (maxBatchChanges != 0 && n > maxBatchChanges) {
logger.atFine().log("%d changes exceeds limit of %d", n, maxBatchChanges);
reject(magicBranch.cmd, "the number of pushed changes in a batch exceeds the max limit " + maxBatchChanges);
return ImmutableList.of();
}
if (commitAlreadyTracked) {
boolean changeExistsOnDestBranch = false;
for (ChangeData cd : pending.get(c).destChanges) {
if (cd.change().getDest().equals(magicBranch.dest)) {
changeExistsOnDestBranch = true;
break;
}
}
if (changeExistsOnDestBranch) {
continue;
}
logger.atFine().log("Creating new change for %s even though it is already tracked", name);
}
BranchCommitValidator.Result validationResult = validator.validateCommit(repo, receivePack.getRevWalk().getObjectReader(), magicBranch.cmd, c, ImmutableListMultimap.copyOf(pushOptions), magicBranch.merged, rejectCommits, null);
messages.addAll(validationResult.messages());
if (!validationResult.isValid()) {
// Not a change the user can propose? Abort as early as possible.
logger.atFine().log("Aborting early due to invalid commit");
return ImmutableList.of();
}
// Don't allow merges to be uploaded in commit chain via all-not-in-target
if (newChangeForAllNotInTarget && c.getParentCount() > 1) {
reject(magicBranch.cmd, "Pushing merges in commit chains with 'all not in target' is not allowed,\n" + "to override please set the base manually");
logger.atFine().log("Rejecting merge commit %s with newChangeForAllNotInTarget", name);
// TODO(dborowitz): Should we early return here?
}
if (idList.isEmpty()) {
newChanges.add(new CreateRequest(c, magicBranch.dest.branch(), newProgress));
continue;
}
}
logger.atFine().log("Finished initial RevWalk with %d commits total: %d already" + " tracked, %d new changes with no Change-Id, and %d deferred" + " lookups", total, alreadyTracked, newChanges.size(), pending.size());
if (rejectImplicitMerges) {
rejectImplicitMerges(mergedParents);
}
for (Iterator<ChangeLookup> itr = pending.values().iterator(); itr.hasNext(); ) {
ChangeLookup p = itr.next();
if (p.changeKey == null) {
continue;
}
if (newChangeIds.contains(p.changeKey)) {
logger.atFine().log("Multiple commits with Change-Id %s", p.changeKey);
reject(magicBranch.cmd, SAME_CHANGE_ID_IN_MULTIPLE_CHANGES);
return ImmutableList.of();
}
List<ChangeData> changes = p.destChanges;
if (changes.size() > 1) {
logger.atFine().log("Multiple changes in branch %s with Change-Id %s: %s", magicBranch.dest, p.changeKey, changes.stream().map(cd -> cd.getId().toString()).collect(joining()));
// WTF, multiple changes in this branch have the same key?
// Since the commit is new, the user should recreate it with
// a different Change-Id. In practice, we should never see
// this error message as Change-Id should be unique per branch.
//
reject(magicBranch.cmd, p.changeKey.get() + " has duplicates");
return ImmutableList.of();
}
if (changes.size() == 1) {
// Schedule as a replacement to this one matching change.
//
ObjectId currentPs = changes.get(0).currentPatchSet().commitId();
// If Commit is already current PatchSet of target Change.
if (p.commit.equals(currentPs)) {
if (pending.size() == 1) {
// There are no commits left to check, all commits in pending were already
// current PatchSet of the corresponding target changes.
reject(magicBranch.cmd, "commit(s) already exists (as current patchset)");
} else {
// Commit is already current PatchSet.
// Remove from pending and try next commit.
itr.remove();
continue;
}
}
if (requestReplaceAndValidateComments(magicBranch.cmd, false, changes.get(0).change(), p.commit)) {
continue;
}
return ImmutableList.of();
}
if (changes.isEmpty()) {
if (!isValidChangeId(p.changeKey.get())) {
reject(magicBranch.cmd, "invalid Change-Id");
return ImmutableList.of();
}
// double check against the existing refs
if (foundInExistingPatchSets(receivePackRefCache.patchSetIdsFromObjectId(p.commit))) {
if (pending.size() == 1) {
reject(magicBranch.cmd, "commit(s) already exists (as current patchset)");
return ImmutableList.of();
}
itr.remove();
continue;
}
newChangeIds.add(p.changeKey);
}
newChanges.add(new CreateRequest(p.commit, magicBranch.dest.branch(), newProgress));
}
logger.atFine().log("Finished deferred lookups with %d updates and %d new changes", replaceByChange.size(), newChanges.size());
} catch (IOException e) {
// identified the missing object earlier before we got control.
throw new StorageException("Invalid pack upload; one or more objects weren't sent", e);
}
if (newChanges.isEmpty() && replaceByChange.isEmpty()) {
reject(magicBranch.cmd, "no new changes");
return ImmutableList.of();
}
if (!newChanges.isEmpty() && magicBranch.edit) {
reject(magicBranch.cmd, "edit is not supported for new changes");
return ImmutableList.copyOf(newChanges);
}
SortedSetMultimap<ObjectId, String> groups = groupCollector.getGroups();
List<Integer> newIds = seq.nextChangeIds(newChanges.size());
for (int i = 0; i < newChanges.size(); i++) {
CreateRequest create = newChanges.get(i);
create.setChangeId(newIds.get(i));
create.groups = ImmutableList.copyOf(groups.get(create.commit));
}
for (ReplaceRequest replace : replaceByChange.values()) {
replace.groups = ImmutableList.copyOf(groups.get(replace.newCommitId));
}
for (UpdateGroupsRequest update : updateGroups) {
update.groups = ImmutableList.copyOf(groups.get(update.commit));
}
logger.atFine().log("Finished updating groups from GroupCollector");
return ImmutableList.copyOf(newChanges);
}
}
use of com.google.gerrit.server.logging.TraceContext.TraceTimer 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 com.google.gerrit.server.logging.TraceContext.TraceTimer in project gerrit by GerritCodeReview.
the class ReceiveCommits method foundInExistingPatchSets.
private boolean foundInExistingPatchSets(Collection<PatchSet.Id> existingPatchSets) {
try (TraceTimer traceTimer = newTimer("foundInExistingPatchSet")) {
for (PatchSet.Id psId : existingPatchSets) {
ChangeNotes notes = notesFactory.create(project.getNameKey(), psId.changeId());
Change change = notes.getChange();
if (change.getDest().equals(magicBranch.dest)) {
logger.atFine().log("Found change %s from existing refs.", change.getKey());
// Reindex the change asynchronously, ignoring errors.
@SuppressWarnings("unused") Future<?> possiblyIgnoredError = indexer.indexAsync(project.getNameKey(), change.getId());
return true;
}
}
return false;
}
}
Aggregations