| // Copyright (C) 2009 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 java.util.stream.Collectors.toSet; |
| |
| import com.google.gerrit.common.Nullable; |
| import com.google.gerrit.entities.PatchSet; |
| import com.google.gerrit.server.CurrentUser; |
| import com.google.gerrit.server.query.change.ChangeData; |
| import com.google.gerrit.server.query.change.InternalChangeQuery; |
| import com.google.inject.Provider; |
| import java.util.List; |
| import java.util.Optional; |
| |
| /** |
| * Status codes set on {@link com.google.gerrit.server.git.CodeReviewCommit}s by {@link |
| * SubmitStrategy} implementations. |
| */ |
| public enum CommitMergeStatus { |
| CLEAN_MERGE("Change has been successfully merged"), |
| |
| CLEAN_PICK("Change has been successfully cherry-picked"), |
| |
| CLEAN_REBASE("Change has been successfully rebased and submitted"), |
| |
| ALREADY_MERGED(""), |
| |
| PATH_CONFLICT( |
| "Change could not be merged due to a path conflict.\n" |
| + "\n" |
| + "Please rebase the change locally and upload the rebased commit for review."), |
| |
| REBASE_MERGE_CONFLICT( |
| "Change could not be merged due to a conflict.\n" |
| + "\n" |
| + "Please rebase the change locally and upload the rebased commit for review."), |
| |
| SKIPPED_IDENTICAL_TREE( |
| "Marking change merged without cherry-picking to branch, as the resulting commit would be" |
| + " empty."), |
| |
| MISSING_DEPENDENCY("Depends on commit that cannot be merged."), |
| |
| MANUAL_RECURSIVE_MERGE( |
| "The change requires a local merge to resolve.\n" |
| + "\n" |
| + "Please merge (or rebase) the change locally and upload the resolution for review."), |
| |
| CANNOT_CHERRY_PICK_ROOT( |
| "Cannot cherry-pick an initial commit onto an existing branch.\n" |
| + "\n" |
| + "Please merge the change locally and upload the merge commit for review."), |
| |
| CANNOT_REBASE_ROOT( |
| "Cannot rebase an initial commit onto an existing branch.\n" |
| + "\n" |
| + "Please merge the change locally and upload the merge commit for review."), |
| |
| NOT_FAST_FORWARD( |
| "Project policy requires all submissions to be a fast-forward.\n" |
| + "\n" |
| + "Please rebase the change locally and upload again for review."), |
| |
| EMPTY_COMMIT( |
| "Change could not be merged because the commit is empty.\n" |
| + "\n" |
| + "Project policy requires all commits to contain modifications to at least one file."), |
| |
| FAST_FORWARD_INDEPENDENT_CHANGES( |
| "Change could not be merged because the submission has two independent changes " |
| + "with the same destination branch.\n" |
| + "\n" |
| + "Independent changes can't be submitted to the same destination branch with " |
| + "FAST_FORWARD_ONLY submit strategy"); |
| |
| private final String description; |
| |
| CommitMergeStatus(String description) { |
| this.description = description; |
| } |
| |
| public String getDescription() { |
| return description; |
| } |
| |
| public static String createMissingDependencyMessage( |
| @Nullable CurrentUser caller, |
| Provider<InternalChangeQuery> queryProvider, |
| String commit, |
| String otherCommit) { |
| List<ChangeData> changes = queryProvider.get().enforceVisibility(true).byCommit(otherCommit); |
| |
| if (changes.isEmpty()) { |
| return String.format( |
| "Commit %s depends on commit %s which cannot be merged." |
| + " Is the change of this commit not visible to '%s' or was it deleted?", |
| commit, otherCommit, caller != null ? caller.getLoggableName() : "<user-not-available>"); |
| } else if (changes.size() == 1) { |
| ChangeData cd = changes.get(0); |
| if (cd.currentPatchSet().commitId().name().equals(otherCommit)) { |
| return String.format( |
| "Commit %s depends on commit %s of change %d which cannot be merged.", |
| commit, otherCommit, cd.getId().get()); |
| } |
| Optional<PatchSet> patchSet = |
| cd.patchSets().stream().filter(ps -> ps.commitId().name().equals(otherCommit)).findAny(); |
| if (patchSet.isPresent()) { |
| return String.format( |
| "Commit %s depends on commit %s, which is outdated patch set %d of change %d." |
| + " The latest patch set is %d.", |
| commit, |
| otherCommit, |
| patchSet.get().id().get(), |
| cd.getId().get(), |
| cd.currentPatchSet().id().get()); |
| } |
| // should not happen, fall-back to default message |
| return String.format( |
| "Commit %s depends on commit %s of change %d which cannot be merged.", |
| commit, otherCommit, cd.getId().get()); |
| } else { |
| return String.format( |
| "Commit %s depends on commit %s of changes %s which cannot be merged.", |
| commit, otherCommit, changes.stream().map(cd -> cd.getId().get()).collect(toSet())); |
| } |
| } |
| } |