5
5
package models
6
6
7
7
import (
8
+ "bufio"
8
9
"fmt"
10
+ "strconv"
11
+ "strings"
9
12
"time"
10
13
14
+ "code.gitea.io/gitea/modules/process"
15
+
11
16
"github.com/go-xorm/xorm"
12
17
)
13
18
19
+ type CodeActivityStats struct {
20
+ AuthorCount int64
21
+ CommitCount int64
22
+ ChangedFiles int64
23
+ Additions int64
24
+ Deletions int64
25
+ CommitCountInAllBranches int64
26
+ }
27
+
14
28
// ActivityStats represets issue and pull request information.
15
29
type ActivityStats struct {
16
30
OpenedPRs PullRequestList
@@ -24,29 +38,35 @@ type ActivityStats struct {
24
38
UnresolvedIssues IssueList
25
39
PublishedReleases []* Release
26
40
PublishedReleaseAuthorCount int64
41
+ Code * CodeActivityStats
27
42
}
28
43
29
44
// GetActivityStats return stats for repository at given time range
30
- func GetActivityStats (repoID int64 , timeFrom time.Time , releases , issues , prs bool ) (* ActivityStats , error ) {
31
- stats := & ActivityStats {}
45
+ func GetActivityStats (repo * Repository , timeFrom time.Time , releases , issues , prs , code bool ) (* ActivityStats , error ) {
46
+ stats := & ActivityStats {Code : & CodeActivityStats {} }
32
47
if releases {
33
- if err := stats .FillReleases (repoID , timeFrom ); err != nil {
48
+ if err := stats .FillReleases (repo . ID , timeFrom ); err != nil {
34
49
return nil , fmt .Errorf ("FillReleases: %v" , err )
35
50
}
36
51
}
37
52
if prs {
38
- if err := stats .FillPullRequests (repoID , timeFrom ); err != nil {
53
+ if err := stats .FillPullRequests (repo . ID , timeFrom ); err != nil {
39
54
return nil , fmt .Errorf ("FillPullRequests: %v" , err )
40
55
}
41
56
}
42
57
if issues {
43
- if err := stats .FillIssues (repoID , timeFrom ); err != nil {
58
+ if err := stats .FillIssues (repo . ID , timeFrom ); err != nil {
44
59
return nil , fmt .Errorf ("FillIssues: %v" , err )
45
60
}
46
61
}
47
- if err := stats .FillUnresolvedIssues (repoID , timeFrom , issues , prs ); err != nil {
62
+ if err := stats .FillUnresolvedIssues (repo . ID , timeFrom , issues , prs ); err != nil {
48
63
return nil , fmt .Errorf ("FillUnresolvedIssues: %v" , err )
49
64
}
65
+ if code {
66
+ if err := stats .Code .FillFromGit (repo , timeFrom ); err != nil {
67
+ return nil , fmt .Errorf ("FillFromGit: %v" , err )
68
+ }
69
+ }
50
70
return stats , nil
51
71
}
52
72
@@ -269,3 +289,83 @@ func releasesForActivityStatement(repoID int64, fromTime time.Time) *xorm.Sessio
269
289
And ("release.is_draft = ?" , false ).
270
290
And ("release.created_unix >= ?" , fromTime .Unix ())
271
291
}
292
+
293
+ // FillFromGit returns code statistics for acitivity page
294
+ func (stats * CodeActivityStats ) FillFromGit (repo * Repository , fromTime time.Time ) error {
295
+ gitPath := repo .RepoPath ()
296
+ since := fromTime .Format (time .RFC3339 )
297
+
298
+ if stdout , stderr , err := process .GetManager ().ExecDir (- 1 , gitPath ,
299
+ fmt .Sprintf ("FillFromGit.RevList (git rev-list): %s" , gitPath ),
300
+ "git" , "rev-list" , "--count" , "--no-merges" , "--branches=*" , "--date=iso" , fmt .Sprintf ("--since='%s'" , since )); err != nil {
301
+ return fmt .Errorf ("git rev-list --count --branch [%s]: %s" , gitPath , stderr )
302
+ } else {
303
+ if c , err := strconv .ParseInt (strings .TrimSpace (stdout ), 10 , 64 ); err != nil {
304
+ return err
305
+ } else {
306
+ stats .CommitCountInAllBranches = c
307
+ }
308
+ }
309
+
310
+ if stdout , stderr , err := process .GetManager ().ExecDir (- 1 , gitPath ,
311
+ fmt .Sprintf ("FillFromGit.RevList (git rev-list): %s" , gitPath ),
312
+ "git" , "log" , "--numstat" , "--no-merges" , "--pretty=format:---%n%h%n%an%n%ae%n" , "--first-parent" , "--date=iso" , fmt .Sprintf ("--since='%s'" , since ), repo .DefaultBranch ); err != nil {
313
+ return fmt .Errorf ("git log --numstat --first-parent [%s -> %s]: %s" , repo .DefaultBranch , gitPath , stderr )
314
+ } else {
315
+ scanner := bufio .NewScanner (strings .NewReader (stdout ))
316
+ scanner .Split (bufio .ScanLines )
317
+ stats .CommitCount = 0
318
+ stats .Additions = 0
319
+ stats .Deletions = 0
320
+ authors := make (map [string ]int64 )
321
+ files := make (map [string ]bool )
322
+ p := 0
323
+ for scanner .Scan () {
324
+ l := strings .TrimSpace (scanner .Text ())
325
+ if l == "---" {
326
+ p = 1
327
+ } else if p == 0 {
328
+ continue
329
+ } else {
330
+ p ++
331
+ }
332
+ if p > 4 && len (l ) == 0 {
333
+ continue
334
+ }
335
+ switch p {
336
+ case 1 : // Seperator
337
+ case 2 : // Commit sha-1
338
+ stats .CommitCount ++
339
+ case 3 : // Author
340
+ //fmt.Println("Author: " + l)
341
+ case 4 : // E-mail
342
+ email := strings .ToLower (l )
343
+ i := authors [email ]
344
+ authors [email ] = i + 1
345
+ default : // Changed fileB
346
+ fmt .Println ("L:" + l )
347
+ if parts := strings .Fields (l ); len (parts ) >= 3 {
348
+ if parts [0 ] != "-" {
349
+ if c , err := strconv .ParseInt (strings .TrimSpace (parts [0 ]), 10 , 64 ); err == nil {
350
+ stats .Additions += c
351
+ }
352
+ }
353
+ if parts [1 ] != "-" {
354
+ if c , err := strconv .ParseInt (strings .TrimSpace (parts [1 ]), 10 , 64 ); err == nil {
355
+ stats .Deletions += c
356
+ }
357
+ }
358
+ if _ , ok := files [parts [2 ]]; ! ok {
359
+ files [parts [2 ]] = true
360
+ }
361
+ } else {
362
+ fmt .Println ("err fields" )
363
+ }
364
+ }
365
+ }
366
+ stats .AuthorCount = int64 (len (authors ))
367
+ stats .ChangedFiles = int64 (len (files ))
368
+ }
369
+
370
+ return nil
371
+ }
0 commit comments