You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

108 lines
2.7 KiB

  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package git
  5. import (
  6. "bufio"
  7. "bytes"
  8. "fmt"
  9. "strconv"
  10. "strings"
  11. "time"
  12. )
  13. // CodeActivityStats represents git statistics data
  14. type CodeActivityStats struct {
  15. AuthorCount int64
  16. CommitCount int64
  17. ChangedFiles int64
  18. Additions int64
  19. Deletions int64
  20. CommitCountInAllBranches int64
  21. Authors map[string]int64
  22. }
  23. // GetCodeActivityStats returns code statistics for acitivity page
  24. func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string) (*CodeActivityStats, error) {
  25. stats := &CodeActivityStats{}
  26. since := fromTime.Format(time.RFC3339)
  27. stdout, err := NewCommand("rev-list", "--count", "--no-merges", "--branches=*", "--date=iso", fmt.Sprintf("--since='%s'", since)).RunInDirBytes(repo.Path)
  28. if err != nil {
  29. return nil, err
  30. }
  31. c, err := strconv.ParseInt(strings.TrimSpace(string(stdout)), 10, 64)
  32. if err != nil {
  33. return nil, err
  34. }
  35. stats.CommitCountInAllBranches = c
  36. args := []string{"log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%an%n%ae%n", "--date=iso", fmt.Sprintf("--since='%s'", since)}
  37. if len(branch) == 0 {
  38. args = append(args, "--branches=*")
  39. } else {
  40. args = append(args, "--first-parent", branch)
  41. }
  42. stdout, err = NewCommand(args...).RunInDirBytes(repo.Path)
  43. if err != nil {
  44. return nil, err
  45. }
  46. scanner := bufio.NewScanner(bytes.NewReader(stdout))
  47. scanner.Split(bufio.ScanLines)
  48. stats.CommitCount = 0
  49. stats.Additions = 0
  50. stats.Deletions = 0
  51. authors := make(map[string]int64)
  52. files := make(map[string]bool)
  53. p := 0
  54. for scanner.Scan() {
  55. l := strings.TrimSpace(scanner.Text())
  56. if l == "---" {
  57. p = 1
  58. } else if p == 0 {
  59. continue
  60. } else {
  61. p++
  62. }
  63. if p > 4 && len(l) == 0 {
  64. continue
  65. }
  66. switch p {
  67. case 1: // Separator
  68. case 2: // Commit sha-1
  69. stats.CommitCount++
  70. case 3: // Author
  71. case 4: // E-mail
  72. email := strings.ToLower(l)
  73. i := authors[email]
  74. authors[email] = i + 1
  75. default: // Changed file
  76. if parts := strings.Fields(l); len(parts) >= 3 {
  77. if parts[0] != "-" {
  78. if c, err := strconv.ParseInt(strings.TrimSpace(parts[0]), 10, 64); err == nil {
  79. stats.Additions += c
  80. }
  81. }
  82. if parts[1] != "-" {
  83. if c, err := strconv.ParseInt(strings.TrimSpace(parts[1]), 10, 64); err == nil {
  84. stats.Deletions += c
  85. }
  86. }
  87. if _, ok := files[parts[2]]; !ok {
  88. files[parts[2]] = true
  89. }
  90. }
  91. }
  92. }
  93. stats.AuthorCount = int64(len(authors))
  94. stats.ChangedFiles = int64(len(files))
  95. stats.Authors = authors
  96. return stats, nil
  97. }