private Iterable<ChangeData> byCommitsOnBranchNotMergedFromDatabase( Repository repo, ReviewDb db, Branch.NameKey branch, List<String> hashes) throws OrmException, IOException { Set<Change.Id> changeIds = Sets.newHashSetWithExpectedSize(hashes.size()); String lastPrefix = null; for (Ref ref : repo.getRefDatabase().getRefs(RefNames.REFS_CHANGES).values()) { String r = ref.getName(); if ((lastPrefix != null && r.startsWith(lastPrefix)) || !hashes.contains(ref.getObjectId().name())) { continue; } Change.Id id = Change.Id.fromRef(r); if (id == null) { continue; } if (changeIds.add(id)) { lastPrefix = r.substring(0, r.lastIndexOf('/')); } } List<ChangeData> cds = new ArrayList<>(hashes.size()); for (Change c : db.changes().get(changeIds)) { if (c.getDest().equals(branch) && c.getStatus() != Change.Status.MERGED) { cds.add(changeDataFactory.create(db, c)); } } return cds; }
/** * Evaluate the submit rules. * * @return List of {@link SubmitRecord} objects returned from the evaluated rules, including any * errors. */ public List<SubmitRecord> evaluate() { Change c = control.getChange(); if (!allowClosed && c.getStatus().isClosed()) { SubmitRecord rec = new SubmitRecord(); rec.status = SubmitRecord.Status.CLOSED; return Collections.singletonList(rec); } if (!allowDraft) { if (c.getStatus() == Change.Status.DRAFT) { return cannotSubmitDraft(); } try { initPatchSet(); } catch (OrmException e) { return ruleError("Error looking up patch set " + control.getChange().currentPatchSetId()); } if (patchSet.isDraft()) { return cannotSubmitDraft(); } } List<Term> results; try { results = evaluateImpl( "locate_submit_rule", "can_submit", "locate_submit_filter", "filter_submit_results", control.getUser()); } catch (RuleEvalException e) { return ruleError(e.getMessage(), e); } if (results.isEmpty()) { // This should never occur. A well written submit rule will always produce // at least one result informing the caller of the labels that are // required for this change to be submittable. Each label will indicate // whether or not that is actually possible given the permissions. return ruleError( String.format( "Submit rule '%s' for change %s of %s has " + "no solution.", getSubmitRuleName(), cd.getId(), getProjectName())); } return resultsToSubmitRecord(getSubmitRule(), results); }
private ListMultimap<SubmitType, ChangeData> validateChangeList(Collection<ChangeData> submitted) throws IntegrationException { logDebug("Validating {} changes", submitted.size()); ListMultimap<SubmitType, ChangeData> toSubmit = ArrayListMultimap.create(); Map<String, Ref> allRefs; try { allRefs = repo.getRefDatabase().getRefs(ALL); } catch (IOException e) { throw new IntegrationException(e.getMessage(), e); } Set<ObjectId> tips = new HashSet<>(); for (Ref r : allRefs.values()) { tips.add(r.getObjectId()); } for (ChangeData cd : submitted) { ChangeControl ctl; Change chg; try { ctl = cd.changeControl(); // Reload change in case index was stale. chg = cd.reloadChange(); } catch (OrmException e) { throw new IntegrationException("Failed to validate changes", e); } Change.Id changeId = cd.getId(); if (chg.getStatus() != Change.Status.NEW) { logDebug("Change {} is not new: {}", changeId, chg.getStatus()); continue; } if (chg.currentPatchSetId() == null) { logError("Missing current patch set on change " + changeId); commits.put(changeId, CodeReviewCommit.noPatchSet(ctl)); continue; } PatchSet ps; Branch.NameKey destBranch = chg.getDest(); try { ps = cd.currentPatchSet(); } catch (OrmException e) { throw new IntegrationException("Cannot query the database", e); } if (ps == null || ps.getRevision() == null || ps.getRevision().get() == null) { logError("Missing patch set or revision on change " + changeId); commits.put(changeId, CodeReviewCommit.noPatchSet(ctl)); continue; } String idstr = ps.getRevision().get(); ObjectId id; try { id = ObjectId.fromString(idstr); } catch (IllegalArgumentException iae) { logError("Invalid revision on patch set " + ps.getId()); commits.put(changeId, CodeReviewCommit.noPatchSet(ctl)); continue; } if (!tips.contains(id)) { // TODO Technically the proper way to do this test is to use a // RevWalk on "$id --not --all" and test for an empty set. But // that is way slower than looking for a ref directly pointing // at the desired tip. We should always have a ref available. // // TODO this is actually an error, the branch is gone but we // want to merge the issue. We can't safely do that if the // tip is not reachable. // logError( "Revision " + idstr + " of patch set " + ps.getId() + " is not contained in any ref"); commits.put(changeId, CodeReviewCommit.revisionGone(ctl)); continue; } CodeReviewCommit commit; try { commit = rw.parseCommit(id); } catch (IOException e) { logError("Invalid commit " + idstr + " on patch set " + ps.getId(), e); commits.put(changeId, CodeReviewCommit.revisionGone(ctl)); continue; } // TODO(dborowitz): Consider putting ChangeData in CodeReviewCommit. commit.setControl(ctl); commit.setPatchsetId(ps.getId()); commits.put(changeId, commit); MergeValidators mergeValidators = mergeValidatorsFactory.create(); try { mergeValidators.validatePreMerge(repo, commit, destProject, destBranch, ps.getId()); } catch (MergeValidationException mve) { logDebug( "Revision {} of patch set {} failed validation: {}", idstr, ps.getId(), mve.getStatus()); commit.setStatusCode(mve.getStatus()); continue; } SubmitType submitType; submitType = getSubmitType(commit.getControl(), ps); if (submitType == null) { logError("No submit type for revision " + idstr + " of patch set " + ps.getId()); commit.setStatusCode(CommitMergeStatus.NO_SUBMIT_TYPE); continue; } commit.add(canMergeFlag); toSubmit.put(submitType, cd); } logDebug("Submitting on this run: {}", toSubmit); return toSubmit; }