All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gerrit.server.submit.MergeOpRepoManager Maven / Gradle / Ivy

The newest version!
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.gerrit.server.submit;

import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.server.project.ProjectCache.noSuchProject;
import static java.util.Objects.requireNonNull;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.git.CodeReviewCommit;
import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeTip;
import com.google.gerrit.server.git.validators.OnSubmitValidators;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.inject.Inject;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevSort;

/**
 * This is a helper class for MergeOp and not intended for general use.
 *
 * 

Some database backends require to open a repository just once within a transaction of a * submission, this caches open repositories to satisfy that requirement. */ public class MergeOpRepoManager implements AutoCloseable { public class OpenRepo { final Repository repo; final CodeReviewRevWalk rw; final RevFlag canMergeFlag; final ObjectInserter ins; final ProjectState project; BatchUpdate update; private final ObjectReader reader; private final Map branches; private OpenRepo(Repository repo, ProjectState project) { this.repo = repo; this.project = project; ins = repo.newObjectInserter(); reader = ins.newReader(); rw = CodeReviewCommit.newRevWalk(reader); rw.sort(RevSort.TOPO); rw.sort(RevSort.COMMIT_TIME_DESC, true); rw.setRetainBody(false); canMergeFlag = rw.newFlag("CAN_MERGE"); rw.retainOnReset(canMergeFlag); branches = Maps.newHashMapWithExpectedSize(1); } OpenBranch getBranch(BranchNameKey branch) throws IntegrationConflictException { OpenBranch ob = branches.get(branch); if (ob == null) { ob = new OpenBranch(this, branch); branches.put(branch, ob); } return ob; } public Repository getRepo() { return repo; } Project.NameKey getProjectName() { return project.getNameKey(); } public CodeReviewRevWalk getCodeReviewRevWalk() { return rw; } public BatchUpdate getUpdate() { checkState(caller != null, "call setContext before getUpdate"); if (update == null) { update = batchUpdateFactory .create(getProjectName(), caller, ts) .setRepository(repo, rw, ins) .setNotify(notify) .setOnSubmitValidators(onSubmitValidatorsFactory.create()); } return update; } // We want to reuse the open repo BUT not the BatchUpdate (because they are already executed) public void resetExecutedUpdates() { if (update != null && update.isExecuted()) { update.close(); update = null; } } private void close() { if (update != null) { update.close(); } rw.close(); reader.close(); ins.close(); repo.close(); } } public static class OpenBranch { final CodeReviewCommit oldTip; MergeTip mergeTip; OpenBranch(OpenRepo or, BranchNameKey name) throws IntegrationConflictException { try { Ref ref = or.getRepo().exactRef(name.branch()); if (ref != null) { oldTip = or.rw.parseCommit(ref.getObjectId()); } else if (Objects.equals(or.repo.getFullBranch(), name.branch()) || Objects.equals(RefNames.REFS_CONFIG, name.branch())) { oldTip = null; } else { throw new IntegrationConflictException( "The destination branch " + name + " does not exist anymore."); } } catch (IOException e) { throw new StorageException("Cannot open branch " + name, e); } } } private final Map openRepos; private final BatchUpdate.Factory batchUpdateFactory; private final OnSubmitValidators.Factory onSubmitValidatorsFactory; private final GitRepositoryManager repoManager; private final ProjectCache projectCache; private Instant ts; private IdentifiedUser caller; private NotifyResolver.Result notify; @Inject MergeOpRepoManager( GitRepositoryManager repoManager, ProjectCache projectCache, BatchUpdate.Factory batchUpdateFactory, OnSubmitValidators.Factory onSubmitValidatorsFactory) { this.repoManager = repoManager; this.projectCache = projectCache; this.batchUpdateFactory = batchUpdateFactory; this.onSubmitValidatorsFactory = onSubmitValidatorsFactory; openRepos = new HashMap<>(); } public void setContext(Instant ts, IdentifiedUser caller, NotifyResolver.Result notify) { this.ts = requireNonNull(ts); this.caller = requireNonNull(caller); this.notify = requireNonNull(notify); } public OpenRepo getRepo(Project.NameKey project) throws NoSuchProjectException, IOException { if (openRepos.containsKey(project)) { return openRepos.get(project); } ProjectState projectState = projectCache.get(project).orElseThrow(noSuchProject(project)); try { OpenRepo or = new OpenRepo(repoManager.openRepository(project), projectState); openRepos.put(project, or); return or; } catch (RepositoryNotFoundException e) { throw new NoSuchProjectException(project, e); } } public List batchUpdates(Collection projects, String refLogMessage) throws NoSuchProjectException, IOException { requireNonNull(refLogMessage, "refLogMessage"); List updates = new ArrayList<>(projects.size()); for (Project.NameKey project : projects) { updates.add(getRepo(project).getUpdate().setNotify(notify).setRefLogMessage(refLogMessage)); } return updates; } public void resetUpdates(ImmutableSet projects) throws NoSuchProjectException, IOException { for (Project.NameKey project : projects) { getRepo(project).resetExecutedUpdates(); } } @Override public void close() { for (OpenRepo repo : openRepos.values()) { repo.close(); } openRepos.clear(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy