/** * Update a reference to point to an object. * * @param <T> type of the target object. * @param ref the name of the reference to update to. If {@code ref} does not start with {@code * refs/} and is not the magic names {@code HEAD} {@code FETCH_HEAD} or {@code MERGE_HEAD}, * then {@code refs/heads/} will be prefixed in front of the given name, thereby assuming it * is a branch. * @param obj the target object. * @return the target object. * @throws Exception */ public <T extends AnyObjectId> T update(String ref, T obj) throws Exception { if (Constants.HEAD.equals(ref)) { // nothing } else if ("FETCH_HEAD".equals(ref)) { // nothing } else if ("MERGE_HEAD".equals(ref)) { // nothing } else if (ref.startsWith(Constants.R_REFS)) { // nothing } else ref = Constants.R_HEADS + ref; RefUpdate u = db.updateRef(ref); u.setNewObjectId(obj); switch (u.forceUpdate()) { case FAST_FORWARD: case FORCED: case NEW: case NO_CHANGE: updateServerInfo(); return obj; default: throw new IOException("Cannot write " + ref + " " + u.getResult()); } }
/** * 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(); } } }
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); } }