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 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; }
protected boolean isVisibleTo(Branch.NameKey branchName, CurrentUser user) { ProjectState pe = projectCache.get(branchName.getParentKey()); if (pe == null) { return false; } ProjectControl pc = pe.controlFor(user); return pc.controlForRef(branchName).isVisible(); }
OpenBranch(OpenRepo or, Branch.NameKey name) throws IntegrationException { this.name = name; try { update = or.repo.updateRef(name.get()); if (update.getOldObjectId() != null) { oldTip = or.rw.parseCommit(update.getOldObjectId()); } else if (Objects.equals(or.repo.getFullBranch(), name.get())) { oldTip = null; update.setExpectedOldObjectId(ObjectId.zeroId()); } else { throw new IntegrationException( "The destination branch " + name + " does not exist anymore."); } } catch (IOException e) { throw new IntegrationException("Cannot open branch " + name, e); } }
private static Predicate<ChangeData> commitsOnBranchNotMerged( Branch.NameKey branch, List<Predicate<ChangeData>> commits) { return and( ref(branch), project(branch.getParentKey()), not(status(Change.Status.MERGED)), or(commits)); }
@AssistedInject Arguments( ApprovalsUtil approvalsUtil, BatchUpdate.Factory batchUpdateFactory, ChangeControl.GenericFactory changeControlFactory, MergeUtil.Factory mergeUtilFactory, PatchSetInfoFactory patchSetInfoFactory, @GerritPersonIdent PersonIdent serverIdent, ProjectCache projectCache, RebaseChangeOp.Factory rebaseFactory, @Assisted Branch.NameKey destBranch, @Assisted CommitStatus commits, @Assisted CodeReviewRevWalk rw, @Assisted IdentifiedUser caller, @Assisted ObjectInserter inserter, @Assisted Repository repo, @Assisted RevFlag canMergeFlag, @Assisted ReviewDb db, @Assisted Set<RevCommit> alreadyAccepted) { this.approvalsUtil = approvalsUtil; this.batchUpdateFactory = batchUpdateFactory; this.changeControlFactory = changeControlFactory; this.patchSetInfoFactory = patchSetInfoFactory; this.projectCache = projectCache; this.rebaseFactory = rebaseFactory; this.serverIdent = serverIdent; this.destBranch = destBranch; this.commits = commits; this.rw = rw; this.caller = caller; this.inserter = inserter; this.repo = repo; this.canMergeFlag = canMergeFlag; this.db = db; this.alreadyAccepted = alreadyAccepted; this.project = checkNotNull( projectCache.get(destBranch.getParentKey()), "project not found: %s", destBranch.getParentKey()); this.mergeSorter = new MergeSorter(rw, alreadyAccepted, canMergeFlag); this.mergeUtil = mergeUtilFactory.create(project); }
private Iterable<ChangeData> byCommitsOnBranchNotMergedFromIndex( Branch.NameKey branch, List<String> hashes) throws OrmException { return query( and( ref(branch), project(branch.getParentKey()), not(status(Change.Status.MERGED)), or(commits(schema(indexes), hashes)))); }
BatchUpdate newBatchUpdate(Timestamp when) { return batchUpdateFactory .create(db, destBranch.getParentKey(), caller, when) .setRepository(repo, rw, inserter); }
private static Predicate<ChangeData> ref(Branch.NameKey branch) { return new RefPredicate(branch.get()); }
public List<ChangeData> byBranchOpen(Branch.NameKey branch) throws OrmException { return query(and(ref(branch), project(branch.getParentKey()), open())); }
private void fireRefUpdated(Branch.NameKey destBranch, RefUpdate branchUpdate) { logDebug("Firing ref updated hooks for {}", branchUpdate.getName()); gitRefUpdated.fire(destBranch.getParentKey(), branchUpdate); hooks.doRefUpdatedHook( destBranch, branchUpdate, getAccount(mergeTips.get(destBranch).getCurrentTip())); }
private RefUpdate updateBranch(Branch.NameKey destBranch, IdentifiedUser caller) throws IntegrationException { RefUpdate branchUpdate = getPendingRefUpdate(destBranch); CodeReviewCommit branchTip = getBranchTip(destBranch); MergeTip mergeTip = mergeTips.get(destBranch); CodeReviewCommit currentTip = mergeTip != null ? mergeTip.getCurrentTip() : null; if (Objects.equals(branchTip, currentTip)) { if (currentTip != null) { logDebug("Branch already at merge tip {}, no update to perform", currentTip.name()); } else { logDebug("Both branch and merge tip are nonexistent, no update"); } return null; } else if (currentTip == null) { logDebug("No merge tip, no update to perform"); return null; } if (RefNames.REFS_CONFIG.equals(branchUpdate.getName())) { logDebug("Loading new configuration from {}", RefNames.REFS_CONFIG); try { ProjectConfig cfg = new ProjectConfig(destProject.getProject().getNameKey()); cfg.load(repo, currentTip); } catch (Exception e) { throw new IntegrationException( "Submit would store invalid" + " project configuration " + currentTip.name() + " for " + destProject.getProject().getName(), e); } } branchUpdate.setRefLogIdent( identifiedUserFactory.create(caller.getAccountId()).newRefLogIdent()); branchUpdate.setForceUpdate(false); branchUpdate.setNewObjectId(currentTip); branchUpdate.setRefLogMessage("merged", true); try { RefUpdate.Result result = branchUpdate.update(rw); logDebug( "Update of {}: {}..{} returned status {}", branchUpdate.getName(), branchUpdate.getOldObjectId(), branchUpdate.getNewObjectId(), result); switch (result) { case NEW: case FAST_FORWARD: if (branchUpdate.getResult() == RefUpdate.Result.FAST_FORWARD) { tagCache.updateFastForward( destBranch.getParentKey(), branchUpdate.getName(), branchUpdate.getOldObjectId(), currentTip); } if (RefNames.REFS_CONFIG.equals(branchUpdate.getName())) { Project p = destProject.getProject(); projectCache.evict(p); destProject = projectCache.get(p.getNameKey()); repoManager.setProjectDescription(p.getNameKey(), p.getDescription()); } return branchUpdate; case LOCK_FAILURE: throw new IntegrationException("Failed to lock " + branchUpdate.getName()); default: throw new IOException(branchUpdate.getResult().name() + '\n' + branchUpdate); } } catch (IOException e) { throw new IntegrationException("Cannot update " + branchUpdate.getName(), e); } }
private void setDestProject(Branch.NameKey destBranch) throws IntegrationException { destProject = projectCache.get(destBranch.getParentKey()); if (destProject == null) { throw new IntegrationException("No such project: " + destBranch.getParentKey()); } }
@Override public void onPreMerge( final Repository repo, final CodeReviewCommit commit, final ProjectState destProject, final Branch.NameKey destBranch, final PatchSet.Id patchSetId) throws MergeValidationException { if (RefNames.REFS_CONFIG.equals(destBranch.get())) { final Project.NameKey newParent; try { ProjectConfig cfg = new ProjectConfig(destProject.getProject().getNameKey()); cfg.load(repo, commit); newParent = cfg.getProject().getParent(allProjectsName); final Project.NameKey oldParent = destProject.getProject().getParent(allProjectsName); if (oldParent == null) { // update of the 'All-Projects' project if (newParent != null) { throw new MergeValidationException(ROOT_NO_PARENT); } } else { if (!oldParent.equals(newParent)) { PatchSetApproval psa = approvalsUtil.getSubmitter(db, commit.notes(), patchSetId); if (psa == null) { throw new MergeValidationException(SET_BY_ADMIN); } final IdentifiedUser submitter = identifiedUserFactory.create(psa.getAccountId()); if (!submitter.getCapabilities().canAdministrateServer()) { throw new MergeValidationException(SET_BY_ADMIN); } if (projectCache.get(newParent) == null) { throw new MergeValidationException(PARENT_NOT_FOUND); } } } for (Entry<ProjectConfigEntry> e : pluginConfigEntries) { PluginConfig pluginCfg = cfg.getPluginConfig(e.getPluginName()); ProjectConfigEntry configEntry = e.getProvider().get(); String value = pluginCfg.getString(e.getExportName()); String oldValue = destProject .getConfig() .getPluginConfig(e.getPluginName()) .getString(e.getExportName()); if ((value == null ? oldValue != null : !value.equals(oldValue)) && !configEntry.isEditable(destProject)) { throw new MergeValidationException(PLUGIN_VALUE_NOT_EDITABLE); } if (ProjectConfigEntry.Type.LIST.equals(configEntry.getType()) && value != null && !configEntry.getPermittedValues().contains(value)) { throw new MergeValidationException(PLUGIN_VALUE_NOT_PERMITTED); } } } catch (ConfigInvalidException | IOException e) { throw new MergeValidationException(INVALID_CONFIG); } } }
private void integrateIntoHistory(ChangeSet cs, IdentifiedUser caller) throws IntegrationException, NoSuchChangeException, ResourceConflictException { logDebug("Beginning merge attempt on {}", cs); Map<Branch.NameKey, BranchBatch> toSubmit = new HashMap<>(); logDebug("Perform the merges"); try { Multimap<Project.NameKey, Branch.NameKey> br = cs.branchesByProject(); Multimap<Branch.NameKey, ChangeData> cbb = cs.changesByBranch(); for (Branch.NameKey branch : cbb.keySet()) { OpenRepo or = openRepo(branch.getParentKey()); toSubmit.put(branch, validateChangeList(or, cbb.get(branch))); } failFast(cs); // Done checks that don't involve running submit strategies. for (Branch.NameKey branch : cbb.keySet()) { OpenRepo or = openRepo(branch.getParentKey()); OpenBranch ob = or.getBranch(branch); BranchBatch submitting = toSubmit.get(branch); SubmitStrategy strategy = createStrategy(or, branch, submitting.submitType(), ob.oldTip, caller); ob.mergeTip = preMerge(strategy, submitting.changes(), ob.oldTip); } checkMergeStrategyResults(cs, toSubmit.values()); for (Project.NameKey project : br.keySet()) { openRepo(project).ins.flush(); } Set<Branch.NameKey> done = Sets.newHashSetWithExpectedSize(cbb.keySet().size()); logDebug("Write out the new branch tips"); SubmoduleOp subOp = subOpProvider.get(); for (Project.NameKey project : br.keySet()) { OpenRepo or = openRepo(project); for (Branch.NameKey branch : br.get(project)) { OpenBranch ob = or.getBranch(branch); boolean updated = updateBranch(or, branch, caller); BranchBatch submitting = toSubmit.get(branch); updateChangeStatus(ob, submitting.changes(), caller); updateSubmoduleSubscriptions(ob, subOp); if (updated) { fireRefUpdated(ob); } done.add(branch); } } updateSuperProjects(subOp, br.values()); checkState( done.equals(cbb.keySet()), "programmer error: did not process" + " all branches in input set.\nExpected: %s\nActual: %s", done, cbb.keySet()); } catch (NoSuchProjectException noProject) { logWarn("Project " + noProject.project() + " no longer exists, " + "abandoning open changes"); abandonAllOpenChanges(noProject.project()); } catch (OrmException e) { throw new IntegrationException("Cannot query the database", e); } catch (IOException e) { throw new IntegrationException("Cannot query the database", e); } }
void updateSubmoduleSubscriptions(ReviewDb db, Branch.NameKey destBranch) throws SubmoduleException { if (urlProvider.get() == null) { logAndThrowSubmoduleException( "Cannot establish canonical web url used " + "to access gerrit. It should be provided in gerrit.config file."); } try (Repository repo = repoManager.openRepository(destBranch.getParentKey()); RevWalk rw = new RevWalk(repo)) { ObjectId id = repo.resolve(destBranch.get()); RevCommit commit = rw.parseCommit(id); Set<SubmoduleSubscription> oldSubscriptions = Sets.newHashSet(db.submoduleSubscriptions().bySuperProject(destBranch)); Set<SubmoduleSubscription> newSubscriptions; TreeWalk tw = TreeWalk.forPath(repo, GIT_MODULES, commit.getTree()); if (tw != null && (FileMode.REGULAR_FILE.equals(tw.getRawMode(0)) || FileMode.EXECUTABLE_FILE.equals(tw.getRawMode(0)))) { BlobBasedConfig bbc = new BlobBasedConfig(null, repo, commit, GIT_MODULES); String thisServer = new URI(urlProvider.get()).getHost(); newSubscriptions = subSecParserFactory.create(bbc, thisServer, destBranch).parseAllSections(); } else { newSubscriptions = Collections.emptySet(); } Set<SubmoduleSubscription> alreadySubscribeds = new HashSet<>(); for (SubmoduleSubscription s : newSubscriptions) { if (oldSubscriptions.contains(s)) { alreadySubscribeds.add(s); } } oldSubscriptions.removeAll(newSubscriptions); newSubscriptions.removeAll(alreadySubscribeds); if (!oldSubscriptions.isEmpty()) { db.submoduleSubscriptions().delete(oldSubscriptions); } if (!newSubscriptions.isEmpty()) { db.submoduleSubscriptions().insert(newSubscriptions); } } catch (OrmException e) { logAndThrowSubmoduleException( "Database problem at update of subscriptions table from " + GIT_MODULES + " file.", e); } catch (ConfigInvalidException e) { logAndThrowSubmoduleException( "Problem at update of subscriptions table: " + GIT_MODULES + " config file is invalid.", e); } catch (IOException e) { logAndThrowSubmoduleException( "Problem at update of subscriptions table from " + GIT_MODULES + ".", e); } catch (URISyntaxException e) { logAndThrowSubmoduleException( "Incorrect gerrit canonical web url provided in gerrit.config file.", e); } }
/** * Update the submodules in one branch of one repository. * * @param subscriber the branch of the repository which should be changed. * @param updates submodule updates which should be updated to. * @throws SubmoduleException */ private void updateGitlinks( ReviewDb db, Branch.NameKey subscriber, Collection<SubmoduleSubscription> updates) throws SubmoduleException { PersonIdent author = null; Repository pdb = null; RevWalk recRw = null; StringBuilder msgbuf = new StringBuilder("Updated git submodules\n\n"); try { boolean sameAuthorForAll = true; pdb = repoManager.openRepository(subscriber.getParentKey()); if (pdb.getRef(subscriber.get()) == null) { throw new SubmoduleException( "The branch was probably deleted from the subscriber repository"); } DirCache dc = readTree(pdb, pdb.getRef(subscriber.get())); DirCacheEditor ed = dc.editor(); for (SubmoduleSubscription s : updates) { try (Repository subrepo = repoManager.openRepository(s.getSubmodule().getParentKey()); RevWalk rw = CodeReviewCommit.newRevWalk(subrepo)) { Ref ref = subrepo.getRefDatabase().exactRef(s.getSubmodule().get()); if (ref == null) { ed.add(new DeletePath(s.getPath())); continue; } final ObjectId updateTo = ref.getObjectId(); RevCommit newCommit = rw.parseCommit(updateTo); if (author == null) { author = newCommit.getAuthorIdent(); } else if (!author.equals(newCommit.getAuthorIdent())) { sameAuthorForAll = false; } DirCacheEntry dce = dc.getEntry(s.getPath()); ObjectId oldId = null; if (dce != null) { if (!dce.getFileMode().equals(FileMode.GITLINK)) { log.error( "Requested to update gitlink " + s.getPath() + " in " + s.getSubmodule().getParentKey().get() + " but entry " + "doesn't have gitlink file mode."); continue; } oldId = dce.getObjectId(); } else { // This submodule did not exist before. We do not want to add // the full submodule history to the commit message, so omit it. oldId = updateTo; } ed.add( new PathEdit(s.getPath()) { @Override public void apply(DirCacheEntry ent) { ent.setFileMode(FileMode.GITLINK); ent.setObjectId(updateTo); } }); if (verboseSuperProject) { msgbuf.append("Project: " + s.getSubmodule().getParentKey().get()); msgbuf.append(" " + s.getSubmodule().getShortName()); msgbuf.append(" " + updateTo.getName()); msgbuf.append("\n\n"); try { rw.markStart(newCommit); if (oldId != null) { rw.markUninteresting(rw.parseCommit(oldId)); } for (RevCommit c : rw) { msgbuf.append(c.getFullMessage() + "\n\n"); } } catch (IOException e) { logAndThrowSubmoduleException( "Could not perform a revwalk to " + "create superproject commit message", e); } } } } ed.finish(); if (!sameAuthorForAll || author == null) { author = myIdent; } ObjectInserter oi = pdb.newObjectInserter(); ObjectId tree = dc.writeTree(oi); ObjectId currentCommitId = pdb.getRef(subscriber.get()).getObjectId(); CommitBuilder commit = new CommitBuilder(); commit.setTreeId(tree); commit.setParentIds(new ObjectId[] {currentCommitId}); commit.setAuthor(author); commit.setCommitter(myIdent); commit.setMessage(msgbuf.toString()); oi.insert(commit); oi.flush(); ObjectId commitId = oi.idFor(Constants.OBJ_COMMIT, commit.build()); final RefUpdate rfu = pdb.updateRef(subscriber.get()); rfu.setForceUpdate(false); rfu.setNewObjectId(commitId); rfu.setExpectedOldObjectId(currentCommitId); rfu.setRefLogMessage("Submit to " + subscriber.getParentKey().get(), true); switch (rfu.update()) { case NEW: case FAST_FORWARD: gitRefUpdated.fire(subscriber.getParentKey(), rfu); changeHooks.doRefUpdatedHook(subscriber, rfu, account); // TODO since this is performed "in the background" no mail will be // sent to inform users about the updated branch break; default: throw new IOException(rfu.getResult().name()); } recRw = new RevWalk(pdb); // Recursive call: update subscribers of the subscriber updateSuperProjects(db, Sets.newHashSet(subscriber)); } catch (IOException e) { throw new SubmoduleException("Cannot update gitlinks for " + subscriber.get(), e); } finally { if (recRw != null) { recRw.close(); } if (pdb != null) { pdb.close(); } } }
public List<ChangeData> byBranchKey(Branch.NameKey branch, Change.Key key) throws OrmException { return query(and(ref(branch), project(branch.getParentKey()), change(key))); }