Let users know which change prevents submit in case of MISSING_DEPENDENCY
If a change cannot be submitted because it depends on a commit that cannot be merged the user got the following message: Depends on change that was not submitted. Commit <commit> depends on commit <other-commit> which cannot be merged. To find the change to which <other-commit> belongs the user must do a change query. Do this for the user and return the change ID too. Now we return: Depends on change that was not submitted. Commit <commit> depends on commit <other-commit> of change <other-change> which cannot be merged. Returning the commit SHA1 of <other-commit> is still useful since the commit that prevents submit may be an outdated patch set and this can be found easier if the commit SHA1 is known. Change-Id: I1368af204702a06d01ee6f812a5da5ac419b89d7 Signed-off-by: Edwin Kempin <ekempin@google.com>
This commit is contained in:
		| @@ -216,7 +216,7 @@ public class MergeUtil { | |||||||
|     List<CodeReviewCommit> result = new ArrayList<>(); |     List<CodeReviewCommit> result = new ArrayList<>(); | ||||||
|     try { |     try { | ||||||
|       result.addAll(mergeSorter.sort(toSort)); |       result.addAll(mergeSorter.sort(toSort)); | ||||||
|     } catch (IOException e) { |     } catch (IOException | OrmException e) { | ||||||
|       throw new IntegrationException("Branch head sorting failed", e); |       throw new IntegrationException("Branch head sorting failed", e); | ||||||
|     } |     } | ||||||
|     result.sort(CodeReviewCommit.ORDER); |     result.sort(CodeReviewCommit.ORDER); | ||||||
| @@ -566,7 +566,7 @@ public class MergeUtil { | |||||||
|       throws IntegrationException { |       throws IntegrationException { | ||||||
|     try { |     try { | ||||||
|       return !mergeSorter.sort(Collections.singleton(toMerge)).contains(toMerge); |       return !mergeSorter.sort(Collections.singleton(toMerge)).contains(toMerge); | ||||||
|     } catch (IOException e) { |     } catch (IOException | OrmException e) { | ||||||
|       throw new IntegrationException("Branch head sorting failed", e); |       throw new IntegrationException("Branch head sorting failed", e); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -14,6 +14,14 @@ | |||||||
|  |  | ||||||
| package com.google.gerrit.server.submit; | package com.google.gerrit.server.submit; | ||||||
|  |  | ||||||
|  | import static java.util.stream.Collectors.toSet; | ||||||
|  |  | ||||||
|  | import com.google.gerrit.server.query.change.ChangeData; | ||||||
|  | import com.google.gerrit.server.query.change.InternalChangeQuery; | ||||||
|  | import com.google.gwtorm.server.OrmException; | ||||||
|  | import com.google.inject.Provider; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Status codes set on {@link com.google.gerrit.server.git.CodeReviewCommit}s by {@link |  * Status codes set on {@link com.google.gerrit.server.git.CodeReviewCommit}s by {@link | ||||||
|  * SubmitStrategy} implementations. |  * SubmitStrategy} implementations. | ||||||
| @@ -76,4 +84,23 @@ public enum CommitMergeStatus { | |||||||
|   public String getDescription() { |   public String getDescription() { | ||||||
|     return description; |     return description; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public static String createMissingDependencyMessage( | ||||||
|  |       Provider<InternalChangeQuery> queryProvider, String commit, String otherCommit) | ||||||
|  |       throws OrmException { | ||||||
|  |     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.", commit, otherCommit); | ||||||
|  |     } else if (changes.size() == 1) { | ||||||
|  |       return String.format( | ||||||
|  |           "Commit %s depends on commit %s of change %d which cannot be merged.", | ||||||
|  |           commit, otherCommit, changes.get(0).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())); | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -16,6 +16,9 @@ package com.google.gerrit.server.submit; | |||||||
|  |  | ||||||
| import com.google.gerrit.server.git.CodeReviewCommit; | import com.google.gerrit.server.git.CodeReviewCommit; | ||||||
| import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk; | import com.google.gerrit.server.git.CodeReviewCommit.CodeReviewRevWalk; | ||||||
|  | import com.google.gerrit.server.query.change.InternalChangeQuery; | ||||||
|  | import com.google.gwtorm.server.OrmException; | ||||||
|  | import com.google.inject.Provider; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.HashSet; | import java.util.HashSet; | ||||||
| @@ -29,21 +32,24 @@ public class MergeSorter { | |||||||
|   private final CodeReviewRevWalk rw; |   private final CodeReviewRevWalk rw; | ||||||
|   private final RevFlag canMergeFlag; |   private final RevFlag canMergeFlag; | ||||||
|   private final Set<RevCommit> accepted; |   private final Set<RevCommit> accepted; | ||||||
|  |   private final Provider<InternalChangeQuery> queryProvider; | ||||||
|   private final Set<CodeReviewCommit> incoming; |   private final Set<CodeReviewCommit> incoming; | ||||||
|  |  | ||||||
|   public MergeSorter( |   public MergeSorter( | ||||||
|       CodeReviewRevWalk rw, |       CodeReviewRevWalk rw, | ||||||
|       Set<RevCommit> alreadyAccepted, |       Set<RevCommit> alreadyAccepted, | ||||||
|       RevFlag canMergeFlag, |       RevFlag canMergeFlag, | ||||||
|  |       Provider<InternalChangeQuery> queryProvider, | ||||||
|       Set<CodeReviewCommit> incoming) { |       Set<CodeReviewCommit> incoming) { | ||||||
|     this.rw = rw; |     this.rw = rw; | ||||||
|     this.canMergeFlag = canMergeFlag; |     this.canMergeFlag = canMergeFlag; | ||||||
|     this.accepted = alreadyAccepted; |     this.accepted = alreadyAccepted; | ||||||
|  |     this.queryProvider = queryProvider; | ||||||
|     this.incoming = incoming; |     this.incoming = incoming; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public Collection<CodeReviewCommit> sort(Collection<CodeReviewCommit> toMerge) |   public Collection<CodeReviewCommit> sort(Collection<CodeReviewCommit> toMerge) | ||||||
|       throws IOException { |       throws IOException, OrmException { | ||||||
|     final Set<CodeReviewCommit> heads = new HashSet<>(); |     final Set<CodeReviewCommit> heads = new HashSet<>(); | ||||||
|     final Set<CodeReviewCommit> sort = new HashSet<>(toMerge); |     final Set<CodeReviewCommit> sort = new HashSet<>(toMerge); | ||||||
|     while (!sort.isEmpty()) { |     while (!sort.isEmpty()) { | ||||||
| @@ -64,8 +70,7 @@ public class MergeSorter { | |||||||
|           // |           // | ||||||
|           n.setStatusCode(CommitMergeStatus.MISSING_DEPENDENCY); |           n.setStatusCode(CommitMergeStatus.MISSING_DEPENDENCY); | ||||||
|           n.setStatusMessage( |           n.setStatusMessage( | ||||||
|               String.format( |               CommitMergeStatus.createMissingDependencyMessage(queryProvider, n.name(), c.name())); | ||||||
|                   "Commit %s depends on commit %s which cannot be merged.", n.name(), c.name())); |  | ||||||
|           break; |           break; | ||||||
|         } |         } | ||||||
|         contents.add(c); |         contents.add(c); | ||||||
|   | |||||||
| @@ -59,7 +59,8 @@ public class RebaseSorter { | |||||||
|     this.incoming = incoming; |     this.incoming = incoming; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public List<CodeReviewCommit> sort(Collection<CodeReviewCommit> toSort) throws IOException { |   public List<CodeReviewCommit> sort(Collection<CodeReviewCommit> toSort) | ||||||
|  |       throws IOException, OrmException { | ||||||
|     final List<CodeReviewCommit> sorted = new ArrayList<>(); |     final List<CodeReviewCommit> sorted = new ArrayList<>(); | ||||||
|     final Set<CodeReviewCommit> sort = new HashSet<>(toSort); |     final Set<CodeReviewCommit> sort = new HashSet<>(toSort); | ||||||
|     while (!sort.isEmpty()) { |     while (!sort.isEmpty()) { | ||||||
| @@ -83,8 +84,8 @@ public class RebaseSorter { | |||||||
|             // |             // | ||||||
|             n.setStatusCode(CommitMergeStatus.MISSING_DEPENDENCY); |             n.setStatusCode(CommitMergeStatus.MISSING_DEPENDENCY); | ||||||
|             n.setStatusMessage( |             n.setStatusMessage( | ||||||
|                 String.format( |                 CommitMergeStatus.createMissingDependencyMessage( | ||||||
|                     "Commit %s depends on commit %s which cannot be merged.", n.name(), c.name())); |                     queryProvider, n.name(), c.name())); | ||||||
|           } |           } | ||||||
|           // Stop RevWalk because c is either a merged commit or a missing |           // Stop RevWalk because c is either a merged commit or a missing | ||||||
|           // dependency. Not need to walk further. |           // dependency. Not need to walk further. | ||||||
|   | |||||||
| @@ -59,7 +59,7 @@ public class RebaseSubmitStrategy extends SubmitStrategy { | |||||||
|     List<CodeReviewCommit> sorted; |     List<CodeReviewCommit> sorted; | ||||||
|     try { |     try { | ||||||
|       sorted = args.rebaseSorter.sort(toMerge); |       sorted = args.rebaseSorter.sort(toMerge); | ||||||
|     } catch (IOException e) { |     } catch (IOException | OrmException e) { | ||||||
|       throw new IntegrationException("Commit sorting failed", e); |       throw new IntegrationException("Commit sorting failed", e); | ||||||
|     } |     } | ||||||
|     List<SubmitStrategyOp> ops = new ArrayList<>(sorted.size()); |     List<SubmitStrategyOp> ops = new ArrayList<>(sorted.size()); | ||||||
|   | |||||||
| @@ -27,7 +27,9 @@ import com.google.gerrit.server.git.MergeUtil; | |||||||
| import com.google.gerrit.server.project.NoSuchProjectException; | import com.google.gerrit.server.project.NoSuchProjectException; | ||||||
| import com.google.gerrit.server.project.ProjectCache; | import com.google.gerrit.server.project.ProjectCache; | ||||||
| import com.google.gerrit.server.project.ProjectState; | import com.google.gerrit.server.project.ProjectState; | ||||||
|  | import com.google.gerrit.server.query.change.InternalChangeQuery; | ||||||
| import com.google.inject.Inject; | import com.google.inject.Inject; | ||||||
|  | import com.google.inject.Provider; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.util.Collection; | import java.util.Collection; | ||||||
| import java.util.HashSet; | import java.util.HashSet; | ||||||
| @@ -91,11 +93,16 @@ public class SubmitDryRun { | |||||||
|  |  | ||||||
|   private final ProjectCache projectCache; |   private final ProjectCache projectCache; | ||||||
|   private final MergeUtil.Factory mergeUtilFactory; |   private final MergeUtil.Factory mergeUtilFactory; | ||||||
|  |   private final Provider<InternalChangeQuery> queryProvider; | ||||||
|  |  | ||||||
|   @Inject |   @Inject | ||||||
|   SubmitDryRun(ProjectCache projectCache, MergeUtil.Factory mergeUtilFactory) { |   SubmitDryRun( | ||||||
|  |       ProjectCache projectCache, | ||||||
|  |       MergeUtil.Factory mergeUtilFactory, | ||||||
|  |       Provider<InternalChangeQuery> queryProvider) { | ||||||
|     this.projectCache = projectCache; |     this.projectCache = projectCache; | ||||||
|     this.mergeUtilFactory = mergeUtilFactory; |     this.mergeUtilFactory = mergeUtilFactory; | ||||||
|  |     this.queryProvider = queryProvider; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   public boolean run( |   public boolean run( | ||||||
| @@ -116,7 +123,8 @@ public class SubmitDryRun { | |||||||
|             repo, |             repo, | ||||||
|             rw, |             rw, | ||||||
|             mergeUtilFactory.create(getProject(destBranch)), |             mergeUtilFactory.create(getProject(destBranch)), | ||||||
|             new MergeSorter(rw, alreadyAccepted, canMerge, ImmutableSet.of(toMergeCommit))); |             new MergeSorter( | ||||||
|  |                 rw, alreadyAccepted, canMerge, queryProvider, ImmutableSet.of(toMergeCommit))); | ||||||
|  |  | ||||||
|     switch (submitType) { |     switch (submitType) { | ||||||
|       case CHERRY_PICK: |       case CHERRY_PICK: | ||||||
|   | |||||||
| @@ -204,7 +204,8 @@ public abstract class SubmitStrategy { | |||||||
|               projectCache.get(destBranch.getParentKey()), |               projectCache.get(destBranch.getParentKey()), | ||||||
|               "project not found: %s", |               "project not found: %s", | ||||||
|               destBranch.getParentKey()); |               destBranch.getParentKey()); | ||||||
|       this.mergeSorter = new MergeSorter(rw, alreadyAccepted, canMergeFlag, incoming); |       this.mergeSorter = | ||||||
|  |           new MergeSorter(rw, alreadyAccepted, canMergeFlag, queryProvider, incoming); | ||||||
|       this.rebaseSorter = |       this.rebaseSorter = | ||||||
|           new RebaseSorter( |           new RebaseSorter( | ||||||
|               rw, mergeTip.getInitialTip(), alreadyAccepted, canMergeFlag, queryProvider, incoming); |               rw, mergeTip.getInitialTip(), alreadyAccepted, canMergeFlag, queryProvider, incoming); | ||||||
|   | |||||||
| @@ -408,6 +408,8 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge { | |||||||
|             + change3a.getCommit().name() |             + change3a.getCommit().name() | ||||||
|             + " depends on commit " |             + " depends on commit " | ||||||
|             + change2.getCommit().name() |             + change2.getCommit().name() | ||||||
|  |             + " of change " | ||||||
|  |             + change2.getChange().getId() | ||||||
|             + " which cannot be merged."); |             + " which cannot be merged."); | ||||||
|  |  | ||||||
|     RevCommit tipbranch = getRemoteLog(project, "branch").get(0); |     RevCommit tipbranch = getRemoteLog(project, "branch").get(0); | ||||||
| @@ -511,6 +513,8 @@ public class SubmitByMergeIfNecessaryIT extends AbstractSubmitByMerge { | |||||||
|             + change3.getCommit().name() |             + change3.getCommit().name() | ||||||
|             + " depends on commit " |             + " depends on commit " | ||||||
|             + change2result.getCommit().name() |             + change2result.getCommit().name() | ||||||
|  |             + " of change " | ||||||
|  |             + change2result.getChange().getId() | ||||||
|             + " which cannot be merged."); |             + " which cannot be merged."); | ||||||
|  |  | ||||||
|     assertRefUpdatedEvents(); |     assertRefUpdatedEvents(); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Edwin Kempin
					Edwin Kempin