mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-21 03:23:26 +02:00
Merge remote-tracking branch 'upstream/main' into limit-repo-size
This commit is contained in:
@@ -1,218 +0,0 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// BlamePart represents block of blame - continuous lines with one sha
|
||||
type BlamePart struct {
|
||||
Sha string
|
||||
Lines []string
|
||||
PreviousSha string
|
||||
PreviousPath string
|
||||
}
|
||||
|
||||
// BlameReader returns part of file blame one by one
|
||||
type BlameReader struct {
|
||||
output io.WriteCloser
|
||||
reader io.ReadCloser
|
||||
bufferedReader *bufio.Reader
|
||||
done chan error
|
||||
lastSha *string
|
||||
ignoreRevsFile string
|
||||
objectFormat ObjectFormat
|
||||
cleanupFuncs []func()
|
||||
}
|
||||
|
||||
func (r *BlameReader) UsesIgnoreRevs() bool {
|
||||
return r.ignoreRevsFile != ""
|
||||
}
|
||||
|
||||
// NextPart returns next part of blame (sequential code lines with the same commit)
|
||||
func (r *BlameReader) NextPart() (*BlamePart, error) {
|
||||
var blamePart *BlamePart
|
||||
|
||||
if r.lastSha != nil {
|
||||
blamePart = &BlamePart{
|
||||
Sha: *r.lastSha,
|
||||
Lines: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
const previousHeader = "previous "
|
||||
var lineBytes []byte
|
||||
var isPrefix bool
|
||||
var err error
|
||||
|
||||
for err != io.EOF {
|
||||
lineBytes, isPrefix, err = r.bufferedReader.ReadLine()
|
||||
if err != nil && err != io.EOF {
|
||||
return blamePart, err
|
||||
}
|
||||
|
||||
if len(lineBytes) == 0 {
|
||||
// isPrefix will be false
|
||||
continue
|
||||
}
|
||||
|
||||
var objectID string
|
||||
objectFormatLength := r.objectFormat.FullLength()
|
||||
|
||||
if len(lineBytes) > objectFormatLength && lineBytes[objectFormatLength] == ' ' && r.objectFormat.IsValid(string(lineBytes[0:objectFormatLength])) {
|
||||
objectID = string(lineBytes[0:objectFormatLength])
|
||||
}
|
||||
if len(objectID) > 0 {
|
||||
if blamePart == nil {
|
||||
blamePart = &BlamePart{
|
||||
Sha: objectID,
|
||||
Lines: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
if blamePart.Sha != objectID {
|
||||
r.lastSha = &objectID
|
||||
// need to munch to end of line...
|
||||
for isPrefix {
|
||||
_, isPrefix, err = r.bufferedReader.ReadLine()
|
||||
if err != nil && err != io.EOF {
|
||||
return blamePart, err
|
||||
}
|
||||
}
|
||||
return blamePart, nil
|
||||
}
|
||||
} else if lineBytes[0] == '\t' {
|
||||
blamePart.Lines = append(blamePart.Lines, string(lineBytes[1:]))
|
||||
} else if bytes.HasPrefix(lineBytes, []byte(previousHeader)) {
|
||||
offset := len(previousHeader) // already includes a space
|
||||
blamePart.PreviousSha = string(lineBytes[offset : offset+objectFormatLength])
|
||||
offset += objectFormatLength + 1 // +1 for space
|
||||
blamePart.PreviousPath = string(lineBytes[offset:])
|
||||
}
|
||||
|
||||
// need to munch to end of line...
|
||||
for isPrefix {
|
||||
_, isPrefix, err = r.bufferedReader.ReadLine()
|
||||
if err != nil && err != io.EOF {
|
||||
return blamePart, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.lastSha = nil
|
||||
|
||||
return blamePart, nil
|
||||
}
|
||||
|
||||
// Close BlameReader - don't run NextPart after invoking that
|
||||
func (r *BlameReader) Close() error {
|
||||
if r.bufferedReader == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := <-r.done
|
||||
r.bufferedReader = nil
|
||||
_ = r.reader.Close()
|
||||
_ = r.output.Close()
|
||||
for _, cleanup := range r.cleanupFuncs {
|
||||
if cleanup != nil {
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateBlameReader creates reader for given repository, commit and file
|
||||
func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (rd *BlameReader, err error) {
|
||||
var ignoreRevsFileName string
|
||||
var ignoreRevsFileCleanup func()
|
||||
defer func() {
|
||||
if err != nil && ignoreRevsFileCleanup != nil {
|
||||
ignoreRevsFileCleanup()
|
||||
}
|
||||
}()
|
||||
|
||||
cmd := gitcmd.NewCommand("blame", "--porcelain")
|
||||
|
||||
if DefaultFeatures().CheckVersionAtLeast("2.23") && !bypassBlameIgnore {
|
||||
ignoreRevsFileName, ignoreRevsFileCleanup, err = tryCreateBlameIgnoreRevsFile(commit)
|
||||
if err != nil && !IsErrNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
if ignoreRevsFileName != "" {
|
||||
// Possible improvement: use --ignore-revs-file /dev/stdin on unix
|
||||
// There is no equivalent on Windows. May be implemented if Gitea uses an external git backend.
|
||||
cmd.AddOptionValues("--ignore-revs-file", ignoreRevsFileName)
|
||||
}
|
||||
}
|
||||
|
||||
cmd.AddDynamicArguments(commit.ID.String()).AddDashesAndList(file)
|
||||
|
||||
done := make(chan error, 1)
|
||||
reader, stdout, err := os.Pipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go func() {
|
||||
stderr := bytes.Buffer{}
|
||||
// TODO: it doesn't work for directories (the directories shouldn't be "blamed"), and the "err" should be returned by "Read" but not by "Close"
|
||||
err := cmd.WithDir(repoPath).
|
||||
WithUseContextTimeout(true).
|
||||
WithStdout(stdout).
|
||||
WithStderr(&stderr).
|
||||
Run(ctx)
|
||||
done <- err
|
||||
_ = stdout.Close()
|
||||
if err != nil {
|
||||
log.Error("Error running git blame (dir: %v): %v, stderr: %v", repoPath, err, stderr.String())
|
||||
}
|
||||
}()
|
||||
|
||||
bufferedReader := bufio.NewReader(reader)
|
||||
return &BlameReader{
|
||||
output: stdout,
|
||||
reader: reader,
|
||||
bufferedReader: bufferedReader,
|
||||
done: done,
|
||||
ignoreRevsFile: ignoreRevsFileName,
|
||||
objectFormat: objectFormat,
|
||||
cleanupFuncs: []func(){ignoreRevsFileCleanup},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func tryCreateBlameIgnoreRevsFile(commit *Commit) (string, func(), error) {
|
||||
entry, err := commit.GetTreeEntryByPath(".git-blame-ignore-revs")
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
r, err := entry.Blob().DataAsync()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
f, cleanup, err := setting.AppDataTempDir("git-repo-content").CreateTempFileRandom("git-blame-ignore-revs")
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
filename := f.Name()
|
||||
_, err = io.Copy(f, r)
|
||||
_ = f.Close()
|
||||
if err != nil {
|
||||
cleanup()
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return filename, cleanup, nil
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReadingBlameOutputSha256(t *testing.T) {
|
||||
setting.AppDataPath = t.TempDir()
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
defer cancel()
|
||||
|
||||
if isGogit {
|
||||
t.Skip("Skipping test since gogit does not support sha256")
|
||||
return
|
||||
}
|
||||
|
||||
t.Run("Without .git-blame-ignore-revs", func(t *testing.T) {
|
||||
repo, err := OpenRepository(ctx, "./tests/repos/repo5_pulls_sha256")
|
||||
assert.NoError(t, err)
|
||||
defer repo.Close()
|
||||
|
||||
commit, err := repo.GetCommit("0b69b7bb649b5d46e14cabb6468685e5dd721290acc7ffe604d37cde57927345")
|
||||
assert.NoError(t, err)
|
||||
|
||||
parts := []*BlamePart{
|
||||
{
|
||||
Sha: "1e35a51dc00fd7de730344c07061acfe80e8117e075ac979b6a29a3a045190ca",
|
||||
Lines: []string{
|
||||
"# test_repo",
|
||||
"Test repository for testing migration from github to gitea",
|
||||
},
|
||||
},
|
||||
{
|
||||
Sha: "0b69b7bb649b5d46e14cabb6468685e5dd721290acc7ffe604d37cde57927345",
|
||||
Lines: []string{"", "Do not make any changes to this repo it is used for unit testing"},
|
||||
PreviousSha: "1e35a51dc00fd7de730344c07061acfe80e8117e075ac979b6a29a3a045190ca",
|
||||
PreviousPath: "README.md",
|
||||
},
|
||||
}
|
||||
|
||||
for _, bypass := range []bool{false, true} {
|
||||
blameReader, err := CreateBlameReader(ctx, Sha256ObjectFormat, "./tests/repos/repo5_pulls_sha256", commit, "README.md", bypass)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, blameReader)
|
||||
defer blameReader.Close()
|
||||
|
||||
assert.False(t, blameReader.UsesIgnoreRevs())
|
||||
|
||||
for _, part := range parts {
|
||||
actualPart, err := blameReader.NextPart()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, part, actualPart)
|
||||
}
|
||||
|
||||
// make sure all parts have been read
|
||||
actualPart, err := blameReader.NextPart()
|
||||
assert.Nil(t, actualPart)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("With .git-blame-ignore-revs", func(t *testing.T) {
|
||||
repo, err := OpenRepository(ctx, "./tests/repos/repo6_blame_sha256")
|
||||
assert.NoError(t, err)
|
||||
defer repo.Close()
|
||||
|
||||
full := []*BlamePart{
|
||||
{
|
||||
Sha: "ab2b57a4fa476fb2edb74dafa577caf918561abbaa8fba0c8dc63c412e17a7cc",
|
||||
Lines: []string{"line", "line"},
|
||||
},
|
||||
{
|
||||
Sha: "9347b0198cd1f25017579b79d0938fa89dba34ad2514f0dd92f6bc975ed1a2fe",
|
||||
Lines: []string{"changed line"},
|
||||
PreviousSha: "ab2b57a4fa476fb2edb74dafa577caf918561abbaa8fba0c8dc63c412e17a7cc",
|
||||
PreviousPath: "blame.txt",
|
||||
},
|
||||
{
|
||||
Sha: "ab2b57a4fa476fb2edb74dafa577caf918561abbaa8fba0c8dc63c412e17a7cc",
|
||||
Lines: []string{"line", "line", ""},
|
||||
},
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
CommitID string
|
||||
UsesIgnoreRevs bool
|
||||
Bypass bool
|
||||
Parts []*BlamePart
|
||||
}{
|
||||
{
|
||||
CommitID: "e2f5660e15159082902960af0ed74fc144921d2b0c80e069361853b3ece29ba3",
|
||||
UsesIgnoreRevs: true,
|
||||
Bypass: false,
|
||||
Parts: []*BlamePart{
|
||||
{
|
||||
Sha: "ab2b57a4fa476fb2edb74dafa577caf918561abbaa8fba0c8dc63c412e17a7cc",
|
||||
Lines: []string{"line", "line", "changed line", "line", "line", ""},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
CommitID: "e2f5660e15159082902960af0ed74fc144921d2b0c80e069361853b3ece29ba3",
|
||||
UsesIgnoreRevs: false,
|
||||
Bypass: true,
|
||||
Parts: full,
|
||||
},
|
||||
{
|
||||
CommitID: "9347b0198cd1f25017579b79d0938fa89dba34ad2514f0dd92f6bc975ed1a2fe",
|
||||
UsesIgnoreRevs: false,
|
||||
Bypass: false,
|
||||
Parts: full,
|
||||
},
|
||||
{
|
||||
CommitID: "9347b0198cd1f25017579b79d0938fa89dba34ad2514f0dd92f6bc975ed1a2fe",
|
||||
UsesIgnoreRevs: false,
|
||||
Bypass: false,
|
||||
Parts: full,
|
||||
},
|
||||
}
|
||||
|
||||
objectFormat, err := repo.GetObjectFormat()
|
||||
assert.NoError(t, err)
|
||||
for _, c := range cases {
|
||||
commit, err := repo.GetCommit(c.CommitID)
|
||||
assert.NoError(t, err)
|
||||
blameReader, err := CreateBlameReader(ctx, objectFormat, "./tests/repos/repo6_blame_sha256", commit, "blame.txt", c.Bypass)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, blameReader)
|
||||
defer blameReader.Close()
|
||||
|
||||
assert.Equal(t, c.UsesIgnoreRevs, blameReader.UsesIgnoreRevs())
|
||||
|
||||
for _, part := range c.Parts {
|
||||
actualPart, err := blameReader.NextPart()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, part, actualPart)
|
||||
}
|
||||
|
||||
// make sure all parts have been read
|
||||
actualPart, err := blameReader.NextPart()
|
||||
assert.Nil(t, actualPart)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReadingBlameOutput(t *testing.T) {
|
||||
setting.AppDataPath = t.TempDir()
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
defer cancel()
|
||||
|
||||
t.Run("Without .git-blame-ignore-revs", func(t *testing.T) {
|
||||
repo, err := OpenRepository(ctx, "./tests/repos/repo5_pulls")
|
||||
assert.NoError(t, err)
|
||||
defer repo.Close()
|
||||
|
||||
commit, err := repo.GetCommit("f32b0a9dfd09a60f616f29158f772cedd89942d2")
|
||||
assert.NoError(t, err)
|
||||
|
||||
parts := []*BlamePart{
|
||||
{
|
||||
Sha: "72866af952e98d02a73003501836074b286a78f6",
|
||||
Lines: []string{
|
||||
"# test_repo",
|
||||
"Test repository for testing migration from github to gitea",
|
||||
},
|
||||
},
|
||||
{
|
||||
Sha: "f32b0a9dfd09a60f616f29158f772cedd89942d2",
|
||||
Lines: []string{"", "Do not make any changes to this repo it is used for unit testing"},
|
||||
PreviousSha: "72866af952e98d02a73003501836074b286a78f6",
|
||||
PreviousPath: "README.md",
|
||||
},
|
||||
}
|
||||
|
||||
for _, bypass := range []bool{false, true} {
|
||||
blameReader, err := CreateBlameReader(ctx, Sha1ObjectFormat, "./tests/repos/repo5_pulls", commit, "README.md", bypass)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, blameReader)
|
||||
defer blameReader.Close()
|
||||
|
||||
assert.False(t, blameReader.UsesIgnoreRevs())
|
||||
|
||||
for _, part := range parts {
|
||||
actualPart, err := blameReader.NextPart()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, part, actualPart)
|
||||
}
|
||||
|
||||
// make sure all parts have been read
|
||||
actualPart, err := blameReader.NextPart()
|
||||
assert.Nil(t, actualPart)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("With .git-blame-ignore-revs", func(t *testing.T) {
|
||||
repo, err := OpenRepository(ctx, "./tests/repos/repo6_blame")
|
||||
assert.NoError(t, err)
|
||||
defer repo.Close()
|
||||
|
||||
full := []*BlamePart{
|
||||
{
|
||||
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
Lines: []string{"line", "line"},
|
||||
},
|
||||
{
|
||||
Sha: "45fb6cbc12f970b04eacd5cd4165edd11c8d7376",
|
||||
Lines: []string{"changed line"},
|
||||
PreviousSha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
PreviousPath: "blame.txt",
|
||||
},
|
||||
{
|
||||
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
Lines: []string{"line", "line", ""},
|
||||
},
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
CommitID string
|
||||
UsesIgnoreRevs bool
|
||||
Bypass bool
|
||||
Parts []*BlamePart
|
||||
}{
|
||||
{
|
||||
CommitID: "544d8f7a3b15927cddf2299b4b562d6ebd71b6a7",
|
||||
UsesIgnoreRevs: true,
|
||||
Bypass: false,
|
||||
Parts: []*BlamePart{
|
||||
{
|
||||
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
Lines: []string{"line", "line", "changed line", "line", "line", ""},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
CommitID: "544d8f7a3b15927cddf2299b4b562d6ebd71b6a7",
|
||||
UsesIgnoreRevs: false,
|
||||
Bypass: true,
|
||||
Parts: full,
|
||||
},
|
||||
{
|
||||
CommitID: "45fb6cbc12f970b04eacd5cd4165edd11c8d7376",
|
||||
UsesIgnoreRevs: false,
|
||||
Bypass: false,
|
||||
Parts: full,
|
||||
},
|
||||
{
|
||||
CommitID: "45fb6cbc12f970b04eacd5cd4165edd11c8d7376",
|
||||
UsesIgnoreRevs: false,
|
||||
Bypass: false,
|
||||
Parts: full,
|
||||
},
|
||||
}
|
||||
|
||||
objectFormat, err := repo.GetObjectFormat()
|
||||
assert.NoError(t, err)
|
||||
for _, c := range cases {
|
||||
commit, err := repo.GetCommit(c.CommitID)
|
||||
assert.NoError(t, err)
|
||||
|
||||
blameReader, err := CreateBlameReader(ctx, objectFormat, "./tests/repos/repo6_blame", commit, "blame.txt", c.Bypass)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, blameReader)
|
||||
defer blameReader.Close()
|
||||
|
||||
assert.Equal(t, c.UsesIgnoreRevs, blameReader.UsesIgnoreRevs())
|
||||
|
||||
for _, part := range c.Parts {
|
||||
actualPart, err := blameReader.NextPart()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, part, actualPart)
|
||||
}
|
||||
|
||||
// make sure all parts have been read
|
||||
actualPart, err := blameReader.NextPart()
|
||||
assert.Nil(t, actualPart)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -323,14 +323,6 @@ func GetFullCommitID(ctx context.Context, repoPath, shortID string) (string, err
|
||||
return strings.TrimSpace(commitID), nil
|
||||
}
|
||||
|
||||
// GetRepositoryDefaultPublicGPGKey returns the default public key for this commit
|
||||
func (c *Commit) GetRepositoryDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings, error) {
|
||||
if c.repo == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return c.repo.GetDefaultPublicGPGKey(forceUpdate)
|
||||
}
|
||||
|
||||
func IsStringLikelyCommitID(objFmt ObjectFormat, s string, minLength ...int) bool {
|
||||
maxLen := 64 // sha256
|
||||
if objFmt != nil {
|
||||
|
||||
+7
-2
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// RawDiffType type of a raw diff.
|
||||
@@ -47,7 +48,9 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
|
||||
switch diffType {
|
||||
case RawDiffNormal:
|
||||
if len(startCommit) != 0 {
|
||||
cmd.AddArguments("diff", "-M").AddDynamicArguments(startCommit, endCommit).AddDashesAndList(files...)
|
||||
cmd.AddArguments("diff").
|
||||
AddOptionFormat("--find-renames=%s", setting.Git.DiffRenameSimilarityThreshold).
|
||||
AddDynamicArguments(startCommit, endCommit).AddDashesAndList(files...)
|
||||
} else if commit.ParentCount() == 0 {
|
||||
cmd.AddArguments("show").AddDynamicArguments(endCommit).AddDashesAndList(files...)
|
||||
} else {
|
||||
@@ -55,7 +58,9 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.AddArguments("diff", "-M").AddDynamicArguments(c.ID.String(), endCommit).AddDashesAndList(files...)
|
||||
cmd.AddArguments("diff").
|
||||
AddOptionFormat("--find-renames=%s", setting.Git.DiffRenameSimilarityThreshold).
|
||||
AddDynamicArguments(c.ID.String(), endCommit).AddDashesAndList(files...)
|
||||
}
|
||||
case RawDiffPatch:
|
||||
if len(startCommit) != 0 {
|
||||
|
||||
@@ -75,9 +75,9 @@ func (f Format) Parser(r io.Reader) *Parser {
|
||||
// hexEscaped produces hex-escpaed characters from a string. For example, "\n\0"
|
||||
// would turn into "%0a%00".
|
||||
func (f Format) hexEscaped(delim []byte) string {
|
||||
escaped := ""
|
||||
var escaped strings.Builder
|
||||
for i := range delim {
|
||||
escaped += "%" + hex.EncodeToString([]byte{delim[i]})
|
||||
escaped.WriteString("%" + hex.EncodeToString([]byte{delim[i]}))
|
||||
}
|
||||
return escaped
|
||||
return escaped.String()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
)
|
||||
|
||||
// GPGSettings represents the default GPG settings for this repository
|
||||
type GPGSettings struct {
|
||||
Sign bool
|
||||
KeyID string
|
||||
Email string
|
||||
Name string
|
||||
PublicKeyContent string
|
||||
Format string
|
||||
}
|
||||
|
||||
// LoadPublicKeyContent will load the key from gpg
|
||||
func (gpgSettings *GPGSettings) LoadPublicKeyContent() error {
|
||||
if gpgSettings.PublicKeyContent != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if gpgSettings.Format == SigningKeyFormatSSH {
|
||||
content, err := os.ReadFile(gpgSettings.KeyID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read SSH public key file: %s, %w", gpgSettings.KeyID, err)
|
||||
}
|
||||
gpgSettings.PublicKeyContent = string(content)
|
||||
return nil
|
||||
}
|
||||
content, stderr, err := process.GetManager().Exec(
|
||||
"gpg -a --export",
|
||||
"gpg", "-a", "--export", gpgSettings.KeyID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get default signing key: %s, %s, %w", gpgSettings.KeyID, stderr, err)
|
||||
}
|
||||
gpgSettings.PublicKeyContent = content
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
loadPublicGPGKeyMutex sync.RWMutex
|
||||
globalGPGSettings *GPGSettings
|
||||
)
|
||||
|
||||
// GetDefaultPublicGPGKey will return and cache the default public GPG settings
|
||||
func GetDefaultPublicGPGKey(ctx context.Context, forceUpdate bool) (*GPGSettings, error) {
|
||||
if !forceUpdate {
|
||||
loadPublicGPGKeyMutex.RLock()
|
||||
if globalGPGSettings != nil {
|
||||
defer loadPublicGPGKeyMutex.RUnlock()
|
||||
return globalGPGSettings, nil
|
||||
}
|
||||
loadPublicGPGKeyMutex.RUnlock()
|
||||
}
|
||||
|
||||
loadPublicGPGKeyMutex.Lock()
|
||||
defer loadPublicGPGKeyMutex.Unlock()
|
||||
|
||||
if globalGPGSettings != nil && !forceUpdate {
|
||||
return globalGPGSettings, nil
|
||||
}
|
||||
|
||||
globalGPGSettings = &GPGSettings{
|
||||
Sign: true,
|
||||
}
|
||||
|
||||
value, _, _ := gitcmd.NewCommand("config", "--global", "--get", "commit.gpgsign").RunStdString(ctx)
|
||||
sign, valid := ParseBool(strings.TrimSpace(value))
|
||||
if !sign || !valid {
|
||||
globalGPGSettings.Sign = false
|
||||
return globalGPGSettings, nil
|
||||
}
|
||||
|
||||
signingKey, _, _ := gitcmd.NewCommand("config", "--global", "--get", "user.signingkey").RunStdString(ctx)
|
||||
globalGPGSettings.KeyID = strings.TrimSpace(signingKey)
|
||||
|
||||
format, _, _ := gitcmd.NewCommand("config", "--global", "--default", SigningKeyFormatOpenPGP, "--get", "gpg.format").RunStdString(ctx)
|
||||
globalGPGSettings.Format = strings.TrimSpace(format)
|
||||
|
||||
defaultEmail, _, _ := gitcmd.NewCommand("config", "--global", "--get", "user.email").RunStdString(ctx)
|
||||
globalGPGSettings.Email = strings.TrimSpace(defaultEmail)
|
||||
|
||||
defaultName, _, _ := gitcmd.NewCommand("config", "--global", "--get", "user.name").RunStdString(ctx)
|
||||
globalGPGSettings.Name = strings.TrimSpace(defaultName)
|
||||
|
||||
if err := globalGPGSettings.LoadPublicKeyContent(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return globalGPGSettings, nil
|
||||
}
|
||||
+6
-6
@@ -32,23 +32,23 @@ func (s *SigningKey) String() string {
|
||||
}
|
||||
|
||||
// GetSigningKey returns the KeyID and git Signature for the repo
|
||||
func GetSigningKey(ctx context.Context, repoPath string) (*SigningKey, *Signature) {
|
||||
func GetSigningKey(ctx context.Context) (*SigningKey, *Signature) {
|
||||
if setting.Repository.Signing.SigningKey == "none" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if setting.Repository.Signing.SigningKey == "default" || setting.Repository.Signing.SigningKey == "" {
|
||||
// Can ignore the error here as it means that commit.gpgsign is not set
|
||||
value, _, _ := gitcmd.NewCommand("config", "--get", "commit.gpgsign").WithDir(repoPath).RunStdString(ctx)
|
||||
value, _, _ := gitcmd.NewCommand("config", "--global", "--get", "commit.gpgsign").RunStdString(ctx)
|
||||
sign, valid := ParseBool(strings.TrimSpace(value))
|
||||
if !sign || !valid {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
format, _, _ := gitcmd.NewCommand("config", "--default", SigningKeyFormatOpenPGP, "--get", "gpg.format").WithDir(repoPath).RunStdString(ctx)
|
||||
signingKey, _, _ := gitcmd.NewCommand("config", "--get", "user.signingkey").WithDir(repoPath).RunStdString(ctx)
|
||||
signingName, _, _ := gitcmd.NewCommand("config", "--get", "user.name").WithDir(repoPath).RunStdString(ctx)
|
||||
signingEmail, _, _ := gitcmd.NewCommand("config", "--get", "user.email").WithDir(repoPath).RunStdString(ctx)
|
||||
format, _, _ := gitcmd.NewCommand("config", "--global", "--default", SigningKeyFormatOpenPGP, "--get", "gpg.format").RunStdString(ctx)
|
||||
signingKey, _, _ := gitcmd.NewCommand("config", "--global", "--get", "user.signingkey").RunStdString(ctx)
|
||||
signingName, _, _ := gitcmd.NewCommand("config", "--global", "--get", "user.name").RunStdString(ctx)
|
||||
signingEmail, _, _ := gitcmd.NewCommand("config", "--global", "--get", "user.email").RunStdString(ctx)
|
||||
|
||||
if strings.TrimSpace(signingKey) == "" {
|
||||
return nil, nil
|
||||
|
||||
@@ -20,16 +20,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/proxy"
|
||||
)
|
||||
|
||||
// GPGSettings represents the default GPG settings for this repository
|
||||
type GPGSettings struct {
|
||||
Sign bool
|
||||
KeyID string
|
||||
Email string
|
||||
Name string
|
||||
PublicKeyContent string
|
||||
Format string
|
||||
}
|
||||
|
||||
const prettyLogFormat = `--pretty=format:%H`
|
||||
|
||||
func (repo *Repository) ShowPrettyFormatLogToList(ctx context.Context, revisionRange string) ([]*Commit, error) {
|
||||
|
||||
@@ -32,7 +32,6 @@ type Repository struct {
|
||||
|
||||
gogitRepo *gogit.Repository
|
||||
gogitStorage *filesystem.Storage
|
||||
gpgSettings *GPGSettings
|
||||
|
||||
Ctx context.Context
|
||||
LastCommitCache *LastCommitCache
|
||||
|
||||
@@ -23,8 +23,6 @@ type Repository struct {
|
||||
|
||||
tagCache *ObjectCache[*Tag]
|
||||
|
||||
gpgSettings *GPGSettings
|
||||
|
||||
batchInUse bool
|
||||
batch *Batch
|
||||
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright 2015 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/git/gitcmd"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
)
|
||||
|
||||
// LoadPublicKeyContent will load the key from gpg
|
||||
func (gpgSettings *GPGSettings) LoadPublicKeyContent() error {
|
||||
if gpgSettings.Format == SigningKeyFormatSSH {
|
||||
content, err := os.ReadFile(gpgSettings.KeyID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read SSH public key file: %s, %w", gpgSettings.KeyID, err)
|
||||
}
|
||||
gpgSettings.PublicKeyContent = string(content)
|
||||
return nil
|
||||
}
|
||||
content, stderr, err := process.GetManager().Exec(
|
||||
"gpg -a --export",
|
||||
"gpg", "-a", "--export", gpgSettings.KeyID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get default signing key: %s, %s, %w", gpgSettings.KeyID, stderr, err)
|
||||
}
|
||||
gpgSettings.PublicKeyContent = content
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDefaultPublicGPGKey will return and cache the default public GPG settings for this repository
|
||||
func (repo *Repository) GetDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings, error) {
|
||||
if repo.gpgSettings != nil && !forceUpdate {
|
||||
return repo.gpgSettings, nil
|
||||
}
|
||||
|
||||
gpgSettings := &GPGSettings{
|
||||
Sign: true,
|
||||
}
|
||||
|
||||
value, _, _ := gitcmd.NewCommand("config", "--get", "commit.gpgsign").WithDir(repo.Path).RunStdString(repo.Ctx)
|
||||
sign, valid := ParseBool(strings.TrimSpace(value))
|
||||
if !sign || !valid {
|
||||
gpgSettings.Sign = false
|
||||
repo.gpgSettings = gpgSettings
|
||||
return gpgSettings, nil
|
||||
}
|
||||
|
||||
signingKey, _, _ := gitcmd.NewCommand("config", "--get", "user.signingkey").WithDir(repo.Path).RunStdString(repo.Ctx)
|
||||
gpgSettings.KeyID = strings.TrimSpace(signingKey)
|
||||
|
||||
format, _, _ := gitcmd.NewCommand("config", "--default", SigningKeyFormatOpenPGP, "--get", "gpg.format").WithDir(repo.Path).RunStdString(repo.Ctx)
|
||||
gpgSettings.Format = strings.TrimSpace(format)
|
||||
|
||||
defaultEmail, _, _ := gitcmd.NewCommand("config", "--get", "user.email").WithDir(repo.Path).RunStdString(repo.Ctx)
|
||||
gpgSettings.Email = strings.TrimSpace(defaultEmail)
|
||||
|
||||
defaultName, _, _ := gitcmd.NewCommand("config", "--get", "user.name").WithDir(repo.Path).RunStdString(repo.Ctx)
|
||||
gpgSettings.Name = strings.TrimSpace(defaultName)
|
||||
|
||||
if err := gpgSettings.LoadPublicKeyContent(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repo.gpgSettings = gpgSettings
|
||||
return repo.gpgSettings, nil
|
||||
}
|
||||
Reference in New Issue
Block a user