Merge branch 'main' into fix-20456-prepend-refs-heads

This commit is contained in:
zeripath 2022-10-24 03:27:08 +01:00 committed by GitHub
commit 5e72df4990
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
184 changed files with 2047 additions and 828 deletions

View file

@ -35,9 +35,6 @@ overrides:
rules:
import/no-unresolved: [0]
import/no-extraneous-dependencies: [0]
- files: ["*.test.js"]
env:
jest: true
- files: ["*.config.js"]
rules:
import/no-unused-modules: [0]

View file

@ -1,6 +1,7 @@
tasks:
- name: Setup
init: |
cp -r contrib/ide/vscode .vscode
make deps
make build
command: |

View file

@ -49,3 +49,4 @@ silentcode <silentcode@senga.org> (@silentcodeg)
Wim <wim@42.be> (@42wim)
xinyu <xinyu@nerv.org.cn> (@penlinux)
Jason Song <i@wolfogre.com> (@wolfogre)
Yarden Shoham <hrsi88@gmail.com> (@yardenshoham)

View file

@ -33,6 +33,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers"
markup_service "code.gitea.io/gitea/services/markup"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
@ -112,7 +113,7 @@ func runPR() {
log.Printf("[PR] Setting up router\n")
// routers.GlobalInit()
external.RegisterRenderers()
markup.Init()
markup.Init(markup_service.ProcessorHelper())
c := routers.NormalRoutes(graceful.GetManager().HammerContext())
log.Printf("[PR] Ready for testing !\n")

View file

@ -71,7 +71,7 @@ choco install gitea
macOS 平台下当前我们仅支持通过 `brew` 来安装。如果你没有安装 [Homebrew](http://brew.sh/),你也可以查看 [从二进制安装]({{< relref "from-binary.zh-cn.md" >}})。在你安装了 `brew` 之后, 你可以执行以下命令:
```
brew tap go-gitea/gitea
brew tap gitea/tap https://gitea.com/gitea/homebrew-gitea
brew install gitea
```

2
go.mod
View file

@ -6,7 +6,7 @@ require (
code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b
code.gitea.io/sdk/gitea v0.15.1
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb
gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681
gitea.com/go-chi/cache v0.2.0
gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8

4
go.sum
View file

@ -81,8 +81,8 @@ contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcig
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg=
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb h1:Yy0Bxzc8R2wxiwXoG/rECGplJUSpXqCsog9PuJFgiHs=
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc=
gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681 h1:MMSPgnVULVwV9kEBgvyEUhC9v/uviZ55hPJEMjpbNR4=
gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc=
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
gitea.com/go-chi/cache v0.2.0 h1:E0npuTfDW6CT1yD8NMDVc1SK6IeRjfmRL2zlEsCEd7w=
gitea.com/go-chi/cache v0.2.0/go.mod h1:iQlVK2aKTZ/rE9UcHyz9pQWGvdP9i1eI2spOpzgCrtE=

View file

@ -1,6 +1,5 @@
-
id: 1
repo_id: 1
hook_id: 1
uuid: uuid1
is_delivered: true

View file

@ -0,0 +1,19 @@
# type Milestone struct {
# ID int64 `xorm:"pk autoincr"`
# IsClosed bool
# NumIssues int
# NumClosedIssues int
# Completeness int // Percentage(1-100).
# }
-
id: 1
is_closed: false
num_issues: 3
num_closed_issues: 1
completeness: 33
-
id: 2
is_closed: true
num_issues: 5
num_closed_issues: 5
completeness: 100

View file

@ -0,0 +1,25 @@
# type Issue struct {
# ID int64 `xorm:"pk autoincr"`
# RepoID int64 `xorm:"INDEX UNIQUE(repo_index)"`
# Index int64 `xorm:"UNIQUE(repo_index)"` // Index in one repository.
# MilestoneID int64 `xorm:"INDEX"`
# IsClosed bool `xorm:"INDEX"`
# }
-
id: 1
repo_id: 1
index: 1
milestone_id: 1
is_closed: false
-
id: 2
repo_id: 1
index: 2
milestone_id: 1
is_closed: true
-
id: 4
repo_id: 1
index: 3
milestone_id: 1
is_closed: false

View file

@ -0,0 +1,19 @@
# type Milestone struct {
# ID int64 `xorm:"pk autoincr"`
# IsClosed bool
# NumIssues int
# NumClosedIssues int
# Completeness int // Percentage(1-100).
# }
-
id: 1
is_closed: false
num_issues: 4
num_closed_issues: 2
completeness: 50
-
id: 2
is_closed: true
num_issues: 5
num_closed_issues: 5
completeness: 100

View file

@ -417,6 +417,10 @@ var migrations = []Migration{
NewMigration("Conan and generic packages do not need to be semantically versioned", fixPackageSemverField),
// v227 -> v228
NewMigration("Create key/value table for system settings", createSystemSettingsTable),
// v228 -> v229
NewMigration("Add TeamInvite table", addTeamInviteTable),
// v229 -> v230
NewMigration("Update counts of all open milestones", updateOpenMilestoneCounts),
}
// GetCurrentDBVersion returns the current db version

View file

@ -83,17 +83,17 @@ func fixMergeBase(x *xorm.Engine) error {
if !pr.HasMerged {
var err error
pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, "merge-base", "--", pr.BaseBranch, gitRefName).RunStdString(&git.RunOpts{Dir: repoPath})
pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).RunStdString(&git.RunOpts{Dir: repoPath})
if err != nil {
var err2 error
pr.MergeBase, _, err2 = git.NewCommand(git.DefaultContext, "rev-parse", git.BranchPrefix+pr.BaseBranch).RunStdString(&git.RunOpts{Dir: repoPath})
pr.MergeBase, _, err2 = git.NewCommand(git.DefaultContext, "rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).RunStdString(&git.RunOpts{Dir: repoPath})
if err2 != nil {
log.Error("Unable to get merge base for PR ID %d, Index %d in %s/%s. Error: %v & %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err, err2)
continue
}
}
} else {
parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1", pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
if err != nil {
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue
@ -103,10 +103,11 @@ func fixMergeBase(x *xorm.Engine) error {
continue
}
args := append([]string{"merge-base", "--"}, parents[1:]...)
args = append(args, gitRefName)
refs := append([]string{}, parents[1:]...)
refs = append(refs, gitRefName)
cmd := git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(refs...)
pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, args...).RunStdString(&git.RunOpts{Dir: repoPath})
pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
if err != nil {
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue

View file

@ -80,7 +80,7 @@ func refixMergeBase(x *xorm.Engine) error {
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1", pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
if err != nil {
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue
@ -91,10 +91,11 @@ func refixMergeBase(x *xorm.Engine) error {
}
// we should recalculate
args := append([]string{"merge-base", "--"}, parents[1:]...)
args = append(args, gitRefName)
refs := append([]string{}, parents[1:]...)
refs = append(refs, gitRefName)
cmd := git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(refs...)
pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, args...).RunStdString(&git.RunOpts{Dir: repoPath})
pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
if err != nil {
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
continue

26
models/migrations/v228.go Normal file
View file

@ -0,0 +1,26 @@
// Copyright 2022 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 migrations
import (
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)
func addTeamInviteTable(x *xorm.Engine) error {
type TeamInvite struct {
ID int64 `xorm:"pk autoincr"`
Token string `xorm:"UNIQUE(token) INDEX NOT NULL DEFAULT ''"`
InviterID int64 `xorm:"NOT NULL DEFAULT 0"`
OrgID int64 `xorm:"INDEX NOT NULL DEFAULT 0"`
TeamID int64 `xorm:"UNIQUE(team_mail) INDEX NOT NULL DEFAULT 0"`
Email string `xorm:"UNIQUE(team_mail) NOT NULL DEFAULT ''"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
return x.Sync2(new(TeamInvite))
}

47
models/migrations/v229.go Normal file
View file

@ -0,0 +1,47 @@
// Copyright 2022 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 migrations
import (
"fmt"
"code.gitea.io/gitea/models/issues"
"xorm.io/builder"
"xorm.io/xorm"
)
func updateOpenMilestoneCounts(x *xorm.Engine) error {
var openMilestoneIDs []int64
err := x.Table("milestone").Select("id").Where(builder.Neq{"is_closed": 1}).Find(&openMilestoneIDs)
if err != nil {
return fmt.Errorf("error selecting open milestone IDs: %w", err)
}
for _, id := range openMilestoneIDs {
_, err := x.ID(id).
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
builder.Eq{"milestone_id": id},
)).
SetExpr("num_closed_issues", builder.Select("count(*)").From("issue").Where(
builder.Eq{
"milestone_id": id,
"is_closed": true,
},
)).
Update(&issues.Milestone{})
if err != nil {
return fmt.Errorf("error updating issue counts in milestone %d: %w", id, err)
}
_, err = x.Exec("UPDATE `milestone` SET completeness=100*num_closed_issues/(CASE WHEN num_issues > 0 THEN num_issues ELSE 1 END) WHERE id=?",
id,
)
if err != nil {
return fmt.Errorf("error setting completeness on milestone %d: %w", id, err)
}
}
return nil
}

View file

@ -0,0 +1,46 @@
// Copyright 2022 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 migrations
import (
"testing"
"code.gitea.io/gitea/models/issues"
"github.com/stretchr/testify/assert"
)
func Test_updateOpenMilestoneCounts(t *testing.T) {
type ExpectedMilestone issues.Milestone
// Prepare and load the testing database
x, deferable := prepareTestEnv(t, 0, new(issues.Milestone), new(ExpectedMilestone), new(issues.Issue))
defer deferable()
if x == nil || t.Failed() {
return
}
if err := updateOpenMilestoneCounts(x); err != nil {
assert.NoError(t, err)
return
}
expected := []ExpectedMilestone{}
if err := x.Table("expected_milestone").Asc("id").Find(&expected); !assert.NoError(t, err) {
return
}
got := []issues.Milestone{}
if err := x.Table("milestone").Asc("id").Find(&got); !assert.NoError(t, err) {
return
}
for i, e := range expected {
got := got[i]
assert.Equal(t, e.ID, got.ID)
assert.Equal(t, e.NumIssues, got.NumIssues)
assert.Equal(t, e.NumClosedIssues, got.NumClosedIssues)
}
}

View file

@ -431,25 +431,15 @@ func DeleteTeam(t *organization.Team) error {
}
}
// Delete team-user.
if _, err := sess.
Where("org_id=?", t.OrgID).
Where("team_id=?", t.ID).
Delete(new(organization.TeamUser)); err != nil {
if err := db.DeleteBeans(ctx,
&organization.Team{ID: t.ID},
&organization.TeamUser{OrgID: t.OrgID, TeamID: t.ID},
&organization.TeamUnit{TeamID: t.ID},
&organization.TeamInvite{TeamID: t.ID},
); err != nil {
return err
}
// Delete team-unit.
if _, err := sess.
Where("team_id=?", t.ID).
Delete(new(organization.TeamUnit)); err != nil {
return err
}
// Delete team.
if _, err := sess.ID(t.ID).Delete(new(organization.Team)); err != nil {
return err
}
// Update organization number of teams.
if _, err := sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
return err

View file

@ -370,8 +370,9 @@ func DeleteOrganization(ctx context.Context, org *Organization) error {
&OrgUser{OrgID: org.ID},
&TeamUser{OrgID: org.ID},
&TeamUnit{OrgID: org.ID},
&TeamInvite{OrgID: org.ID},
); err != nil {
return fmt.Errorf("deleteBeans: %v", err)
return fmt.Errorf("DeleteBeans: %v", err)
}
if _, err := db.GetEngine(ctx).ID(org.ID).Delete(new(user_model.User)); err != nil {

View file

@ -94,6 +94,7 @@ func init() {
db.RegisterModel(new(TeamUser))
db.RegisterModel(new(TeamRepo))
db.RegisterModel(new(TeamUnit))
db.RegisterModel(new(TeamInvite))
}
// SearchTeamOptions holds the search options

View file

@ -0,0 +1,162 @@
// Copyright 2022 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 organization
import (
"context"
"fmt"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
type ErrTeamInviteAlreadyExist struct {
TeamID int64
Email string
}
func IsErrTeamInviteAlreadyExist(err error) bool {
_, ok := err.(ErrTeamInviteAlreadyExist)
return ok
}
func (err ErrTeamInviteAlreadyExist) Error() string {
return fmt.Sprintf("team invite already exists [team_id: %d, email: %s]", err.TeamID, err.Email)
}
func (err ErrTeamInviteAlreadyExist) Unwrap() error {
return util.ErrAlreadyExist
}
type ErrTeamInviteNotFound struct {
Token string
}
func IsErrTeamInviteNotFound(err error) bool {
_, ok := err.(ErrTeamInviteNotFound)
return ok
}
func (err ErrTeamInviteNotFound) Error() string {
return fmt.Sprintf("team invite was not found [token: %s]", err.Token)
}
func (err ErrTeamInviteNotFound) Unwrap() error {
return util.ErrNotExist
}
// ErrUserEmailAlreadyAdded represents a "user by email already added to team" error.
type ErrUserEmailAlreadyAdded struct {
Email string
}
// IsErrUserEmailAlreadyAdded checks if an error is a ErrUserEmailAlreadyAdded.
func IsErrUserEmailAlreadyAdded(err error) bool {
_, ok := err.(ErrUserEmailAlreadyAdded)
return ok
}
func (err ErrUserEmailAlreadyAdded) Error() string {
return fmt.Sprintf("user with email already added [email: %s]", err.Email)
}
func (err ErrUserEmailAlreadyAdded) Unwrap() error {
return util.ErrAlreadyExist
}
// TeamInvite represents an invite to a team
type TeamInvite struct {
ID int64 `xorm:"pk autoincr"`
Token string `xorm:"UNIQUE(token) INDEX NOT NULL DEFAULT ''"`
InviterID int64 `xorm:"NOT NULL DEFAULT 0"`
OrgID int64 `xorm:"INDEX NOT NULL DEFAULT 0"`
TeamID int64 `xorm:"UNIQUE(team_mail) INDEX NOT NULL DEFAULT 0"`
Email string `xorm:"UNIQUE(team_mail) NOT NULL DEFAULT ''"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
func CreateTeamInvite(ctx context.Context, doer *user_model.User, team *Team, email string) (*TeamInvite, error) {
has, err := db.GetEngine(ctx).Exist(&TeamInvite{
TeamID: team.ID,
Email: email,
})
if err != nil {
return nil, err
}
if has {
return nil, ErrTeamInviteAlreadyExist{
TeamID: team.ID,
Email: email,
}
}
// check if the user is already a team member by email
exist, err := db.GetEngine(ctx).
Where(builder.Eq{
"team_user.org_id": team.OrgID,
"team_user.team_id": team.ID,
"`user`.email": email,
}).
Join("INNER", "`user`", "`user`.id = team_user.uid").
Table("team_user").
Exist()
if err != nil {
return nil, err
}
if exist {
return nil, ErrUserEmailAlreadyAdded{
Email: email,
}
}
token, err := util.CryptoRandomString(25)
if err != nil {
return nil, err
}
invite := &TeamInvite{
Token: token,
InviterID: doer.ID,
OrgID: team.OrgID,
TeamID: team.ID,
Email: email,
}
return invite, db.Insert(ctx, invite)
}
func RemoveInviteByID(ctx context.Context, inviteID, teamID int64) error {
_, err := db.DeleteByBean(ctx, &TeamInvite{
ID: inviteID,
TeamID: teamID,
})
return err
}
func GetInvitesByTeamID(ctx context.Context, teamID int64) ([]*TeamInvite, error) {
invites := make([]*TeamInvite, 0, 10)
return invites, db.GetEngine(ctx).
Where("team_id=?", teamID).
Find(&invites)
}
func GetInviteByToken(ctx context.Context, token string) (*TeamInvite, error) {
invite := &TeamInvite{}
has, err := db.GetEngine(ctx).Where("token=?", token).Get(invite)
if err != nil {
return nil, err
}
if !has {
return nil, ErrTeamInviteNotFound{Token: token}
}
return invite, nil
}

View file

@ -0,0 +1,49 @@
// Copyright 2022 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 organization_test
import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
)
func TestTeamInvite(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
t.Run("MailExistsInTeam", func(t *testing.T) {
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
// user 2 already added to team 2, should result in error
_, err := organization.CreateTeamInvite(db.DefaultContext, user2, team, user2.Email)
assert.Error(t, err)
})
t.Run("CreateAndRemove", func(t *testing.T) {
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
invite, err := organization.CreateTeamInvite(db.DefaultContext, user1, team, "user3@example.com")
assert.NotNil(t, invite)
assert.NoError(t, err)
// Shouldn't allow duplicate invite
_, err = organization.CreateTeamInvite(db.DefaultContext, user1, team, "user3@example.com")
assert.Error(t, err)
// should remove invite
assert.NoError(t, organization.RemoveInviteByID(db.DefaultContext, invite.ID, invite.TeamID))
// invite should not exist
_, err = organization.GetInviteByToken(db.DefaultContext, invite.Token)
assert.Error(t, err)
})
}

View file

@ -165,6 +165,7 @@ type ImageTagsSearchOptions struct {
PackageID int64
Query string
IsTagged bool
Sort packages.VersionSort
db.Paginator
}
@ -195,12 +196,26 @@ func (opts *ImageTagsSearchOptions) toConds() builder.Cond {
return cond
}
func (opts *ImageTagsSearchOptions) configureOrderBy(e db.Engine) {
switch opts.Sort {
case packages.SortVersionDesc:
e.Desc("package_version.version")
case packages.SortVersionAsc:
e.Asc("package_version.version")
case packages.SortCreatedAsc:
e.Asc("package_version.created_unix")
default:
e.Desc("package_version.created_unix")
}
}
// SearchImageTags gets a sorted list of the tags of an image
func SearchImageTags(ctx context.Context, opts *ImageTagsSearchOptions) ([]*packages.PackageVersion, int64, error) {
sess := db.GetEngine(ctx).
Join("INNER", "package", "package.id = package_version.package_id").
Where(opts.toConds()).
Desc("package_version.created_unix")
Where(opts.toConds())
opts.configureOrderBy(sess)
if opts.Paginator != nil {
sess = db.SetSessionPagination(sess, opts)

View file

@ -163,6 +163,17 @@ type SearchValue struct {
ExactMatch bool
}
type VersionSort = string
const (
SortNameAsc VersionSort = "name_asc"
SortNameDesc VersionSort = "name_desc"
SortVersionAsc VersionSort = "version_asc"
SortVersionDesc VersionSort = "version_desc"
SortCreatedAsc VersionSort = "created_asc"
SortCreatedDesc VersionSort = "created_desc"
)
// PackageSearchOptions are options for SearchXXX methods
// Besides IsInternal are all fields optional and are not used if they have their default value (nil, "", 0)
type PackageSearchOptions struct {
@ -176,7 +187,7 @@ type PackageSearchOptions struct {
IsInternal util.OptionalBool
HasFileWithName string // only results are found which are associated with a file with the specific name
HasFiles util.OptionalBool // only results are found which have associated files
Sort string
Sort VersionSort
db.Paginator
}
@ -254,15 +265,15 @@ func (opts *PackageSearchOptions) toConds() builder.Cond {
func (opts *PackageSearchOptions) configureOrderBy(e db.Engine) {
switch opts.Sort {
case "alphabetically":
case SortNameAsc:
e.Asc("package.name")
case "reversealphabetically":
case SortNameDesc:
e.Desc("package.name")
case "highestversion":
case SortVersionDesc:
e.Desc("package_version.version")
case "lowestversion":
case SortVersionAsc:
e.Asc("package_version.version")
case "oldest":
case SortCreatedAsc:
e.Asc("package_version.created_unix")
default:
e.Desc("package_version.created_unix")

View file

@ -123,6 +123,11 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
return err
}
if _, err := db.GetEngine(ctx).In("hook_id", builder.Select("id").From("webhook").Where(builder.Eq{"webhook.repo_id": repo.ID})).
Delete(&webhook.HookTask{}); err != nil {
return err
}
if err := db.DeleteBeans(ctx,
&access_model.Access{RepoID: repo.ID},
&activities_model.Action{RepoID: repo.ID},
@ -130,7 +135,6 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
&issues_model.Comment{RefRepoID: repoID},
&git_model.CommitStatus{RepoID: repoID},
&git_model.DeletedBranch{RepoID: repoID},
&webhook.HookTask{RepoID: repoID},
&git_model.LFSLock{RepoID: repoID},
&repo_model.LanguageStat{RepoID: repoID},
&issues_model.Milestone{RepoID: repoID},

View file

@ -104,7 +104,6 @@ type HookResponse struct {
// HookTask represents a hook task.
type HookTask struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX"`
HookID int64
UUID string
api.Payloader `xorm:"-"`
@ -178,14 +177,29 @@ func HookTasks(hookID int64, page int) ([]*HookTask, error) {
// CreateHookTask creates a new hook task,
// it handles conversion from Payload to PayloadContent.
func CreateHookTask(t *HookTask) error {
func CreateHookTask(ctx context.Context, t *HookTask) (*HookTask, error) {
data, err := t.Payloader.JSONPayload()
if err != nil {
return err
return nil, err
}
t.UUID = gouuid.New().String()
t.PayloadContent = string(data)
return db.Insert(db.DefaultContext, t)
return t, db.Insert(ctx, t)
}
func GetHookTaskByID(ctx context.Context, id int64) (*HookTask, error) {
t := &HookTask{}
has, err := db.GetEngine(ctx).ID(id).Get(t)
if err != nil {
return nil, err
}
if !has {
return nil, ErrHookTaskNotExist{
TaskID: id,
}
}
return t, nil
}
// UpdateHookTask updates information of hook task.
@ -195,53 +209,36 @@ func UpdateHookTask(t *HookTask) error {
}
// ReplayHookTask copies a hook task to get re-delivered
func ReplayHookTask(hookID int64, uuid string) (*HookTask, error) {
var newTask *HookTask
err := db.WithTx(func(ctx context.Context) error {
task := &HookTask{
func ReplayHookTask(ctx context.Context, hookID int64, uuid string) (*HookTask, error) {
task := &HookTask{
HookID: hookID,
UUID: uuid,
}
has, err := db.GetByBean(ctx, task)
if err != nil {
return nil, err
} else if !has {
return nil, ErrHookTaskNotExist{
HookID: hookID,
UUID: uuid,
}
has, err := db.GetByBean(ctx, task)
if err != nil {
return err
} else if !has {
return ErrHookTaskNotExist{
HookID: hookID,
UUID: uuid,
}
}
}
newTask = &HookTask{
UUID: gouuid.New().String(),
RepoID: task.RepoID,
HookID: task.HookID,
PayloadContent: task.PayloadContent,
EventType: task.EventType,
}
return db.Insert(ctx, newTask)
})
return newTask, err
newTask := &HookTask{
UUID: gouuid.New().String(),
HookID: task.HookID,
PayloadContent: task.PayloadContent,
EventType: task.EventType,
}
return newTask, db.Insert(ctx, newTask)
}
// FindUndeliveredHookTasks represents find the undelivered hook tasks
func FindUndeliveredHookTasks() ([]*HookTask, error) {
func FindUndeliveredHookTasks(ctx context.Context) ([]*HookTask, error) {
tasks := make([]*HookTask, 0, 10)
if err := db.GetEngine(db.DefaultContext).Where("is_delivered=?", false).Find(&tasks); err != nil {
return nil, err
}
return tasks, nil
}
// FindRepoUndeliveredHookTasks represents find the undelivered hook tasks of one repository
func FindRepoUndeliveredHookTasks(repoID int64) ([]*HookTask, error) {
tasks := make([]*HookTask, 0, 5)
if err := db.GetEngine(db.DefaultContext).Where("repo_id=? AND is_delivered=?", repoID, false).Find(&tasks); err != nil {
return nil, err
}
return tasks, nil
return tasks, db.GetEngine(ctx).
Where("is_delivered=?", false).
Find(&tasks)
}
// CleanupHookTaskTable deletes rows from hook_task as needed.
@ -250,7 +247,7 @@ func CleanupHookTaskTable(ctx context.Context, cleanupType HookTaskCleanupType,
if cleanupType == OlderThan {
deleteOlderThan := time.Now().Add(-olderThan).UnixNano()
deletes, err := db.GetEngine(db.DefaultContext).
deletes, err := db.GetEngine(ctx).
Where("is_delivered = ? and delivered < ?", true, deleteOlderThan).
Delete(new(HookTask))
if err != nil {
@ -259,7 +256,8 @@ func CleanupHookTaskTable(ctx context.Context, cleanupType HookTaskCleanupType,
log.Trace("Deleted %d rows from hook_task", deletes)
} else if cleanupType == PerWebhook {
hookIDs := make([]int64, 0, 10)
err := db.GetEngine(db.DefaultContext).Table("webhook").
err := db.GetEngine(ctx).
Table("webhook").
Where("id > 0").
Cols("id").
Find(&hookIDs)

View file

@ -19,13 +19,6 @@ import (
"xorm.io/builder"
)
// __ __ ___. .__ __
// / \ / \ ____\_ |__ | |__ ____ ____ | | __
// \ \/\/ // __ \| __ \| | \ / _ \ / _ \| |/ /
// \ /\ ___/| \_\ \ Y ( <_> | <_> ) <
// \__/\ / \___ >___ /___| /\____/ \____/|__|_ \
// \/ \/ \/ \/ \/
// ErrWebhookNotExist represents a "WebhookNotExist" kind of error.
type ErrWebhookNotExist struct {
ID int64
@ -47,6 +40,7 @@ func (err ErrWebhookNotExist) Unwrap() error {
// ErrHookTaskNotExist represents a "HookTaskNotExist" kind of error.
type ErrHookTaskNotExist struct {
TaskID int64
HookID int64
UUID string
}
@ -58,7 +52,7 @@ func IsErrHookTaskNotExist(err error) bool {
}
func (err ErrHookTaskNotExist) Error() string {
return fmt.Sprintf("hook task does not exist [hook: %d, uuid: %s]", err.HookID, err.UUID)
return fmt.Sprintf("hook task does not exist [task: %d, hook: %d, uuid: %s]", err.TaskID, err.HookID, err.UUID)
}
func (err ErrHookTaskNotExist) Unwrap() error {

View file

@ -208,12 +208,12 @@ func TestHookTasks(t *testing.T) {
func TestCreateHookTask(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hookTask := &HookTask{
RepoID: 3,
HookID: 3,
Payloader: &api.PushPayload{},
}
unittest.AssertNotExistsBean(t, hookTask)
assert.NoError(t, CreateHookTask(hookTask))
_, err := CreateHookTask(db.DefaultContext, hookTask)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask)
}
@ -232,14 +232,14 @@ func TestUpdateHookTask(t *testing.T) {
func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hookTask := &HookTask{
RepoID: 3,
HookID: 3,
Payloader: &api.PushPayload{},
IsDelivered: true,
Delivered: time.Now().UnixNano(),
}
unittest.AssertNotExistsBean(t, hookTask)
assert.NoError(t, CreateHookTask(hookTask))
_, err := CreateHookTask(db.DefaultContext, hookTask)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask)
assert.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 0))
@ -249,13 +249,13 @@ func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) {
func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hookTask := &HookTask{
RepoID: 2,
HookID: 4,
Payloader: &api.PushPayload{},
IsDelivered: false,
}
unittest.AssertNotExistsBean(t, hookTask)
assert.NoError(t, CreateHookTask(hookTask))
_, err := CreateHookTask(db.DefaultContext, hookTask)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask)
assert.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 0))
@ -265,14 +265,14 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) {
func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hookTask := &HookTask{
RepoID: 2,
HookID: 4,
Payloader: &api.PushPayload{},
IsDelivered: true,
Delivered: time.Now().UnixNano(),
}
unittest.AssertNotExistsBean(t, hookTask)
assert.NoError(t, CreateHookTask(hookTask))
_, err := CreateHookTask(db.DefaultContext, hookTask)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask)
assert.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 1))
@ -282,14 +282,14 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) {
func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hookTask := &HookTask{
RepoID: 3,
HookID: 3,
Payloader: &api.PushPayload{},
IsDelivered: true,
Delivered: time.Now().AddDate(0, 0, -8).UnixNano(),
}
unittest.AssertNotExistsBean(t, hookTask)
assert.NoError(t, CreateHookTask(hookTask))
_, err := CreateHookTask(db.DefaultContext, hookTask)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask)
assert.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0))
@ -299,13 +299,13 @@ func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) {
func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hookTask := &HookTask{
RepoID: 2,
HookID: 4,
Payloader: &api.PushPayload{},
IsDelivered: false,
}
unittest.AssertNotExistsBean(t, hookTask)
assert.NoError(t, CreateHookTask(hookTask))
_, err := CreateHookTask(db.DefaultContext, hookTask)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask)
assert.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0))
@ -315,14 +315,14 @@ func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) {
func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hookTask := &HookTask{
RepoID: 2,
HookID: 4,
Payloader: &api.PushPayload{},
IsDelivered: true,
Delivered: time.Now().AddDate(0, 0, -6).UnixNano(),
}
unittest.AssertNotExistsBean(t, hookTask)
assert.NoError(t, CreateHookTask(hookTask))
_, err := CreateHookTask(db.DefaultContext, hookTask)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, hookTask)
assert.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0))

View file

@ -21,7 +21,7 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool)
numRepos++
runOpts := &git.RunOpts{Dir: repo.RepoPath()}
_, _, defaultBranchErr := git.NewCommand(ctx, "rev-parse", "--", repo.DefaultBranch).RunStdString(runOpts)
_, _, defaultBranchErr := git.NewCommand(ctx, "rev-parse").AddDashesAndList(repo.DefaultBranch).RunStdString(runOpts)
head, _, headErr := git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD").RunStdString(runOpts)
@ -49,7 +49,7 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool)
}
// otherwise, let's try fixing HEAD
err := git.NewCommand(ctx, "symbolic-ref", "--", "HEAD", repo.DefaultBranch).Run(runOpts)
err := git.NewCommand(ctx, "symbolic-ref").AddDashesAndList("HEAD", repo.DefaultBranch).Run(runOpts)
if err != nil {
logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err)
return nil

View file

@ -44,17 +44,17 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro
if !pr.HasMerged {
var err error
pr.MergeBase, _, err = git.NewCommand(ctx, "merge-base", "--", pr.BaseBranch, pr.GetGitRefName()).RunStdString(&git.RunOpts{Dir: repoPath})
pr.MergeBase, _, err = git.NewCommand(ctx, "merge-base").AddDashesAndList(pr.BaseBranch, pr.GetGitRefName()).RunStdString(&git.RunOpts{Dir: repoPath})
if err != nil {
var err2 error
pr.MergeBase, _, err2 = git.NewCommand(ctx, "rev-parse", git.BranchPrefix+pr.BaseBranch).RunStdString(&git.RunOpts{Dir: repoPath})
pr.MergeBase, _, err2 = git.NewCommand(ctx, "rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).RunStdString(&git.RunOpts{Dir: repoPath})
if err2 != nil {
logger.Warn("Unable to get merge base for PR ID %d, #%d onto %s in %s/%s. Error: %v & %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err, err2)
return nil
}
}
} else {
parentsString, _, err := git.NewCommand(ctx, "rev-list", "--parents", "-n", "1", pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
parentsString, _, err := git.NewCommand(ctx, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
if err != nil {
logger.Warn("Unable to get parents for merged PR ID %d, #%d onto %s in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err)
return nil
@ -64,10 +64,10 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro
return nil
}
args := append([]string{"merge-base", "--"}, parents[1:]...)
args = append(args, pr.GetGitRefName())
pr.MergeBase, _, err = git.NewCommand(ctx, args...).RunStdString(&git.RunOpts{Dir: repoPath})
refs := append([]string{}, parents[1:]...)
refs = append(refs, pr.GetGitRefName())
cmd := git.NewCommand(ctx, "merge-base").AddDashesAndList(refs...)
pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
if err != nil {
logger.Warn("Unable to get merge base for merged PR ID %d, #%d onto %s in %s/%s. Error: %v", pr.ID, pr.Index, pr.BaseBranch, pr.BaseRepo.OwnerName, pr.BaseRepo.Name, err)
return nil

View file

@ -24,7 +24,7 @@ import (
var (
// globalCommandArgs global command args for external package setting
globalCommandArgs []string
globalCommandArgs []CmdArg
// defaultCommandExecutionTimeout default command execution timeout duration
defaultCommandExecutionTimeout = 360 * time.Second
@ -43,6 +43,8 @@ type Command struct {
brokenArgs []string
}
type CmdArg string
func (c *Command) String() string {
if len(c.args) == 0 {
return c.name
@ -52,13 +54,18 @@ func (c *Command) String() string {
// NewCommand creates and returns a new Git Command based on given command and arguments.
// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
func NewCommand(ctx context.Context, args ...string) *Command {
func NewCommand(ctx context.Context, args ...CmdArg) *Command {
// Make an explicit copy of globalCommandArgs, otherwise append might overwrite it
cargs := make([]string, len(globalCommandArgs))
copy(cargs, globalCommandArgs)
cargs := make([]string, 0, len(globalCommandArgs)+len(args))
for _, arg := range globalCommandArgs {
cargs = append(cargs, string(arg))
}
for _, arg := range args {
cargs = append(cargs, string(arg))
}
return &Command{
name: GitExecutable,
args: append(cargs, args...),
args: cargs,
parentContext: ctx,
globalArgsLength: len(globalCommandArgs),
}
@ -66,16 +73,20 @@ func NewCommand(ctx context.Context, args ...string) *Command {
// NewCommandNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args
// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
func NewCommandNoGlobals(args ...string) *Command {
func NewCommandNoGlobals(args ...CmdArg) *Command {
return NewCommandContextNoGlobals(DefaultContext, args...)
}
// NewCommandContextNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args
// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
func NewCommandContextNoGlobals(ctx context.Context, args ...string) *Command {
func NewCommandContextNoGlobals(ctx context.Context, args ...CmdArg) *Command {
cargs := make([]string, 0, len(args))
for _, arg := range args {
cargs = append(cargs, string(arg))
}
return &Command{
name: GitExecutable,
args: args,
args: cargs,
parentContext: ctx,
}
}
@ -93,10 +104,12 @@ func (c *Command) SetDescription(desc string) *Command {
return c
}
// AddArguments adds new argument(s) to the command. Each argument must be safe to be trusted.
// AddArguments adds new git argument(s) to the command. Each argument must be safe to be trusted.
// User-provided arguments should be passed to AddDynamicArguments instead.
func (c *Command) AddArguments(args ...string) *Command {
c.args = append(c.args, args...)
func (c *Command) AddArguments(args ...CmdArg) *Command {
for _, arg := range args {
c.args = append(c.args, string(arg))
}
return c
}
@ -115,6 +128,26 @@ func (c *Command) AddDynamicArguments(args ...string) *Command {
return c
}
// AddDashesAndList adds the "--" and then add the list as arguments, it's usually for adding file list
// At the moment, this function can be only called once, maybe in future it can be refactored to support multiple calls (if necessary)
func (c *Command) AddDashesAndList(list ...string) *Command {
c.args = append(c.args, "--")
// Some old code also checks `arg != ""`, IMO it's not necessary.
// If the check is needed, the list should be prepared before the call to this function
c.args = append(c.args, list...)
return c
}
// CmdArgCheck checks whether the string is safe to be used as a dynamic argument.
// It panics if the check fails. Usually it should not be used, it's just for refactoring purpose
// deprecated
func CmdArgCheck(s string) CmdArg {
if s != "" && s[0] == '-' {
panic("invalid git cmd argument: " + s)
}
return CmdArg(s)
}
// RunOpts represents parameters to run the command. If UseContextTimeout is specified, then Timeout is ignored.
type RunOpts struct {
Env []string
@ -153,7 +186,7 @@ func CommonGitCmdEnvs() []string {
}...)
}
// CommonCmdServEnvs is like CommonGitCmdEnvs but it only returns minimal required environment variables for the "gitea serv" command
// CommonCmdServEnvs is like CommonGitCmdEnvs, but it only returns minimal required environment variables for the "gitea serv" command
func CommonCmdServEnvs() []string {
return commonBaseEnvs()
}
@ -318,12 +351,12 @@ func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunS
}
// AllowLFSFiltersArgs return globalCommandArgs with lfs filter, it should only be used for tests
func AllowLFSFiltersArgs() []string {
func AllowLFSFiltersArgs() []CmdArg {
// Now here we should explicitly allow lfs filters to run
filteredLFSGlobalArgs := make([]string, len(globalCommandArgs))
filteredLFSGlobalArgs := make([]CmdArg, len(globalCommandArgs))
j := 0
for _, arg := range globalCommandArgs {
if strings.Contains(arg, "lfs") {
if strings.Contains(string(arg), "lfs") {
j--
} else {
filteredLFSGlobalArgs[j] = arg

View file

@ -92,13 +92,13 @@ func AddChanges(repoPath string, all bool, files ...string) error {
}
// AddChangesWithArgs marks local changes to be ready for commit.
func AddChangesWithArgs(repoPath string, globalArgs []string, all bool, files ...string) error {
func AddChangesWithArgs(repoPath string, globalArgs []CmdArg, all bool, files ...string) error {
cmd := NewCommandNoGlobals(append(globalArgs, "add")...)
if all {
cmd.AddArguments("--all")
}
cmd.AddArguments("--")
_, _, err := cmd.AddArguments(files...).RunStdString(&RunOpts{Dir: repoPath})
cmd.AddDashesAndList(files...)
_, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
return err
}
@ -112,17 +112,17 @@ type CommitChangesOptions struct {
// CommitChanges commits local changes with given committer, author and message.
// If author is nil, it will be the same as committer.
func CommitChanges(repoPath string, opts CommitChangesOptions) error {
cargs := make([]string, len(globalCommandArgs))
cargs := make([]CmdArg, len(globalCommandArgs))
copy(cargs, globalCommandArgs)
return CommitChangesWithArgs(repoPath, cargs, opts)
}
// CommitChangesWithArgs commits local changes with given committer, author and message.
// If author is nil, it will be the same as committer.
func CommitChangesWithArgs(repoPath string, args []string, opts CommitChangesOptions) error {
func CommitChangesWithArgs(repoPath string, args []CmdArg, opts CommitChangesOptions) error {
cmd := NewCommandNoGlobals(args...)
if opts.Committer != nil {
cmd.AddArguments("-c", "user.name="+opts.Committer.Name, "-c", "user.email="+opts.Committer.Email)
cmd.AddArguments("-c", CmdArg("user.name="+opts.Committer.Name), "-c", CmdArg("user.email="+opts.Committer.Email))
}
cmd.AddArguments("commit")
@ -130,9 +130,9 @@ func CommitChangesWithArgs(repoPath string, args []string, opts CommitChangesOpt
opts.Author = opts.Committer
}
if opts.Author != nil {
cmd.AddArguments(fmt.Sprintf("--author='%s <%s>'", opts.Author.Name, opts.Author.Email))
cmd.AddArguments(CmdArg(fmt.Sprintf("--author='%s <%s>'", opts.Author.Name, opts.Author.Email)))
}
cmd.AddArguments("-m", opts.Message)
cmd.AddArguments("-m").AddDynamicArguments(opts.Message)
_, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
// No stderr but exit status 1 means nothing to commit.
@ -144,15 +144,13 @@ func CommitChangesWithArgs(repoPath string, args []string, opts CommitChangesOpt
// AllCommitsCount returns count of all commits in repository
func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, files ...string) (int64, error) {
args := []string{"--all", "--count"}
if hidePRRefs {
args = append([]string{"--exclude=" + PullPrefix + "*"}, args...)
}
cmd := NewCommand(ctx, "rev-list")
cmd.AddArguments(args...)
if hidePRRefs {
cmd.AddArguments("--exclude=" + PullPrefix + "*")
}
cmd.AddArguments("--all", "--count")
if len(files) > 0 {
cmd.AddArguments("--")
cmd.AddArguments(files...)
cmd.AddDashesAndList(files...)
}
stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
@ -168,8 +166,7 @@ func CommitsCountFiles(ctx context.Context, repoPath string, revision, relpath [
cmd := NewCommand(ctx, "rev-list", "--count")
cmd.AddDynamicArguments(revision...)
if len(relpath) > 0 {
cmd.AddArguments("--")
cmd.AddArguments(relpath...)
cmd.AddDashesAndList(relpath...)
}
stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
@ -209,7 +206,7 @@ func (c *Commit) HasPreviousCommit(commitHash SHA1) (bool, error) {
return false, nil
}
_, _, err := NewCommand(c.repo.Ctx, "merge-base", "--is-ancestor", that, this).RunStdString(&RunOpts{Dir: c.repo.Path})
_, _, err := NewCommand(c.repo.Ctx, "merge-base", "--is-ancestor").AddDynamicArguments(that, this).RunStdString(&RunOpts{Dir: c.repo.Path})
if err == nil {
return true, nil
}
@ -392,15 +389,12 @@ func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
// GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only')
func (c *Commit) GetBranchName() (string, error) {
args := []string{
"name-rev",
}
cmd := NewCommand(c.repo.Ctx, "name-rev")
if CheckGitVersionAtLeast("2.13.0") == nil {
args = append(args, "--exclude", "refs/tags/*")
cmd.AddArguments("--exclude", "refs/tags/*")
}
args = append(args, "--name-only", "--no-undefined", c.ID.String())
data, _, err := NewCommand(c.repo.Ctx, args...).RunStdString(&RunOpts{Dir: c.repo.Path})
cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String())
data, _, err := cmd.RunStdString(&RunOpts{Dir: c.repo.Path})
if err != nil {
// handle special case where git can not describe commit
if strings.Contains(err.Error(), "cannot describe") {
@ -426,7 +420,7 @@ func (c *Commit) LoadBranchName() (err error) {
// GetTagName gets the current tag name for given commit
func (c *Commit) GetTagName() (string, error) {
data, _, err := NewCommand(c.repo.Ctx, "describe", "--exact-match", "--tags", "--always", c.ID.String()).RunStdString(&RunOpts{Dir: c.repo.Path})
data, _, err := NewCommand(c.repo.Ctx, "describe", "--exact-match", "--tags", "--always").AddDynamicArguments(c.ID.String()).RunStdString(&RunOpts{Dir: c.repo.Path})
if err != nil {
// handle special case where there is no tag for this commit
if strings.Contains(err.Error(), "no tag exactly matches") {
@ -503,9 +497,7 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi
}()
stderr := new(bytes.Buffer)
args := []string{"log", "--name-status", "-c", "--pretty=format:", "--parents", "--no-renames", "-z", "-1", commitID}
err := NewCommand(ctx, args...).Run(&RunOpts{
err := NewCommand(ctx, "log", "--name-status", "-c", "--pretty=format:", "--parents", "--no-renames", "-z", "-1").AddDynamicArguments(commitID).Run(&RunOpts{
Dir: repoPath,
Stdout: w,
Stderr: stderr,
@ -521,7 +513,7 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi
// GetFullCommitID returns full length (40) of commit ID by given short SHA in a repository.
func GetFullCommitID(ctx context.Context, repoPath, shortID string) (string, error) {
commitID, _, err := NewCommand(ctx, "rev-parse", shortID).RunStdString(&RunOpts{Dir: repoPath})
commitID, _, err := NewCommand(ctx, "rev-parse").AddDynamicArguments(shortID).RunStdString(&RunOpts{Dir: repoPath})
if err != nil {
if strings.Contains(err.Error(), "exit status 128") {
return "", ErrNotExist{shortID, ""}

View file

@ -35,7 +35,7 @@ func GetRawDiff(repo *Repository, commitID string, diffType RawDiffType, writer
// GetReverseRawDiff dumps the reverse diff results of repository in given commit ID to io.Writer.
func GetReverseRawDiff(ctx context.Context, repoPath, commitID string, writer io.Writer) error {
stderr := new(bytes.Buffer)
cmd := NewCommand(ctx, "show", "--pretty=format:revert %H%n", "-R", commitID)
cmd := NewCommand(ctx, "show", "--pretty=format:revert %H%n", "-R").AddDynamicArguments(commitID)
if err := cmd.Run(&RunOpts{
Dir: repoPath,
Stdout: writer,
@ -52,39 +52,38 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
if err != nil {
return err
}
fileArgs := make([]string, 0)
var files []string
if len(file) > 0 {
fileArgs = append(fileArgs, "--", file)
files = append(files, file)
}
var args []string
cmd := NewCommand(repo.Ctx)
switch diffType {
case RawDiffNormal:
if len(startCommit) != 0 {
args = append([]string{"diff", "-M", startCommit, endCommit}, fileArgs...)
cmd.AddArguments("diff", "-M").AddDynamicArguments(startCommit, endCommit).AddDashesAndList(files...)
} else if commit.ParentCount() == 0 {
args = append([]string{"show", endCommit}, fileArgs...)
cmd.AddArguments("show").AddDynamicArguments(endCommit).AddDashesAndList(files...)
} else {
c, _ := commit.Parent(0)
args = append([]string{"diff", "-M", c.ID.String(), endCommit}, fileArgs...)
cmd.AddArguments("diff", "-M").AddDynamicArguments(c.ID.String(), endCommit).AddDashesAndList(files...)
}
case RawDiffPatch:
if len(startCommit) != 0 {
query := fmt.Sprintf("%s...%s", endCommit, startCommit)
args = append([]string{"format-patch", "--no-signature", "--stdout", "--root", query}, fileArgs...)
cmd.AddArguments("format-patch", "--no-signature", "--stdout", "--root").AddDynamicArguments(query).AddDashesAndList(files...)
} else if commit.ParentCount() == 0 {
args = append([]string{"format-patch", "--no-signature", "--stdout", "--root", endCommit}, fileArgs...)
cmd.AddArguments("format-patch", "--no-signature", "--stdout", "--root").AddDynamicArguments(endCommit).AddDashesAndList(files...)
} else {
c, _ := commit.Parent(0)
query := fmt.Sprintf("%s...%s", endCommit, c.ID.String())
args = append([]string{"format-patch", "--no-signature", "--stdout", query}, fileArgs...)
cmd.AddArguments("format-patch", "--no-signature", "--stdout").AddDynamicArguments(query).AddDashesAndList(files...)
}
default:
return fmt.Errorf("invalid diffType: %s", diffType)
}
stderr := new(bytes.Buffer)
cmd := NewCommand(repo.Ctx, args...)
if err = cmd.Run(&RunOpts{
Dir: repo.Path,
Stdout: writer,
@ -287,7 +286,7 @@ func GetAffectedFiles(repo *Repository, oldCommitID, newCommitID string, env []s
affectedFiles := make([]string, 0, 32)
// Run `git diff --name-only` to get the names of the changed files
err = NewCommand(repo.Ctx, "diff", "--name-only", oldCommitID, newCommitID).
err = NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(oldCommitID, newCommitID).
Run(&RunOpts{
Env: env,
Dir: repo.Path,

View file

@ -313,7 +313,7 @@ func CheckGitVersionAtLeast(atLeast string) error {
}
func configSet(key, value string) error {
stdout, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil)
stdout, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key).RunStdString(nil)
if err != nil && !err.IsExitCode(1) {
return fmt.Errorf("failed to get git config %s, err: %w", key, err)
}
@ -323,7 +323,7 @@ func configSet(key, value string) error {
return nil
}
_, _, err = NewCommand(DefaultContext, "config", "--global", key, value).RunStdString(nil)
_, _, err = NewCommand(DefaultContext, "config", "--global").AddDynamicArguments(key, value).RunStdString(nil)
if err != nil {
return fmt.Errorf("failed to set git global config %s, err: %w", key, err)
}
@ -332,14 +332,14 @@ func configSet(key, value string) error {
}
func configSetNonExist(key, value string) error {
_, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil)
_, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key).RunStdString(nil)
if err == nil {
// already exist
return nil
}
if err.IsExitCode(1) {
// not exist, set new config
_, _, err = NewCommand(DefaultContext, "config", "--global", key, value).RunStdString(nil)
_, _, err = NewCommand(DefaultContext, "config", "--global").AddDynamicArguments(key, value).RunStdString(nil)
if err != nil {
return fmt.Errorf("failed to set git global config %s, err: %w", key, err)
}
@ -350,14 +350,14 @@ func configSetNonExist(key, value string) error {
}
func configAddNonExist(key, value string) error {
_, _, err := NewCommand(DefaultContext, "config", "--get", key, regexp.QuoteMeta(value)).RunStdString(nil)
_, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(nil)
if err == nil {
// already exist
return nil
}
if err.IsExitCode(1) {
// not exist, add new config
_, _, err = NewCommand(DefaultContext, "config", "--global", "--add", key, value).RunStdString(nil)
_, _, err = NewCommand(DefaultContext, "config", "--global", "--add").AddDynamicArguments(key, value).RunStdString(nil)
if err != nil {
return fmt.Errorf("failed to add git global config %s, err: %w", key, err)
}
@ -367,10 +367,10 @@ func configAddNonExist(key, value string) error {
}
func configUnsetAll(key, value string) error {
_, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil)
_, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key).RunStdString(nil)
if err == nil {
// exist, need to remove
_, _, err = NewCommand(DefaultContext, "config", "--global", "--unset-all", key, regexp.QuoteMeta(value)).RunStdString(nil)
_, _, err = NewCommand(DefaultContext, "config", "--global", "--unset-all").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(nil)
if err != nil {
return fmt.Errorf("failed to unset git global config %s, err: %w", key, err)
}
@ -384,6 +384,6 @@ func configUnsetAll(key, value string) error {
}
// Fsck verifies the connectivity and validity of the objects in the database
func Fsck(ctx context.Context, repoPath string, timeout time.Duration, args ...string) error {
func Fsck(ctx context.Context, repoPath string, timeout time.Duration, args ...CmdArg) error {
return NewCommand(ctx, "fsck").AddArguments(args...).Run(&RunOpts{Timeout: timeout, Dir: repoPath})
}

View file

@ -35,30 +35,33 @@ func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, p
_ = stdoutWriter.Close()
}
args := make([]string, 0, 8+len(paths))
args = append(args, "log", "--name-status", "-c", "--format=commit%x00%H %P%x00", "--parents", "--no-renames", "-t", "-z", head, "--")
cmd := NewCommand(ctx)
cmd.AddArguments("log", "--name-status", "-c", "--format=commit%x00%H %P%x00", "--parents", "--no-renames", "-t", "-z").AddDynamicArguments(head)
var files []string
if len(paths) < 70 {
if treepath != "" {
args = append(args, treepath)
files = append(files, treepath)
for _, pth := range paths {
if pth != "" {
args = append(args, path.Join(treepath, pth))
files = append(files, path.Join(treepath, pth))
}
}
} else {
for _, pth := range paths {
if pth != "" {
args = append(args, pth)
files = append(files, pth)
}
}
}
} else if treepath != "" {
args = append(args, treepath)
files = append(files, treepath)
}
cmd.AddDashesAndList(files...)
go func() {
stderr := strings.Builder{}
err := NewCommand(ctx, args...).Run(&RunOpts{
err := cmd.Run(&RunOpts{
Dir: repository,
Stdout: stdoutWriter,
Stderr: &stderr,

View file

@ -43,7 +43,7 @@ func RevListObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.
defer revListWriter.Close()
stderr := new(bytes.Buffer)
var errbuf strings.Builder
cmd := git.NewCommand(ctx, "rev-list", "--objects", headSHA, "--not", baseSHA)
cmd := git.NewCommand(ctx, "rev-list", "--objects").AddDynamicArguments(headSHA).AddArguments("--not").AddDynamicArguments(baseSHA)
if err := cmd.Run(&git.RunOpts{
Dir: tmpBasePath,
Stdout: revListWriter,

View file

@ -14,9 +14,9 @@ import (
func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) {
var cmd *Command
if CheckGitVersionAtLeast("2.7") == nil {
cmd = NewCommand(ctx, "remote", "get-url", remoteName)
cmd = NewCommand(ctx, "remote", "get-url").AddDynamicArguments(remoteName)
} else {
cmd = NewCommand(ctx, "config", "--get", "remote."+remoteName+".url")
cmd = NewCommand(ctx, "config", "--get").AddDynamicArguments("remote." + remoteName + ".url")
}
result, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})

View file

@ -59,7 +59,7 @@ func (repo *Repository) parsePrettyFormatLogToList(logs []byte) ([]*Commit, erro
// IsRepoURLAccessible checks if given repository URL is accessible.
func IsRepoURLAccessible(ctx context.Context, url string) bool {
_, _, err := NewCommand(ctx, "ls-remote", "-q", "-h", url, "HEAD").RunStdString(nil)
_, _, err := NewCommand(ctx, "ls-remote", "-q", "-h").AddDynamicArguments(url, "HEAD").RunStdString(nil)
return err == nil
}
@ -112,13 +112,11 @@ type CloneRepoOptions struct {
// Clone clones original repository to target path.
func Clone(ctx context.Context, from, to string, opts CloneRepoOptions) error {
cargs := make([]string, len(globalCommandArgs))
copy(cargs, globalCommandArgs)
return CloneWithArgs(ctx, from, to, cargs, opts)
return CloneWithArgs(ctx, globalCommandArgs, from, to, opts)
}
// CloneWithArgs original repository to target path.
func CloneWithArgs(ctx context.Context, from, to string, args []string, opts CloneRepoOptions) (err error) {
func CloneWithArgs(ctx context.Context, args []CmdArg, from, to string, opts CloneRepoOptions) (err error) {
toDir := path.Dir(to)
if err = os.MkdirAll(toDir, os.ModePerm); err != nil {
return err
@ -144,15 +142,15 @@ func CloneWithArgs(ctx context.Context, from, to string, args []string, opts Clo
cmd.AddArguments("--no-checkout")
}
if opts.Depth > 0 {
cmd.AddArguments("--depth", strconv.Itoa(opts.Depth))
cmd.AddArguments("--depth").AddDynamicArguments(strconv.Itoa(opts.Depth))
}
if opts.Filter != "" {
cmd.AddArguments("--filter", opts.Filter)
cmd.AddArguments("--filter").AddDynamicArguments(opts.Filter)
}
if len(opts.Branch) > 0 {
cmd.AddArguments("-b", opts.Branch)
cmd.AddArguments("-b").AddDynamicArguments(opts.Branch)
}
cmd.AddArguments("--", from, to)
cmd.AddDashesAndList(from, to)
if strings.Contains(from, "://") && strings.Contains(from, "@") {
cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, util.SanitizeCredentialURLs(from), to, opts.Shared, opts.Mirror, opts.Depth))
@ -203,10 +201,12 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
if opts.Mirror {
cmd.AddArguments("--mirror")
}
cmd.AddArguments("--", opts.Remote)
remoteBranchArgs := []string{opts.Remote}
if len(opts.Branch) > 0 {
cmd.AddArguments(opts.Branch)
remoteBranchArgs = append(remoteBranchArgs, opts.Branch)
}
cmd.AddDashesAndList(remoteBranchArgs...)
if strings.Contains(opts.Remote, "://") && strings.Contains(opts.Remote, "@") {
cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, util.SanitizeCredentialURLs(opts.Remote), opts.Force, opts.Mirror))
} else {
@ -276,7 +276,7 @@ type DivergeObject struct {
func checkDivergence(ctx context.Context, repoPath, baseBranch, targetBranch string) (int, error) {
branches := fmt.Sprintf("%s..%s", baseBranch, targetBranch)
cmd := NewCommand(ctx, "rev-list", "--count", branches)
cmd := NewCommand(ctx, "rev-list", "--count").AddDynamicArguments(branches)
stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
if err != nil {
return -1, err
@ -319,7 +319,7 @@ func (repo *Repository) CreateBundle(ctx context.Context, commit string, out io.
return err
}
_, _, err = NewCommand(ctx, "reset", "--soft", commit).RunStdString(&RunOpts{Dir: tmp, Env: env})
_, _, err = NewCommand(ctx, "reset", "--soft").AddDynamicArguments(commit).RunStdString(&RunOpts{Dir: tmp, Env: env})
if err != nil {
return err
}
@ -330,7 +330,7 @@ func (repo *Repository) CreateBundle(ctx context.Context, commit string, out io.
}
tmpFile := filepath.Join(tmp, "bundle")
_, _, err = NewCommand(ctx, "bundle", "create", tmpFile, "bundle", "HEAD").RunStdString(&RunOpts{Dir: tmp, Env: env})
_, _, err = NewCommand(ctx, "bundle", "create").AddDynamicArguments(tmpFile, "bundle", "HEAD").RunStdString(&RunOpts{Dir: tmp, Env: env})
if err != nil {
return err
}

View file

@ -44,20 +44,15 @@ func (repo *Repository) CreateArchive(ctx context.Context, format ArchiveType, t
return fmt.Errorf("unknown format: %v", format)
}
args := []string{
"archive",
}
cmd := NewCommand(ctx, "archive")
if usePrefix {
args = append(args, "--prefix="+filepath.Base(strings.TrimSuffix(repo.Path, ".git"))+"/")
cmd.AddArguments(CmdArg("--prefix=" + filepath.Base(strings.TrimSuffix(repo.Path, ".git")) + "/"))
}
args = append(args,
"--format="+format.String(),
commitID,
)
cmd.AddArguments(CmdArg("--format=" + format.String()))
cmd.AddDynamicArguments(commitID)
var stderr strings.Builder
err := NewCommand(ctx, args...).Run(&RunOpts{
err := cmd.Run(&RunOpts{
Dir: repo.Path,
Stdout: target,
Stderr: &stderr,

View file

@ -20,7 +20,7 @@ import (
type CheckAttributeOpts struct {
CachedOnly bool
AllAttributes bool
Attributes []string
Attributes []CmdArg
Filenames []string
IndexFile string
WorkTree string
@ -44,31 +44,23 @@ func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[
stdOut := new(bytes.Buffer)
stdErr := new(bytes.Buffer)
cmdArgs := []string{"check-attr", "-z"}
cmd := NewCommand(repo.Ctx, "check-attr", "-z")
if opts.AllAttributes {
cmdArgs = append(cmdArgs, "-a")
cmd.AddArguments("-a")
} else {
for _, attribute := range opts.Attributes {
if attribute != "" {
cmdArgs = append(cmdArgs, attribute)
cmd.AddArguments(attribute)
}
}
}
if opts.CachedOnly {
cmdArgs = append(cmdArgs, "--cached")
cmd.AddArguments("--cached")
}
cmdArgs = append(cmdArgs, "--")
for _, arg := range opts.Filenames {
if arg != "" {
cmdArgs = append(cmdArgs, arg)
}
}
cmd := NewCommand(repo.Ctx, cmdArgs...)
cmd.AddDashesAndList(opts.Filenames...)
if err := cmd.Run(&RunOpts{
Env: env,
@ -106,7 +98,7 @@ func (repo *Repository) CheckAttribute(opts CheckAttributeOpts) (map[string]map[
// CheckAttributeReader provides a reader for check-attribute content that can be long running
type CheckAttributeReader struct {
// params
Attributes []string
Attributes []CmdArg
Repo *Repository
IndexFile string
WorkTree string
@ -122,7 +114,7 @@ type CheckAttributeReader struct {
// Init initializes the CheckAttributeReader
func (c *CheckAttributeReader) Init(ctx context.Context) error {
cmdArgs := []string{"check-attr", "--stdin", "-z"}
cmdArgs := []CmdArg{"check-attr", "--stdin", "-z"}
if len(c.IndexFile) > 0 {
cmdArgs = append(cmdArgs, "--cached")
@ -401,7 +393,7 @@ func (repo *Repository) CheckAttributeReader(commitID string) (*CheckAttributeRe
}
checker := &CheckAttributeReader{
Attributes: []string{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"},
Attributes: []CmdArg{"linguist-vendored", "linguist-generated", "linguist-language", "gitlab-language"},
Repo: repo,
IndexFile: indexFilename,
WorkTree: worktree,

View file

@ -8,13 +8,16 @@ import "fmt"
// FileBlame return the Blame object of file
func (repo *Repository) FileBlame(revision, path, file string) ([]byte, error) {
stdout, _, err := NewCommand(repo.Ctx, "blame", "--root", "--", file).RunStdBytes(&RunOpts{Dir: path})
stdout, _, err := NewCommand(repo.Ctx, "blame", "--root").AddDashesAndList(file).RunStdBytes(&RunOpts{Dir: path})
return stdout, err
}
// LineBlame returns the latest commit at the given line
func (repo *Repository) LineBlame(revision, path, file string, line uint) (*Commit, error) {
res, _, err := NewCommand(repo.Ctx, "blame", fmt.Sprintf("-L %d,%d", line, line), "-p", revision, "--", file).RunStdString(&RunOpts{Dir: path})
res, _, err := NewCommand(repo.Ctx, "blame").
AddArguments(CmdArg(fmt.Sprintf("-L %d,%d", line, line))).
AddArguments("-p").AddDynamicArguments(revision).
AddDashesAndList(file).RunStdString(&RunOpts{Dir: path})
if err != nil {
return nil, err
}

View file

@ -25,7 +25,7 @@ const PullRequestPrefix = "refs/for/"
// IsReferenceExist returns true if given reference exists in the repository.
func IsReferenceExist(ctx context.Context, repoPath, name string) bool {
_, _, err := NewCommand(ctx, "show-ref", "--verify", "--", name).RunStdString(&RunOpts{Dir: repoPath})
_, _, err := NewCommand(ctx, "show-ref", "--verify").AddDashesAndList(name).RunStdString(&RunOpts{Dir: repoPath})
return err == nil
}
@ -66,7 +66,7 @@ func (repo *Repository) GetHEADBranch() (*Branch, error) {
// SetDefaultBranch sets default branch of repository.
func (repo *Repository) SetDefaultBranch(name string) error {
_, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD", BranchPrefix+name).RunStdString(&RunOpts{Dir: repo.Path})
_, _, err := NewCommand(repo.Ctx, "symbolic-ref", "HEAD").AddDynamicArguments(BranchPrefix + name).RunStdString(&RunOpts{Dir: repo.Path})
return err
}
@ -141,7 +141,7 @@ func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) erro
cmd.AddArguments("-d")
}
cmd.AddArguments("--", name)
cmd.AddDashesAndList(name)
_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
return err
@ -150,7 +150,7 @@ func (repo *Repository) DeleteBranch(name string, opts DeleteBranchOptions) erro
// CreateBranch create a new branch
func (repo *Repository) CreateBranch(branch, oldbranchOrCommit string) error {
cmd := NewCommand(repo.Ctx, "branch")
cmd.AddArguments("--", branch, oldbranchOrCommit)
cmd.AddDashesAndList(branch, oldbranchOrCommit)
_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
@ -163,7 +163,7 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
if fetch {
cmd.AddArguments("-f")
}
cmd.AddArguments(name, url)
cmd.AddDynamicArguments(name, url)
_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
return err
@ -171,7 +171,7 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
// RemoveRemote removes a remote from repository.
func (repo *Repository) RemoveRemote(name string) error {
_, _, err := NewCommand(repo.Ctx, "remote", "rm", name).RunStdString(&RunOpts{Dir: repo.Path})
_, _, err := NewCommand(repo.Ctx, "remote", "rm").AddDynamicArguments(name).RunStdString(&RunOpts{Dir: repo.Path})
return err
}
@ -182,6 +182,6 @@ func (branch *Branch) GetCommit() (*Commit, error) {
// RenameBranch rename a branch
func (repo *Repository) RenameBranch(from, to string) error {
_, _, err := NewCommand(repo.Ctx, "branch", "-m", from, to).RunStdString(&RunOpts{Dir: repo.Path})
_, _, err := NewCommand(repo.Ctx, "branch", "-m").AddDynamicArguments(from, to).RunStdString(&RunOpts{Dir: repo.Path})
return err
}

View file

@ -63,7 +63,7 @@ func (repo *Repository) IsBranchExist(name string) bool {
// GetBranchNames returns branches from the repository, skipping skip initial branches and
// returning at most limit branches, or all branches if limit is 0.
func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
return callShowRef(repo.Ctx, repo.Path, BranchPrefix, []string{BranchPrefix, "--sort=-committerdate"}, skip, limit)
return callShowRef(repo.Ctx, repo.Path, BranchPrefix, []CmdArg{BranchPrefix, "--sort=-committerdate"}, skip, limit)
}
// WalkReferences walks all the references from the repository
@ -74,19 +74,19 @@ func WalkReferences(ctx context.Context, repoPath string, walkfn func(sha1, refn
// WalkReferences walks all the references from the repository
// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
var args []string
var args []CmdArg
switch refType {
case ObjectTag:
args = []string{TagPrefix, "--sort=-taggerdate"}
args = []CmdArg{TagPrefix, "--sort=-taggerdate"}
case ObjectBranch:
args = []string{BranchPrefix, "--sort=-committerdate"}
args = []CmdArg{BranchPrefix, "--sort=-committerdate"}
}
return walkShowRef(repo.Ctx, repo.Path, args, skip, limit, walkfn)
}
// callShowRef return refs, if limit = 0 it will not limit
func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs []string, skip, limit int) (branchNames []string, countAll int, err error) {
func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs []CmdArg, skip, limit int) (branchNames []string, countAll int, err error) {
countAll, err = walkShowRef(ctx, repoPath, extraArgs, skip, limit, func(_, branchName string) error {
branchName = strings.TrimPrefix(branchName, trimPrefix)
branchNames = append(branchNames, branchName)
@ -96,7 +96,7 @@ func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs []s
return branchNames, countAll, err
}
func walkShowRef(ctx context.Context, repoPath string, extraArgs []string, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
func walkShowRef(ctx context.Context, repoPath string, extraArgs []CmdArg, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
stdoutReader, stdoutWriter := io.Pipe()
defer func() {
_ = stdoutReader.Close()
@ -105,7 +105,7 @@ func walkShowRef(ctx context.Context, repoPath string, extraArgs []string, skip,
go func() {
stderrBuilder := &strings.Builder{}
args := []string{"for-each-ref", "--format=%(objectname) %(refname)"}
args := []CmdArg{"for-each-ref", "--format=%(objectname) %(refname)"}
args = append(args, extraArgs...)
err := NewCommand(ctx, args...).Run(&RunOpts{
Dir: repoPath,

View file

@ -61,7 +61,7 @@ func (repo *Repository) getCommitByPathWithID(id SHA1, relpath string) (*Commit,
relpath = `\` + relpath
}
stdout, _, runErr := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat, id.String(), "--", relpath).RunStdString(&RunOpts{Dir: repo.Path})
stdout, _, runErr := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat).AddDynamicArguments(id.String()).AddDashesAndList(relpath).RunStdString(&RunOpts{Dir: repo.Path})
if runErr != nil {
return nil, runErr
}
@ -76,7 +76,7 @@ func (repo *Repository) getCommitByPathWithID(id SHA1, relpath string) (*Commit,
// GetCommitByPath returns the last commit of relative path.
func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
stdout, _, runErr := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat, "--", relpath).RunStdBytes(&RunOpts{Dir: repo.Path})
stdout, _, runErr := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat).AddDashesAndList(relpath).RunStdBytes(&RunOpts{Dir: repo.Path})
if runErr != nil {
return nil, runErr
}
@ -89,8 +89,10 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
}
func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) ([]*Commit, error) {
stdout, _, err := NewCommand(repo.Ctx, "log", id.String(), "--skip="+strconv.Itoa((page-1)*pageSize),
"--max-count="+strconv.Itoa(pageSize), prettyLogFormat).RunStdBytes(&RunOpts{Dir: repo.Path})
stdout, _, err := NewCommand(repo.Ctx, "log").
AddArguments(CmdArg("--skip="+strconv.Itoa((page-1)*pageSize)), CmdArg("--max-count="+strconv.Itoa(pageSize)), prettyLogFormat).
AddDynamicArguments(id.String()).
RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
@ -99,30 +101,30 @@ func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) ([]*Commit,
func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Commit, error) {
// create new git log command with limit of 100 commis
cmd := NewCommand(repo.Ctx, "log", id.String(), "-100", prettyLogFormat)
cmd := NewCommand(repo.Ctx, "log", "-100", prettyLogFormat).AddDynamicArguments(id.String())
// ignore case
args := []string{"-i"}
args := []CmdArg{"-i"}
// add authors if present in search query
if len(opts.Authors) > 0 {
for _, v := range opts.Authors {
args = append(args, "--author="+v)
args = append(args, CmdArg("--author="+v))
}
}
// add committers if present in search query
if len(opts.Committers) > 0 {
for _, v := range opts.Committers {
args = append(args, "--committer="+v)
args = append(args, CmdArg("--committer="+v))
}
}
// add time constraints if present in search query
if len(opts.After) > 0 {
args = append(args, "--after="+opts.After)
args = append(args, CmdArg("--after="+opts.After))
}
if len(opts.Before) > 0 {
args = append(args, "--before="+opts.Before)
args = append(args, CmdArg("--before="+opts.Before))
}
// pretend that all refs along with HEAD were listed on command line as <commis>
@ -136,7 +138,7 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
// note this is done only for command created above
if len(opts.Keywords) > 0 {
for _, v := range opts.Keywords {
cmd.AddArguments("--grep=" + v)
cmd.AddArguments(CmdArg("--grep=" + v))
}
}
@ -178,7 +180,7 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
}
func (repo *Repository) getFilesChanged(id1, id2 string) ([]string, error) {
stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only", id1, id2).RunStdBytes(&RunOpts{Dir: repo.Path})
stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(id1, id2).RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
@ -188,7 +190,7 @@ func (repo *Repository) getFilesChanged(id1, id2 string) ([]string, error) {
// FileChangedBetweenCommits Returns true if the file changed between commit IDs id1 and id2
// You must ensure that id1 and id2 are valid commit ids.
func (repo *Repository) FileChangedBetweenCommits(filename, id1, id2 string) (bool, error) {
stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only", "-z", id1, id2, "--", filename).RunStdBytes(&RunOpts{Dir: repo.Path})
stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only", "-z").AddDynamicArguments(id1, id2).AddDashesAndList(filename).RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil {
return false, err
}
@ -211,12 +213,11 @@ func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (
}()
go func() {
stderr := strings.Builder{}
gitCmd := NewCommand(repo.Ctx, "rev-list",
"--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize*page),
"--skip="+strconv.Itoa(skip),
)
gitCmd := NewCommand(repo.Ctx, "rev-list").
AddArguments(CmdArg("--max-count=" + strconv.Itoa(setting.Git.CommitsRangeSize*page))).
AddArguments(CmdArg("--skip=" + strconv.Itoa(skip)))
gitCmd.AddDynamicArguments(revision)
gitCmd.AddArguments("--", file)
gitCmd.AddDashesAndList(file)
err := gitCmd.Run(&RunOpts{
Dir: repo.Path,
Stdout: stdoutWriter,
@ -257,11 +258,11 @@ func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (
// FilesCountBetween return the number of files changed between two commits
func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) {
stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only", startCommitID+"..."+endCommitID).RunStdString(&RunOpts{Dir: repo.Path})
stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(startCommitID + "..." + endCommitID).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil && strings.Contains(err.Error(), "no merge base") {
// git >= 2.28 now returns an error if startCommitID and endCommitID have become unrelated.
// previously it would return the results of git diff --name-only startCommitID endCommitID so let's try that...
stdout, _, err = NewCommand(repo.Ctx, "diff", "--name-only", startCommitID, endCommitID).RunStdString(&RunOpts{Dir: repo.Path})
stdout, _, err = NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(startCommitID, endCommitID).RunStdString(&RunOpts{Dir: repo.Path})
}
if err != nil {
return 0, err
@ -275,13 +276,13 @@ func (repo *Repository) CommitsBetween(last, before *Commit) ([]*Commit, error)
var stdout []byte
var err error
if before == nil {
stdout, _, err = NewCommand(repo.Ctx, "rev-list", last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
stdout, _, err = NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
} else {
stdout, _, err = NewCommand(repo.Ctx, "rev-list", before.ID.String()+".."+last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
stdout, _, err = NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(before.ID.String() + ".." + last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list before last so let's try that...
stdout, _, err = NewCommand(repo.Ctx, "rev-list", before.ID.String(), last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
stdout, _, err = NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
}
}
if err != nil {
@ -295,13 +296,22 @@ func (repo *Repository) CommitsBetweenLimit(last, before *Commit, limit, skip in
var stdout []byte
var err error
if before == nil {
stdout, _, err = NewCommand(repo.Ctx, "rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
stdout, _, err = NewCommand(repo.Ctx, "rev-list",
"--max-count", CmdArg(strconv.Itoa(limit)),
"--skip", CmdArg(strconv.Itoa(skip))).
AddDynamicArguments(last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
} else {
stdout, _, err = NewCommand(repo.Ctx, "rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String()+".."+last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
stdout, _, err = NewCommand(repo.Ctx, "rev-list",
"--max-count", CmdArg(strconv.Itoa(limit)),
"--skip", CmdArg(strconv.Itoa(skip))).
AddDynamicArguments(before.ID.String() + ".." + last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list --max-count n before last so let's try that...
stdout, _, err = NewCommand(repo.Ctx, "rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String(), last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
stdout, _, err = NewCommand(repo.Ctx, "rev-list",
"--max-count", CmdArg(strconv.Itoa(limit)),
"--skip", CmdArg(strconv.Itoa(skip))).
AddDynamicArguments(before.ID.String(), last.ID.String()).RunStdBytes(&RunOpts{Dir: repo.Path})
}
}
if err != nil {
@ -342,9 +352,9 @@ func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
func (repo *Repository) commitsBefore(id SHA1, limit int) ([]*Commit, error) {
cmd := NewCommand(repo.Ctx, "log")
if limit > 0 {
cmd.AddArguments("-"+strconv.Itoa(limit), prettyLogFormat, id.String())
cmd.AddArguments(CmdArg("-"+strconv.Itoa(limit)), prettyLogFormat).AddDynamicArguments(id.String())
} else {
cmd.AddArguments(prettyLogFormat, id.String())
cmd.AddArguments(prettyLogFormat).AddDynamicArguments(id.String())
}
stdout, _, runErr := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
@ -384,7 +394,11 @@ func (repo *Repository) getCommitsBeforeLimit(id SHA1, num int) ([]*Commit, erro
func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) {
if CheckGitVersionAtLeast("2.7.0") == nil {
stdout, _, err := NewCommand(repo.Ctx, "for-each-ref", "--count="+strconv.Itoa(limit), "--format=%(refname:strip=2)", "--contains", commit.ID.String(), BranchPrefix).RunStdString(&RunOpts{Dir: repo.Path})
stdout, _, err := NewCommand(repo.Ctx, "for-each-ref",
CmdArg("--count="+strconv.Itoa(limit)),
"--format=%(refname:strip=2)", "--contains").
AddDynamicArguments(commit.ID.String(), BranchPrefix).
RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
@ -393,7 +407,7 @@ func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error)
return branches, nil
}
stdout, _, err := NewCommand(repo.Ctx, "branch", "--contains", commit.ID.String()).RunStdString(&RunOpts{Dir: repo.Path})
stdout, _, err := NewCommand(repo.Ctx, "branch", "--contains").AddDynamicArguments(commit.ID.String()).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
@ -432,7 +446,7 @@ func (repo *Repository) GetCommitsFromIDs(commitIDs []string) []*Commit {
// IsCommitInBranch check if the commit is on the branch
func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err error) {
stdout, _, err := NewCommand(repo.Ctx, "branch", "--contains", commitID, branch).RunStdString(&RunOpts{Dir: repo.Path})
stdout, _, err := NewCommand(repo.Ctx, "branch", "--contains").AddDynamicArguments(commitID, branch).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
return false, err
}

View file

@ -49,7 +49,7 @@ func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
}
}
actualCommitID, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify", commitID).RunStdString(&RunOpts{Dir: repo.Path})
actualCommitID, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(commitID).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
if strings.Contains(err.Error(), "unknown revision or path") ||
strings.Contains(err.Error(), "fatal: Needed a single revision") {

View file

@ -17,7 +17,7 @@ import (
// ResolveReference resolves a name to a reference
func (repo *Repository) ResolveReference(name string) (string, error) {
stdout, _, err := NewCommand(repo.Ctx, "show-ref", "--hash", name).RunStdString(&RunOpts{Dir: repo.Path})
stdout, _, err := NewCommand(repo.Ctx, "show-ref", "--hash").AddDynamicArguments(name).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
if strings.Contains(err.Error(), "not a valid ref") {
return "", ErrNotExist{name, ""}
@ -50,19 +50,19 @@ func (repo *Repository) GetRefCommitID(name string) (string, error) {
// SetReference sets the commit ID string of given reference (e.g. branch or tag).
func (repo *Repository) SetReference(name, commitID string) error {
_, _, err := NewCommand(repo.Ctx, "update-ref", name, commitID).RunStdString(&RunOpts{Dir: repo.Path})
_, _, err := NewCommand(repo.Ctx, "update-ref").AddDynamicArguments(name, commitID).RunStdString(&RunOpts{Dir: repo.Path})
return err
}
// RemoveReference removes the given reference (e.g. branch or tag).
func (repo *Repository) RemoveReference(name string) error {
_, _, err := NewCommand(repo.Ctx, "update-ref", "--no-deref", "-d", name).RunStdString(&RunOpts{Dir: repo.Path})
_, _, err := NewCommand(repo.Ctx, "update-ref", "--no-deref", "-d").AddDynamicArguments(name).RunStdString(&RunOpts{Dir: repo.Path})
return err
}
// IsCommitExist returns true if given commit exists in current repository.
func (repo *Repository) IsCommitExist(name string) bool {
_, _, err := NewCommand(repo.Ctx, "cat-file", "-e", name).RunStdString(&RunOpts{Dir: repo.Path})
_, _, err := NewCommand(repo.Ctx, "cat-file", "-e").AddDynamicArguments(name).RunStdString(&RunOpts{Dir: repo.Path})
return err == nil
}

View file

@ -40,13 +40,13 @@ func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, stri
if tmpRemote != "origin" {
tmpBaseName := RemotePrefix + tmpRemote + "/tmp_" + base
// Fetch commit into a temporary branch in order to be able to handle commits and tags
_, _, err := NewCommand(repo.Ctx, "fetch", "--no-tags", tmpRemote, "--", base+":"+tmpBaseName).RunStdString(&RunOpts{Dir: repo.Path})
_, _, err := NewCommand(repo.Ctx, "fetch", "--no-tags").AddDynamicArguments(tmpRemote).AddDashesAndList(base + ":" + tmpBaseName).RunStdString(&RunOpts{Dir: repo.Path})
if err == nil {
base = tmpBaseName
}
}
stdout, _, err := NewCommand(repo.Ctx, "merge-base", "--", base, head).RunStdString(&RunOpts{Dir: repo.Path})
stdout, _, err := NewCommand(repo.Ctx, "merge-base").AddDashesAndList(base, head).RunStdString(&RunOpts{Dir: repo.Path})
return strings.TrimSpace(stdout), base, err
}
@ -94,7 +94,7 @@ func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string,
// We have a common base - therefore we know that ... should work
if !fileOnly {
var logs []byte
logs, _, err = NewCommand(repo.Ctx, "log", baseCommitID+separator+headBranch, prettyLogFormat).RunStdBytes(&RunOpts{Dir: repo.Path})
logs, _, err = NewCommand(repo.Ctx, "log").AddDynamicArguments(baseCommitID + separator + headBranch).AddArguments(prettyLogFormat).RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
@ -147,7 +147,7 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
separator = ".."
}
if err := NewCommand(repo.Ctx, "diff", "-z", "--name-only", base+separator+head).
if err := NewCommand(repo.Ctx, "diff", "-z", "--name-only").AddDynamicArguments(base + separator + head).
Run(&RunOpts{
Dir: repo.Path,
Stdout: w,
@ -158,7 +158,7 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
// previously it would return the results of git diff -z --name-only base head so let's try that...
w = &lineCountWriter{}
stderr.Reset()
if err = NewCommand(repo.Ctx, "diff", "-z", "--name-only", base, head).Run(&RunOpts{
if err = NewCommand(repo.Ctx, "diff", "-z", "--name-only").AddDynamicArguments(base, head).Run(&RunOpts{
Dir: repo.Path,
Stdout: w,
Stderr: stderr,
@ -173,20 +173,20 @@ func (repo *Repository) GetDiffNumChangedFiles(base, head string, directComparis
// GetDiffShortStat counts number of changed files, number of additions and deletions
func (repo *Repository) GetDiffShortStat(base, head string) (numFiles, totalAdditions, totalDeletions int, err error) {
numFiles, totalAdditions, totalDeletions, err = GetDiffShortStat(repo.Ctx, repo.Path, base+"..."+head)
numFiles, totalAdditions, totalDeletions, err = GetDiffShortStat(repo.Ctx, repo.Path, CmdArgCheck(base+"..."+head))
if err != nil && strings.Contains(err.Error(), "no merge base") {
return GetDiffShortStat(repo.Ctx, repo.Path, base, head)
return GetDiffShortStat(repo.Ctx, repo.Path, CmdArgCheck(base), CmdArgCheck(head))
}
return numFiles, totalAdditions, totalDeletions, err
}
// GetDiffShortStat counts number of changed files, number of additions and deletions
func GetDiffShortStat(ctx context.Context, repoPath string, args ...string) (numFiles, totalAdditions, totalDeletions int, err error) {
func GetDiffShortStat(ctx context.Context, repoPath string, args ...CmdArg) (numFiles, totalAdditions, totalDeletions int, err error) {
// Now if we call:
// $ git diff --shortstat 1ebb35b98889ff77299f24d82da426b434b0cca0...788b8b1440462d477f45b0088875
// we get:
// " 9902 files changed, 2034198 insertions(+), 298800 deletions(-)\n"
args = append([]string{
args = append([]CmdArg{
"diff",
"--shortstat",
}, args...)
@ -247,7 +247,7 @@ func (repo *Repository) GetDiffOrPatch(base, head string, w io.Writer, patch, bi
// GetDiff generates and returns patch data between given revisions, optimized for human readability
func (repo *Repository) GetDiff(base, head string, w io.Writer) error {
return NewCommand(repo.Ctx, "diff", "-p", base, head).Run(&RunOpts{
return NewCommand(repo.Ctx, "diff", "-p").AddDynamicArguments(base, head).Run(&RunOpts{
Dir: repo.Path,
Stdout: w,
})
@ -255,7 +255,7 @@ func (repo *Repository) GetDiff(base, head string, w io.Writer) error {
// GetDiffBinary generates and returns patch data between given revisions, including binary diffs.
func (repo *Repository) GetDiffBinary(base, head string, w io.Writer) error {
return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--histogram", base, head).Run(&RunOpts{
return NewCommand(repo.Ctx, "diff", "-p", "--binary", "--histogram").AddDynamicArguments(base, head).Run(&RunOpts{
Dir: repo.Path,
Stdout: w,
})
@ -264,14 +264,14 @@ func (repo *Repository) GetDiffBinary(base, head string, w io.Writer) error {
// GetPatch generates and returns format-patch data between given revisions, able to be used with `git apply`
func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
stderr := new(bytes.Buffer)
err := NewCommand(repo.Ctx, "format-patch", "--binary", "--stdout", base+"..."+head).
err := NewCommand(repo.Ctx, "format-patch", "--binary", "--stdout").AddDynamicArguments(base + "..." + head).
Run(&RunOpts{
Dir: repo.Path,
Stdout: w,
Stderr: stderr,
})
if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) {
return NewCommand(repo.Ctx, "format-patch", "--binary", "--stdout", base, head).
return NewCommand(repo.Ctx, "format-patch", "--binary", "--stdout").AddDynamicArguments(base, head).
Run(&RunOpts{
Dir: repo.Path,
Stdout: w,
@ -282,7 +282,7 @@ func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
// GetFilesChangedBetween returns a list of all files that have been changed between the given commits
func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, error) {
stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only", base+".."+head).RunStdString(&RunOpts{Dir: repo.Path})
stdout, _, err := NewCommand(repo.Ctx, "diff", "--name-only").AddDynamicArguments(base + ".." + head).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
@ -292,7 +292,7 @@ func (repo *Repository) GetFilesChangedBetween(base, head string) ([]string, err
// GetDiffFromMergeBase generates and return patch data from merge base to head
func (repo *Repository) GetDiffFromMergeBase(base, head string, w io.Writer) error {
stderr := new(bytes.Buffer)
err := NewCommand(repo.Ctx, "diff", "-p", "--binary", base+"..."+head).
err := NewCommand(repo.Ctx, "diff", "-p", "--binary").AddDynamicArguments(base + "..." + head).
Run(&RunOpts{
Dir: repo.Path,
Stdout: w,

View file

@ -18,7 +18,7 @@ import (
// ReadTreeToIndex reads a treeish to the index
func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string) error {
if len(treeish) != 40 {
res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify", treeish).RunStdString(&RunOpts{Dir: repo.Path})
res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(treeish).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
return err
}
@ -38,7 +38,7 @@ func (repo *Repository) readTreeToIndex(id SHA1, indexFilename ...string) error
if len(indexFilename) > 0 {
env = append(os.Environ(), "GIT_INDEX_FILE="+indexFilename[0])
}
_, _, err := NewCommand(repo.Ctx, "read-tree", id.String()).RunStdString(&RunOpts{Dir: repo.Path, Env: env})
_, _, err := NewCommand(repo.Ctx, "read-tree").AddDynamicArguments(id.String()).RunStdString(&RunOpts{Dir: repo.Path, Env: env})
if err != nil {
return err
}
@ -75,12 +75,7 @@ func (repo *Repository) EmptyIndex() error {
// LsFiles checks if the given filenames are in the index
func (repo *Repository) LsFiles(filenames ...string) ([]string, error) {
cmd := NewCommand(repo.Ctx, "ls-files", "-z", "--")
for _, arg := range filenames {
if arg != "" {
cmd.AddArguments(arg)
}
}
cmd := NewCommand(repo.Ctx, "ls-files", "-z").AddDashesAndList(filenames...)
res, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
@ -116,7 +111,7 @@ func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
// AddObjectToIndex adds the provided object hash to the index at the provided filename
func (repo *Repository) AddObjectToIndex(mode string, object SHA1, filename string) error {
cmd := NewCommand(repo.Ctx, "update-index", "--add", "--replace", "--cacheinfo", mode, object.String(), filename)
cmd := NewCommand(repo.Ctx, "update-index", "--add", "--replace", "--cacheinfo").AddDynamicArguments(mode, object.String(), filename)
_, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path})
return err
}

View file

@ -41,7 +41,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
since := fromTime.Format(time.RFC3339)
stdout, _, runErr := NewCommand(repo.Ctx, "rev-list", "--count", "--no-merges", "--branches=*", "--date=iso", fmt.Sprintf("--since='%s'", since)).RunStdString(&RunOpts{Dir: repo.Path})
stdout, _, runErr := NewCommand(repo.Ctx, "rev-list", "--count", "--no-merges", "--branches=*", "--date=iso", CmdArg(fmt.Sprintf("--since='%s'", since))).RunStdString(&RunOpts{Dir: repo.Path})
if runErr != nil {
return nil, runErr
}
@ -61,7 +61,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
_ = stdoutWriter.Close()
}()
gitCmd := NewCommand(repo.Ctx, "log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso", fmt.Sprintf("--since='%s'", since))
gitCmd := NewCommand(repo.Ctx, "log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso", CmdArg(fmt.Sprintf("--since='%s'", since)))
if len(branch) == 0 {
gitCmd.AddArguments("--branches=*")
} else {

View file

@ -25,13 +25,13 @@ func IsTagExist(ctx context.Context, repoPath, name string) bool {
// CreateTag create one tag in the repository
func (repo *Repository) CreateTag(name, revision string) error {
_, _, err := NewCommand(repo.Ctx, "tag", "--", name, revision).RunStdString(&RunOpts{Dir: repo.Path})
_, _, err := NewCommand(repo.Ctx, "tag").AddDashesAndList(name, revision).RunStdString(&RunOpts{Dir: repo.Path})
return err
}
// CreateAnnotatedTag create one annotated tag in the repository
func (repo *Repository) CreateAnnotatedTag(name, message, revision string) error {
_, _, err := NewCommand(repo.Ctx, "tag", "-a", "-m", message, "--", name, revision).RunStdString(&RunOpts{Dir: repo.Path})
_, _, err := NewCommand(repo.Ctx, "tag", "-a", "-m").AddDynamicArguments(message).AddDashesAndList(name, revision).RunStdString(&RunOpts{Dir: repo.Path})
return err
}
@ -64,7 +64,7 @@ func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
// GetTagID returns the object ID for a tag (annotated tags have both an object SHA AND a commit SHA)
func (repo *Repository) GetTagID(name string) (string, error) {
stdout, _, err := NewCommand(repo.Ctx, "show-ref", "--tags", "--", name).RunStdString(&RunOpts{Dir: repo.Path})
stdout, _, err := NewCommand(repo.Ctx, "show-ref", "--tags").AddDashesAndList(name).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
return "", err
}
@ -122,7 +122,7 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
rc := &RunOpts{Dir: repo.Path, Stdout: stdoutWriter, Stderr: &stderr}
go func() {
err := NewCommand(repo.Ctx, "for-each-ref", "--format", forEachRefFmt.Flag(), "--sort", "-*creatordate", "refs/tags").Run(rc)
err := NewCommand(repo.Ctx, "for-each-ref", CmdArg("--format="+forEachRefFmt.Flag()), "--sort", "-*creatordate", "refs/tags").Run(rc)
if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, stderr.String()))
} else {

View file

@ -26,7 +26,7 @@ func (repo *Repository) IsTagExist(name string) bool {
// GetTags returns all tags of the repository.
// returning at most limit tags, or all if limit is 0.
func (repo *Repository) GetTags(skip, limit int) (tags []string, err error) {
tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, []string{TagPrefix, "--sort=-taggerdate"}, skip, limit)
tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, []CmdArg{TagPrefix, "--sort=-taggerdate"}, skip, limit)
return tags, err
}

View file

@ -35,10 +35,10 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
"GIT_COMMITTER_EMAIL="+committer.Email,
"GIT_COMMITTER_DATE="+commitTimeStr,
)
cmd := NewCommand(repo.Ctx, "commit-tree", tree.ID.String())
cmd := NewCommand(repo.Ctx, "commit-tree").AddDynamicArguments(tree.ID.String())
for _, parent := range opts.Parents {
cmd.AddArguments("-p", parent)
cmd.AddArguments("-p").AddDynamicArguments(parent)
}
messageBytes := new(bytes.Buffer)
@ -46,7 +46,7 @@ func (repo *Repository) CommitTree(author, committer *Signature, tree *Tree, opt
_, _ = messageBytes.WriteString("\n")
if opts.KeyID != "" || opts.AlwaysSign {
cmd.AddArguments(fmt.Sprintf("-S%s", opts.KeyID))
cmd.AddArguments(CmdArg(fmt.Sprintf("-S%s", opts.KeyID)))
}
if opts.NoGPGSign {

View file

@ -21,7 +21,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) {
// GetTree find the tree object in the repository.
func (repo *Repository) GetTree(idStr string) (*Tree, error) {
if len(idStr) != 40 {
res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify", idStr).RunStdString(&RunOpts{Dir: repo.Path})
res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(idStr).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}

View file

@ -49,12 +49,9 @@ func (t *Tree) SubTree(rpath string) (*Tree, error) {
// LsTree checks if the given filenames are in the tree
func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error) {
cmd := NewCommand(repo.Ctx, "ls-tree", "-z", "--name-only", "--", ref)
for _, arg := range filenames {
if arg != "" {
cmd.AddArguments(arg)
}
}
cmd := NewCommand(repo.Ctx, "ls-tree", "-z", "--name-only").
AddDashesAndList(append([]string{ref}, filenames...)...)
res, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err

View file

@ -80,7 +80,7 @@ func (t *Tree) ListEntries() (Entries, error) {
}
}
stdout, _, runErr := NewCommand(t.repo.Ctx, "ls-tree", "-l", t.ID.String()).RunStdBytes(&RunOpts{Dir: t.repo.Path})
stdout, _, runErr := NewCommand(t.repo.Ctx, "ls-tree", "-l").AddDynamicArguments(t.ID.String()).RunStdBytes(&RunOpts{Dir: t.repo.Path})
if runErr != nil {
if strings.Contains(runErr.Error(), "fatal: Not a valid object name") || strings.Contains(runErr.Error(), "fatal: not a tree object") {
return nil, ErrNotExist{
@ -101,13 +101,13 @@ func (t *Tree) ListEntries() (Entries, error) {
// listEntriesRecursive returns all entries of current tree recursively including all subtrees
// extraArgs could be "-l" to get the size, which is slower
func (t *Tree) listEntriesRecursive(extraArgs ...string) (Entries, error) {
func (t *Tree) listEntriesRecursive(extraArgs ...CmdArg) (Entries, error) {
if t.entriesRecursiveParsed {
return t.entriesRecursive, nil
}
args := append([]string{"ls-tree", "-t", "-r"}, extraArgs...)
args = append(args, t.ID.String())
args := append([]CmdArg{"ls-tree", "-t", "-r"}, extraArgs...)
args = append(args, CmdArg(t.ID.String()))
stdout, _, runErr := NewCommand(t.repo.Ctx, args...).RunStdBytes(&RunOpts{Dir: t.repo.Path})
if runErr != nil {
return nil, runErr

View file

@ -37,16 +37,15 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
graphCmd.AddArguments(
"-C",
"-M",
fmt.Sprintf("-n %d", setting.UI.GraphMaxCommitNum*page),
git.CmdArg(fmt.Sprintf("-n %d", setting.UI.GraphMaxCommitNum*page)),
"--date=iso",
fmt.Sprintf("--pretty=format:%s", format))
git.CmdArg(fmt.Sprintf("--pretty=format:%s", format)))
if len(branches) > 0 {
graphCmd.AddDynamicArguments(branches...)
}
if len(files) > 0 {
graphCmd.AddArguments("--")
graphCmd.AddArguments(files...)
graphCmd.AddDashesAndList(files...)
}
graph := NewGraph()

View file

@ -194,7 +194,7 @@ func (b *BleveIndexer) addUpdate(ctx context.Context, batchWriter git.WriteClose
var err error
if !update.Sized {
var stdout string
stdout, _, err = git.NewCommand(ctx, "cat-file", "-s", update.BlobSha).RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
stdout, _, err = git.NewCommand(ctx, "cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
if err != nil {
return err
}

View file

@ -223,7 +223,7 @@ func (b *ElasticSearchIndexer) addUpdate(ctx context.Context, batchWriter git.Wr
var err error
if !update.Sized {
var stdout string
stdout, _, err = git.NewCommand(ctx, "cat-file", "-s", update.BlobSha).RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
stdout, _, err = git.NewCommand(ctx, "cat-file", "-s").AddDynamicArguments(update.BlobSha).RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
if err != nil {
return nil, err
}

View file

@ -29,7 +29,7 @@ type repoChanges struct {
}
func getDefaultBranchSha(ctx context.Context, repo *repo_model.Repository) (string, error) {
stdout, _, err := git.NewCommand(ctx, "show-ref", "-s", git.BranchPrefix+repo.DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
stdout, _, err := git.NewCommand(ctx, "show-ref", "-s").AddDynamicArguments(git.BranchPrefix + repo.DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
if err != nil {
return "", err
}
@ -92,7 +92,7 @@ func parseGitLsTreeOutput(stdout []byte) ([]fileUpdate, error) {
// genesisChanges get changes to add repo to the indexer for the first time
func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*repoChanges, error) {
var changes repoChanges
stdout, _, runErr := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l", "-r", revision).RunStdBytes(&git.RunOpts{Dir: repo.RepoPath()})
stdout, _, runErr := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l", "-r").AddDynamicArguments(revision).RunStdBytes(&git.RunOpts{Dir: repo.RepoPath()})
if runErr != nil {
return nil, runErr
}
@ -104,7 +104,7 @@ func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision s
// nonGenesisChanges get changes since the previous indexer update
func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revision string) (*repoChanges, error) {
diffCmd := git.NewCommand(ctx, "diff", "--name-status", repo.CodeIndexerStatus.CommitSha, revision)
diffCmd := git.NewCommand(ctx, "diff", "--name-status").AddDynamicArguments(repo.CodeIndexerStatus.CommitSha, revision)
stdout, _, runErr := diffCmd.RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
if runErr != nil {
// previous commit sha may have been removed by a force push, so
@ -169,8 +169,8 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio
}
}
cmd := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l", revision, "--")
cmd.AddArguments(updatedFilenames...)
cmd := git.NewCommand(ctx, "ls-tree", "--full-tree", "-l").AddDynamicArguments(revision).
AddDashesAndList(updatedFilenames...)
lsTreeStdout, _, err := cmd.RunStdBytes(&git.RunOpts{Dir: repo.RepoPath()})
if err != nil {
return nil, err

View file

@ -603,8 +603,14 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
start = loc.End
continue
}
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mention[1:]), mention, "mention"))
node = node.NextSibling.NextSibling
mentionedUsername := mention[1:]
if processorHelper.IsUsernameMentionable != nil && processorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) {
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mentionedUsername), mention, "mention"))
node = node.NextSibling.NextSibling
} else {
node = node.NextSibling
}
start = 0
}
}

View file

@ -144,3 +144,39 @@ func IsIcon(node ast.Node) bool {
_, ok := node.(*Icon)
return ok
}
// ColorPreview is an inline for a color preview
type ColorPreview struct {
ast.BaseInline
Color []byte
}
// Dump implements Node.Dump.
func (n *ColorPreview) Dump(source []byte, level int) {
m := map[string]string{}
m["Color"] = string(n.Color)
ast.DumpHelper(n, source, level, m, nil)
}
// KindColorPreview is the NodeKind for ColorPreview
var KindColorPreview = ast.NewNodeKind("ColorPreview")
// Kind implements Node.Kind.
func (n *ColorPreview) Kind() ast.NodeKind {
return KindColorPreview
}
// NewColorPreview returns a new Span node.
func NewColorPreview(color []byte) *ColorPreview {
return &ColorPreview{
BaseInline: ast.BaseInline{},
Color: color,
}
}
// IsColorPreview returns true if the given node implements the ColorPreview interface,
// otherwise false.
func IsColorPreview(node ast.Node) bool {
_, ok := node.(*ColorPreview)
return ok
}

View file

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/setting"
giteautil "code.gitea.io/gitea/modules/util"
"github.com/microcosm-cc/bluemonday/css"
"github.com/yuin/goldmark/ast"
east "github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser"
@ -178,6 +179,11 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInDocuments)
}
}
case *ast.CodeSpan:
colorContent := n.Text(reader.Source())
if css.ColorHandler(strings.ToLower(string(colorContent))) {
v.AppendChild(v, NewColorPreview(colorContent))
}
}
return ast.WalkContinue, nil
})
@ -266,10 +272,43 @@ func (r *HTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(KindDetails, r.renderDetails)
reg.Register(KindSummary, r.renderSummary)
reg.Register(KindIcon, r.renderIcon)
reg.Register(ast.KindCodeSpan, r.renderCodeSpan)
reg.Register(KindTaskCheckBoxListItem, r.renderTaskCheckBoxListItem)
reg.Register(east.KindTaskCheckBox, r.renderTaskCheckBox)
}
// renderCodeSpan renders CodeSpan elements (like goldmark upstream does) but also renders ColorPreview elements.
// See #21474 for reference
func (r *HTMLRenderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
if n.Attributes() != nil {
_, _ = w.WriteString("<code")
html.RenderAttributes(w, n, html.CodeAttributeFilter)
_ = w.WriteByte('>')
} else {
_, _ = w.WriteString("<code>")
}
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
switch v := c.(type) {
case *ast.Text:
segment := v.Segment
value := segment.Value(source)
if bytes.HasSuffix(value, []byte("\n")) {
r.Writer.RawWrite(w, value[:len(value)-1])
r.Writer.RawWrite(w, []byte(" "))
} else {
r.Writer.RawWrite(w, value)
}
case *ColorPreview:
_, _ = w.WriteString(fmt.Sprintf(`<span class="color-preview" style="background-color: %v"></span>`, string(v.Color)))
}
}
return ast.WalkSkipChildren, nil
}
_, _ = w.WriteString("</code>")
return ast.WalkContinue, nil
}
func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Document)

View file

@ -38,6 +38,11 @@ func TestMain(m *testing.M) {
if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err)
}
markup.Init(&markup.ProcessorHelper{
IsUsernameMentionable: func(ctx context.Context, username string) bool {
return username == "r-lyeh"
},
})
os.Exit(m.Run())
}
@ -429,6 +434,61 @@ func TestRenderEmojiInLinks_Issue12331(t *testing.T) {
assert.Equal(t, expected, res)
}
func TestColorPreview(t *testing.T) {
const nl = "\n"
positiveTests := []struct {
testcase string
expected string
}{
{ // hex
"`#FF0000`",
`<p><code>#FF0000<span class="color-preview" style="background-color: #FF0000"></span></code></p>` + nl,
},
{ // rgb
"`rgb(16, 32, 64)`",
`<p><code>rgb(16, 32, 64)<span class="color-preview" style="background-color: rgb(16, 32, 64)"></span></code></p>` + nl,
},
{ // short hex
"This is the color white `#000`",
`<p>This is the color white <code>#000<span class="color-preview" style="background-color: #000"></span></code></p>` + nl,
},
{ // hsl
"HSL stands for hue, saturation, and lightness. An example: `hsl(0, 100%, 50%)`.",
`<p>HSL stands for hue, saturation, and lightness. An example: <code>hsl(0, 100%, 50%)<span class="color-preview" style="background-color: hsl(0, 100%, 50%)"></span></code>.</p>` + nl,
},
{ // uppercase hsl
"HSL stands for hue, saturation, and lightness. An example: `HSL(0, 100%, 50%)`.",
`<p>HSL stands for hue, saturation, and lightness. An example: <code>HSL(0, 100%, 50%)<span class="color-preview" style="background-color: HSL(0, 100%, 50%)"></span></code>.</p>` + nl,
},
}
for _, test := range positiveTests {
res, err := RenderString(&markup.RenderContext{}, test.testcase)
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase)
}
negativeTests := []string{
// not a color code
"`FF0000`",
// inside a code block
"```javascript" + nl + `const red = "#FF0000";` + nl + "```",
// no backticks
"rgb(166, 32, 64)",
// typo
"`hsI(0, 100%, 50%)`",
// looks like a color but not really
"`hsl(40, 60, 80)`",
}
for _, test := range negativeTests {
res, err := RenderString(&markup.RenderContext{}, test)
assert.NoError(t, err, "Unexpected error in testcase: %q", test)
assert.NotContains(t, res, `<span class="color-preview" style="background-color: `, "Unexpected result in testcase %q", test)
}
}
func TestMathBlock(t *testing.T) {
const nl = "\n"
testcases := []struct {

View file

@ -10,8 +10,6 @@ import (
"unicode"
"unicode/utf8"
"code.gitea.io/gitea/modules/log"
"gopkg.in/yaml.v3"
)
@ -99,8 +97,6 @@ func ExtractMetadataBytes(contents []byte, out interface{}) ([]byte, error) {
return contents, errors.New("could not determine metadata")
}
log.Info("%s", string(front))
if err := yaml.Unmarshal(front, out); err != nil {
return contents, err
}

View file

@ -19,8 +19,18 @@ import (
"code.gitea.io/gitea/modules/setting"
)
type ProcessorHelper struct {
IsUsernameMentionable func(ctx context.Context, username string) bool
}
var processorHelper ProcessorHelper
// Init initialize regexps for markdown parsing
func Init() {
func Init(ph *ProcessorHelper) {
if ph != nil {
processorHelper = *ph
}
NewSanitizer()
if len(setting.Markdown.CustomURLSchemes) > 0 {
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)

View file

@ -55,6 +55,9 @@ func createDefaultPolicy() *bluemonday.Policy {
// For JS code copy and Mermaid loading state
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-block( is-loading)?$`)).OnElements("pre")
// For color preview
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^color-preview$`)).OnElements("span")
// For Chroma markdown plugin
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(chroma )?language-[\w-]+( display)?( is-loading)?$`)).OnElements("code")
@ -88,8 +91,8 @@ func createDefaultPolicy() *bluemonday.Policy {
// Allow 'style' attribute on text elements.
policy.AllowAttrs("style").OnElements("span", "p")
// Allow 'color' property for the style attribute on text elements.
policy.AllowStyles("color").OnElements("span", "p")
// Allow 'color' and 'background-color' properties for the style attribute on text elements.
policy.AllowStyles("color", "background-color").OnElements("span", "p")
// Allow generally safe attributes
generalSafeAttrs := []string{

View file

@ -61,7 +61,7 @@ func (m *webhookNotifier) NotifyIssueClearLabels(doer *user_model.User, issue *i
return
}
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestLabel, &api.PullRequestPayload{
err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequestLabel, &api.PullRequestPayload{
Action: api.HookIssueLabelCleared,
Index: issue.Index,
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil),
@ -69,7 +69,7 @@ func (m *webhookNotifier) NotifyIssueClearLabels(doer *user_model.User, issue *i
Sender: convert.ToUser(doer, nil),
})
} else {
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueLabel, &api.IssuePayload{
err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssueLabel, &api.IssuePayload{
Action: api.HookIssueLabelCleared,
Index: issue.Index,
Issue: convert.ToAPIIssue(issue),
@ -87,7 +87,7 @@ func (m *webhookNotifier) NotifyForkRepository(doer *user_model.User, oldRepo, r
mode, _ := access_model.AccessLevel(doer, repo)
// forked webhook
if err := webhook_services.PrepareWebhooks(oldRepo, webhook.HookEventFork, &api.ForkPayload{
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: oldRepo}, webhook.HookEventFork, &api.ForkPayload{
Forkee: convert.ToRepo(oldRepo, oldMode),
Repo: convert.ToRepo(repo, mode),
Sender: convert.ToUser(doer, nil),
@ -99,7 +99,7 @@ func (m *webhookNotifier) NotifyForkRepository(doer *user_model.User, oldRepo, r
// Add to hook queue for created repo after session commit.
if u.IsOrganization() {
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventRepository, &api.RepositoryPayload{
Action: api.HookRepoCreated,
Repository: convert.ToRepo(repo, perm.AccessModeOwner),
Organization: convert.ToUser(u, nil),
@ -112,7 +112,7 @@ func (m *webhookNotifier) NotifyForkRepository(doer *user_model.User, oldRepo, r
func (m *webhookNotifier) NotifyCreateRepository(doer, u *user_model.User, repo *repo_model.Repository) {
// Add to hook queue for created repo after session commit.
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventRepository, &api.RepositoryPayload{
Action: api.HookRepoCreated,
Repository: convert.ToRepo(repo, perm.AccessModeOwner),
Organization: convert.ToUser(u, nil),
@ -125,7 +125,7 @@ func (m *webhookNotifier) NotifyCreateRepository(doer, u *user_model.User, repo
func (m *webhookNotifier) NotifyDeleteRepository(doer *user_model.User, repo *repo_model.Repository) {
u := repo.MustOwner()
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventRepository, &api.RepositoryPayload{
Action: api.HookRepoDeleted,
Repository: convert.ToRepo(repo, perm.AccessModeOwner),
Organization: convert.ToUser(u, nil),
@ -137,7 +137,7 @@ func (m *webhookNotifier) NotifyDeleteRepository(doer *user_model.User, repo *re
func (m *webhookNotifier) NotifyMigrateRepository(doer, u *user_model.User, repo *repo_model.Repository) {
// Add to hook queue for created repo after session commit.
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventRepository, &api.RepositoryPayload{
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventRepository, &api.RepositoryPayload{
Action: api.HookRepoCreated,
Repository: convert.ToRepo(repo, perm.AccessModeOwner),
Organization: convert.ToUser(u, nil),
@ -171,7 +171,7 @@ func (m *webhookNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue
apiPullRequest.Action = api.HookIssueAssigned
}
// Assignee comment triggers a webhook
if err := webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestAssign, apiPullRequest); err != nil {
if err := webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequestAssign, apiPullRequest); err != nil {
log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
return
}
@ -189,7 +189,7 @@ func (m *webhookNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue
apiIssue.Action = api.HookIssueAssigned
}
// Assignee comment triggers a webhook
if err := webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueAssign, apiIssue); err != nil {
if err := webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssueAssign, apiIssue); err != nil {
log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
return
}
@ -208,7 +208,7 @@ func (m *webhookNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *i
return
}
issue.PullRequest.Issue = issue
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequest, &api.PullRequestPayload{
err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueEdited,
Index: issue.Index,
Changes: &api.ChangesPayload{
@ -221,7 +221,7 @@ func (m *webhookNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *i
Sender: convert.ToUser(doer, nil),
})
} else {
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssues, &api.IssuePayload{
err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssues, &api.IssuePayload{
Action: api.HookIssueEdited,
Index: issue.Index,
Changes: &api.ChangesPayload{
@ -263,7 +263,7 @@ func (m *webhookNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *
} else {
apiPullRequest.Action = api.HookIssueReOpened
}
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequest, apiPullRequest)
err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequest, apiPullRequest)
} else {
apiIssue := &api.IssuePayload{
Index: issue.Index,
@ -276,7 +276,7 @@ func (m *webhookNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *
} else {
apiIssue.Action = api.HookIssueReOpened
}
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssues, apiIssue)
err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssues, apiIssue)
}
if err != nil {
log.Error("PrepareWebhooks [is_pull: %v, is_closed: %v]: %v", issue.IsPull, isClosed, err)
@ -294,7 +294,7 @@ func (m *webhookNotifier) NotifyNewIssue(issue *issues_model.Issue, mentions []*
}
mode, _ := access_model.AccessLevel(issue.Poster, issue.Repo)
if err := webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssues, &api.IssuePayload{
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssues, &api.IssuePayload{
Action: api.HookIssueOpened,
Index: issue.Index,
Issue: convert.ToAPIIssue(issue),
@ -323,7 +323,7 @@ func (m *webhookNotifier) NotifyNewPullRequest(pull *issues_model.PullRequest, m
}
mode, _ := access_model.AccessLevel(pull.Issue.Poster, pull.Issue.Repo)
if err := webhook_services.PrepareWebhooks(pull.Issue.Repo, webhook.HookEventPullRequest, &api.PullRequestPayload{
if err := webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: pull.Issue.Repo}, webhook.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueOpened,
Index: pull.Issue.Index,
PullRequest: convert.ToAPIPullRequest(ctx, pull, nil),
@ -342,7 +342,7 @@ func (m *webhookNotifier) NotifyIssueChangeContent(doer *user_model.User, issue
var err error
if issue.IsPull {
issue.PullRequest.Issue = issue
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequest, &api.PullRequestPayload{
err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueEdited,
Index: issue.Index,
Changes: &api.ChangesPayload{
@ -355,7 +355,7 @@ func (m *webhookNotifier) NotifyIssueChangeContent(doer *user_model.User, issue
Sender: convert.ToUser(doer, nil),
})
} else {
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssues, &api.IssuePayload{
err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssues, &api.IssuePayload{
Action: api.HookIssueEdited,
Index: issue.Index,
Changes: &api.ChangesPayload{
@ -374,54 +374,41 @@ func (m *webhookNotifier) NotifyIssueChangeContent(doer *user_model.User, issue
}
func (m *webhookNotifier) NotifyUpdateComment(doer *user_model.User, c *issues_model.Comment, oldContent string) {
var err error
if err = c.LoadPoster(); err != nil {
if err := c.LoadPoster(); err != nil {
log.Error("LoadPoster: %v", err)
return
}
if err = c.LoadIssue(); err != nil {
if err := c.LoadIssue(); err != nil {
log.Error("LoadIssue: %v", err)
return
}
if err = c.Issue.LoadAttributes(db.DefaultContext); err != nil {
if err := c.Issue.LoadAttributes(db.DefaultContext); err != nil {
log.Error("LoadAttributes: %v", err)
return
}
mode, _ := access_model.AccessLevel(doer, c.Issue.Repo)
var eventType webhook.HookEventType
if c.Issue.IsPull {
err = webhook_services.PrepareWebhooks(c.Issue.Repo, webhook.HookEventPullRequestComment, &api.IssueCommentPayload{
Action: api.HookIssueCommentEdited,
Issue: convert.ToAPIIssue(c.Issue),
Comment: convert.ToComment(c),
Changes: &api.ChangesPayload{
Body: &api.ChangesFromPayload{
From: oldContent,
},
},
Repository: convert.ToRepo(c.Issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: true,
})
eventType = webhook.HookEventPullRequestComment
} else {
err = webhook_services.PrepareWebhooks(c.Issue.Repo, webhook.HookEventIssueComment, &api.IssueCommentPayload{
Action: api.HookIssueCommentEdited,
Issue: convert.ToAPIIssue(c.Issue),
Comment: convert.ToComment(c),
Changes: &api.ChangesPayload{
Body: &api.ChangesFromPayload{
From: oldContent,
},
},
Repository: convert.ToRepo(c.Issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: false,
})
eventType = webhook.HookEventIssueComment
}
if err != nil {
mode, _ := access_model.AccessLevel(doer, c.Issue.Repo)
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: c.Issue.Repo}, eventType, &api.IssueCommentPayload{
Action: api.HookIssueCommentEdited,
Issue: convert.ToAPIIssue(c.Issue),
Comment: convert.ToComment(c),
Changes: &api.ChangesPayload{
Body: &api.ChangesFromPayload{
From: oldContent,
},
},
Repository: convert.ToRepo(c.Issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: c.Issue.IsPull,
}); err != nil {
log.Error("PrepareWebhooks [comment_id: %d]: %v", c.ID, err)
}
}
@ -429,30 +416,22 @@ func (m *webhookNotifier) NotifyUpdateComment(doer *user_model.User, c *issues_m
func (m *webhookNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository,
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
) {
mode, _ := access_model.AccessLevel(doer, repo)
var err error
var eventType webhook.HookEventType
if issue.IsPull {
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestComment, &api.IssueCommentPayload{
Action: api.HookIssueCommentCreated,
Issue: convert.ToAPIIssue(issue),
Comment: convert.ToComment(comment),
Repository: convert.ToRepo(repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: true,
})
eventType = webhook.HookEventPullRequestComment
} else {
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueComment, &api.IssueCommentPayload{
Action: api.HookIssueCommentCreated,
Issue: convert.ToAPIIssue(issue),
Comment: convert.ToComment(comment),
Repository: convert.ToRepo(repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: false,
})
eventType = webhook.HookEventIssueComment
}
if err != nil {
mode, _ := access_model.AccessLevel(doer, repo)
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: issue.Repo}, eventType, &api.IssueCommentPayload{
Action: api.HookIssueCommentCreated,
Issue: convert.ToAPIIssue(issue),
Comment: convert.ToComment(comment),
Repository: convert.ToRepo(repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: issue.IsPull,
}); err != nil {
log.Error("PrepareWebhooks [comment_id: %d]: %v", comment.ID, err)
}
}
@ -474,36 +453,29 @@ func (m *webhookNotifier) NotifyDeleteComment(doer *user_model.User, comment *is
return
}
mode, _ := access_model.AccessLevel(doer, comment.Issue.Repo)
var eventType webhook.HookEventType
if comment.Issue.IsPull {
err = webhook_services.PrepareWebhooks(comment.Issue.Repo, webhook.HookEventPullRequestComment, &api.IssueCommentPayload{
Action: api.HookIssueCommentDeleted,
Issue: convert.ToAPIIssue(comment.Issue),
Comment: convert.ToComment(comment),
Repository: convert.ToRepo(comment.Issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: true,
})
eventType = webhook.HookEventPullRequestComment
} else {
err = webhook_services.PrepareWebhooks(comment.Issue.Repo, webhook.HookEventIssueComment, &api.IssueCommentPayload{
Action: api.HookIssueCommentDeleted,
Issue: convert.ToAPIIssue(comment.Issue),
Comment: convert.ToComment(comment),
Repository: convert.ToRepo(comment.Issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: false,
})
eventType = webhook.HookEventIssueComment
}
if err != nil {
mode, _ := access_model.AccessLevel(doer, comment.Issue.Repo)
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: comment.Issue.Repo}, eventType, &api.IssueCommentPayload{
Action: api.HookIssueCommentDeleted,
Issue: convert.ToAPIIssue(comment.Issue),
Comment: convert.ToComment(comment),
Repository: convert.ToRepo(comment.Issue.Repo, mode),
Sender: convert.ToUser(doer, nil),
IsPull: comment.Issue.IsPull,
}); err != nil {
log.Error("PrepareWebhooks [comment_id: %d]: %v", comment.ID, err)
}
}
func (m *webhookNotifier) NotifyNewWikiPage(doer *user_model.User, repo *repo_model.Repository, page, comment string) {
// Add to hook queue for created wiki page.
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventWiki, &api.WikiPayload{
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventWiki, &api.WikiPayload{
Action: api.HookWikiCreated,
Repository: convert.ToRepo(repo, perm.AccessModeOwner),
Sender: convert.ToUser(doer, nil),
@ -516,7 +488,7 @@ func (m *webhookNotifier) NotifyNewWikiPage(doer *user_model.User, repo *repo_mo
func (m *webhookNotifier) NotifyEditWikiPage(doer *user_model.User, repo *repo_model.Repository, page, comment string) {
// Add to hook queue for edit wiki page.
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventWiki, &api.WikiPayload{
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventWiki, &api.WikiPayload{
Action: api.HookWikiEdited,
Repository: convert.ToRepo(repo, perm.AccessModeOwner),
Sender: convert.ToUser(doer, nil),
@ -529,7 +501,7 @@ func (m *webhookNotifier) NotifyEditWikiPage(doer *user_model.User, repo *repo_m
func (m *webhookNotifier) NotifyDeleteWikiPage(doer *user_model.User, repo *repo_model.Repository, page string) {
// Add to hook queue for edit wiki page.
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventWiki, &api.WikiPayload{
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventWiki, &api.WikiPayload{
Action: api.HookWikiDeleted,
Repository: convert.ToRepo(repo, perm.AccessModeOwner),
Sender: convert.ToUser(doer, nil),
@ -567,7 +539,7 @@ func (m *webhookNotifier) NotifyIssueChangeLabels(doer *user_model.User, issue *
log.Error("LoadIssue: %v", err)
return
}
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestLabel, &api.PullRequestPayload{
err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequestLabel, &api.PullRequestPayload{
Action: api.HookIssueLabelUpdated,
Index: issue.Index,
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil),
@ -575,7 +547,7 @@ func (m *webhookNotifier) NotifyIssueChangeLabels(doer *user_model.User, issue *
Sender: convert.ToUser(doer, nil),
})
} else {
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueLabel, &api.IssuePayload{
err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssueLabel, &api.IssuePayload{
Action: api.HookIssueLabelUpdated,
Index: issue.Index,
Issue: convert.ToAPIIssue(issue),
@ -612,7 +584,7 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *user_model.User, issu
log.Error("LoadIssue: %v", err)
return
}
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequestMilestone, &api.PullRequestPayload{
err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequestMilestone, &api.PullRequestPayload{
Action: hookAction,
Index: issue.Index,
PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil),
@ -620,7 +592,7 @@ func (m *webhookNotifier) NotifyIssueChangeMilestone(doer *user_model.User, issu
Sender: convert.ToUser(doer, nil),
})
} else {
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventIssueMilestone, &api.IssuePayload{
err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventIssueMilestone, &api.IssuePayload{
Action: hookAction,
Index: issue.Index,
Issue: convert.ToAPIIssue(issue),
@ -644,7 +616,7 @@ func (m *webhookNotifier) NotifyPushCommits(pusher *user_model.User, repo *repo_
return
}
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventPush, &api.PushPayload{
if err := webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: repo}, webhook.HookEventPush, &api.PushPayload{
Ref: opts.RefFullName,
Before: opts.OldCommitID,
After: opts.NewCommitID,
@ -695,7 +667,7 @@ func (*webhookNotifier) NotifyMergePullRequest(pr *issues_model.PullRequest, doe
Action: api.HookIssueClosed,
}
err = webhook_services.PrepareWebhooks(pr.Issue.Repo, webhook.HookEventPullRequest, apiPullRequest)
err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: pr.Issue.Repo}, webhook.HookEventPullRequest, apiPullRequest)
if err != nil {
log.Error("PrepareWebhooks: %v", err)
}
@ -717,7 +689,7 @@ func (m *webhookNotifier) NotifyPullRequestChangeTargetBranch(doer *user_model.U
}
issue.PullRequest.Issue = issue
mode, _ := access_model.AccessLevel(issue.Poster, issue.Repo)
err = webhook_services.PrepareWebhooks(issue.Repo, webhook.HookEventPullRequest, &api.PullRequestPayload{
err = webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: issue.Repo}, webhook.HookEventPullRequest, &api.PullRequestPayload{
Action: api.HookIssueEdited,
Index: issue.Index,
Changes: &api.ChangesPayload{
@ -764,7 +736,7 @@ func (m *webhookNotifier) NotifyPullRequestReview(pr *issues_model.PullRequest,
log.Error("models.AccessLevel: %v", err)
return
}
if err := webhook_services.PrepareWebhooks(review.Issue.Repo, reviewHookType, &api.PullRequestPayload{
if err := webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: review.Issue.Repo}, reviewHookType, &api.PullRequestPayload{
Action: api.HookIssueReviewed,
Index: review.Issue.Index,
PullRequest: convert.ToAPIPullRequest(ctx, pr, nil),
@ -784,7 +756,7 @@ func (m *webhookNotifier) NotifyCreateRef(pusher *user_model.User, repo *repo_mo
apiRepo := convert.ToRepo(repo, perm.AccessModeNone)
refName := git.RefEndName(refFullName)
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventCreate, &api.CreatePayload{
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventCreate, &api.CreatePayload{
Ref: refName,
Sha: refID,
RefType: refType,
@ -808,7 +780,7 @@ func (m *webhookNotifier) NotifyPullRequestSynchronized(doer *user_model.User, p
return
}
if err := webhook_services.PrepareWebhooks(pr.Issue.Repo, webhook.HookEventPullRequestSync, &api.PullRequestPayload{
if err := webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: pr.Issue.Repo}, webhook.HookEventPullRequestSync, &api.PullRequestPayload{
Action: api.HookIssueSynchronized,
Index: pr.Issue.Index,
PullRequest: convert.ToAPIPullRequest(ctx, pr, nil),
@ -824,7 +796,7 @@ func (m *webhookNotifier) NotifyDeleteRef(pusher *user_model.User, repo *repo_mo
apiRepo := convert.ToRepo(repo, perm.AccessModeNone)
refName := git.RefEndName(refFullName)
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventDelete, &api.DeletePayload{
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: repo}, webhook.HookEventDelete, &api.DeletePayload{
Ref: refName,
RefType: refType,
PusherType: api.PusherTypeUser,
@ -842,7 +814,7 @@ func sendReleaseHook(doer *user_model.User, rel *repo_model.Release, action api.
}
mode, _ := access_model.AccessLevel(doer, rel.Repo)
if err := webhook_services.PrepareWebhooks(rel.Repo, webhook.HookEventRelease, &api.ReleasePayload{
if err := webhook_services.PrepareWebhooks(db.DefaultContext, webhook_services.EventSource{Repository: rel.Repo}, webhook.HookEventRelease, &api.ReleasePayload{
Action: action,
Release: convert.ToRelease(rel),
Repository: convert.ToRepo(rel.Repo, mode),
@ -875,7 +847,7 @@ func (m *webhookNotifier) NotifySyncPushCommits(pusher *user_model.User, repo *r
return
}
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventPush, &api.PushPayload{
if err := webhook_services.PrepareWebhooks(ctx, webhook_services.EventSource{Repository: repo}, webhook.HookEventPush, &api.PushPayload{
Ref: opts.RefFullName,
Before: opts.OldCommitID,
After: opts.NewCommitID,
@ -908,9 +880,9 @@ func (m *webhookNotifier) NotifyPackageDelete(doer *user_model.User, pd *package
}
func notifyPackage(sender *user_model.User, pd *packages_model.PackageDescriptor, action api.HookPackageAction) {
if pd.Repository == nil {
// TODO https://github.com/go-gitea/gitea/pull/17940
return
source := webhook_services.EventSource{
Repository: pd.Repository,
Owner: pd.Owner,
}
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("webhook.notifyPackage Package: %s[%d]", pd.Package.Name, pd.Package.ID))
@ -922,7 +894,7 @@ func notifyPackage(sender *user_model.User, pd *packages_model.PackageDescriptor
return
}
if err := webhook_services.PrepareWebhooks(pd.Repository, webhook.HookEventPackage, &api.PackagePayload{
if err := webhook_services.PrepareWebhooks(ctx, source, webhook.HookEventPackage, &api.PackagePayload{
Action: action,
Package: apiPackage,
Sender: convert.ToUser(sender, nil),

View file

@ -211,7 +211,7 @@ func generateRepoCommit(ctx context.Context, repo, templateRepo, generateRepo *r
}
repoPath := repo.RepoPath()
if stdout, _, err := git.NewCommand(ctx, "remote", "add", "origin", repoPath).
if stdout, _, err := git.NewCommand(ctx, "remote", "add", "origin").AddDynamicArguments(repoPath).
SetDescription(fmt.Sprintf("generateRepoCommit (git remote add): %s to %s", templateRepoPath, tmpDir)).
RunStdString(&git.RunOpts{Dir: tmpDir, Env: env}); err != nil {
log.Error("Unable to add %v as remote origin to temporary repo to %s: stdout %s\nError: %v", repo, tmpDir, stdout, err)

View file

@ -228,7 +228,7 @@ func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir,
)
// Clone to temporary path and do the init commit.
if stdout, _, err := git.NewCommand(ctx, "clone", repoPath, tmpDir).
if stdout, _, err := git.NewCommand(ctx, "clone").AddDynamicArguments(repoPath, tmpDir).
SetDescription(fmt.Sprintf("prepareRepoCommit (git clone): %s to %s", repoPath, tmpDir)).
RunStdString(&git.RunOpts{Dir: "", Env: env}); err != nil {
log.Error("Failed to clone from %v into %s: stdout: %s\nError: %v", repo, tmpDir, stdout, err)
@ -317,14 +317,14 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
return fmt.Errorf("git add --all: %v", err)
}
args := []string{
"commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
cmd := git.NewCommand(ctx,
"commit", git.CmdArg(fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email)),
"-m", "Initial commit",
}
)
sign, keyID, signer, _ := asymkey_service.SignInitialCommit(ctx, tmpPath, u)
if sign {
args = append(args, "-S"+keyID)
cmd.AddArguments(git.CmdArg("-S" + keyID))
if repo.GetTrustModel() == repo_model.CommitterTrustModel || repo.GetTrustModel() == repo_model.CollaboratorCommitterTrustModel {
// need to set the committer to the KeyID owner
@ -332,7 +332,7 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
committerEmail = signer.Email
}
} else {
args = append(args, "--no-gpg-sign")
cmd.AddArguments("--no-gpg-sign")
}
env = append(env,
@ -340,10 +340,10 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
"GIT_COMMITTER_EMAIL="+committerEmail,
)
if stdout, _, err := git.NewCommand(ctx, args...).
if stdout, _, err := cmd.
SetDescription(fmt.Sprintf("initRepoCommit (git commit): %s", tmpPath)).
RunStdString(&git.RunOpts{Dir: tmpPath, Env: env}); err != nil {
log.Error("Failed to commit: %v: Stdout: %s\nError: %v", args, stdout, err)
log.Error("Failed to commit: %v: Stdout: %s\nError: %v", cmd.String(), stdout, err)
return fmt.Errorf("git commit: %v", err)
}
@ -351,7 +351,7 @@ func initRepoCommit(ctx context.Context, tmpPath string, repo *repo_model.Reposi
defaultBranch = setting.Repository.DefaultBranch
}
if stdout, _, err := git.NewCommand(ctx, "push", "origin", "HEAD:"+defaultBranch).
if stdout, _, err := git.NewCommand(ctx, "push", "origin").AddDynamicArguments("HEAD:" + defaultBranch).
SetDescription(fmt.Sprintf("initRepoCommit (git push): %s", tmpPath)).
RunStdString(&git.RunOpts{Dir: tmpPath, Env: InternalPushingEnvironment(u, repo)}); err != nil {
log.Error("Failed to push back to HEAD: Stdout: %s\nError: %v", stdout, err)

View file

@ -104,7 +104,7 @@ func IsForcePush(ctx context.Context, opts *PushUpdateOptions) (bool, error) {
return false, nil
}
output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1", opts.OldCommitID, "^"+opts.NewCommitID).
output, _, err := git.NewCommand(ctx, "rev-list", "--max-count=1").AddDynamicArguments(opts.OldCommitID, "^"+opts.NewCommitID).
RunStdString(&git.RunOpts{Dir: repo_model.RepoPath(opts.RepoUserName, opts.RepoName)})
if err != nil {
return false, err

View file

@ -459,6 +459,19 @@ func NewFuncMap() []template.FuncMap {
return items
},
"HasPrefix": strings.HasPrefix,
"CompareLink": func(baseRepo, repo *repo_model.Repository, branchName string) string {
var curBranch string
if repo.ID != baseRepo.ID {
curBranch += fmt.Sprintf("%s/%s:", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name))
}
curBranch += util.PathEscapeSegments(branchName)
return fmt.Sprintf("%s/compare/%s...%s",
baseRepo.Link(),
util.PathEscapeSegments(baseRepo.DefaultBranch),
curBranch,
)
},
}}
}

View file

@ -261,6 +261,7 @@ register_success=Успешна регистрация
[modal]
yes=Да
no=Не

View file

@ -268,8 +268,11 @@ users=Uživatelé
organizations=Organizace
search=Vyhledat
code=Zdrojový kód
search.type.tooltip=Druh vyhledávání
search.fuzzy=Fuzzy
search.fuzzy.tooltip=Zahrnout výsledky, které také úzce odpovídají hledanému výrazu
search.match=Shoda
search.match.tooltip=Zahrnout pouze výsledky, které odpovídají přesnému hledanému výrazu
code_search_unavailable=V současné době není vyhledávání kódu dostupné. Obraťte se na správce webu.
repo_no_results=Nebyly nalezeny žádné odpovídající repozitáře.
user_no_results=Nebyly nalezeni žádní odpovídající uživatelé.
@ -409,6 +412,7 @@ repo.transfer.body=Chcete-li ji přijmout nebo odmítnout, navštivte %s nebo ji
repo.collaborator.added.subject=%s vás přidal do %s
repo.collaborator.added.text=Byl jste přidán jako spolupracovník repozitáře:
[modal]
yes=Ano
no=Ne
@ -507,6 +511,7 @@ activity=Veřejná aktivita
followers=Sledující
starred=Oblíbené repozitáře
watched=Sledované repozitáře
code=Kód
projects=Projekty
following=Sledovaní
follow=Sledovat
@ -1763,8 +1768,11 @@ activity.git_stats_deletion_n=%d odebrání
search=Vyhledat
search.search_repo=Hledat repozitář
search.type.tooltip=Druh vyhledávání
search.fuzzy=Fuzzy
search.fuzzy.tooltip=Zahrnout výsledky, které také úzce odpovídají hledanému výrazu
search.match=Shoda
search.match.tooltip=Zahrnout pouze výsledky, které odpovídají přesnému hledanému výrazu
search.results=Výsledky hledání „%s“ v <a href="%s">%s</a>
search.code_no_results=Nebyl nalezen žádný zdrojový kód odpovídající hledanému výrazu.
search.code_search_unavailable=V současné době není vyhledávání kódu dostupné. Obraťte se na správce webu.
@ -2310,6 +2318,7 @@ create_org=Vytvořit organizaci
repo_updated=Upraveno
people=Lidé
teams=Týmy
code=Kód
lower_members=členové
lower_repositories=repozitáře
create_new_team=Nový tým
@ -2871,6 +2880,8 @@ config.access_log_template=Šablona
config.xorm_log_mode=Režim logování XORM
config.xorm_log_sql=Logovat SQL
config.get_setting_failed=Získání nastavení %s se nezdařilo
config.set_setting_failed=Nastavení %s se nezdařilo
monitor.cron=Naplánované úlohy
monitor.name=Název
@ -3035,6 +3046,9 @@ pin=Připnout upozornění
mark_as_read=Označit jako přečtené
mark_as_unread=Označit jako nepřečtené
mark_all_as_read=Označit vše jako přečtené
subscriptions=Odběry
watching=Sledované
no_subscriptions=Žádné odběry
[gpg]
default_key=Podepsáno výchozím klíčem

View file

@ -407,6 +407,7 @@ repo.transfer.body=Um es anzunehmen oder abzulehnen, öffne %s, oder ignoriere e
repo.collaborator.added.subject=%s hat dich zu %s hinzugefügt
repo.collaborator.added.text=Du wurdest als Mitarbeiter für folgendes Repository hinzugefügt:
[modal]
yes=Ja
no=Abbrechen

View file

@ -409,6 +409,7 @@ repo.transfer.body=Για να το αποδεχτείτε ή να το απορ
repo.collaborator.added.subject=%s σας πρόσθεσε στο %s
repo.collaborator.added.text=Έχετε προστεθεί ως συνεργάτης του αποθετηρίου:
[modal]
yes=Ναι
no=Όχι

View file

@ -106,6 +106,10 @@ never = Never
rss_feed = RSS Feed
[filter]
string.asc = A - Z
string.desc = Z - A
[error]
occurred = An error occurred
report_message = If you are sure this is a Gitea bug, please search for issues on <a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a> or open a new issue if necessary.
@ -412,6 +416,11 @@ repo.transfer.body = To accept or reject it visit %s or just ignore it.
repo.collaborator.added.subject = %s added you to %s
repo.collaborator.added.text = You have been added as a collaborator of repository:
team_invite.subject = %[1]s has invited you to join the %[2]s organization
team_invite.text_1 = %[1]s has invited you to join team %[2]s in organization %[3]s.
team_invite.text_2 = Please click the following link to join the team:
team_invite.text_3 = Note: This invitation was intended for %[1]s. If you were not expecting this invitation, you can ignore this email.
[modal]
yes = Yes
no = No
@ -487,6 +496,7 @@ user_not_exist = The user does not exist.
team_not_exist = The team does not exist.
last_org_owner = You cannot remove the last user from the 'owners' team. There must be at least one owner for an organization.
cannot_add_org_to_team = An organization cannot be added as a team member.
duplicate_invite_to_team = The user was already invited as a team member.
invalid_ssh_key = Can not verify your SSH key: %s
invalid_gpg_key = Can not verify your GPG key: %s
@ -2402,6 +2412,8 @@ teams.members = Team Members
teams.update_settings = Update Settings
teams.delete_team = Delete Team
teams.add_team_member = Add Team Member
teams.invite_team_member = Invite to %s
teams.invite_team_member.list = Pending Invitations
teams.delete_team_title = Delete Team
teams.delete_team_desc = Deleting a team revokes repository access from its members. Continue?
teams.delete_team_success = The team has been deleted.
@ -2426,6 +2438,9 @@ teams.all_repositories_helper = Team has access to all repositories. Selecting t
teams.all_repositories_read_permission_desc = This team grants <strong>Read</strong> access to <strong>all repositories</strong>: members can view and clone repositories.
teams.all_repositories_write_permission_desc = This team grants <strong>Write</strong> access to <strong>all repositories</strong>: members can read from and push to repositories.
teams.all_repositories_admin_permission_desc = This team grants <strong>Admin</strong> access to <strong>all repositories</strong>: members can read from, push to and add collaborators to repositories.
teams.invite.title = You've been invited to join team <strong>%s</strong> in organization <strong>%s</strong>.
teams.invite.by = Invited by %s
teams.invite.description = Please click the button below to join the team.
[admin]
dashboard = Dashboard

View file

@ -411,6 +411,7 @@ repo.transfer.body=Para aceptarlo o rechazarlo, visita %s o simplemente ignórel
repo.collaborator.added.subject=%s le añadió en %s
repo.collaborator.added.text=Has sido añadido como colaborador del repositorio:
[modal]
yes=
no=No

View file

@ -379,6 +379,7 @@ repo.transfer.body=برای تایید یا رد آن %s را ببینید یا
repo.collaborator.added.subject=%s شما را به پروژه %s اضافه کرد
repo.collaborator.added.text=شما به عنوان مشارکت‌کننده در این مخزن اضافه شدید:
[modal]
yes=بله
no=خیر

View file

@ -358,6 +358,7 @@ release.download.targz=Lähdekoodi (TAR.GZ)
repo.transfer.to_you=sinä
[modal]
yes=Kyllä
no=Ei

View file

@ -412,6 +412,7 @@ repo.transfer.body=Pour l'accepter ou le rejeter, visitez %s ou ignorez-le.
repo.collaborator.added.subject=%s vous a ajouté à %s
repo.collaborator.added.text=Vous avez été ajouté en tant que collaborateur du dépôt :
[modal]
yes=Oui
no=Non

View file

@ -291,6 +291,7 @@ register_success=Sikeres regisztráció
[modal]
yes=Igen
no=Nem

View file

@ -282,6 +282,7 @@ register_success=Pendaftaran berhasil
[modal]
yes=Ya
no=Tidak

View file

@ -339,6 +339,7 @@ repo.transfer.body=Til að samþykkja eða hafna því skaltu fara á %s eða hu
repo.collaborator.added.subject=%s bætti þér við í %s
repo.collaborator.added.text=Þér hefur verið bætt við sem aðila hugbúnaðarsafns:
[modal]
yes=
no=Nei

View file

@ -407,6 +407,7 @@ repo.transfer.body=Per accettare o respingerla visita %s o semplicemente ignorar
repo.collaborator.added.subject=%s ti ha aggiunto a %s
repo.collaborator.added.text=Sei stato aggiunto come collaboratore del repository:
[modal]
yes=
no=No

View file

@ -412,6 +412,7 @@ repo.transfer.body=承認または拒否するには %s を開きます。 も
repo.collaborator.added.subject=%s が %s にあなたを追加しました
repo.collaborator.added.text=あなたは次のリポジトリの共同作業者に追加されました:
[modal]
yes=はい
no=いいえ

View file

@ -273,6 +273,7 @@ register_success=등록 완료
[modal]
yes=
no=아니오

View file

@ -407,6 +407,7 @@ repo.transfer.body=Ja vēlaties to noraidīt vai apstiprināt, tad apmeklējiet
repo.collaborator.added.subject=%s pievienoja Jūs repozitorijam %s
repo.collaborator.added.text=Jūs tikāt pievienots kā līdzstrādnieks repozitorijam:
[modal]
yes=
no=

View file

@ -261,6 +261,7 @@ register_success=രജിസ്ട്രേഷൻ വിജയകരം
[modal]
yes=അതെ
no=ഇല്ല

View file

@ -407,6 +407,7 @@ repo.transfer.body=Om het te accepteren of afwijzen, bezoek %s of negeer het gew
repo.collaborator.added.subject=%s heeft jou toegevoegd aan %s
repo.collaborator.added.text=U bent toegevoegd als een medewerker van de repository:
[modal]
yes=Ja
no=Nee

View file

@ -392,6 +392,7 @@ repo.transfer.body=Aby zaakceptować lub odrzucić go, odwiedź %s lub po prostu
repo.collaborator.added.subject=%s dodał Cię do %s
repo.collaborator.added.text=Zostałeś dodany jako współtwórca repozytorium:
[modal]
yes=Tak
no=Nie

View file

@ -409,6 +409,7 @@ repo.transfer.body=Para o aceitar ou rejeitar visite %s, ou simplesmente o ignor
repo.collaborator.added.subject=%s adicionou você a %s
repo.collaborator.added.text=Você foi adicionado como um colaborador do repositório:
[modal]
yes=Sim
no=Não

View file

@ -412,6 +412,11 @@ repo.transfer.body=Para o aceitar ou rejeitar visite %s, ou ignore-o, simplesmen
repo.collaborator.added.subject=%s adicionou você a %s
repo.collaborator.added.text=Foi adicionado(a) como colaborador(a) do repositório:
team_invite.subject=%[1]s fez-lhe um convite para se juntar à organização %[2]s
team_invite.text_1=%[1]s fez-lhe um convite para se juntar à equipa %[2]s na organização %[3]s.
team_invite.text_2=Clique na ligação seguinte para se juntar à equipa:
team_invite.text_3=Nota: Este convite é dirigido a %[1]s. Se não estava à espera deste convite, pode ignorar este email.
[modal]
yes=Sim
no=Não
@ -487,6 +492,7 @@ user_not_exist=O utilizador não existe.
team_not_exist=A equipa não existe.
last_org_owner=Não pode remover o último utilizador da equipa 'proprietários'. Tem que haver pelo menos um proprietário numa organização.
cannot_add_org_to_team=Uma organização não pode ser adicionada como membro de uma equipa.
duplicate_invite_to_team=O(A) utilizador(a) já tinha sido convidado(a) para ser membro da equipa.
invalid_ssh_key=Não é possível validar a sua chave SSH: %s
invalid_gpg_key=Não é possível validar a sua chave GPG: %s
@ -2402,6 +2408,8 @@ teams.members=Membros da equipa
teams.update_settings=Modificar configurações
teams.delete_team=Eliminar equipa
teams.add_team_member=Adicionar membro da equipa
teams.invite_team_member=Convidar para %s
teams.invite_team_member.list=Convites pendentes
teams.delete_team_title=Eliminar equipa
teams.delete_team_desc=Eliminar uma equipa revoga o acesso dos seus membros ao repositório. Quer continuar?
teams.delete_team_success=A equipa foi eliminada.
@ -2426,6 +2434,9 @@ teams.all_repositories_helper=A equipa tem acesso a todos os repositórios. Esco
teams.all_repositories_read_permission_desc=Esta equipa atribui o acesso de <strong>leitura</strong> a <strong>todos os repositórios</strong>: os seus membros podem ver e clonar os repositórios.
teams.all_repositories_write_permission_desc=Esta equipa atribui o acesso de <strong>escrita</strong> a <strong>todos os repositórios</strong>: os seus membros podem ler de, e enviar para os repositórios.
teams.all_repositories_admin_permission_desc=Esta equipa atribui o acesso de <strong>administração</strong> a <strong>todos os repositórios</strong>: os seus membros podem ler de, enviar para, e adicionar colaboradores aos repositórios.
teams.invite.title=Foi-lhe feito um convite para se juntar à equipa <strong>%s</strong> na organização<strong>%s</strong>.
teams.invite.by=Convidado(a) por %s
teams.invite.description=Clique no botão abaixo para se juntar à equipa.
[admin]
dashboard=Painel de controlo

View file

@ -403,6 +403,7 @@ repo.transfer.body=Для того чтобы принять или отклон
repo.collaborator.added.subject=%s добавил вас в %s
repo.collaborator.added.text=Вы были добавлены в качестве соавтора репозитория:
[modal]
yes=Да
no=Нет

View file

@ -365,6 +365,7 @@ repo.transfer.body=එය පිළිගැනීමට හෝ ප්රති
repo.collaborator.added.subject=%s ඔබව %s ට එකතු කළා
repo.collaborator.added.text=ඔබ ගබඩාවේ සහයෝගිතාකරුවෙකු ලෙස එකතු කර ඇත:
[modal]
yes=ඔව්
no=නැහැ

View file

@ -409,6 +409,7 @@ repo.transfer.body=Ak to chcete prijať alebo odmietnuť, navštívte %s alebo t
repo.collaborator.added.subject=%s vás pridal do %s
repo.collaborator.added.text=Boli ste pridaný ako spolupracovník repozitára:
[modal]
yes=Áno
no=Nie

View file

@ -304,6 +304,7 @@ register_success=Registreringen lyckades
[modal]
yes=Ja
no=Nej

View file

@ -412,6 +412,7 @@ repo.transfer.body=Kabul veya reddetmek için %s ziyaret edin veya görmezden ge
repo.collaborator.added.subject=%s sizi %s ekledi
repo.collaborator.added.text=Bu depo için katkıcı olarak eklendiniz:
[modal]
yes=Evet
no=Hayır

View file

@ -382,6 +382,7 @@ repo.transfer.body=Щоб прийняти або відхилити перей
repo.collaborator.added.subject=%s додав вас до %s
repo.collaborator.added.text=Ви були додані в якості співавтора репозиторію:
[modal]
yes=Так
no=Ні

View file

@ -412,6 +412,11 @@ repo.transfer.body=访问 %s 以接受或拒绝转移,亦可忽略此邮件。
repo.collaborator.added.subject=%s 把你添加到了 %s
repo.collaborator.added.text=您已被添加为代码库的协作者:
team_invite.subject=%[1]s 邀请您加入组织 %[2]s
team_invite.text_1=%[1]s 邀请您加入组织 %[3]s 中的团队 %[2]s。
team_invite.text_2=请点击下面的链接加入团队:
team_invite.text_3=注意:这是发送给 %[1]s 的邀请。如果您未曾收到过此类邀请,请忽略这封电子邮件。
[modal]
yes=确认操作
no=取消操作
@ -487,6 +492,7 @@ user_not_exist=该用户不存在
team_not_exist=团队不存在
last_org_owner=您不能从 "所有者" 团队中删除最后一个用户。组织中必须至少有一个所有者。
cannot_add_org_to_team=组织不能被加入到团队中。
duplicate_invite_to_team=此用户已被邀请为团队成员。
invalid_ssh_key=无法验证您的 SSH 密钥: %s
invalid_gpg_key=无法验证您的 GPG 密钥: %s
@ -861,8 +867,8 @@ readme_helper_desc=这是您可以为您的项目撰写完整描述的地方。
auto_init=初始化仓库(添加. gitignore、许可证和自述文件)
trust_model_helper=选择签名验证的“信任模型”。可能的选项是:
trust_model_helper_collaborator=协作者:信任协作者的签名
trust_model_helper_committer=提交者:信任匹配提交者的签名
trust_model_helper_collaborator_committer=协作者+提交者:信任与提交者匹配的协作者的签名
trust_model_helper_committer=提交者:信任与提交者相符的签名
trust_model_helper_collaborator_committer=协作者+提交者:信任协作者同时是提交者的签名
trust_model_helper_default=默认:使用此安装的默认信任模型
create_repo=创建仓库
default_branch=默认分支
@ -1043,7 +1049,7 @@ file_view_raw=查看原始文件
file_permalink=永久链接
file_too_large=文件过大,无法显示。
invisible_runes_header=`此文件包含不可见的 Unicode 字符!`
invisible_runes_description=`这个文件包含不可见的 Unicode 字符,其处理方式可能不同于下面显示的字符。 如果您是有意且正当地使用它们,您可以安全地忽略这个警告。使用 Escape 按钮来显示隐藏的字符。
invisible_runes_description=`这个文件包含不可见的 Unicode 字符,其处理方式可能不同于下面显示的字符。 如果您是有意且正当地使用它们,您可以安全地忽略这个警告。使用 Escape 按钮来显示隐藏的字符。`
ambiguous_runes_header=`此行包含模棱两可的 Unicode 字符!`
ambiguous_runes_description=`此文件包含模棱两可的 Unicode 字符,这些字符可能会与您当前语言环境的其他字符混淆。 如果您是有意且正当地使用它们,您可以安全地忽略这个警告。使用 Escape 按钮来高亮这些字符。`
invisible_runes_line=`此行含有不可见的 unicode 字符`
@ -1882,13 +1888,13 @@ settings.trust_model.default=默认信任模型
settings.trust_model.default.desc=为此安装使用默认仓库信任模型。
settings.trust_model.collaborator=协作者
settings.trust_model.collaborator.long=协作者:信任协作者的签名
settings.trust_model.collaborator.desc=此仓库中协作者的有效签名将被标记为“可信” - 不管他们是否是提交者。否则,如果签名匹配了提交者,有效的签名将被标记为“不可信”
settings.trust_model.collaborator.desc=此仓库中协作者的有效签名将被标记为「可信」(无论它们是否是提交者),签名只符合提交者时将标记为「不可信」,都不匹配时标记为「不匹配」
settings.trust_model.committer=提交者
settings.trust_model.committer.long=提交者: 信任与提交者匹配的签名 (匹配GitHub 并强制Gitea签名的提交者将Gitea作为提交者)
settings.trust_model.committer.desc=有效的签名只有与提交者匹配时才会被标记为“可信”否则会被标记为“不匹配”。这会强制Gitea成为已签名提交的提交者而实际提交者在提交中被标记为Co-authored-by: 和Co-committed-by: trailer。默认的Gitea密钥必须与数据库中的一位用户相匹配
settings.trust_model.committer.long=提交者: 信任与提交者相符的签名 (此特性类似 GitHub这会强制采用 Gitea 作为提交者和签名者)
settings.trust_model.committer.desc=提交者的有效签名将被标记为「可信」,否则将被标记为「不匹配」。这会强制 Gitea 成为签名者和提交者实际的提交者将被标记于提交消息结尾处的「Co-Authored-By:」和「Co-Committed-By:」。默认的 Gitea 签名密钥必须匹配数据库中的一个用户密钥
settings.trust_model.collaboratorcommitter=协作者+提交者
settings.trust_model.collaboratorcommitter.long=协作者+提交者:信任协作者同时是提交者的签名
settings.trust_model.collaboratorcommitter.desc=如果匹配为提交者,此仓库中协作者的有效签名将被标记为“可信”。否则,如果签名匹配了提交者或者未匹配,有效的签名将被标记为“不可信”。这将强制 Gitea 在签名提交上将实际提交者加上 Co-Authored-By: 和 Co-Committed-By: 。默认的Gitea密钥必须匹配Gitea用户
settings.trust_model.collaboratorcommitter.desc=此仓库中协作者的有效签名在他同时是提交者时将被标记为「可信」,签名只匹配了提交者时将标记为「不可信」,都不匹配时标记为「不匹配」。这会强制 Gitea 成为签名者和提交者实际的提交者将被标记于提交消息结尾处的「Co-Authored-By:」和「Co-Committed-By:」。默认的 Gitea 签名密钥必须匹配数据库中的一个用户密钥
settings.wiki_delete=删除百科数据
settings.wiki_delete_desc=删除仓库百科数据是永久性的,无法撤消。
settings.wiki_delete_notices_1=- 这将永久删除和禁用 %s 的百科。
@ -2402,6 +2408,8 @@ teams.members=团队成员
teams.update_settings=更新团队设置
teams.delete_team=删除团队
teams.add_team_member=添加团队成员
teams.invite_team_member=邀请加入 %s
teams.invite_team_member.list=待处理的邀请
teams.delete_team_title=删除团队
teams.delete_team_desc=删除一个团队将删除团队成员的访问权限,继续?
teams.delete_team_success=该团队已被删除。
@ -2426,6 +2434,9 @@ teams.all_repositories_helper=团队可以访问所有仓库。选择此选项
teams.all_repositories_read_permission_desc=此团队授予<strong>读取</strong><strong>所有仓库</strong>的访问权限: 成员可以查看和克隆仓库。
teams.all_repositories_write_permission_desc=此团队授予<strong>修改</strong><strong>所有仓库</strong>的访问权限: 成员可以查看和推送至仓库。
teams.all_repositories_admin_permission_desc=该团队拥有 <strong>管理</strong> <strong>所有仓库</strong>的权限:团队成员可以读取、克隆、推送以及添加其它仓库协作者。
teams.invite.title=您已被邀请加入组织 <strong>%s</strong> 中的团队 <strong>%s</strong>。
teams.invite.by=邀请人 %s
teams.invite.description=请点击下面的按钮加入团队。
[admin]
dashboard=管理面板

View file

@ -124,6 +124,7 @@ register_success=註冊成功
[modal]
yes=確認操作
no=取消操作

Some files were not shown because too many files have changed in this diff Show more