diff options
author | Lauris BH <lauris@nix.lv> | 2019-05-04 15:39:03 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-04 15:39:03 +0300 |
commit | 1fa96629461ac4229932b0a4526fc2f60c88ec51 (patch) | |
tree | caa3906758c6998bb6450932393336ac57c3db19 /modules | |
parent | 2933ae4e88bb954af76c4e1e67c7ab1e4892e4a4 (diff) | |
download | gitea-1fa96629461ac4229932b0a4526fc2f60c88ec51.tar.gz gitea-1fa96629461ac4229932b0a4526fc2f60c88ec51.zip |
Git statistics in Activity tab (#4724)
* Initial implementation for git statistics in Activity tab
* Create top user by commit count endpoint
* Add UI and update src-d/go-git dependency
* Add coloring
* Fix typo
* Move git activity stats data extraction to git module
* Fix message
* Add git code stats test
Diffstat (limited to 'modules')
-rw-r--r-- | modules/git/repo_stats.go | 108 | ||||
-rw-r--r-- | modules/git/repo_stats_test.go | 35 |
2 files changed, 143 insertions, 0 deletions
diff --git a/modules/git/repo_stats.go b/modules/git/repo_stats.go new file mode 100644 index 0000000000..aa62e74203 --- /dev/null +++ b/modules/git/repo_stats.go @@ -0,0 +1,108 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package git + +import ( + "bufio" + "bytes" + "fmt" + "strconv" + "strings" + "time" +) + +// CodeActivityStats represents git statistics data +type CodeActivityStats struct { + AuthorCount int64 + CommitCount int64 + ChangedFiles int64 + Additions int64 + Deletions int64 + CommitCountInAllBranches int64 + Authors map[string]int64 +} + +// GetCodeActivityStats returns code statistics for acitivity page +func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string) (*CodeActivityStats, error) { + stats := &CodeActivityStats{} + + since := fromTime.Format(time.RFC3339) + + stdout, err := NewCommand("rev-list", "--count", "--no-merges", "--branches=*", "--date=iso", fmt.Sprintf("--since='%s'", since)).RunInDirBytes(repo.Path) + if err != nil { + return nil, err + } + + c, err := strconv.ParseInt(strings.TrimSpace(string(stdout)), 10, 64) + if err != nil { + return nil, err + } + stats.CommitCountInAllBranches = c + + args := []string{"log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%an%n%ae%n", "--date=iso", fmt.Sprintf("--since='%s'", since)} + if len(branch) == 0 { + args = append(args, "--branches=*") + } else { + args = append(args, "--first-parent", branch) + } + + stdout, err = NewCommand(args...).RunInDirBytes(repo.Path) + if err != nil { + return nil, err + } + + scanner := bufio.NewScanner(bytes.NewReader(stdout)) + scanner.Split(bufio.ScanLines) + stats.CommitCount = 0 + stats.Additions = 0 + stats.Deletions = 0 + authors := make(map[string]int64) + files := make(map[string]bool) + p := 0 + for scanner.Scan() { + l := strings.TrimSpace(scanner.Text()) + if l == "---" { + p = 1 + } else if p == 0 { + continue + } else { + p++ + } + if p > 4 && len(l) == 0 { + continue + } + switch p { + case 1: // Separator + case 2: // Commit sha-1 + stats.CommitCount++ + case 3: // Author + case 4: // E-mail + email := strings.ToLower(l) + i := authors[email] + authors[email] = i + 1 + default: // Changed file + if parts := strings.Fields(l); len(parts) >= 3 { + if parts[0] != "-" { + if c, err := strconv.ParseInt(strings.TrimSpace(parts[0]), 10, 64); err == nil { + stats.Additions += c + } + } + if parts[1] != "-" { + if c, err := strconv.ParseInt(strings.TrimSpace(parts[1]), 10, 64); err == nil { + stats.Deletions += c + } + } + if _, ok := files[parts[2]]; !ok { + files[parts[2]] = true + } + } + } + } + stats.AuthorCount = int64(len(authors)) + stats.ChangedFiles = int64(len(files)) + stats.Authors = authors + + return stats, nil +} diff --git a/modules/git/repo_stats_test.go b/modules/git/repo_stats_test.go new file mode 100644 index 0000000000..2e8565b9e2 --- /dev/null +++ b/modules/git/repo_stats_test.go @@ -0,0 +1,35 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package git + +import ( + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestRepository_GetCodeActivityStats(t *testing.T) { + bareRepo1Path := filepath.Join(testReposDir, "repo1_bare") + bareRepo1, err := OpenRepository(bareRepo1Path) + assert.NoError(t, err) + + timeFrom, err := time.Parse(time.RFC3339, "2016-01-01T00:00:00+00:00") + + code, err := bareRepo1.GetCodeActivityStats(timeFrom, "") + assert.NoError(t, err) + assert.NotNil(t, code) + + assert.EqualValues(t, 8, code.CommitCount) + assert.EqualValues(t, 2, code.AuthorCount) + assert.EqualValues(t, 8, code.CommitCountInAllBranches) + assert.EqualValues(t, 10, code.Additions) + assert.EqualValues(t, 1, code.Deletions) + assert.Len(t, code.Authors, 2) + assert.Contains(t, code.Authors, "tris.git@shoddynet.org") + assert.EqualValues(t, 3, code.Authors["tris.git@shoddynet.org"]) + assert.EqualValues(t, 5, code.Authors[""]) +} |