123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- package hercules
- import (
- "sort"
- "gopkg.in/src-d/go-git.v4"
- "gopkg.in/src-d/go-git.v4/plumbing/object"
- "gopkg.in/src-d/go-git.v4/utils/merkletrie"
- )
- type Couples struct {
- // The number of developers for which to build the matrix. 0 disables this analysis.
- PeopleNumber int
- // people store how many times every developer committed to every file.
- people []map[string]int
- // people_commits is the number of commits each author made
- people_commits []int
- // files store every file occurred in the same commit with every other file.
- files map[string]map[string]int
- }
- type CouplesResult struct {
- PeopleMatrix []map[int]int64
- PeopleFiles [][]int
- FilesMatrix []map[int]int64
- Files []string
- }
- func (couples *Couples) Name() string {
- return "Couples"
- }
- func (couples *Couples) Provides() []string {
- return []string{}
- }
- func (couples *Couples) Requires() []string {
- arr := [...]string{"author", "renamed_changes"}
- return arr[:]
- }
- func (couples *Couples) Initialize(repository *git.Repository) {
- couples.people = make([]map[string]int, couples.PeopleNumber+1)
- for i := range couples.people {
- couples.people[i] = map[string]int{}
- }
- couples.people_commits = make([]int, couples.PeopleNumber+1)
- couples.files = map[string]map[string]int{}
- }
- func (couples *Couples) Consume(deps map[string]interface{}) (map[string]interface{}, error) {
- author := deps["author"].(int)
- if author == MISSING_AUTHOR {
- author = couples.PeopleNumber
- }
- couples.people_commits[author] += 1
- tree_diff := deps["renamed_changes"].(object.Changes)
- context := make([]string, 0)
- deleteFile := func(name string) {
- // we do not remove the file from people - the context does not expire
- delete(couples.files, name)
- for _, otherFiles := range couples.files {
- delete(otherFiles, name)
- }
- }
- for _, change := range tree_diff {
- action, err := change.Action()
- if err != nil {
- return nil, err
- }
- toName := change.To.Name
- fromName := change.From.Name
- switch action {
- case merkletrie.Insert:
- context = append(context, toName)
- couples.people[author][toName] += 1
- case merkletrie.Delete:
- deleteFile(fromName)
- couples.people[author][fromName] += 1
- case merkletrie.Modify:
- if fromName != toName {
- // renamed
- couples.files[toName] = couples.files[fromName]
- for _, otherFiles := range couples.files {
- val, exists := otherFiles[fromName]
- if exists {
- otherFiles[toName] = val
- }
- }
- deleteFile(fromName)
- for _, authorFiles := range couples.people {
- val, exists := authorFiles[fromName]
- if exists {
- authorFiles[toName] = val
- delete(authorFiles, fromName)
- }
- }
- }
- context = append(context, toName)
- couples.people[author][toName] += 1
- }
- }
- for _, file := range context {
- for _, otherFile := range context {
- lane, exists := couples.files[file]
- if !exists {
- lane = map[string]int{}
- couples.files[file] = lane
- }
- lane[otherFile] += 1
- }
- }
- return nil, nil
- }
- func (couples *Couples) Finalize() interface{} {
- filesSequence := make([]string, len(couples.files))
- i := 0
- for file := range couples.files {
- filesSequence[i] = file
- i++
- }
- sort.Strings(filesSequence)
- filesIndex := map[string]int{}
- for i, file := range filesSequence {
- filesIndex[file] = i
- }
- peopleMatrix := make([]map[int]int64, couples.PeopleNumber+1)
- peopleFiles := make([][]int, couples.PeopleNumber+1)
- for i := range peopleMatrix {
- peopleMatrix[i] = map[int]int64{}
- for file, commits := range couples.people[i] {
- fi, exists := filesIndex[file]
- if exists {
- peopleFiles[i] = append(peopleFiles[i], fi)
- }
- for j, otherFiles := range couples.people {
- otherCommits := otherFiles[file]
- delta := otherCommits
- if otherCommits > commits {
- delta = commits
- }
- if delta > 0 {
- peopleMatrix[i][j] += int64(delta)
- }
- }
- }
- sort.Ints(peopleFiles[i])
- }
- filesMatrix := make([]map[int]int64, len(filesIndex))
- for i := range filesMatrix {
- filesMatrix[i] = map[int]int64{}
- for otherFile, cooccs := range couples.files[filesSequence[i]] {
- filesMatrix[i][filesIndex[otherFile]] = int64(cooccs)
- }
- }
- return CouplesResult{
- PeopleMatrix: peopleMatrix, PeopleFiles: peopleFiles,
- Files: filesSequence, FilesMatrix: filesMatrix}
- }
|