summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorzeripath <art27@cantab.net>2020-11-08 17:21:54 +0000
committerGitHub <noreply@github.com>2020-11-08 12:21:54 -0500
commitc05a8abc762f868e67dd131d34f45218a0fb95ab (patch)
tree9aa0909c736933adc895ef830941dca85206ee07 /modules
parentd4e0b286558a68c96b0001a0676099c06067511b (diff)
downloadgitea-c05a8abc762f868e67dd131d34f45218a0fb95ab.tar.gz
gitea-c05a8abc762f868e67dd131d34f45218a0fb95ab.zip
Multiple GitGraph improvements: Exclude PR heads, Add branch/PR links, Show only certain branches, (#12766)
* Multiple GitGraph improvements. Add backend support for excluding PRs, selecting branches and files. Fix #10327 Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * Only show refs in dropdown we display on the graph Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * use flexbox for ui header Signed-off-by: Andrew Thornton <art27@cantab.net> * Move Hide Pull Request button to the dropdown Signed-off-by: Andrew Thornton <art27@cantab.net> * Add SHA and user pictures Signed-off-by: Andrew Thornton <art27@cantab.net> * fix test Signed-off-by: Andrew Thornton <art27@cantab.net> * fix test 2 Signed-off-by: Andrew Thornton <art27@cantab.net> * fixes * async * more tweaks * use tabs in tmpl Signed-off-by: Andrew Thornton <art27@cantab.net> * remove commented thing Signed-off-by: Andrew Thornton <art27@cantab.net> * fix linting Signed-off-by: Andrew Thornton <art27@cantab.net> * Update web_src/js/features/gitgraph.js Co-authored-by: silverwind <me@silverwind.io> * graph tweaks * more tweaks * add title Signed-off-by: Andrew Thornton <art27@cantab.net> * fix loading indicator z-index and position Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: Lauris BH <lauris@nix.lv>
Diffstat (limited to 'modules')
-rw-r--r--modules/context/pagination.go6
-rw-r--r--modules/context/repo.go12
-rw-r--r--modules/git/commit.go22
-rw-r--r--modules/git/ref.go43
-rw-r--r--modules/git/repo.go2
-rw-r--r--modules/git/repo_commit.go6
-rw-r--r--modules/gitgraph/graph.go35
-rw-r--r--modules/gitgraph/graph_models.go106
-rw-r--r--modules/gitgraph/graph_test.go8
-rw-r--r--modules/templates/helper.go21
10 files changed, 219 insertions, 42 deletions
diff --git a/modules/context/pagination.go b/modules/context/pagination.go
index 9a6ad0b5c4..a6638f4086 100644
--- a/modules/context/pagination.go
+++ b/modules/context/pagination.go
@@ -37,6 +37,12 @@ func (p *Pagination) AddParam(ctx *Context, paramKey string, ctxKey string) {
p.urlParams = append(p.urlParams, urlParam)
}
+// AddParamString adds a string parameter directly
+func (p *Pagination) AddParamString(key string, value string) {
+ urlParam := fmt.Sprintf("%s=%v", url.QueryEscape(key), url.QueryEscape(value))
+ p.urlParams = append(p.urlParams, urlParam)
+}
+
// GetParams returns the configured URL params
func (p *Pagination) GetParams() template.URL {
return template.URL(strings.Join(p.urlParams, "&"))
diff --git a/modules/context/repo.go b/modules/context/repo.go
index f34b05d1d0..0ef644b522 100644
--- a/modules/context/repo.go
+++ b/modules/context/repo.go
@@ -171,6 +171,18 @@ func (r *Repository) GetCommitsCount() (int64, error) {
})
}
+// GetCommitGraphsCount returns cached commit count for current view
+func (r *Repository) GetCommitGraphsCount(hidePRRefs bool, branches []string, files []string) (int64, error) {
+ cacheKey := fmt.Sprintf("commits-count-%d-graph-%t-%s-%s", r.Repository.ID, hidePRRefs, branches, files)
+
+ return cache.GetInt64(cacheKey, func() (int64, error) {
+ if len(branches) == 0 {
+ return git.AllCommitsCount(r.Repository.RepoPath(), hidePRRefs, files...)
+ }
+ return git.CommitsCountFiles(r.Repository.RepoPath(), branches, files)
+ })
+}
+
// BranchNameSubURL sub-URL for the BranchName field
func (r *Repository) BranchNameSubURL() string {
switch {
diff --git a/modules/git/commit.go b/modules/git/commit.go
index 87278af9c7..6425345ea8 100644
--- a/modules/git/commit.go
+++ b/modules/git/commit.go
@@ -262,8 +262,19 @@ func CommitChangesWithArgs(repoPath string, args []string, opts CommitChangesOpt
}
// AllCommitsCount returns count of all commits in repository
-func AllCommitsCount(repoPath string) (int64, error) {
- stdout, err := NewCommand("rev-list", "--all", "--count").RunInDir(repoPath)
+func AllCommitsCount(repoPath string, hidePRRefs bool, files ...string) (int64, error) {
+ args := []string{"--all", "--count"}
+ if hidePRRefs {
+ args = append([]string{"--exclude=refs/pull/*"}, args...)
+ }
+ cmd := NewCommand("rev-list")
+ cmd.AddArguments(args...)
+ if len(files) > 0 {
+ cmd.AddArguments("--")
+ cmd.AddArguments(files...)
+ }
+
+ stdout, err := cmd.RunInDir(repoPath)
if err != nil {
return 0, err
}
@@ -271,7 +282,8 @@ func AllCommitsCount(repoPath string) (int64, error) {
return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
}
-func commitsCount(repoPath string, revision, relpath []string) (int64, error) {
+// CommitsCountFiles returns number of total commits of until given revision.
+func CommitsCountFiles(repoPath string, revision, relpath []string) (int64, error) {
cmd := NewCommand("rev-list", "--count")
cmd.AddArguments(revision...)
if len(relpath) > 0 {
@@ -288,8 +300,8 @@ func commitsCount(repoPath string, revision, relpath []string) (int64, error) {
}
// CommitsCount returns number of total commits of until given revision.
-func CommitsCount(repoPath, revision string) (int64, error) {
- return commitsCount(repoPath, []string{revision}, []string{})
+func CommitsCount(repoPath string, revision ...string) (int64, error) {
+ return CommitsCountFiles(repoPath, revision, []string{})
}
// CommitsCount returns number of total commits of until current revision.
diff --git a/modules/git/ref.go b/modules/git/ref.go
index 67b56ac999..2a2798b18f 100644
--- a/modules/git/ref.go
+++ b/modules/git/ref.go
@@ -4,6 +4,8 @@
package git
+import "strings"
+
// Reference represents a Git ref.
type Reference struct {
Name string
@@ -16,3 +18,44 @@ type Reference struct {
func (ref *Reference) Commit() (*Commit, error) {
return ref.repo.getCommit(ref.Object)
}
+
+// ShortName returns the short name of the reference
+func (ref *Reference) ShortName() string {
+ if ref == nil {
+ return ""
+ }
+ if strings.HasPrefix(ref.Name, "refs/heads/") {
+ return ref.Name[11:]
+ }
+ if strings.HasPrefix(ref.Name, "refs/tags/") {
+ return ref.Name[10:]
+ }
+ if strings.HasPrefix(ref.Name, "refs/remotes/") {
+ return ref.Name[13:]
+ }
+ if strings.HasPrefix(ref.Name, "refs/pull/") && strings.IndexByte(ref.Name[10:], '/') > -1 {
+ return ref.Name[10 : strings.IndexByte(ref.Name[10:], '/')+10]
+ }
+
+ return ref.Name
+}
+
+// RefGroup returns the group type of the reference
+func (ref *Reference) RefGroup() string {
+ if ref == nil {
+ return ""
+ }
+ if strings.HasPrefix(ref.Name, "refs/heads/") {
+ return "heads"
+ }
+ if strings.HasPrefix(ref.Name, "refs/tags/") {
+ return "tags"
+ }
+ if strings.HasPrefix(ref.Name, "refs/remotes/") {
+ return "remotes"
+ }
+ if strings.HasPrefix(ref.Name, "refs/pull/") && strings.IndexByte(ref.Name[10:], '/') > -1 {
+ return "pull"
+ }
+ return ""
+}
diff --git a/modules/git/repo.go b/modules/git/repo.go
index 644ff09284..ae370d3da9 100644
--- a/modules/git/repo.go
+++ b/modules/git/repo.go
@@ -49,7 +49,7 @@ const prettyLogFormat = `--pretty=format:%H`
// GetAllCommitsCount returns count of all commits in repository
func (repo *Repository) GetAllCommitsCount() (int64, error) {
- return AllCommitsCount(repo.Path)
+ return AllCommitsCount(repo.Path, false)
}
func (repo *Repository) parsePrettyFormatLogToList(logs []byte) (*list.List, error) {
diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go
index 1f123c97fb..ee3b05447b 100644
--- a/modules/git/repo_commit.go
+++ b/modules/git/repo_commit.go
@@ -318,7 +318,7 @@ func (repo *Repository) FileChangedBetweenCommits(filename, id1, id2 string) (bo
// FileCommitsCount return the number of files at a revison
func (repo *Repository) FileCommitsCount(revision, file string) (int64, error) {
- return commitsCount(repo.Path, []string{revision}, []string{file})
+ return CommitsCountFiles(repo.Path, []string{revision}, []string{file})
}
// CommitsByFileAndRange return the commits according revison file and the page
@@ -413,11 +413,11 @@ func (repo *Repository) CommitsBetweenIDs(last, before string) (*list.List, erro
// CommitsCountBetween return numbers of commits between two commits
func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
- count, err := commitsCount(repo.Path, []string{start + "..." + end}, []string{})
+ count, err := CommitsCountFiles(repo.Path, []string{start + "..." + end}, []string{})
if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list before last so let's try that...
- return commitsCount(repo.Path, []string{start, end}, []string{})
+ return CommitsCountFiles(repo.Path, []string{start, end}, []string{})
}
return count, err
diff --git a/modules/gitgraph/graph.go b/modules/gitgraph/graph.go
index 257e4f3af0..8505678639 100644
--- a/modules/gitgraph/graph.go
+++ b/modules/gitgraph/graph.go
@@ -17,23 +17,42 @@ import (
)
// GetCommitGraph return a list of commit (GraphItems) from all branches
-func GetCommitGraph(r *git.Repository, page int, maxAllowedColors int) (*Graph, error) {
- format := "DATA:%d|%H|%ad|%an|%ae|%h|%s"
+func GetCommitGraph(r *git.Repository, page int, maxAllowedColors int, hidePRRefs bool, branches, files []string) (*Graph, error) {
+ format := "DATA:%D|%H|%ad|%h|%s"
if page == 0 {
page = 1
}
- graphCmd := git.NewCommand("log")
- graphCmd.AddArguments("--graph",
- "--date-order",
- "--all",
+ args := make([]string, 0, 12+len(branches)+len(files))
+
+ args = append(args, "--graph", "--date-order", "--decorate=full")
+
+ if hidePRRefs {
+ args = append(args, "--exclude=refs/pull/*")
+ }
+
+ if len(branches) == 0 {
+ args = append(args, "--all")
+ }
+
+ args = append(args,
"-C",
"-M",
fmt.Sprintf("-n %d", setting.UI.GraphMaxCommitNum*page),
"--date=iso",
- fmt.Sprintf("--pretty=format:%s", format),
- )
+ fmt.Sprintf("--pretty=format:%s", format))
+
+ if len(branches) > 0 {
+ args = append(args, branches...)
+ }
+ args = append(args, "--")
+ if len(files) > 0 {
+ args = append(args, files...)
+ }
+
+ graphCmd := git.NewCommand("log")
+ graphCmd.AddArguments(args...)
graph := NewGraph()
stderr := new(strings.Builder)
diff --git a/modules/gitgraph/graph_models.go b/modules/gitgraph/graph_models.go
index ea6ba96084..ba168ab19d 100644
--- a/modules/gitgraph/graph_models.go
+++ b/modules/gitgraph/graph_models.go
@@ -7,6 +7,10 @@ package gitgraph
import (
"bytes"
"fmt"
+
+ "code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/log"
)
// NewGraph creates a basic graph
@@ -77,6 +81,48 @@ func (graph *Graph) AddCommit(row, column int, flowID int64, data []byte) error
return nil
}
+// LoadAndProcessCommits will load the git.Commits for each commit in the graph,
+// the associate the commit with the user author, and check the commit verification
+// before finally retrieving the latest status
+func (graph *Graph) LoadAndProcessCommits(repository *models.Repository, gitRepo *git.Repository) error {
+ var err error
+
+ var ok bool
+
+ emails := map[string]*models.User{}
+ keyMap := map[string]bool{}
+
+ for _, c := range graph.Commits {
+ if len(c.Rev) == 0 {
+ continue
+ }
+ c.Commit, err = gitRepo.GetCommit(c.Rev)
+ if err != nil {
+ return fmt.Errorf("GetCommit: %s Error: %w", c.Rev, err)
+ }
+
+ if c.Commit.Author != nil {
+ email := c.Commit.Author.Email
+ if c.User, ok = emails[email]; !ok {
+ c.User, _ = models.GetUserByEmail(email)
+ emails[email] = c.User
+ }
+ }
+
+ c.Verification = models.ParseCommitWithSignature(c.Commit)
+
+ _ = models.CalculateTrustStatus(c.Verification, repository, &keyMap)
+
+ statuses, err := models.GetLatestCommitStatus(repository, c.Commit.ID.String(), 0)
+ if err != nil {
+ log.Error("GetLatestCommitStatus: %v", err)
+ } else {
+ c.Status = models.CalcCommitStatus(statuses)
+ }
+ }
+ return nil
+}
+
// NewFlow creates a new flow
func NewFlow(flowID int64, color, row, column int) *Flow {
return &Flow{
@@ -142,42 +188,60 @@ var RelationCommit = &Commit{
// NewCommit creates a new commit from a provided line
func NewCommit(row, column int, line []byte) (*Commit, error) {
- data := bytes.SplitN(line, []byte("|"), 7)
- if len(data) < 7 {
+ data := bytes.SplitN(line, []byte("|"), 5)
+ if len(data) < 5 {
return nil, fmt.Errorf("malformed data section on line %d with commit: %s", row, string(line))
}
return &Commit{
Row: row,
Column: column,
// 0 matches git log --pretty=format:%d => ref names, like the --decorate option of git-log(1)
- Branch: string(data[0]),
+ Refs: newRefsFromRefNames(data[0]),
// 1 matches git log --pretty=format:%H => commit hash
Rev: string(data[1]),
// 2 matches git log --pretty=format:%ad => author date (format respects --date= option)
Date: string(data[2]),
- // 3 matches git log --pretty=format:%an => author name
- Author: string(data[3]),
- // 4 matches git log --pretty=format:%ae => author email
- AuthorEmail: string(data[4]),
- // 5 matches git log --pretty=format:%h => abbreviated commit hash
- ShortRev: string(data[5]),
- // 6 matches git log --pretty=format:%s => subject
- Subject: string(data[6]),
+ // 3 matches git log --pretty=format:%h => abbreviated commit hash
+ ShortRev: string(data[3]),
+ // 4 matches git log --pretty=format:%s => subject
+ Subject: string(data[4]),
}, nil
}
+func newRefsFromRefNames(refNames []byte) []git.Reference {
+ refBytes := bytes.Split(refNames, []byte{',', ' '})
+ refs := make([]git.Reference, 0, len(refBytes))
+ for _, refNameBytes := range refBytes {
+ if len(refNameBytes) == 0 {
+ continue
+ }
+ refName := string(refNameBytes)
+ if refName[0:5] == "tag: " {
+ refName = refName[5:]
+ } else if refName[0:8] == "HEAD -> " {
+ refName = refName[8:]
+ }
+ refs = append(refs, git.Reference{
+ Name: refName,
+ })
+ }
+ return refs
+}
+
// Commit represents a commit at co-ordinate X, Y with the data
type Commit struct {
- Flow int64
- Row int
- Column int
- Branch string
- Rev string
- Date string
- Author string
- AuthorEmail string
- ShortRev string
- Subject string
+ Commit *git.Commit
+ User *models.User
+ Verification *models.CommitVerification
+ Status *models.CommitStatus
+ Flow int64
+ Row int
+ Column int
+ Refs []git.Reference
+ Rev string
+ Date string
+ ShortRev string
+ Subject string
}
// OnlyRelation returns whether this a relation only commit
diff --git a/modules/gitgraph/graph_test.go b/modules/gitgraph/graph_test.go
index ca9d653cee..c2726a731a 100644
--- a/modules/gitgraph/graph_test.go
+++ b/modules/gitgraph/graph_test.go
@@ -22,7 +22,7 @@ func BenchmarkGetCommitGraph(b *testing.B) {
defer currentRepo.Close()
for i := 0; i < b.N; i++ {
- graph, err := GetCommitGraph(currentRepo, 1, 0)
+ graph, err := GetCommitGraph(currentRepo, 1, 0, false, nil, nil)
if err != nil {
b.Error("Could get commit graph")
}
@@ -34,7 +34,7 @@ func BenchmarkGetCommitGraph(b *testing.B) {
}
func BenchmarkParseCommitString(b *testing.B) {
- testString := "* DATA:|4e61bacab44e9b4730e44a6615d04098dd3a8eaf|2016-12-20 21:10:41 +0100|Kjell Kvinge|kjell@kvinge.biz|4e61bac|Add route for graph"
+ testString := "* DATA:|4e61bacab44e9b4730e44a6615d04098dd3a8eaf|2016-12-20 21:10:41 +0100|4e61bac|Add route for graph"
parser := &Parser{}
parser.Reset()
@@ -44,7 +44,7 @@ func BenchmarkParseCommitString(b *testing.B) {
if err := parser.AddLineToGraph(graph, 0, []byte(testString)); err != nil {
b.Error("could not parse teststring")
}
- if graph.Flows[1].Commits[0].Author != "Kjell Kvinge" {
+ if graph.Flows[1].Commits[0].Rev != "4e61bacab44e9b4730e44a6615d04098dd3a8eaf" {
b.Error("Did not get expected data")
}
}
@@ -244,7 +244,7 @@ func TestParseGlyphs(t *testing.T) {
}
func TestCommitStringParsing(t *testing.T) {
- dataFirstPart := "* DATA:|4e61bacab44e9b4730e44a6615d04098dd3a8eaf|2016-12-20 21:10:41 +0100|Author|user@mail.something|4e61bac|"
+ dataFirstPart := "* DATA:|4e61bacab44e9b4730e44a6615d04098dd3a8eaf|2016-12-20 21:10:41 +0100|4e61bac|"
tests := []struct {
shouldPass bool
testName string
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 03ec80f99c..e4107dfa9a 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -16,6 +16,7 @@ import (
"mime"
"net/url"
"path/filepath"
+ "reflect"
"regexp"
"runtime"
"strings"
@@ -310,6 +311,26 @@ func NewFuncMap() []template.FuncMap {
"EventSourceUpdateTime": int(setting.UI.Notification.EventSourceUpdateTime / time.Millisecond),
}
},
+ "containGeneric": func(arr interface{}, v interface{}) bool {
+ arrV := reflect.ValueOf(arr)
+ if arrV.Kind() == reflect.String && reflect.ValueOf(v).Kind() == reflect.String {
+ return strings.Contains(arr.(string), v.(string))
+ }
+
+ if arrV.Kind() == reflect.Slice {
+ for i := 0; i < arrV.Len(); i++ {
+ iV := arrV.Index(i)
+ if !iV.CanInterface() {
+ continue
+ }
+ if iV.Interface() == v {
+ return true
+ }
+ }
+ }
+
+ return false
+ },
"contain": func(s []int64, id int64) bool {
for i := 0; i < len(s); i++ {
if s[i] == id {