@@ -7,6 +7,7 @@ package models
7
7
import (
8
8
"bufio"
9
9
"fmt"
10
+ "sort"
10
11
"strconv"
11
12
"strings"
12
13
"time"
@@ -16,13 +17,23 @@ import (
16
17
"github.com/go-xorm/xorm"
17
18
)
18
19
20
+ // ActivityAuthorData represents statistical git commit count data
21
+ type ActivityAuthorData struct {
22
+ Name string `json:"name"`
23
+ Login string `json:"login"`
24
+ AvatarLink string `json:"avatar_link"`
25
+ Commits int64 `json:"commits"`
26
+ }
27
+
28
+ // CodeActivityStats represents git statistics data
19
29
type CodeActivityStats struct {
20
30
AuthorCount int64
21
31
CommitCount int64
22
32
ChangedFiles int64
23
33
Additions int64
24
34
Deletions int64
25
35
CommitCountInAllBranches int64
36
+ Authors map [string ]int64
26
37
}
27
38
28
39
// ActivityStats represets issue and pull request information.
@@ -63,13 +74,22 @@ func GetActivityStats(repo *Repository, timeFrom time.Time, releases, issues, pr
63
74
return nil , fmt .Errorf ("FillUnresolvedIssues: %v" , err )
64
75
}
65
76
if code {
66
- if err := stats .Code .FillFromGit (repo , timeFrom ); err != nil {
77
+ if err := stats .Code .FillFromGit (repo , timeFrom , false ); err != nil {
67
78
return nil , fmt .Errorf ("FillFromGit: %v" , err )
68
79
}
69
80
}
70
81
return stats , nil
71
82
}
72
83
84
+ // GetActivityStatsAuthors returns stats for git commits for all branches
85
+ func GetActivityStatsAuthors (repo * Repository , timeFrom time.Time ) (* CodeActivityStats , error ) {
86
+ code := & CodeActivityStats {}
87
+ if err := code .FillFromGit (repo , timeFrom , true ); err != nil {
88
+ return nil , fmt .Errorf ("FillFromGit: %v" , err )
89
+ }
90
+ return code , nil
91
+ }
92
+
73
93
// ActivePRCount returns total active pull request count
74
94
func (stats * ActivityStats ) ActivePRCount () int {
75
95
return stats .OpenedPRCount () + stats .MergedPRCount ()
@@ -291,81 +311,133 @@ func releasesForActivityStatement(repoID int64, fromTime time.Time) *xorm.Sessio
291
311
}
292
312
293
313
// FillFromGit returns code statistics for acitivity page
294
- func (stats * CodeActivityStats ) FillFromGit (repo * Repository , fromTime time.Time ) error {
314
+ func (stats * CodeActivityStats ) FillFromGit (repo * Repository , fromTime time.Time , allBranches bool ) error {
295
315
gitPath := repo .RepoPath ()
296
316
since := fromTime .Format (time .RFC3339 )
297
317
298
- if stdout , stderr , err := process .GetManager ().ExecDir (- 1 , gitPath ,
318
+ stdout , stderr , err := process .GetManager ().ExecDir (- 1 , gitPath ,
299
319
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 {
320
+ "git" , "rev-list" , "--count" , "--no-merges" , "--branches=*" , "--date=iso" , fmt .Sprintf ("--since='%s'" , since ))
321
+ if err != nil {
301
322
return fmt .Errorf ("git rev-list --count --branch [%s]: %s" , gitPath , stderr )
323
+ }
324
+
325
+ c , err := strconv .ParseInt (strings .TrimSpace (stdout ), 10 , 64 )
326
+ if err != nil {
327
+ return err
328
+ }
329
+ stats .CommitCountInAllBranches = c
330
+
331
+ args := []string {"log" , "--numstat" , "--no-merges" , "--pretty=format:---%n%h%n%an%n%ae%n" , "--date=iso" , fmt .Sprintf ("--since='%s'" , since )}
332
+ if allBranches {
333
+ args = append (args , "--branches=*" )
302
334
} else {
303
- if c , err := strconv .ParseInt (strings .TrimSpace (stdout ), 10 , 64 ); err != nil {
304
- return err
305
- } else {
306
- stats .CommitCountInAllBranches = c
307
- }
335
+ args = append (args , "--first-parent" , repo .DefaultBranch )
308
336
}
309
337
310
- if stdout , stderr , err : = process .GetManager ().ExecDir (- 1 , gitPath ,
338
+ stdout , stderr , err = process .GetManager ().ExecDir (- 1 , gitPath ,
311
339
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
- }
340
+ "git" , args ... )
341
+ if err != nil {
342
+ return fmt .Errorf ("git log --numstat [%s]: %s" , gitPath , stderr )
343
+ }
344
+
345
+ scanner := bufio .NewScanner (strings .NewReader (stdout ))
346
+ scanner .Split (bufio .ScanLines )
347
+ stats .CommitCount = 0
348
+ stats .Additions = 0
349
+ stats .Deletions = 0
350
+ authors := make (map [string ]int64 )
351
+ files := make (map [string ]bool )
352
+ p := 0
353
+ for scanner .Scan () {
354
+ l := strings .TrimSpace (scanner .Text ())
355
+ if l == "---" {
356
+ p = 1
357
+ } else if p == 0 {
358
+ continue
359
+ } else {
360
+ p ++
361
+ }
362
+ if p > 4 && len (l ) == 0 {
363
+ continue
364
+ }
365
+ switch p {
366
+ case 1 : // Seperator
367
+ case 2 : // Commit sha-1
368
+ stats .CommitCount ++
369
+ case 3 : // Author
370
+ //fmt.Println("Author: " + l)
371
+ case 4 : // E-mail
372
+ email := strings .ToLower (l )
373
+ i := authors [email ]
374
+ authors [email ] = i + 1
375
+ default : // Changed file
376
+ if parts := strings .Fields (l ); len (parts ) >= 3 {
377
+ if parts [0 ] != "-" {
378
+ if c , err := strconv .ParseInt (strings .TrimSpace (parts [0 ]), 10 , 64 ); err == nil {
379
+ stats .Additions += c
357
380
}
358
- if _ , ok := files [parts [2 ]]; ! ok {
359
- files [parts [2 ]] = true
381
+ }
382
+ if parts [1 ] != "-" {
383
+ if c , err := strconv .ParseInt (strings .TrimSpace (parts [1 ]), 10 , 64 ); err == nil {
384
+ stats .Deletions += c
360
385
}
361
- } else {
362
- fmt .Println ("err fields" )
386
+ }
387
+ if _ , ok := files [parts [2 ]]; ! ok {
388
+ files [parts [2 ]] = true
363
389
}
364
390
}
365
391
}
366
- stats .AuthorCount = int64 (len (authors ))
367
- stats .ChangedFiles = int64 (len (files ))
368
392
}
393
+ stats .AuthorCount = int64 (len (authors ))
394
+ stats .ChangedFiles = int64 (len (files ))
395
+ stats .Authors = authors
369
396
370
397
return nil
371
398
}
399
+
400
+ // GetTopAuthors get top users with most commit count based on already loaded data from git
401
+ func (stats * CodeActivityStats ) GetTopAuthors (count int ) ([]* ActivityAuthorData , error ) {
402
+ if stats .Authors == nil {
403
+ return nil , nil
404
+ }
405
+ users := make (map [int64 ]* ActivityAuthorData )
406
+ for k , v := range stats .Authors {
407
+ if len (k ) == 0 {
408
+ continue
409
+ }
410
+ u , err := GetUserByEmail (k )
411
+ if u == nil || IsErrUserNotExist (err ) {
412
+ continue
413
+ }
414
+ if err != nil {
415
+ return nil , err
416
+ }
417
+ if user , ok := users [u .ID ]; ! ok {
418
+ users [u .ID ] = & ActivityAuthorData {
419
+ Name : u .DisplayName (),
420
+ Login : u .LowerName ,
421
+ AvatarLink : u .AvatarLink (),
422
+ Commits : v ,
423
+ }
424
+ } else {
425
+ user .Commits += v
426
+ }
427
+ }
428
+ v := make ([]* ActivityAuthorData , 0 )
429
+ for _ , u := range users {
430
+ v = append (v , u )
431
+ }
432
+
433
+ sort .Slice (v [:], func (i , j int ) bool {
434
+ return v [i ].Commits < v [j ].Commits
435
+ })
436
+
437
+ cnt := count
438
+ if cnt > len (v ) {
439
+ cnt = len (v )
440
+ }
441
+
442
+ return v [:cnt ], nil
443
+ }
0 commit comments