go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/gerrit/equi.go (about) 1 // Copyright 2020 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gerrit 16 17 import ( 18 "sort" 19 20 "go.chromium.org/luci/common/errors" 21 gerritpb "go.chromium.org/luci/common/proto/gerrit" 22 ) 23 24 // EquivalentPatchsetRange computes range of patchsets code-wise equivalent to 25 // the current patchset. 26 // 27 // Gerrit categorises each new patchset (aka Revision) according to difference 28 // from prior one. The rebases are counted as equivalent, even though 29 // dependencies may have changed. Thus, only REWORK changes code. 30 // 31 // Generally, all patchsets are numbered 1,2,3,...n without gaps. But this 32 // function doesn't assume this, thus Gerrit might potentially support wiping 33 // out individual patchsets, creating gaps without affecting CV. 34 func EquivalentPatchsetRange(info *gerritpb.ChangeInfo) (minEquiPatchset, currentPatchset int, err error) { 35 if len(info.Revisions) == 0 { 36 err = errors.Reason("ChangeInfo must have all revisions populated").Err() 37 return 38 } 39 revs := make([]*gerritpb.RevisionInfo, 0, len(info.Revisions)) 40 for _, rev := range info.Revisions { 41 revs = append(revs, rev) 42 } 43 sort.Slice(revs, func(i, j int) bool { 44 return revs[i].Number > revs[j].Number // largest patchset first. 45 }) 46 47 // Validate ChangeInfo to avoid problems later. 48 switch rev, ok := info.Revisions[info.CurrentRevision]; { 49 case !ok: 50 err = errors.Reason("ChangeInfo must have current_revision populated").Err() 51 return 52 case rev != revs[0]: 53 err = errors.Reason("ChangeInfo.currentPatchset %v doesn't have largest patchset %v", rev, revs[0]).Err() 54 return 55 } 56 57 currentPatchset = int(revs[0].Number) 58 minEquiPatchset = currentPatchset 59 for i, rev := range revs[:len(revs)-1] { 60 switch rev.Kind { 61 case gerritpb.RevisionInfo_REWORK: 62 return 63 case gerritpb.RevisionInfo_NO_CHANGE, 64 gerritpb.RevisionInfo_NO_CODE_CHANGE, 65 gerritpb.RevisionInfo_MERGE_FIRST_PARENT_UPDATE, 66 gerritpb.RevisionInfo_TRIVIAL_REBASE: 67 minEquiPatchset = int(revs[i+1].Number) 68 default: 69 err = errors.Reason("Unknown revision kind %d %s ps#%d", 70 rev.Kind, rev.Kind, rev.GetNumber()).Err() 71 return 72 } 73 } 74 return 75 }