aboutsummaryrefslogtreecommitdiffstats
path: root/modules/gitgraph
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gitgraph')
-rw-r--r--modules/gitgraph/graph.go116
-rw-r--r--modules/gitgraph/graph_models.go265
-rw-r--r--modules/gitgraph/graph_test.go712
-rw-r--r--modules/gitgraph/parser.go336
4 files changed, 0 insertions, 1429 deletions
diff --git a/modules/gitgraph/graph.go b/modules/gitgraph/graph.go
deleted file mode 100644
index 7e12be030f..0000000000
--- a/modules/gitgraph/graph.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2016 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package gitgraph
-
-import (
- "bufio"
- "bytes"
- "context"
- "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, maxAllowedColors int, hidePRRefs bool, branches, files []string) (*Graph, error) {
- format := "DATA:%D|%H|%ad|%h|%s"
-
- if page == 0 {
- page = 1
- }
-
- graphCmd := git.NewCommand(r.Ctx, "log", "--graph", "--date-order", "--decorate=full")
-
- if hidePRRefs {
- graphCmd.AddArguments("--exclude=" + git.PullPrefix + "*")
- }
-
- if len(branches) == 0 {
- graphCmd.AddArguments("--all")
- }
-
- graphCmd.AddArguments("-C", "-M", "--date=iso-strict").
- AddOptionFormat("-n %d", setting.UI.GraphMaxCommitNum*page).
- AddOptionFormat("--pretty=format:%s", format)
-
- if len(branches) > 0 {
- graphCmd.AddDynamicArguments(branches...)
- }
- if len(files) > 0 {
- graphCmd.AddDashesAndList(files...)
- }
- 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.Run(&git.RunOpts{
- Dir: r.Path,
- Stdout: stdoutWriter,
- Stderr: stderr,
- PipelineFunc: 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
-}
diff --git a/modules/gitgraph/graph_models.go b/modules/gitgraph/graph_models.go
deleted file mode 100644
index 191b0b3afc..0000000000
--- a/modules/gitgraph/graph_models.go
+++ /dev/null
@@ -1,265 +0,0 @@
-// Copyright 2020 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package gitgraph
-
-import (
- "bytes"
- "context"
- "fmt"
- "strings"
- "time"
-
- asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/log"
-)
-
-// NewGraph creates a basic graph
-func NewGraph() *Graph {
- graph := &Graph{}
- graph.relationCommit = &Commit{
- Row: -1,
- Column: -1,
- }
- graph.Flows = map[int64]*Flow{}
- return graph
-}
-
-// Graph represents a collection of flows
-type Graph struct {
- Flows map[int64]*Flow
- Commits []*Commit
- MinRow int
- MinColumn int
- MaxRow int
- MaxColumn int
- relationCommit *Commit
-}
-
-// Width returns the width of the graph
-func (graph *Graph) Width() int {
- return graph.MaxColumn - graph.MinColumn + 1
-}
-
-// Height returns the height of the graph
-func (graph *Graph) Height() int {
- return graph.MaxRow - graph.MinRow + 1
-}
-
-// AddGlyph adds glyph to flows
-func (graph *Graph) AddGlyph(row, column int, flowID int64, color int, glyph byte) {
- flow, ok := graph.Flows[flowID]
- if !ok {
- flow = NewFlow(flowID, color, row, column)
- graph.Flows[flowID] = flow
- }
- flow.AddGlyph(row, column, glyph)
-
- if row < graph.MinRow {
- graph.MinRow = row
- }
- if row > graph.MaxRow {
- graph.MaxRow = row
- }
- if column < graph.MinColumn {
- graph.MinColumn = column
- }
- if column > graph.MaxColumn {
- graph.MaxColumn = column
- }
-}
-
-// AddCommit adds a commit at row, column on flowID with the provided data
-func (graph *Graph) AddCommit(row, column int, flowID int64, data []byte) error {
- commit, err := NewCommit(row, column, data)
- if err != nil {
- return err
- }
- commit.Flow = flowID
- graph.Commits = append(graph.Commits, commit)
-
- graph.Flows[flowID].Commits = append(graph.Flows[flowID].Commits, commit)
- 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(ctx context.Context, repository *repo_model.Repository, gitRepo *git.Repository) error {
- var err error
- var ok bool
-
- emails := map[string]*user_model.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, _ = user_model.GetUserByEmail(ctx, email)
- emails[email] = c.User
- }
- }
-
- c.Verification = asymkey_model.ParseCommitWithSignature(ctx, c.Commit)
-
- _ = asymkey_model.CalculateTrustStatus(c.Verification, repository.GetTrustModel(), func(user *user_model.User) (bool, error) {
- return repo_model.IsOwnerMemberCollaborator(ctx, repository, user.ID)
- }, &keyMap)
-
- statuses, _, err := git_model.GetLatestCommitStatus(ctx, repository.ID, c.Commit.ID.String(), db.ListOptions{})
- if err != nil {
- log.Error("GetLatestCommitStatus: %v", err)
- } else {
- c.Status = git_model.CalcCommitStatus(statuses)
- }
- }
- return nil
-}
-
-// NewFlow creates a new flow
-func NewFlow(flowID int64, color, row, column int) *Flow {
- return &Flow{
- ID: flowID,
- ColorNumber: color,
- MinRow: row,
- MinColumn: column,
- MaxRow: row,
- MaxColumn: column,
- }
-}
-
-// Flow represents a series of glyphs
-type Flow struct {
- ID int64
- ColorNumber int
- Glyphs []Glyph
- Commits []*Commit
- MinRow int
- MinColumn int
- MaxRow int
- MaxColumn int
-}
-
-// Color16 wraps the color numbers around mod 16
-func (flow *Flow) Color16() int {
- return flow.ColorNumber % 16
-}
-
-// AddGlyph adds glyph at row and column
-func (flow *Flow) AddGlyph(row, column int, glyph byte) {
- if row < flow.MinRow {
- flow.MinRow = row
- }
- if row > flow.MaxRow {
- flow.MaxRow = row
- }
- if column < flow.MinColumn {
- flow.MinColumn = column
- }
- if column > flow.MaxColumn {
- flow.MaxColumn = column
- }
-
- flow.Glyphs = append(flow.Glyphs, Glyph{
- row,
- column,
- glyph,
- })
-}
-
-// Glyph represents a co-ordinate and glyph
-type Glyph struct {
- Row int
- Column int
- Glyph byte
-}
-
-// RelationCommit represents an empty relation commit
-var RelationCommit = &Commit{
- Row: -1,
-}
-
-func parseGitTime(timeStr string) time.Time {
- t, err := time.Parse(time.RFC3339, timeStr)
- if err != nil {
- return time.Unix(0, 0)
- }
- return t
-}
-
-// NewCommit creates a new commit from a provided line
-func NewCommit(row, column int, line []byte) (*Commit, error) {
- 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)
- 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: parseGitTime(string(data[2])),
- // 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 strings.HasPrefix(refName, "tag: ") {
- refName = strings.TrimPrefix(refName, "tag: ")
- } else {
- refName = strings.TrimPrefix(refName, "HEAD -> ")
- }
- refs = append(refs, git.Reference{
- Name: refName,
- })
- }
- return refs
-}
-
-// Commit represents a commit at co-ordinate X, Y with the data
-type Commit struct {
- Commit *git.Commit
- User *user_model.User
- Verification *asymkey_model.CommitVerification
- Status *git_model.CommitStatus
- Flow int64
- Row int
- Column int
- Refs []git.Reference
- Rev string
- Date time.Time
- ShortRev string
- Subject string
-}
-
-// OnlyRelation returns whether this a relation only commit
-func (c *Commit) OnlyRelation() bool {
- return c.Row == -1
-}
diff --git a/modules/gitgraph/graph_test.go b/modules/gitgraph/graph_test.go
deleted file mode 100644
index 2f647aaf83..0000000000
--- a/modules/gitgraph/graph_test.go
+++ /dev/null
@@ -1,712 +0,0 @@
-// Copyright 2016 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package gitgraph
-
-import (
- "bytes"
- "fmt"
- "strings"
- "testing"
-
- "code.gitea.io/gitea/modules/git"
-
- "github.com/stretchr/testify/assert"
-)
-
-func BenchmarkGetCommitGraph(b *testing.B) {
- currentRepo, err := git.OpenRepository(git.DefaultContext, ".")
- if err != nil || currentRepo == nil {
- b.Error("Could not open repository")
- }
- defer currentRepo.Close()
-
- for i := 0; i < b.N; i++ {
- graph, err := GetCommitGraph(currentRepo, 1, 0, false, nil, nil)
- if err != nil {
- b.Error("Could get commit graph")
- }
-
- if len(graph.Commits) < 100 {
- b.Error("Should get 100 log lines.")
- }
- }
-}
-
-func BenchmarkParseCommitString(b *testing.B) {
- testString := "* DATA:|4e61bacab44e9b4730e44a6615d04098dd3a8eaf|2016-12-20 21:10:41 +0100|4e61bac|Add route for graph"
-
- parser := &Parser{}
- parser.Reset()
- for i := 0; i < b.N; i++ {
- parser.Reset()
- graph := NewGraph()
- if err := parser.AddLineToGraph(graph, 0, []byte(testString)); err != nil {
- b.Error("could not parse teststring")
- }
- if graph.Flows[1].Commits[0].Rev != "4e61bacab44e9b4730e44a6615d04098dd3a8eaf" {
- b.Error("Did not get expected data")
- }
- }
-}
-
-func BenchmarkParseGlyphs(b *testing.B) {
- parser := &Parser{}
- parser.Reset()
- tgBytes := []byte(testglyphs)
- var tg []byte
- for i := 0; i < b.N; i++ {
- parser.Reset()
- tg = tgBytes
- idx := bytes.Index(tg, []byte("\n"))
- for idx > 0 {
- parser.ParseGlyphs(tg[:idx])
- tg = tg[idx+1:]
- idx = bytes.Index(tg, []byte("\n"))
- }
- }
-}
-
-func TestReleaseUnusedColors(t *testing.T) {
- testcases := []struct {
- availableColors []int
- oldColors []int
- firstInUse int // these values have to be either be correct or suggest less is
- firstAvailable int // available than possibly is - i.e. you cannot say 10 is available when it
- }{
- {
- availableColors: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
- oldColors: []int{1, 1, 1, 1, 1},
- firstAvailable: -1,
- firstInUse: 1,
- },
- {
- availableColors: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
- oldColors: []int{1, 2, 3, 4},
- firstAvailable: 6,
- firstInUse: 0,
- },
- {
- availableColors: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
- oldColors: []int{6, 0, 3, 5, 3, 4, 0, 0},
- firstAvailable: 6,
- firstInUse: 0,
- },
- {
- availableColors: []int{1, 2, 3, 4, 5, 6, 7},
- oldColors: []int{6, 1, 3, 5, 3, 4, 2, 7},
- firstAvailable: -1,
- firstInUse: 0,
- },
- {
- availableColors: []int{1, 2, 3, 4, 5, 6, 7},
- oldColors: []int{6, 0, 3, 5, 3, 4, 2, 7},
- firstAvailable: -1,
- firstInUse: 0,
- },
- }
- for _, testcase := range testcases {
- parser := &Parser{}
- parser.Reset()
- parser.availableColors = append([]int{}, testcase.availableColors...)
- parser.oldColors = append(parser.oldColors, testcase.oldColors...)
- parser.firstAvailable = testcase.firstAvailable
- parser.firstInUse = testcase.firstInUse
- parser.releaseUnusedColors()
-
- if parser.firstAvailable == -1 {
- // All in use
- for _, color := range parser.availableColors {
- found := false
- for _, oldColor := range parser.oldColors {
- if oldColor == color {
- found = true
- break
- }
- }
- if !found {
- t.Errorf("In testcase:\n%d\t%d\t%d %d =>\n%d\t%d\t%d %d: %d should be available but is not",
- testcase.availableColors,
- testcase.oldColors,
- testcase.firstAvailable,
- testcase.firstInUse,
- parser.availableColors,
- parser.oldColors,
- parser.firstAvailable,
- parser.firstInUse,
- color)
- }
- }
- } else if parser.firstInUse != -1 {
- // Some in use
- for i := parser.firstInUse; i != parser.firstAvailable; i = (i + 1) % len(parser.availableColors) {
- color := parser.availableColors[i]
- found := false
- for _, oldColor := range parser.oldColors {
- if oldColor == color {
- found = true
- break
- }
- }
- if !found {
- t.Errorf("In testcase:\n%d\t%d\t%d %d =>\n%d\t%d\t%d %d: %d should be available but is not",
- testcase.availableColors,
- testcase.oldColors,
- testcase.firstAvailable,
- testcase.firstInUse,
- parser.availableColors,
- parser.oldColors,
- parser.firstAvailable,
- parser.firstInUse,
- color)
- }
- }
- for i := parser.firstAvailable; i != parser.firstInUse; i = (i + 1) % len(parser.availableColors) {
- color := parser.availableColors[i]
- found := false
- for _, oldColor := range parser.oldColors {
- if oldColor == color {
- found = true
- break
- }
- }
- if found {
- t.Errorf("In testcase:\n%d\t%d\t%d %d =>\n%d\t%d\t%d %d: %d should not be available but is",
- testcase.availableColors,
- testcase.oldColors,
- testcase.firstAvailable,
- testcase.firstInUse,
- parser.availableColors,
- parser.oldColors,
- parser.firstAvailable,
- parser.firstInUse,
- color)
- }
- }
- } else {
- // None in use
- for _, color := range parser.oldColors {
- if color != 0 {
- t.Errorf("In testcase:\n%d\t%d\t%d %d =>\n%d\t%d\t%d %d: %d should not be available but is",
- testcase.availableColors,
- testcase.oldColors,
- testcase.firstAvailable,
- testcase.firstInUse,
- parser.availableColors,
- parser.oldColors,
- parser.firstAvailable,
- parser.firstInUse,
- color)
- }
- }
- }
- }
-}
-
-func TestParseGlyphs(t *testing.T) {
- parser := &Parser{}
- parser.Reset()
- tgBytes := []byte(testglyphs)
- tg := tgBytes
- idx := bytes.Index(tg, []byte("\n"))
- row := 0
- for idx > 0 {
- parser.ParseGlyphs(tg[:idx])
- tg = tg[idx+1:]
- idx = bytes.Index(tg, []byte("\n"))
- if parser.flows[0] != 1 {
- t.Errorf("First column flow should be 1 but was %d", parser.flows[0])
- }
- colorToFlow := map[int]int64{}
- flowToColor := map[int64]int{}
-
- for i, flow := range parser.flows {
- if flow == 0 {
- continue
- }
- color := parser.colors[i]
-
- if fColor, in := flowToColor[flow]; in && fColor != color {
- t.Errorf("Row %d column %d flow %d has color %d but should be %d", row, i, flow, color, fColor)
- }
- flowToColor[flow] = color
- if cFlow, in := colorToFlow[color]; in && cFlow != flow {
- t.Errorf("Row %d column %d flow %d has color %d but conflicts with flow %d", row, i, flow, color, cFlow)
- }
- colorToFlow[color] = flow
- }
- row++
- }
- assert.Len(t, parser.availableColors, 9)
-}
-
-func TestCommitStringParsing(t *testing.T) {
- dataFirstPart := "* DATA:|4e61bacab44e9b4730e44a6615d04098dd3a8eaf|2016-12-20 21:10:41 +0100|4e61bac|"
- tests := []struct {
- shouldPass bool
- testName string
- commitMessage string
- }{
- {true, "normal", "not a fancy message"},
- {true, "extra pipe", "An extra pipe: |"},
- {true, "extra 'Data:'", "DATA: might be trouble"},
- }
-
- for _, test := range tests {
- t.Run(test.testName, func(t *testing.T) {
- testString := fmt.Sprintf("%s%s", dataFirstPart, test.commitMessage)
- idx := strings.Index(testString, "DATA:")
- commit, err := NewCommit(0, 0, []byte(testString[idx+5:]))
- if err != nil && test.shouldPass {
- t.Errorf("Could not parse %s", testString)
- return
- }
-
- assert.Equal(t, test.commitMessage, commit.Subject)
- })
- }
-}
-
-var testglyphs = `*
-*
-*
-*
-*
-*
-*
-*
-|\
-* |
-* |
-* |
-* |
-* |
-| *
-* |
-| *
-| |\
-* | |
-| | *
-| | |\
-* | | \
-|\ \ \ \
-| * | | |
-| |\| | |
-* | | | |
-|/ / / /
-| | | *
-| * | |
-| * | |
-| * | |
-* | | |
-* | | |
-* | | |
-* | | |
-* | | |
-|\ \ \ \
-| | * | |
-| | |\| |
-| | | * |
-| | | | *
-* | | | |
-* | | | |
-* | | | |
-* | | | |
-* | | | |
-|\ \ \ \ \
-| * | | | |
-|/| | | | |
-| | |/ / /
-| |/| | |
-| | | | *
-| * | | |
-|/| | | |
-| * | | |
-|/| | | |
-| | |/ /
-| |/| |
-| * | |
-| * | |
-| |\ \ \
-| | * | |
-| |/| | |
-| | | |/
-| | |/|
-| * | |
-| * | |
-| * | |
-| | * |
-| | |\ \
-| | | * |
-| | |/| |
-| | | * |
-| | | |\ \
-| | | | * |
-| | | |/| |
-| | * | | |
-| | * | | |
-| | |\ \ \ \
-| | | * | | |
-| | |/| | | |
-| | | | | * |
-| | | | |/ /
-* | | | / /
-|/ / / / /
-* | | | |
-|\ \ \ \ \
-| * | | | |
-|/| | | | |
-| * | | | |
-| * | | | |
-| |\ \ \ \ \
-| | | * \ \ \
-| | | |\ \ \ \
-| | | | * | | |
-| | | |/| | | |
-| | | | | |/ /
-| | | | |/| |
-* | | | | | |
-* | | | | | |
-* | | | | | |
-| | | | * | |
-* | | | | | |
-| | * | | | |
-| |/| | | | |
-* | | | | | |
-| |/ / / / /
-|/| | | | |
-| | | | * |
-| | | |/ /
-| | |/| |
-| * | | |
-| | | | *
-| | * | |
-| | |\ \ \
-| | | * | |
-| | |/| | |
-| | | |/ /
-| | | * |
-| | * | |
-| | |\ \ \
-| | | * | |
-| | |/| | |
-| | | |/ /
-| | | * |
-* | | | |
-|\ \ \ \ \
-| * \ \ \ \
-| |\ \ \ \ \
-| | | |/ / /
-| | |/| | |
-| | | | * |
-| | | | * |
-* | | | | |
-* | | | | |
-|/ / / / /
-| | | * |
-* | | | |
-* | | | |
-* | | | |
-* | | | |
-|\ \ \ \ \
-| * | | | |
-|/| | | | |
-| | * | | |
-| | |\ \ \ \
-| | | * | | |
-| | |/| | | |
-| |/| | |/ /
-| | | |/| |
-| | | | | *
-| |_|_|_|/
-|/| | | |
-| | * | |
-| |/ / /
-* | | |
-* | | |
-| | * |
-* | | |
-* | | |
-| * | |
-| | * |
-| * | |
-* | | |
-|\ \ \ \
-| * | | |
-|/| | | |
-| |/ / /
-| * | |
-| |\ \ \
-| | * | |
-| |/| | |
-| | |/ /
-| | * |
-| | |\ \
-| | | * |
-| | |/| |
-* | | | |
-* | | | |
-|\ \ \ \ \
-| * | | | |
-|/| | | | |
-| | * | | |
-| | * | | |
-| | * | | |
-| |/ / / /
-| * | | |
-| |\ \ \ \
-| | * | | |
-| |/| | | |
-* | | | | |
-* | | | | |
-* | | | | |
-* | | | | |
-* | | | | |
-| | | | * |
-* | | | | |
-|\ \ \ \ \ \
-| * | | | | |
-|/| | | | | |
-| | | | | * |
-| | | | |/ /
-* | | | | |
-|\ \ \ \ \ \
-* | | | | | |
-* | | | | | |
-| | | | * | |
-* | | | | | |
-* | | | | | |
-|\ \ \ \ \ \ \
-| | |_|_|/ / /
-| |/| | | | |
-| | | | * | |
-| | | | * | |
-| | | | * | |
-| | | | * | |
-| | | | * | |
-| | | | * | |
-| | | |/ / /
-| | | * | |
-| | | * | |
-| | | * | |
-| | |/| | |
-| | | * | |
-| | |/| | |
-| | | |/ /
-| | * | |
-| |/| | |
-| | | * |
-| | |/ /
-| | * |
-| * | |
-| |\ \ \
-| * | | |
-| | * | |
-| |/| | |
-| | |/ /
-| | * |
-| | |\ \
-| | * | |
-* | | | |
-|\| | | |
-| * | | |
-| * | | |
-| * | | |
-| | * | |
-| * | | |
-| |\| | |
-| * | | |
-| | * | |
-| | * | |
-| * | | |
-| * | | |
-| * | | |
-| * | | |
-| * | | |
-| * | | |
-| * | | |
-| * | | |
-| | * | |
-| * | | |
-| * | | |
-| * | | |
-| * | | |
-| | * | |
-* | | | |
-|\| | | |
-| | * | |
-| * | | |
-| |\| | |
-| | * | |
-| | * | |
-| | * | |
-| | | * |
-* | | | |
-|\| | | |
-| | * | |
-| | |/ /
-| * | |
-| * | |
-| |\| |
-* | | |
-|\| | |
-| | * |
-| | * |
-| | * |
-| * | |
-| | * |
-| * | |
-| | * |
-| | * |
-| | * |
-| * | |
-| * | |
-| * | |
-| * | |
-| * | |
-| * | |
-| * | |
-* | | |
-|\| | |
-| * | |
-| |\| |
-| | * |
-| | |\ \
-* | | | |
-|\| | | |
-| * | | |
-| |\| | |
-| | * | |
-| | | * |
-| | |/ /
-* | | |
-* | | |
-|\| | |
-| * | |
-| |\| |
-| | * |
-| | * |
-| | * |
-| | | *
-* | | |
-|\| | |
-| * | |
-| * | |
-| | | *
-| | | |\
-* | | | |
-| |_|_|/
-|/| | |
-| * | |
-| |\| |
-| | * |
-| | * |
-| | * |
-| | * |
-| | * |
-| * | |
-* | | |
-|\| | |
-| * | |
-|/| | |
-| |/ /
-| * |
-| |\ \
-| * | |
-| * | |
-* | | |
-|\| | |
-| | * |
-| * | |
-| * | |
-| * | |
-* | | |
-|\| | |
-| * | |
-| * | |
-| | * |
-| | |\ \
-| | |/ /
-| |/| |
-| * | |
-* | | |
-|\| | |
-| * | |
-* | | |
-|\| | |
-| * | |
-| |\ \ \
-| * | | |
-| * | | |
-| | | * |
-| * | | |
-| * | | |
-| | |/ /
-| |/| |
-| | * |
-* | | |
-|\| | |
-| * | |
-| * | |
-| * | |
-| * | |
-| * | |
-| |\ \ \
-* | | | |
-|\| | | |
-| * | | |
-| * | | |
-* | | | |
-* | | | |
-|\| | | |
-| | | | *
-| | | | |\
-| |_|_|_|/
-|/| | | |
-| * | | |
-* | | | |
-* | | | |
-|\| | | |
-| * | | |
-| |\ \ \ \
-| | | |/ /
-| | |/| |
-| * | | |
-| * | | |
-| * | | |
-| * | | |
-| | * | |
-| | | * |
-| | |/ /
-| |/| |
-* | | |
-|\| | |
-| * | |
-| * | |
-| * | |
-| * | |
-| * | |
-* | | |
-|\| | |
-| * | |
-| * | |
-* | | |
-| * | |
-| * | |
-| * | |
-* | | |
-* | | |
-* | | |
-|\| | |
-| * | |
-* | | |
-* | | |
-* | | |
-* | | |
-| | | *
-* | | |
-|\| | |
-| * | |
-| * | |
-| * | |
-`
diff --git a/modules/gitgraph/parser.go b/modules/gitgraph/parser.go
deleted file mode 100644
index f6bf9b0b90..0000000000
--- a/modules/gitgraph/parser.go
+++ /dev/null
@@ -1,336 +0,0 @@
-// Copyright 2020 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package gitgraph
-
-import (
- "bytes"
- "fmt"
-)
-
-// Parser represents a git graph parser. It is stateful containing the previous
-// glyphs, detected flows and color assignments.
-type Parser struct {
- glyphs []byte
- oldGlyphs []byte
- flows []int64
- oldFlows []int64
- maxFlow int64
- colors []int
- oldColors []int
- availableColors []int
- nextAvailable int
- firstInUse int
- firstAvailable int
- maxAllowedColors int
-}
-
-// Reset resets the internal parser state.
-func (parser *Parser) Reset() {
- parser.glyphs = parser.glyphs[0:0]
- parser.oldGlyphs = parser.oldGlyphs[0:0]
- parser.flows = parser.flows[0:0]
- parser.oldFlows = parser.oldFlows[0:0]
- parser.maxFlow = 0
- parser.colors = parser.colors[0:0]
- parser.oldColors = parser.oldColors[0:0]
- parser.availableColors = parser.availableColors[0:0]
- parser.availableColors = append(parser.availableColors, 1, 2)
- parser.nextAvailable = 0
- parser.firstInUse = -1
- parser.firstAvailable = 0
- parser.maxAllowedColors = 0
-}
-
-// AddLineToGraph adds the line as a row to the graph
-func (parser *Parser) AddLineToGraph(graph *Graph, row int, line []byte) error {
- idx := bytes.Index(line, []byte("DATA:"))
- if idx < 0 {
- parser.ParseGlyphs(line)
- } else {
- parser.ParseGlyphs(line[:idx])
- }
-
- var err error
- commitDone := false
-
- for column, glyph := range parser.glyphs {
- if glyph == ' ' {
- continue
- }
-
- flowID := parser.flows[column]
-
- graph.AddGlyph(row, column, flowID, parser.colors[column], glyph)
-
- if glyph == '*' {
- if commitDone {
- if err != nil {
- err = fmt.Errorf("double commit on line %d: %s. %w", row, string(line), err)
- } else {
- err = fmt.Errorf("double commit on line %d: %s", row, string(line))
- }
- }
- commitDone = true
- if idx < 0 {
- if err != nil {
- err = fmt.Errorf("missing data section on line %d with commit: %s. %w", row, string(line), err)
- } else {
- err = fmt.Errorf("missing data section on line %d with commit: %s", row, string(line))
- }
- continue
- }
- err2 := graph.AddCommit(row, column, flowID, line[idx+5:])
- if err != nil && err2 != nil {
- err = fmt.Errorf("%v %w", err2, err)
- continue
- } else if err2 != nil {
- err = err2
- continue
- }
- }
- }
- if !commitDone {
- graph.Commits = append(graph.Commits, RelationCommit)
- }
- return err
-}
-
-func (parser *Parser) releaseUnusedColors() {
- if parser.firstInUse > -1 {
- // Here we step through the old colors, searching for them in the
- // "in-use" section of availableColors (that is, the colors between
- // firstInUse and firstAvailable)
- // Ensure that the benchmarks are not worsened with proposed changes
- stepstaken := 0
- position := parser.firstInUse
- for _, color := range parser.oldColors {
- if color == 0 {
- continue
- }
- found := false
- i := position
- for j := stepstaken; i != parser.firstAvailable && j < len(parser.availableColors); j++ {
- colorToCheck := parser.availableColors[i]
- if colorToCheck == color {
- found = true
- break
- }
- i = (i + 1) % len(parser.availableColors)
- }
- if !found {
- // Duplicate color
- continue
- }
- // Swap them around
- parser.availableColors[position], parser.availableColors[i] = parser.availableColors[i], parser.availableColors[position]
- stepstaken++
- position = (parser.firstInUse + stepstaken) % len(parser.availableColors)
- if position == parser.firstAvailable || stepstaken == len(parser.availableColors) {
- break
- }
- }
- if stepstaken == len(parser.availableColors) {
- parser.firstAvailable = -1
- } else {
- parser.firstAvailable = position
- if parser.nextAvailable == -1 {
- parser.nextAvailable = parser.firstAvailable
- }
- }
- }
-}
-
-// ParseGlyphs parses the provided glyphs and sets the internal state
-func (parser *Parser) ParseGlyphs(glyphs []byte) {
- // Clean state for parsing this row
- parser.glyphs, parser.oldGlyphs = parser.oldGlyphs, parser.glyphs
- parser.glyphs = parser.glyphs[0:0]
- parser.flows, parser.oldFlows = parser.oldFlows, parser.flows
- parser.flows = parser.flows[0:0]
- parser.colors, parser.oldColors = parser.oldColors, parser.colors
-
- // Ensure we have enough flows and colors
- parser.colors = parser.colors[0:0]
- for range glyphs {
- parser.flows = append(parser.flows, 0)
- parser.colors = append(parser.colors, 0)
- }
-
- // Copy the provided glyphs in to state.glyphs for safekeeping
- parser.glyphs = append(parser.glyphs, glyphs...)
-
- // release unused colors
- parser.releaseUnusedColors()
-
- for i := len(glyphs) - 1; i >= 0; i-- {
- glyph := glyphs[i]
- switch glyph {
- case '|':
- fallthrough
- case '*':
- parser.setUpFlow(i)
- case '/':
- parser.setOutFlow(i)
- case '\\':
- parser.setInFlow(i)
- case '_':
- parser.setRightFlow(i)
- case '.':
- fallthrough
- case '-':
- parser.setLeftFlow(i)
- case ' ':
- // no-op
- default:
- parser.newFlow(i)
- }
- }
-}
-
-func (parser *Parser) takePreviousFlow(i, j int) {
- if j < len(parser.oldFlows) && parser.oldFlows[j] > 0 {
- parser.flows[i] = parser.oldFlows[j]
- parser.oldFlows[j] = 0
- parser.colors[i] = parser.oldColors[j]
- parser.oldColors[j] = 0
- } else {
- parser.newFlow(i)
- }
-}
-
-func (parser *Parser) takeCurrentFlow(i, j int) {
- if j < len(parser.flows) && parser.flows[j] > 0 {
- parser.flows[i] = parser.flows[j]
- parser.colors[i] = parser.colors[j]
- } else {
- parser.newFlow(i)
- }
-}
-
-func (parser *Parser) newFlow(i int) {
- parser.maxFlow++
- parser.flows[i] = parser.maxFlow
-
- // Now give this flow a color
- if parser.nextAvailable == -1 {
- next := len(parser.availableColors)
- if parser.maxAllowedColors < 1 || next < parser.maxAllowedColors {
- parser.nextAvailable = next
- parser.firstAvailable = next
- parser.availableColors = append(parser.availableColors, next+1)
- }
- }
- parser.colors[i] = parser.availableColors[parser.nextAvailable]
- if parser.firstInUse == -1 {
- parser.firstInUse = parser.nextAvailable
- }
- parser.availableColors[parser.firstAvailable], parser.availableColors[parser.nextAvailable] = parser.availableColors[parser.nextAvailable], parser.availableColors[parser.firstAvailable]
-
- parser.nextAvailable = (parser.nextAvailable + 1) % len(parser.availableColors)
- parser.firstAvailable = (parser.firstAvailable + 1) % len(parser.availableColors)
-
- if parser.nextAvailable == parser.firstInUse {
- parser.nextAvailable = parser.firstAvailable
- }
- if parser.nextAvailable == parser.firstInUse {
- parser.nextAvailable = -1
- parser.firstAvailable = -1
- }
-}
-
-// setUpFlow handles '|' or '*'
-func (parser *Parser) setUpFlow(i int) {
- // In preference order:
- //
- // Previous Row: '\? ' ' |' ' /'
- // Current Row: ' | ' ' |' ' | '
- if i > 0 && i-1 < len(parser.oldGlyphs) && parser.oldGlyphs[i-1] == '\\' {
- parser.takePreviousFlow(i, i-1)
- } else if i < len(parser.oldGlyphs) && (parser.oldGlyphs[i] == '|' || parser.oldGlyphs[i] == '*') {
- parser.takePreviousFlow(i, i)
- } else if i+1 < len(parser.oldGlyphs) && parser.oldGlyphs[i+1] == '/' {
- parser.takePreviousFlow(i, i+1)
- } else {
- parser.newFlow(i)
- }
-}
-
-// setOutFlow handles '/'
-func (parser *Parser) setOutFlow(i int) {
- // In preference order:
- //
- // Previous Row: ' |/' ' |_' ' |' ' /' ' _' '\'
- // Current Row: '/| ' '/| ' '/ ' '/ ' '/ ' '/'
- if i+2 < len(parser.oldGlyphs) &&
- (parser.oldGlyphs[i+1] == '|' || parser.oldGlyphs[i+1] == '*') &&
- (parser.oldGlyphs[i+2] == '/' || parser.oldGlyphs[i+2] == '_') &&
- i+1 < len(parser.glyphs) &&
- (parser.glyphs[i+1] == '|' || parser.glyphs[i+1] == '*') {
- parser.takePreviousFlow(i, i+2)
- } else if i+1 < len(parser.oldGlyphs) &&
- (parser.oldGlyphs[i+1] == '|' || parser.oldGlyphs[i+1] == '*' ||
- parser.oldGlyphs[i+1] == '/' || parser.oldGlyphs[i+1] == '_') {
- parser.takePreviousFlow(i, i+1)
- if parser.oldGlyphs[i+1] == '/' {
- parser.glyphs[i] = '|'
- }
- } else if i < len(parser.oldGlyphs) && parser.oldGlyphs[i] == '\\' {
- parser.takePreviousFlow(i, i)
- } else {
- parser.newFlow(i)
- }
-}
-
-// setInFlow handles '\'
-func (parser *Parser) setInFlow(i int) {
- // In preference order:
- //
- // Previous Row: '| ' '-. ' '| ' '\ ' '/' '---'
- // Current Row: '|\' ' \' ' \' ' \' '\' ' \ '
- if i > 0 && i-1 < len(parser.oldGlyphs) &&
- (parser.oldGlyphs[i-1] == '|' || parser.oldGlyphs[i-1] == '*') &&
- (parser.glyphs[i-1] == '|' || parser.glyphs[i-1] == '*') {
- parser.newFlow(i)
- } else if i > 0 && i-1 < len(parser.oldGlyphs) &&
- (parser.oldGlyphs[i-1] == '|' || parser.oldGlyphs[i-1] == '*' ||
- parser.oldGlyphs[i-1] == '.' || parser.oldGlyphs[i-1] == '\\') {
- parser.takePreviousFlow(i, i-1)
- if parser.oldGlyphs[i-1] == '\\' {
- parser.glyphs[i] = '|'
- }
- } else if i < len(parser.oldGlyphs) && parser.oldGlyphs[i] == '/' {
- parser.takePreviousFlow(i, i)
- } else {
- parser.newFlow(i)
- }
-}
-
-// setRightFlow handles '_'
-func (parser *Parser) setRightFlow(i int) {
- // In preference order:
- //
- // Current Row: '__' '_/' '_|_' '_|/'
- if i+1 < len(parser.glyphs) &&
- (parser.glyphs[i+1] == '_' || parser.glyphs[i+1] == '/') {
- parser.takeCurrentFlow(i, i+1)
- } else if i+2 < len(parser.glyphs) &&
- (parser.glyphs[i+1] == '|' || parser.glyphs[i+1] == '*') &&
- (parser.glyphs[i+2] == '_' || parser.glyphs[i+2] == '/') {
- parser.takeCurrentFlow(i, i+2)
- } else {
- parser.newFlow(i)
- }
-}
-
-// setLeftFlow handles '----.'
-func (parser *Parser) setLeftFlow(i int) {
- if parser.glyphs[i] == '.' {
- parser.newFlow(i)
- } else if i+1 < len(parser.glyphs) &&
- (parser.glyphs[i+1] == '-' || parser.glyphs[i+1] == '.') {
- parser.takeCurrentFlow(i, i+1)
- } else {
- parser.newFlow(i)
- }
-}