|
|
|
// 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").
|
Refactor git command package to improve security and maintainability (#22678)
This PR follows #21535 (and replace #22592)
## Review without space diff
https://github.com/go-gitea/gitea/pull/22678/files?diff=split&w=1
## Purpose of this PR
1. Make git module command completely safe (risky user inputs won't be
passed as argument option anymore)
2. Avoid low-level mistakes like
https://github.com/go-gitea/gitea/pull/22098#discussion_r1045234918
3. Remove deprecated and dirty `CmdArgCheck` function, hide the `CmdArg`
type
4. Simplify code when using git command
## The main idea of this PR
* Move the `git.CmdArg` to the `internal` package, then no other package
except `git` could use it. Then developers could never do
`AddArguments(git.CmdArg(userInput))` any more.
* Introduce `git.ToTrustedCmdArgs`, it's for user-provided and already
trusted arguments. It's only used in a few cases, for example: use git
arguments from config file, help unit test with some arguments.
* Introduce `AddOptionValues` and `AddOptionFormat`, they make code more
clear and simple:
* Before: `AddArguments("-m").AddDynamicArguments(message)`
* After: `AddOptionValues("-m", message)`
* -
* Before: `AddArguments(git.CmdArg(fmt.Sprintf("--author='%s <%s>'",
sig.Name, sig.Email)))`
* After: `AddOptionFormat("--author='%s <%s>'", sig.Name, sig.Email)`
## FAQ
### Why these changes were not done in #21535 ?
#21535 is mainly a search&replace, it did its best to not change too
much logic.
Making the framework better needs a lot of changes, so this separate PR
is needed as the second step.
### The naming of `AddOptionXxx`
According to git's manual, the `--xxx` part is called `option`.
### How can it guarantee that `internal.CmdArg` won't be not misused?
Go's specification guarantees that. Trying to access other package's
internal package causes compilation error.
And, `golangci-lint` also denies the git/internal package. Only the
`git/command.go` can use it carefully.
### There is still a `ToTrustedCmdArgs`, will it still allow developers
to make mistakes and pass untrusted arguments?
Generally speaking, no. Because when using `ToTrustedCmdArgs`, the code
will be very complex (see the changes for examples). Then developers and
reviewers can know that something might be unreasonable.
### Why there was a `CmdArgCheck` and why it's removed?
At the moment of #21535, to reduce unnecessary changes, `CmdArgCheck`
was introduced as a hacky patch. Now, almost all code could be written
as `cmd := NewCommand(); cmd.AddXxx(...)`, then there is no need for
`CmdArgCheck` anymore.
### Why many codes for `signArg == ""` is deleted?
Because in the old code, `signArg` could never be empty string, it's
either `-S[key-id]` or `--no-gpg-sign`. So the `signArg == ""` is just
dead code.
---------
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2 years ago
|
|
|
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
|
|
|
|
}
|