Skip to content

Multiple GitGraph improvements: Exclude PR heads, Add branch/PR links, Show only certain branches, #12766

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
64cd026
Multiple GitGraph improvements.
zeripath Sep 7, 2020
7783e8c
Merge remote-tracking branch 'origin/master' into fix-10327-commit-gr…
zeripath Oct 2, 2020
03ba299
as per @silverwind
zeripath Oct 2, 2020
fa66961
as per @silverwind
zeripath Oct 2, 2020
6a230d0
Only show refs in dropdown we display on the graph
zeripath Oct 2, 2020
9405925
as per @silverwind
zeripath Oct 2, 2020
efe57d2
Merge remote-tracking branch 'origin/master' into fix-10327-commit-gr…
zeripath Oct 2, 2020
8616c28
use flexbox for ui header
zeripath Oct 2, 2020
e8eeed0
Merge branch 'master' into fix-10327-commit-graph-options
zeripath Oct 14, 2020
3bf2cc6
Merge branch 'master' into fix-10327-commit-graph-options
zeripath Oct 16, 2020
aec4fd5
Merge remote-tracking branch 'origin/master' into fix-10327-commit-gr…
zeripath Oct 18, 2020
c345ac6
Move Hide Pull Request button to the dropdown
zeripath Oct 18, 2020
b61d07e
Add SHA and user pictures
zeripath Oct 18, 2020
eedb406
fix test
zeripath Oct 18, 2020
808a2ef
Merge remote-tracking branch 'origin/master' into fix-10327-commit-gr…
zeripath Oct 20, 2020
2489fa3
fix test 2
zeripath Oct 20, 2020
9b720f1
Merge branch 'master' into fix-10327-commit-graph-options
zeripath Oct 21, 2020
36863be
fixes
silverwind Oct 22, 2020
8ad0515
async
silverwind Oct 22, 2020
cb192c2
more tweaks
silverwind Oct 22, 2020
2c7e9bd
Merge remote-tracking branch 'origin/master' into fix-10327-commit-gr…
zeripath Oct 22, 2020
26f6b40
use tabs in tmpl
zeripath Oct 22, 2020
a5eab5f
remove commented thing
zeripath Oct 22, 2020
2203be6
Silverwind patches (#5)
zeripath Oct 22, 2020
ba1f133
fix linting
zeripath Oct 22, 2020
f886c36
Update web_src/js/features/gitgraph.js
zeripath Oct 24, 2020
aa7c35d
Merge branch 'master' into fix-10327-commit-graph-options
zeripath Oct 24, 2020
4fe9aaf
Merge branch 'master' into fix-10327-commit-graph-options
zeripath Oct 27, 2020
c2ce5dc
graph tweaks
silverwind Oct 27, 2020
f633478
more tweaks
silverwind Oct 27, 2020
b8d141d
Merge pull request #6 from silverwind/fix-10327-commit-graph-options
zeripath Oct 27, 2020
fa27ccc
add title
zeripath Oct 27, 2020
a44ac28
fix loading indicator z-index and position
silverwind Oct 27, 2020
9266edc
Merge pull request #7 from silverwind/fix-10327-commit-graph-options
zeripath Oct 27, 2020
ca4dea8
Merge branch 'master' into fix-10327-commit-graph-options
zeripath Oct 30, 2020
4eb95ec
Merge remote-tracking branch 'origin/master' into fix-10327-commit-gr…
zeripath Nov 2, 2020
e36c3a7
Merge branch 'master' into fix-10327-commit-graph-options
techknowlogick Nov 2, 2020
9fdd600
Merge branch 'master' into fix-10327-commit-graph-options
lafriks Nov 7, 2020
252c38d
Merge branch 'master' into fix-10327-commit-graph-options
zeripath Nov 8, 2020
e14d7eb
Merge branch 'master' into fix-10327-commit-graph-options
zeripath Nov 8, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions modules/context/pagination.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, "&"))
Expand Down
12 changes: 12 additions & 0 deletions modules/context/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
22 changes: 17 additions & 5 deletions modules/git/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,16 +262,28 @@ 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
}

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 {
Expand All @@ -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.
Expand Down
43 changes: 43 additions & 0 deletions modules/git/ref.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

package git

import "strings"

// Reference represents a Git ref.
type Reference struct {
Name string
Expand All @@ -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 ""
}
2 changes: 1 addition & 1 deletion modules/git/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
6 changes: 3 additions & 3 deletions modules/git/repo_commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
35 changes: 27 additions & 8 deletions modules/gitgraph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
106 changes: 85 additions & 21 deletions modules/gitgraph/graph_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions modules/gitgraph/graph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand All @@ -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|[email protected]|4e61bac|Add route for graph"
testString := "* DATA:|4e61bacab44e9b4730e44a6615d04098dd3a8eaf|2016-12-20 21:10:41 +0100|4e61bac|Add route for graph"

parser := &Parser{}
parser.Reset()
Expand All @@ -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")
}
}
Expand Down Expand Up @@ -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|[email protected]|4e61bac|"
dataFirstPart := "* DATA:|4e61bacab44e9b4730e44a6615d04098dd3a8eaf|2016-12-20 21:10:41 +0100|4e61bac|"
tests := []struct {
shouldPass bool
testName string
Expand Down
Loading