code.gitea.io/gitea@v1.19.3/modules/gitgraph/graph.go (about)

     1  // Copyright 2016 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package gitgraph
     5  
     6  import (
     7  	"bufio"
     8  	"bytes"
     9  	"context"
    10  	"os"
    11  	"strings"
    12  
    13  	"code.gitea.io/gitea/modules/git"
    14  	"code.gitea.io/gitea/modules/setting"
    15  )
    16  
    17  // GetCommitGraph return a list of commit (GraphItems) from all branches
    18  func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bool, branches, files []string) (*Graph, error) {
    19  	format := "DATA:%D|%H|%ad|%h|%s"
    20  
    21  	if page == 0 {
    22  		page = 1
    23  	}
    24  
    25  	graphCmd := git.NewCommand(r.Ctx, "log", "--graph", "--date-order", "--decorate=full")
    26  
    27  	if hidePRRefs {
    28  		graphCmd.AddArguments("--exclude=" + git.PullPrefix + "*")
    29  	}
    30  
    31  	if len(branches) == 0 {
    32  		graphCmd.AddArguments("--all")
    33  	}
    34  
    35  	graphCmd.AddArguments("-C", "-M", "--date=iso").
    36  		AddOptionFormat("-n %d", setting.UI.GraphMaxCommitNum*page).
    37  		AddOptionFormat("--pretty=format:%s", format)
    38  
    39  	if len(branches) > 0 {
    40  		graphCmd.AddDynamicArguments(branches...)
    41  	}
    42  	if len(files) > 0 {
    43  		graphCmd.AddDashesAndList(files...)
    44  	}
    45  	graph := NewGraph()
    46  
    47  	stderr := new(strings.Builder)
    48  	stdoutReader, stdoutWriter, err := os.Pipe()
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	commitsToSkip := setting.UI.GraphMaxCommitNum * (page - 1)
    53  
    54  	scanner := bufio.NewScanner(stdoutReader)
    55  
    56  	if err := graphCmd.Run(&git.RunOpts{
    57  		Dir:    r.Path,
    58  		Stdout: stdoutWriter,
    59  		Stderr: stderr,
    60  		PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
    61  			_ = stdoutWriter.Close()
    62  			defer stdoutReader.Close()
    63  			parser := &Parser{}
    64  			parser.firstInUse = -1
    65  			parser.maxAllowedColors = maxAllowedColors
    66  			if maxAllowedColors > 0 {
    67  				parser.availableColors = make([]int, maxAllowedColors)
    68  				for i := range parser.availableColors {
    69  					parser.availableColors[i] = i + 1
    70  				}
    71  			} else {
    72  				parser.availableColors = []int{1, 2}
    73  			}
    74  			for commitsToSkip > 0 && scanner.Scan() {
    75  				line := scanner.Bytes()
    76  				dataIdx := bytes.Index(line, []byte("DATA:"))
    77  				if dataIdx < 0 {
    78  					dataIdx = len(line)
    79  				}
    80  				starIdx := bytes.IndexByte(line, '*')
    81  				if starIdx >= 0 && starIdx < dataIdx {
    82  					commitsToSkip--
    83  				}
    84  				parser.ParseGlyphs(line[:dataIdx])
    85  			}
    86  
    87  			row := 0
    88  
    89  			// Skip initial non-commit lines
    90  			for scanner.Scan() {
    91  				line := scanner.Bytes()
    92  				if bytes.IndexByte(line, '*') >= 0 {
    93  					if err := parser.AddLineToGraph(graph, row, line); err != nil {
    94  						cancel()
    95  						return err
    96  					}
    97  					break
    98  				}
    99  				parser.ParseGlyphs(line)
   100  			}
   101  
   102  			for scanner.Scan() {
   103  				row++
   104  				line := scanner.Bytes()
   105  				if err := parser.AddLineToGraph(graph, row, line); err != nil {
   106  					cancel()
   107  					return err
   108  				}
   109  			}
   110  			return scanner.Err()
   111  		},
   112  	}); err != nil {
   113  		return graph, err
   114  	}
   115  	return graph, nil
   116  }