blob: 4c5e2cd665906ed994d16365aaeb658cd35f442a [file] [log] [blame]
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"log"
"os/exec"
"sort"
"strings"
)
// Adapted from git-codereview/mail.go, but uses Author lines
// in addition to Reviewed-By lines. The effect should be the same,
// since the most common reviewers are the most common authors too,
// but admitting authors lets us shorten CL owners too.
// reviewers is the list of reviewers for the current repository,
// sorted by how many reviews each has done.
var reviewers []reviewer
const ReviewScale = 1000000000
type reviewer struct {
addr string
count int64 // #reviews * ReviewScale + #CLs
}
var mailLookup = map[string]string{} // rsc -> rsc@golang.org
var isReviewer = map[string]bool{} // rsc@golang.org -> true
// loadReviewers reads the reviewer list from the current git repo
// and leaves it in the global variable reviewers.
// See the comment on mailLookup for a description of how the
// list is generated and used.
func loadReviewers() {
if reviewers != nil {
return
}
output, err := exec.Command("go", "env", "GOROOT").CombinedOutput()
if err != nil {
log.Fatalf("go env GOROOT: %v\n%s", err, output)
}
goroot := strings.TrimSpace(string(output))
cmd := exec.Command("git", "log", "--format=format:Author: <%aE>%n%B")
cmd.Dir = goroot
output, err = cmd.CombinedOutput()
if err != nil {
log.Fatalf("git log: %v\n%s", err, output)
}
countByAddr := map[string]int64{}
for _, line := range strings.Split(string(output), "\n") {
if strings.HasPrefix(line, "Reviewed-by:") || strings.HasPrefix(line, "Author:") {
delta := int64(1)
if strings.HasPrefix(line, "Reviewed-by:") {
delta = ReviewScale
}
f := strings.Fields(line)
addr := f[len(f)-1]
if strings.HasPrefix(addr, "<") && strings.Contains(addr, "@") && strings.HasSuffix(addr, ">") {
email := addr[1 : len(addr)-1]
countByAddr[email] += delta
if delta == ReviewScale {
isReviewer[email] = true
}
}
}
}
reviewers = []reviewer{}
for addr, count := range countByAddr {
reviewers = append(reviewers, reviewer{addr, count})
}
sort.Sort(reviewersByCount(reviewers))
for _, r := range reviewers {
short := r.addr
if i := strings.Index(short, "@"); i >= 0 {
short = short[:i]
}
if mailLookup[short] == "" {
mailLookup[short] = r.addr
}
}
}
type reviewersByCount []reviewer
func (x reviewersByCount) Len() int { return len(x) }
func (x reviewersByCount) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x reviewersByCount) Less(i, j int) bool {
if x[i].count != x[j].count {
return x[i].count > x[j].count
}
return x[i].addr < x[j].addr
}