예제 #1
0
파일: MergeOp.java 프로젝트: web1992/gerrit
  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);
    }
  }
예제 #2
0
  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;
  }
예제 #3
0
 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();
 }
예제 #4
0
 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);
   }
 }
예제 #5
0
 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));
 }
예제 #6
0
    @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);
    }
예제 #7
0
 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))));
 }
예제 #8
0
 BatchUpdate newBatchUpdate(Timestamp when) {
   return batchUpdateFactory
       .create(db, destBranch.getParentKey(), caller, when)
       .setRepository(repo, rw, inserter);
 }
예제 #9
0
 private static Predicate<ChangeData> ref(Branch.NameKey branch) {
   return new RefPredicate(branch.get());
 }
예제 #10
0
 public List<ChangeData> byBranchOpen(Branch.NameKey branch) throws OrmException {
   return query(and(ref(branch), project(branch.getParentKey()), open()));
 }
예제 #11
0
파일: MergeOp.java 프로젝트: web1992/gerrit
 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()));
 }
예제 #12
0
파일: MergeOp.java 프로젝트: web1992/gerrit
  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);
    }
  }
예제 #13
0
파일: MergeOp.java 프로젝트: web1992/gerrit
 private void setDestProject(Branch.NameKey destBranch) throws IntegrationException {
   destProject = projectCache.get(destBranch.getParentKey());
   if (destProject == null) {
     throw new IntegrationException("No such project: " + destBranch.getParentKey());
   }
 }
예제 #14
0
    @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);
        }
      }
    }
예제 #15
0
  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);
    }
  }
예제 #16
0
  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);
    }
  }
예제 #17
0
  /**
   * 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();
      }
    }
  }
예제 #18
0
 public List<ChangeData> byBranchKey(Branch.NameKey branch, Change.Key key) throws OrmException {
   return query(and(ref(branch), project(branch.getParentKey()), change(key)));
 }