couples.go 3.8 KB

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