Merge branch 'master'

This commit is contained in:
Nicolas Gourdon
2019-05-08 23:57:57 +02:00
parent c097a29d69
commit 3a3ccf801b
326 changed files with 37528 additions and 829 deletions

View File

@@ -124,7 +124,6 @@ func getFileHashes(c *object.Commit, treePath string, paths []string) (map[strin
func getLastCommitForPaths(c *object.Commit, treePath string, paths []string) (map[string]*object.Commit, error) {
// We do a tree traversal with nodes sorted by commit time
seen := make(map[plumbing.Hash]bool)
heap := binaryheap.NewWith(func(a, b interface{}) int {
if a.(*commitAndPaths).commit.Committer.When.Before(b.(*commitAndPaths).commit.Committer.When) {
return 1
@@ -202,15 +201,10 @@ func getLastCommitForPaths(c *object.Commit, treePath string, paths []string) (m
// Add the parent nodes along with remaining paths to the heap for further
// processing.
for j, parent := range parents {
if seen[parent.ID()] {
continue
}
seen[parent.ID()] = true
// Combine remainingPath with paths available on the parent branch
// and make union of them
var remainingPathsForParent []string
var newRemainingPaths []string
remainingPathsForParent := make([]string, 0, len(remainingPaths))
newRemainingPaths := make([]string, 0, len(remainingPaths))
for _, path := range remainingPaths {
if parentHashes[j][path] == current.hashes[path] {
remainingPathsForParent = append(remainingPathsForParent, path)

View File

@@ -9,9 +9,11 @@ import (
"bytes"
"container/list"
"errors"
"fmt"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"time"
@@ -306,3 +308,40 @@ func GetLatestCommitTime(repoPath string) (time.Time, error) {
commitTime := strings.TrimSpace(stdout)
return time.Parse(GitTimeLayout, commitTime)
}
// DivergeObject represents commit count diverging commits
type DivergeObject struct {
Ahead int
Behind int
}
func checkDivergence(repoPath string, baseBranch string, targetBranch string) (int, error) {
branches := fmt.Sprintf("%s..%s", baseBranch, targetBranch)
cmd := NewCommand("rev-list", "--count", branches)
stdout, err := cmd.RunInDir(repoPath)
if err != nil {
return -1, err
}
outInteger, errInteger := strconv.Atoi(strings.Trim(stdout, "\n"))
if errInteger != nil {
return -1, errInteger
}
return outInteger, nil
}
// GetDivergingCommits returns the number of commits a targetBranch is ahead or behind a baseBranch
func GetDivergingCommits(repoPath string, baseBranch string, targetBranch string) (DivergeObject, error) {
// $(git rev-list --count master..feature) commits ahead of master
ahead, errorAhead := checkDivergence(repoPath, baseBranch, targetBranch)
if errorAhead != nil {
return DivergeObject{}, errorAhead
}
// $(git rev-list --count feature..master) commits behind master
behind, errorBehind := checkDivergence(repoPath, targetBranch, baseBranch)
if errorBehind != nil {
return DivergeObject{}, errorBehind
}
return DivergeObject{ahead, behind}, nil
}

108
modules/git/repo_stats.go Normal file
View File

@@ -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
}

View File

@@ -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[""])
}

View File

@@ -35,14 +35,15 @@ func (repo *Repository) GetTree(idStr string) (*Tree, error) {
if err != nil {
return nil, err
}
resolvedID := id
commitObject, err := repo.gogitRepo.CommitObject(plumbing.Hash(id))
if err == nil {
id = SHA1(commitObject.TreeHash)
}
treeObject, err := repo.getTree(id)
if err != nil {
return nil, err
}
treeObject, err := repo.getTree(SHA1(commitObject.TreeHash))
if err != nil {
return nil, err
}
treeObject.CommitID = id
treeObject.ResolvedID = resolvedID
return treeObject, nil
}

View File

@@ -15,9 +15,9 @@ import (
// Tree represents a flat directory listing.
type Tree struct {
ID SHA1
CommitID SHA1
repo *Repository
ID SHA1
ResolvedID SHA1
repo *Repository
gogitTree *object.Tree
@@ -106,7 +106,7 @@ func (t *Tree) ListEntriesRecursive() (Entries, error) {
seen := map[plumbing.Hash]bool{}
walker := object.NewTreeWalker(t.gogitTree, true, seen)
for {
_, entry, err := walker.Next()
fullName, entry, err := walker.Next()
if err == io.EOF {
break
}
@@ -121,6 +121,7 @@ func (t *Tree) ListEntriesRecursive() (Entries, error) {
ID: entry.Hash,
gogitTreeEntry: &entry,
ptree: t,
fullName: fullName,
}
entries = append(entries, convertedEntry)
}

View File

@@ -40,12 +40,16 @@ type TreeEntry struct {
gogitTreeEntry *object.TreeEntry
ptree *Tree
size int64
sized bool
size int64
sized bool
fullName string
}
// Name returns the name of the entry
func (te *TreeEntry) Name() string {
if te.fullName != "" {
return te.fullName
}
return te.gogitTreeEntry.Name
}