couples.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. package hercules
  2. import (
  3. "fmt"
  4. "sort"
  5. "gopkg.in/src-d/go-git.v4"
  6. "gopkg.in/src-d/go-git.v4/plumbing/object"
  7. "gopkg.in/src-d/go-git.v4/utils/merkletrie"
  8. )
  9. type Couples struct {
  10. // The number of developers for which to build the matrix. 0 disables this analysis.
  11. PeopleNumber int
  12. // people store how many times every developer committed to every file.
  13. people []map[string]int
  14. // people_commits is the number of commits each author made
  15. people_commits []int
  16. // files store every file occurred in the same commit with every other file.
  17. files map[string]map[string]int
  18. }
  19. type CouplesResult struct {
  20. PeopleMatrix []map[int]int64
  21. FilesMatrix []map[int]int
  22. Files []string
  23. }
  24. func (couples *Couples) Name() string {
  25. return "Couples"
  26. }
  27. func (couples *Couples) Provides() []string {
  28. return []string{}
  29. }
  30. func (couples *Couples) Requires() []string {
  31. arr := [...]string{"author", "renamed_changes"}
  32. return arr[:]
  33. }
  34. func (couples *Couples) Initialize(repository *git.Repository) {
  35. couples.people = make([]map[string]int, couples.PeopleNumber)
  36. for i := range couples.people {
  37. couples.people[i] = map[string]int{}
  38. }
  39. couples.people_commits = make([]int, couples.PeopleNumber)
  40. couples.files = map[string]map[string]int{}
  41. }
  42. func (couples *Couples) Consume(deps map[string]interface{}) (map[string]interface{}, error) {
  43. author := deps["author"].(int)
  44. couples.people_commits[author] += 1
  45. tree_diff := deps["renamed_changes"].(object.Changes)
  46. context := make([]string, 0)
  47. deleteFile := func(name string) {
  48. // we do not remove the file from people - the context does not expire
  49. delete(couples.files, name)
  50. for _, otherFiles := range couples.files {
  51. delete(otherFiles, name)
  52. }
  53. }
  54. for _, change := range tree_diff {
  55. action, err := change.Action()
  56. if err != nil {
  57. return nil, err
  58. }
  59. switch action {
  60. case merkletrie.Insert:
  61. context = append(context, change.To.Name)
  62. couples.people[author][change.To.Name] += 1
  63. case merkletrie.Delete:
  64. deleteFile(change.From.Name)
  65. case merkletrie.Modify:
  66. toName := change.To.Name
  67. fromName := change.From.Name
  68. if fromName != toName {
  69. // renamed
  70. couples.files[toName] = couples.files[fromName]
  71. for _, otherFiles := range couples.files {
  72. val, exists := otherFiles[fromName]
  73. if exists {
  74. otherFiles[toName] = val
  75. }
  76. }
  77. deleteFile(change.From.Name)
  78. }
  79. context = append(context, toName)
  80. couples.people[author][toName] += 1
  81. }
  82. }
  83. for _, file := range context {
  84. for _, otherFile := range context {
  85. lane, exists := couples.files[file]
  86. if !exists {
  87. lane = map[string]int{}
  88. couples.files[file] = lane
  89. }
  90. lane[otherFile] += 1
  91. }
  92. }
  93. return nil, nil
  94. }
  95. func (couples *Couples) Finalize() interface{} {
  96. peopleMatrix := make([]map[int]int64, couples.PeopleNumber)
  97. fmt.Printf("people_files:\n")
  98. for i := range peopleMatrix {
  99. peopleMatrix[i] = map[int]int64{}
  100. fmt.Printf(" %d: ", i)
  101. for file, commits := range couples.people[i] {
  102. fmt.Printf("%s, ", file)
  103. for j, otherFiles := range couples.people {
  104. if i == j {
  105. continue
  106. }
  107. otherCommits := otherFiles[file]
  108. delta := otherCommits
  109. if otherCommits > commits {
  110. delta = commits
  111. }
  112. if delta > 0 {
  113. peopleMatrix[i][j] += int64(delta)
  114. }
  115. }
  116. }
  117. peopleMatrix[i][i] = int64(couples.people_commits[i])
  118. fmt.Printf("\n")
  119. }
  120. filesSequence := make([]string, len(couples.files))
  121. i := 0
  122. for file := range couples.files {
  123. filesSequence[i] = file
  124. i++
  125. }
  126. sort.Strings(filesSequence)
  127. filesIndex := map[string]int{}
  128. for i, file := range filesSequence {
  129. filesIndex[file] = i
  130. }
  131. filesMatrix := make([]map[int]int, len(filesIndex))
  132. for i := range filesMatrix {
  133. filesMatrix[i] = map[int]int{}
  134. for otherFile, cooccs := range couples.files[filesSequence[i]] {
  135. filesMatrix[i][filesIndex[otherFile]] = cooccs
  136. }
  137. }
  138. return CouplesResult{PeopleMatrix: peopleMatrix, Files: filesSequence, FilesMatrix: filesMatrix}
  139. }