Skip to content

Commit 5886742

Browse files
Ly-Joeytymzd
authored andcommitted
feat(gitter): add cherrypicked events to affected-commits response (google#5101)
Add list of cherrypicked events to `/affected-commits` response because worker needs the info. Also updated some logic so we don't include the introduced / limit / fixed event from input in the result cherrypicked list.
1 parent 646a860 commit 5886742

5 files changed

Lines changed: 157 additions & 54 deletions

File tree

go/cmd/gitter/gitter.go

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -675,15 +675,40 @@ func affectedCommitsHandler(w http.ResponseWriter, r *http.Request) {
675675
}
676676

677677
var affectedCommits []*Commit
678+
var newIntroHashes []string
679+
var newFixedHashes []string
680+
var newLimitHashes []string
681+
678682
if len(se.Limit) > 0 {
679-
affectedCommits = repo.Limit(ctx, se, cherrypickIntro, cherrypickLimit)
683+
affectedCommits, newIntroHashes, newLimitHashes = repo.Limit(ctx, se, cherrypickIntro, cherrypickLimit)
680684
} else {
681-
affectedCommits = repo.Affected(ctx, se, cherrypickIntro, cherrypickFixed)
685+
affectedCommits, newIntroHashes, newFixedHashes = repo.Affected(ctx, se, cherrypickIntro, cherrypickFixed)
686+
}
687+
688+
cherryPickedEvents := make([]*pb.Event, 0, len(newIntroHashes)+len(newFixedHashes)+len(newLimitHashes))
689+
for _, h := range newIntroHashes {
690+
cherryPickedEvents = append(cherryPickedEvents, &pb.Event{
691+
EventType: pb.EventType_INTRODUCED,
692+
Hash: h,
693+
})
694+
}
695+
for _, h := range newFixedHashes {
696+
cherryPickedEvents = append(cherryPickedEvents, &pb.Event{
697+
EventType: pb.EventType_FIXED,
698+
Hash: h,
699+
})
700+
}
701+
for _, h := range newLimitHashes {
702+
cherryPickedEvents = append(cherryPickedEvents, &pb.Event{
703+
EventType: pb.EventType_LIMIT,
704+
Hash: h,
705+
})
682706
}
683707

684708
resp := &pb.AffectedCommitsResponse{
685-
Commits: make([]*pb.Commit, 0, len(affectedCommits)),
686-
Refs: make([]*pb.Ref, 0),
709+
Commits: make([]*pb.Commit, 0, len(affectedCommits)),
710+
Refs: make([]*pb.Ref, 0),
711+
CherryPickedEvents: cherryPickedEvents,
687712
}
688713
for _, c := range affectedCommits {
689714
resp.Commits = append(resp.Commits, &pb.Commit{

go/cmd/gitter/pb/repository/repository.pb.go

Lines changed: 24 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go/cmd/gitter/pb/repository/repository.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ message Ref {
2727
message AffectedCommitsResponse {
2828
repeated Commit commits = 1;
2929
repeated Ref refs = 2;
30+
repeated Event cherry_picked_events = 3;
3031
}
3132

3233
enum EventType {

go/cmd/gitter/repository.go

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ import (
77
"errors"
88
"fmt"
99
"log/slog"
10-
"maps"
1110
"os"
12-
"slices"
1311
"strings"
1412
"sync"
1513
"time"
@@ -469,49 +467,74 @@ func (r *Repository) parseHashes(ctx context.Context, hashesStr []string) []int
469467
return indices
470468
}
471469

472-
// expandByCherrypick expands a slice of commits by adding commits that have the same Patch ID (cherrypicked commits) returns a new list containing the original commits + any other commits that share the same Patch ID
470+
// hexHashes converts a slice of commit indices into a slice of their hex string hashes.
471+
func (r *Repository) hexHashes(indices []int) []string {
472+
if len(indices) == 0 {
473+
return nil
474+
}
475+
hashes := make([]string, 0, len(indices))
476+
for _, idx := range indices {
477+
hashes = append(hashes, hex.EncodeToString(r.commits[idx].Hash[:]))
478+
}
479+
480+
return hashes
481+
}
482+
483+
// expandByCherrypick finds cherry picked commits that have the same Patch ID as the input commits.
484+
// It returns a slice of commit indices of only the new commits
473485
func (r *Repository) expandByCherrypick(commits []int) []int {
474-
unique := make(map[int]struct{}, len(commits)) // avoid duplication
475-
var zeroPatchID SHA1
486+
// Track seen commits to avoid duplicates and exclude original input commits.
487+
seen := make([]bool, len(r.commits))
488+
for _, idx := range commits {
489+
seen[idx] = true
490+
}
476491

492+
var cherrypicked []int
477493
for _, idx := range commits {
478-
// Find patch ID from commit details
479-
commit := r.commits[idx]
480-
if commit.PatchID == zeroPatchID {
481-
unique[idx] = struct{}{}
494+
pid := r.commits[idx].PatchID
495+
if pid == (SHA1{}) {
496+
// A commit made with --allow-empty could have no file diff and therefore a 0 patch ID
482497
continue
483498
}
484499

485-
// Add equivalent commits with the same Patch ID (including the current commit)
486-
equivalents := r.patchIDToCommits[commit.PatchID]
487-
for _, eq := range equivalents {
488-
unique[eq] = struct{}{}
500+
// Add equivalent commits with the same patch ID
501+
for _, eqIdx := range r.patchIDToCommits[pid] {
502+
if !seen[eqIdx] {
503+
seen[eqIdx] = true
504+
cherrypicked = append(cherrypicked, eqIdx)
505+
}
489506
}
490507
}
491508

492-
keys := slices.Collect(maps.Keys(unique))
493-
494-
return keys
509+
return cherrypicked
495510
}
496511

497-
// Affected returns a list of commits that are affected by the given introduced, fixed and last_affected events
512+
// Affected returns a list of commits that are affected by the given introduced, fixed and last_affected events.
513+
// It also returns two slices of hex hashes for newly identified cherry-picked introduced and fixed commits.
498514
// A commit is affected when: from at least one introduced that is an ancestor of the commit, there is no path between them that passes through a fix.
499515
// A fix can either be a fixed commit, or the children of a lastAffected commit.
500-
func (r *Repository) Affected(ctx context.Context, se *SeparatedEvents, cherrypickIntro, cherrypickFixed bool) []*Commit {
516+
func (r *Repository) Affected(ctx context.Context, se *SeparatedEvents, cherrypickIntro, cherrypickFixed bool) ([]*Commit, []string, []string) {
501517
logger.InfoContext(ctx, "Starting affected commit walking")
502518
start := time.Now()
503519

504520
introduced := r.parseHashes(ctx, se.Introduced)
505521
fixed := r.parseHashes(ctx, se.Fixed)
506522
lastAffected := r.parseHashes(ctx, se.LastAffected)
507523

524+
var newIntroHashes []string
525+
var newFixedHashes []string
526+
508527
// Expands the introduced and fixed commits to include cherrypick equivalents
509528
// lastAffected should not be expanded because it does not imply a "fix" commit that can be cherrypicked to other branches
510529
if cherrypickIntro {
511-
introduced = r.expandByCherrypick(introduced)
530+
newIntro := r.expandByCherrypick(introduced)
531+
newIntroHashes = r.hexHashes(newIntro)
532+
introduced = append(introduced, newIntro...)
512533
}
513534
if cherrypickFixed {
514-
fixed = r.expandByCherrypick(fixed)
535+
newFixed := r.expandByCherrypick(fixed)
536+
newFixedHashes = r.hexHashes(newFixed)
537+
fixed = append(fixed, newFixed...)
515538
}
516539

517540
// Fixed commits and children of last affected are both in this set
@@ -620,19 +643,27 @@ func (r *Repository) Affected(ctx context.Context, se *SeparatedEvents, cherrypi
620643

621644
logger.InfoContext(ctx, "Affected commit walking completed", slog.Duration("duration", time.Since(start)))
622645

623-
return affectedCommits
646+
return affectedCommits, newIntroHashes, newFixedHashes
624647
}
625648

626-
// Limit walks and returns the commits that are strictly between introduced (inclusive) and limit (exclusive)
627-
func (r *Repository) Limit(ctx context.Context, se *SeparatedEvents, cherrypickIntro, cherrypickLimit bool) []*Commit {
649+
// Limit walks and returns the commits that are strictly between introduced (inclusive) and limit (exclusive).
650+
// It also returns two slices of hex hashes for newly identified cherry-picked introduced and limit commits.
651+
func (r *Repository) Limit(ctx context.Context, se *SeparatedEvents, cherrypickIntro, cherrypickLimit bool) ([]*Commit, []string, []string) {
628652
introduced := r.parseHashes(ctx, se.Introduced)
629653
limit := r.parseHashes(ctx, se.Limit)
630654

655+
var newIntroHashes []string
656+
var newLimitHashes []string
657+
631658
if cherrypickIntro {
632-
introduced = r.expandByCherrypick(introduced)
659+
newIntro := r.expandByCherrypick(introduced)
660+
newIntroHashes = r.hexHashes(newIntro)
661+
introduced = append(introduced, newIntro...)
633662
}
634663
if cherrypickLimit {
635-
limit = r.expandByCherrypick(limit)
664+
newLimit := r.expandByCherrypick(limit)
665+
newLimitHashes = r.hexHashes(newLimit)
666+
limit = append(limit, newLimit...)
636667
}
637668

638669
var affectedCommits []*Commit
@@ -678,5 +709,5 @@ func (r *Repository) Limit(ctx context.Context, se *SeparatedEvents, cherrypickI
678709
}
679710
}
680711

681-
return affectedCommits
712+
return affectedCommits, newIntroHashes, newLimitHashes
682713
}

go/cmd/gitter/repository_test.go

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -220,12 +220,12 @@ func TestExpandByCherrypick(t *testing.T) {
220220
{
221221
name: "Expand single commit with cherry-pick",
222222
input: []int{idx1},
223-
expected: []SHA1{h1, h3},
223+
expected: []SHA1{h3},
224224
},
225225
{
226226
name: "No expansion for commit without cherry-pick",
227227
input: []int{idx2},
228-
expected: []SHA1{h2},
228+
expected: []SHA1{},
229229
},
230230
}
231231

@@ -347,7 +347,7 @@ func TestAffected_Introduced_Fixed(t *testing.T) {
347347

348348
for _, tt := range tests {
349349
t.Run(tt.name, func(t *testing.T) {
350-
gotCommits := repo.Affected(t.Context(), tt.se, false, false)
350+
gotCommits, _, _ := repo.Affected(t.Context(), tt.se, false, false)
351351

352352
var got []SHA1
353353
for _, c := range gotCommits {
@@ -463,7 +463,7 @@ func TestAffected_Introduced_LastAffected(t *testing.T) {
463463

464464
for _, tt := range tests {
465465
t.Run(tt.name, func(t *testing.T) {
466-
gotCommits := repo.Affected(t.Context(), tt.se, false, false)
466+
gotCommits, _, _ := repo.Affected(t.Context(), tt.se, false, false)
467467

468468
var got []SHA1
469469
for _, c := range gotCommits {
@@ -568,7 +568,7 @@ func TestAffected_Combined(t *testing.T) {
568568

569569
for _, tt := range tests {
570570
t.Run(tt.name, func(t *testing.T) {
571-
gotCommits := repo.Affected(t.Context(), tt.se, false, false)
571+
gotCommits, _, _ := repo.Affected(t.Context(), tt.se, false, false)
572572

573573
var got []SHA1
574574
for _, c := range gotCommits {
@@ -677,15 +677,33 @@ func TestAffected_Cherrypick(t *testing.T) {
677677

678678
for _, tt := range tests {
679679
t.Run(tt.name, func(t *testing.T) {
680-
gotCommits := repo.Affected(t.Context(), tt.se, tt.cherrypickIntro, tt.cherrypickFixed)
680+
gotCommits, gotIntro, gotFixed := repo.Affected(t.Context(), tt.se, tt.cherrypickIntro, tt.cherrypickFixed)
681681

682682
var got []SHA1
683683
for _, c := range gotCommits {
684684
got = append(got, c.Hash)
685685
}
686686

687+
// Check affected commits
687688
if diff := cmp.Diff(tt.expected, got, cmpSHA1Opts...); diff != "" {
688-
t.Errorf("TestAffected_Cherrypick() mismatch (-want +got):\n%s", diff)
689+
t.Errorf("TestAffected_Cherrypick() commits mismatch (-want +got):\n%s", diff)
690+
}
691+
692+
// Check cherrypicked events
693+
var expectedIntro []string
694+
var expectedFixed []string
695+
if tt.cherrypickIntro && (tt.se.Introduced[0] == encodeSHA1(hA) || tt.se.Introduced[0] == "0") {
696+
expectedIntro = append(expectedIntro, encodeSHA1(hE))
697+
}
698+
if tt.cherrypickFixed && len(tt.se.Fixed) > 0 && tt.se.Fixed[0] == encodeSHA1(hG) {
699+
expectedFixed = append(expectedFixed, encodeSHA1(hC))
700+
}
701+
702+
if diff := cmp.Diff(expectedIntro, gotIntro); diff != "" {
703+
t.Errorf("TestAffected_Cherrypick() intro mismatch (-want +got):\n%s", diff)
704+
}
705+
if diff := cmp.Diff(expectedFixed, gotFixed); diff != "" {
706+
t.Errorf("TestAffected_Cherrypick() fixed mismatch (-want +got):\n%s", diff)
689707
}
690708
})
691709
}
@@ -759,7 +777,7 @@ func TestLimit(t *testing.T) {
759777

760778
for _, tt := range tests {
761779
t.Run(tt.name, func(t *testing.T) {
762-
gotCommits := repo.Limit(t.Context(), tt.se, false, false)
780+
gotCommits, _, _ := repo.Limit(t.Context(), tt.se, false, false)
763781

764782
var got []SHA1
765783
for _, c := range gotCommits {
@@ -868,15 +886,33 @@ func TestLimit_Cherrypick(t *testing.T) {
868886

869887
for _, tt := range tests {
870888
t.Run(tt.name, func(t *testing.T) {
871-
gotCommits := repo.Limit(t.Context(), tt.se, tt.cherrypickIntro, tt.cherrypickLimit)
889+
gotCommits, gotIntro, gotLimit := repo.Limit(t.Context(), tt.se, tt.cherrypickIntro, tt.cherrypickLimit)
872890

873891
var got []SHA1
874892
for _, c := range gotCommits {
875893
got = append(got, c.Hash)
876894
}
877895

896+
// Check affected commits
878897
if diff := cmp.Diff(tt.expected, got, cmpSHA1Opts...); diff != "" {
879-
t.Errorf("TestLimit_Cherrypick() mismatch (-want +got):\n%s", diff)
898+
t.Errorf("TestLimit_Cherrypick() commits mismatch (-want +got):\n%s", diff)
899+
}
900+
901+
// Check cherrypicked events
902+
var expectedIntro []string
903+
var expectedLimit []string
904+
if tt.cherrypickIntro && tt.se.Introduced[0] == encodeSHA1(hB) {
905+
expectedIntro = append(expectedIntro, encodeSHA1(hF))
906+
}
907+
if tt.cherrypickLimit && tt.se.Limit[0] == encodeSHA1(hG) {
908+
expectedLimit = append(expectedLimit, encodeSHA1(hC))
909+
}
910+
911+
if diff := cmp.Diff(expectedIntro, gotIntro); diff != "" {
912+
t.Errorf("TestLimit_Cherrypick() intro mismatch (-want +got):\n%s", diff)
913+
}
914+
if diff := cmp.Diff(expectedLimit, gotLimit); diff != "" {
915+
t.Errorf("TestLimit_Cherrypick() limit mismatch (-want +got):\n%s", diff)
880916
}
881917
})
882918
}

0 commit comments

Comments
 (0)