forgejo/modules/gitgraph/graph.go
zeripath 2c1ae6c82d
Render the git graph on the server (#12333)
Rendering the git graph on the server means that we can properly track flows and switch from the Canvas implementation to a SVG implementation.

* This implementation provides a 16 limited color selection
* The uniqued color numbers are also provided
* And there is also a monochrome version
*In addition is a hover highlight that allows users to highlight commits on the same flow.

Closes #12209

Signed-off-by: Andrew Thornton art27@cantab.net
Co-authored-by: silverwind <me@silverwind.io>
2020-08-06 09:04:08 +01:00

104 lines
2.4 KiB
Go

// Copyright 2016 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 gitgraph
import (
"bufio"
"bytes"
"context"
"fmt"
"os"
"strings"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
)
// 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"
if page == 0 {
page = 1
}
graphCmd := git.NewCommand("log")
graphCmd.AddArguments("--graph",
"--date-order",
"--all",
"-C",
"-M",
fmt.Sprintf("-n %d", setting.UI.GraphMaxCommitNum*page),
"--date=iso",
fmt.Sprintf("--pretty=format:%s", format),
)
graph := NewGraph()
stderr := new(strings.Builder)
stdoutReader, stdoutWriter, err := os.Pipe()
if err != nil {
return nil, err
}
commitsToSkip := setting.UI.GraphMaxCommitNum * (page - 1)
scanner := bufio.NewScanner(stdoutReader)
if err := graphCmd.RunInDirTimeoutEnvFullPipelineFunc(nil, -1, r.Path, stdoutWriter, stderr, nil, func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close()
defer stdoutReader.Close()
parser := &Parser{}
parser.firstInUse = -1
parser.maxAllowedColors = maxAllowedColors
if maxAllowedColors > 0 {
parser.availableColors = make([]int, maxAllowedColors)
for i := range parser.availableColors {
parser.availableColors[i] = i + 1
}
} else {
parser.availableColors = []int{1, 2}
}
for commitsToSkip > 0 && scanner.Scan() {
line := scanner.Bytes()
dataIdx := bytes.Index(line, []byte("DATA:"))
if dataIdx < 0 {
dataIdx = len(line)
}
starIdx := bytes.IndexByte(line, '*')
if starIdx >= 0 && starIdx < dataIdx {
commitsToSkip--
}
parser.ParseGlyphs(line[:dataIdx])
}
row := 0
// Skip initial non-commit lines
for scanner.Scan() {
line := scanner.Bytes()
if bytes.IndexByte(line, '*') >= 0 {
if err := parser.AddLineToGraph(graph, row, line); err != nil {
cancel()
return err
}
break
}
parser.ParseGlyphs(line)
}
for scanner.Scan() {
row++
line := scanner.Bytes()
if err := parser.AddLineToGraph(graph, row, line); err != nil {
cancel()
return err
}
}
return scanner.Err()
}); err != nil {
return graph, err
}
return graph, nil
}