private RefUpdate getPendingRefUpdate(Branch.NameKey destBranch) throws IntegrationException { if (pendingRefUpdates.containsKey(destBranch)) { logDebug("Access cached open branch {}: {}", destBranch.get(), openBranches.get(destBranch)); return pendingRefUpdates.get(destBranch); } try { RefUpdate branchUpdate = repo.updateRef(destBranch.get()); CodeReviewCommit branchTip; if (branchUpdate.getOldObjectId() != null) { branchTip = rw.parseCommit(branchUpdate.getOldObjectId()); } else if (Objects.equals(repo.getFullBranch(), destBranch.get())) { branchTip = null; branchUpdate.setExpectedOldObjectId(ObjectId.zeroId()); } else { throw new IntegrationException( "The destination branch " + destBranch.get() + " does not exist anymore."); } logDebug("Opened branch {}: {}", destBranch.get(), branchTip); pendingRefUpdates.put(destBranch, branchUpdate); openBranches.put(destBranch, branchTip); return branchUpdate; } catch (IOException e) { throw new IntegrationException("Cannot open branch", e); } }
public CodeReviewCommit createCherryPickFromCommit( Repository repo, ObjectInserter inserter, RevCommit mergeTip, RevCommit originalCommit, PersonIdent cherryPickCommitterIdent, String commitMsg, CodeReviewRevWalk rw) throws MissingObjectException, IncorrectObjectTypeException, IOException, MergeIdenticalTreeException, MergeConflictException { final ThreeWayMerger m = newThreeWayMerger(repo, inserter); m.setBase(originalCommit.getParent(0)); if (m.merge(mergeTip, originalCommit)) { ObjectId tree = m.getResultTreeId(); if (tree.equals(mergeTip.getTree())) { throw new MergeIdenticalTreeException("identical tree"); } CommitBuilder mergeCommit = new CommitBuilder(); mergeCommit.setTreeId(tree); mergeCommit.setParentId(mergeTip); mergeCommit.setAuthor(originalCommit.getAuthorIdent()); mergeCommit.setCommitter(cherryPickCommitterIdent); mergeCommit.setMessage(commitMsg); return rw.parseCommit(commit(inserter, mergeCommit)); } else { throw new MergeConflictException("merge conflict"); } }
public CodeReviewCommit writeMergeCommit( PersonIdent author, PersonIdent committer, CodeReviewRevWalk rw, ObjectInserter inserter, RevFlag canMergeFlag, Branch.NameKey destBranch, CodeReviewCommit mergeTip, ObjectId treeId, CodeReviewCommit n) throws IOException, MissingObjectException, IncorrectObjectTypeException { final List<CodeReviewCommit> merged = new ArrayList<>(); rw.resetRetain(canMergeFlag); rw.markStart(n); rw.markUninteresting(mergeTip); CodeReviewCommit crc; while ((crc = rw.next()) != null) { if (crc.getPatchsetId() != null) { merged.add(crc); } } StringBuilder msgbuf = new StringBuilder().append(summarize(rw, merged)); if (!R_HEADS_MASTER.equals(destBranch.get())) { msgbuf.append(" into "); msgbuf.append(destBranch.getShortName()); } if (merged.size() > 1) { msgbuf.append("\n\n* changes:\n"); for (final CodeReviewCommit c : merged) { rw.parseBody(c); msgbuf.append(" "); msgbuf.append(c.getShortMessage()); msgbuf.append("\n"); } } final CommitBuilder mergeCommit = new CommitBuilder(); mergeCommit.setTreeId(treeId); mergeCommit.setParentIds(mergeTip, n); mergeCommit.setAuthor(author); mergeCommit.setCommitter(committer); mergeCommit.setMessage(msgbuf.toString()); CodeReviewCommit mergeResult = rw.parseCommit(commit(inserter, mergeCommit)); mergeResult.setControl(n.getControl()); return mergeResult; }
private Set<RevCommit> getAlreadyAccepted(CodeReviewCommit branchTip) throws IntegrationException { Set<RevCommit> alreadyAccepted = new HashSet<>(); if (branchTip != null) { alreadyAccepted.add(branchTip); } try { for (Ref r : repo.getRefDatabase().getRefs(Constants.R_HEADS).values()) { try { alreadyAccepted.add(rw.parseCommit(r.getObjectId())); } catch (IncorrectObjectTypeException iote) { // Not a commit? Skip over it. } } } catch (IOException e) { throw new IntegrationException("Failed to determine already accepted commits.", e); } logDebug("Found {} existing heads", alreadyAccepted.size()); return alreadyAccepted; }
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; }