diff options
author | Vincent Ambo <mail@tazj.in> | 2021-12-09T13·11+0300 |
---|---|---|
committer | Vincent Ambo <mail@tazj.in> | 2021-12-09T13·13+0300 |
commit | 59f97332b3076afe80ed3fe92eb0e1da676e3a99 (patch) | |
tree | 07e3cb9b1cf29693e3de3f6a2dfcf22bd58ebf92 /third_party/gerrit-queue/gerrit/series.go | |
parent | ff10b7ab8303d050a8d7d751611da88bc13a75b4 (diff) | |
parent | 24f5a642af3aa1627bbff977f0a101907a02c69f (diff) |
subtree(3p/gerrit-queue): Vendor at commit '24f5a642' r/3170
Imported from github/tvlfyi/gerrit-queue, originally from github/tweag/gerrit-queue but that upstream is unmaintained. git-subtree-dir: third_party/gerrit-queue git-subtree-mainline: ff10b7ab8303d050a8d7d751611da88bc13a75b4 git-subtree-split: 24f5a642af3aa1627bbff977f0a101907a02c69f Change-Id: I307cc38185ab9e25eb102c95096298a150ae13a2
Diffstat (limited to 'third_party/gerrit-queue/gerrit/series.go')
-rw-r--r-- | third_party/gerrit-queue/gerrit/series.go | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/third_party/gerrit-queue/gerrit/series.go b/third_party/gerrit-queue/gerrit/series.go new file mode 100644 index 000000000000..295193ee9503 --- /dev/null +++ b/third_party/gerrit-queue/gerrit/series.go @@ -0,0 +1,126 @@ +package gerrit + +import ( + "sort" + + "github.com/apex/log" +) + +// AssembleSeries consumes a list of `Changeset`, and groups them together to series +// +// We initially put every Changeset in its own Serie +// +// As we have no control over the order of the passed changesets, +// we maintain a lookup table, mapLeafToSerie, +// which allows to lookup a serie by its leaf commit id +// We concat series in a fixpoint approach +// because both appending and prepending is much more complex. +// Concatenation moves changesets of the later changeset in the previous one +// in a cleanup phase, we remove orphaned series (those without any changesets inside) +// afterwards, we do an integrity check, just to be on the safe side. +func AssembleSeries(changesets []*Changeset, logger *log.Logger) ([]*Serie, error) { + series := make([]*Serie, 0) + mapLeafToSerie := make(map[string]*Serie, 0) + + for _, changeset := range changesets { + l := logger.WithField("changeset", changeset.String()) + + l.Debug("creating initial serie") + serie := &Serie{ + ChangeSets: []*Changeset{changeset}, + } + series = append(series, serie) + mapLeafToSerie[changeset.CommitID] = serie + } + + // Combine series using a fixpoint approach, with a max iteration count. + logger.Debug("glueing together phase") + for i := 1; i < 100; i++ { + didUpdate := false + logger.Debugf("at iteration %d", i) + for j, serie := range series { + l := logger.WithFields(log.Fields{ + "i": i, + "j": j, + "serie": serie.String(), + }) + parentCommitIDs, err := serie.GetParentCommitIDs() + if err != nil { + return series, err + } + if len(parentCommitIDs) != 1 { + // We can't append merge commits to other series + l.Infof("No single parent, skipping.") + continue + } + parentCommitID := parentCommitIDs[0] + l.Debug("Looking for a predecessor.") + // if there's another serie that has this parent as a leaf, glue together + if otherSerie, ok := mapLeafToSerie[parentCommitID]; ok { + if otherSerie == serie { + continue + } + l = l.WithField("otherSerie", otherSerie) + + myLeafCommitID, err := serie.GetLeafCommitID() + if err != nil { + return series, err + } + + // append our changesets to the other serie + l.Debug("Splicing together.") + otherSerie.ChangeSets = append(otherSerie.ChangeSets, serie.ChangeSets...) + + delete(mapLeafToSerie, parentCommitID) + mapLeafToSerie[myLeafCommitID] = otherSerie + + // orphan our serie + serie.ChangeSets = []*Changeset{} + // remove the orphaned serie from the lookup table + delete(mapLeafToSerie, myLeafCommitID) + + didUpdate = true + } else { + l.Debug("Not found.") + } + } + series = removeOrphanedSeries(series) + if !didUpdate { + logger.Infof("converged after %d iterations", i) + break + } + } + + // Check integrity, just to be on the safe side. + for _, serie := range series { + l := logger.WithField("serie", serie.String()) + l.Debugf("checking integrity") + err := serie.CheckIntegrity() + if err != nil { + l.Errorf("checking integrity failed: %s", err) + } + } + return series, nil +} + +// removeOrphanedSeries removes all empty series (that contain zero changesets) +func removeOrphanedSeries(series []*Serie) []*Serie { + newSeries := []*Serie{} + for _, serie := range series { + if len(serie.ChangeSets) != 0 { + newSeries = append(newSeries, serie) + } + } + return newSeries +} + +// SortSeries sorts a list of series by the number of changesets in each serie, descending +func SortSeries(series []*Serie) []*Serie { + newSeries := make([]*Serie, len(series)) + copy(newSeries, series) + sort.Slice(newSeries, func(i, j int) bool { + // the weight depends on the amount of changesets series changeset size + return len(series[i].ChangeSets) > len(series[j].ChangeSets) + }) + return newSeries +} |