Merge remote-tracking branch 'origin/actually_disable_stars' into actually_disable_stars

pull/33208/head
HeCorr 3 weeks ago
commit 39a1b4c8cd
No known key found for this signature in database
GPG Key ID: 9333B03459C34098
  1. 2
      .eslintrc.cjs
  2. 4
      cmd/web.go
  3. 12
      flake.lock
  4. 5
      flake.nix
  5. 2
      models/actions/run.go
  6. 2
      models/activities/action.go
  7. 23
      models/db/engine_hook.go
  8. 2
      models/db/engine_init.go
  9. 6
      models/fixtures/access.yml
  10. 21
      models/fixtures/webhook.yml
  11. 7
      models/git/branch.go
  12. 4
      models/issues/comment.go
  13. 7
      models/issues/stopwatch.go
  14. 45
      models/perm/access/repo_permission.go
  15. 12
      models/perm/access/repo_permission_test.go
  16. 11
      models/repo/archiver.go
  17. 1
      models/repo/license.go
  18. 48
      models/unittest/fscopy.go
  19. 13
      models/webhook/webhook_system.go
  20. 37
      models/webhook/webhook_system_test.go
  21. 7
      modules/cache/cache.go
  22. 3
      modules/cache/cache_test.go
  23. 12
      modules/git/command.go
  24. 2
      modules/git/command_test.go
  25. 8
      modules/git/commit.go
  26. 10
      modules/git/diff.go
  27. 54
      modules/git/ref.go
  28. 11
      modules/git/ref_test.go
  29. 36
      modules/git/repo_archive.go
  30. 32
      modules/git/repo_archive_test.go
  31. 2
      modules/git/repo_branch_gogit.go
  32. 32
      modules/gtprof/event.go
  33. 175
      modules/gtprof/trace.go
  34. 96
      modules/gtprof/trace_builtin.go
  35. 19
      modules/gtprof/trace_const.go
  36. 93
      modules/gtprof/trace_test.go
  37. 2
      modules/markup/asciicast/asciicast.go
  38. 4
      modules/markup/render.go
  39. 4
      modules/repository/branch.go
  40. 73
      modules/tailmsg/talimsg.go
  41. 2
      modules/templates/helper.go
  42. 55
      modules/util/sec_to_time.go
  43. 21
      modules/util/sec_to_time_test.go
  44. 4
      modules/web/handler.go
  45. 19
      modules/web/routing/context.go
  46. 6
      options/gitignore/Node
  47. 3
      options/gitignore/Python
  48. 4
      options/gitignore/Rust
  49. 11
      options/locale/locale_cs-CZ.ini
  50. 11
      options/locale/locale_de-DE.ini
  51. 6
      options/locale/locale_el-GR.ini
  52. 23
      options/locale/locale_en-US.ini
  53. 6
      options/locale/locale_es-ES.ini
  54. 40
      options/locale/locale_fr-FR.ini
  55. 25
      options/locale/locale_ga-IE.ini
  56. 1
      options/locale/locale_id-ID.ini
  57. 31
      options/locale/locale_ja-JP.ini
  58. 6
      options/locale/locale_lv-LV.ini
  59. 1
      options/locale/locale_pl-PL.ini
  60. 3
      options/locale/locale_pt-BR.ini
  61. 24
      options/locale/locale_pt-PT.ini
  62. 5
      options/locale/locale_ru-RU.ini
  63. 1
      options/locale/locale_sk-SK.ini
  64. 7
      options/locale/locale_tr-TR.ini
  65. 11
      options/locale/locale_zh-CN.ini
  66. 11
      options/locale/locale_zh-TW.ini
  67. 963
      package-lock.json
  68. 38
      package.json
  69. 73
      poetry.lock
  70. 2
      pyproject.toml
  71. 14
      routers/api/actions/runner/main_test.go
  72. 146
      routers/api/actions/runner/utils.go
  73. 21
      routers/api/v1/admin/hooks.go
  74. 8
      routers/api/v1/repo/branch.go
  75. 8
      routers/api/v1/repo/download.go
  76. 9
      routers/api/v1/repo/file.go
  77. 2
      routers/api/v1/repo/hook_test.go
  78. 4
      routers/api/v1/repo/repo_test.go
  79. 27
      routers/common/middleware.go
  80. 2
      routers/init.go
  81. 1
      routers/web/admin/admin.go
  82. 18
      routers/web/admin/diagnosis.go
  83. 18
      routers/web/admin/perftrace.go
  84. 11
      routers/web/admin/stacktrace.go
  85. 11
      routers/web/auth/linkaccount.go
  86. 4
      routers/web/base.go
  87. 2
      routers/web/feed/branch.go
  88. 4
      routers/web/feed/file.go
  89. 2
      routers/web/repo/actions/view.go
  90. 4
      routers/web/repo/blame.go
  91. 17
      routers/web/repo/branch.go
  92. 4
      routers/web/repo/cherry_pick.go
  93. 2
      routers/web/repo/code_frequency.go
  94. 25
      routers/web/repo/commit.go
  95. 2
      routers/web/repo/contributors.go
  96. 8
      routers/web/repo/editor.go
  97. 15
      routers/web/repo/helper.go
  98. 2
      routers/web/repo/issue_dependency.go
  99. 12
      routers/web/repo/issue_label_test.go
  100. 10
      routers/web/repo/issue_poster.go
  101. Some files were not shown because too many files have changed in this diff Show More

@ -403,7 +403,7 @@ module.exports = {
'github/a11y-svg-has-accessible-name': [0], 'github/a11y-svg-has-accessible-name': [0],
'github/array-foreach': [0], 'github/array-foreach': [0],
'github/async-currenttarget': [2], 'github/async-currenttarget': [2],
'github/async-preventdefault': [2], 'github/async-preventdefault': [0], // https://github.com/github/eslint-plugin-github/issues/599
'github/authenticity-token': [0], 'github/authenticity-token': [0],
'github/get-attribute': [0], 'github/get-attribute': [0],
'github/js-class-name': [0], 'github/js-class-name': [0],

@ -18,10 +18,12 @@ import (
"code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/gtprof"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/public" "code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers" "code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/install" "code.gitea.io/gitea/routers/install"
@ -218,6 +220,8 @@ func serveInstalled(ctx *cli.Context) error {
} }
} }
gtprof.EnableBuiltinTracer(util.Iif(setting.IsProd, 2000*time.Millisecond, 100*time.Millisecond))
// Set up Chi routes // Set up Chi routes
webRoutes := routers.NormalRoutes() webRoutes := routers.NormalRoutes()
err := listen(webRoutes, true) err := listen(webRoutes, true)

@ -5,11 +5,11 @@
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1726560853, "lastModified": 1731533236,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -20,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1731139594, "lastModified": 1736798957,
"narHash": "sha256-IigrKK3vYRpUu+HEjPL/phrfh7Ox881er1UEsZvw9Q4=", "narHash": "sha256-qwpCtZhSsSNQtK4xYGzMiyEDhkNzOCz/Vfu4oL2ETsQ=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "76612b17c0ce71689921ca12d9ffdc9c23ce40b2", "rev": "9abb87b552b7f55ac8916b6fc9e5cb486656a2f3",
"type": "github" "type": "github"
}, },
"original": { "original": {

@ -29,9 +29,14 @@
poetry poetry
# backend # backend
go_1_23
gofumpt gofumpt
sqlite sqlite
]; ];
shellHook = ''
export GO="${pkgs.go_1_23}/bin/go"
export GOROOT="${pkgs.go_1_23}/share/go"
'';
}; };
} }
); );

@ -88,7 +88,7 @@ func (run *ActionRun) RefLink() string {
if refName.IsPull() { if refName.IsPull() {
return run.Repo.Link() + "/pulls/" + refName.ShortName() return run.Repo.Link() + "/pulls/" + refName.ShortName()
} }
return git.RefURL(run.Repo.Link(), run.Ref) return run.Repo.Link() + "/src/" + refName.RefWebLinkPath()
} }
// PrettyRef return #id for pull ref or ShortName for others // PrettyRef return #id for pull ref or ShortName for others

@ -355,7 +355,7 @@ func (a *Action) GetBranch() string {
// GetRefLink returns the action's ref link. // GetRefLink returns the action's ref link.
func (a *Action) GetRefLink(ctx context.Context) string { func (a *Action) GetRefLink(ctx context.Context) string {
return git.RefURL(a.GetRepoLink(ctx), a.RefName) return a.GetRepoLink(ctx) + "/src/" + git.RefName(a.RefName).RefWebLinkPath()
} }
// GetTag returns the action's repository tag. // GetTag returns the action's repository tag.

@ -7,23 +7,36 @@ import (
"context" "context"
"time" "time"
"code.gitea.io/gitea/modules/gtprof"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"xorm.io/xorm/contexts" "xorm.io/xorm/contexts"
) )
type SlowQueryHook struct { type EngineHook struct {
Threshold time.Duration Threshold time.Duration
Logger log.Logger Logger log.Logger
} }
var _ contexts.Hook = (*SlowQueryHook)(nil) var _ contexts.Hook = (*EngineHook)(nil)
func (*SlowQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) { func (*EngineHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
return c.Ctx, nil ctx, _ := gtprof.GetTracer().Start(c.Ctx, gtprof.TraceSpanDatabase)
return ctx, nil
} }
func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error { func (h *EngineHook) AfterProcess(c *contexts.ContextHook) error {
span := gtprof.GetContextSpan(c.Ctx)
if span != nil {
// Do not record SQL parameters here:
// * It shouldn't expose the parameters because they contain sensitive information, end users need to report the trace details safely.
// * Some parameters contain quite long texts, waste memory and are difficult to display.
span.SetAttributeString(gtprof.TraceAttrDbSQL, c.SQL)
span.End()
} else {
setting.PanicInDevOrTesting("span in database engine hook is nil")
}
if c.ExecuteTime >= h.Threshold { if c.ExecuteTime >= h.Threshold {
// 8 is the amount of skips passed to runtime.Caller, so that in the log the correct function // 8 is the amount of skips passed to runtime.Caller, so that in the log the correct function
// is being displayed (the function that ultimately wants to execute the query in the code) // is being displayed (the function that ultimately wants to execute the query in the code)

@ -72,7 +72,7 @@ func InitEngine(ctx context.Context) error {
xe.SetDefaultContext(ctx) xe.SetDefaultContext(ctx)
if setting.Database.SlowQueryThreshold > 0 { if setting.Database.SlowQueryThreshold > 0 {
xe.AddHook(&SlowQueryHook{ xe.AddHook(&EngineHook{
Threshold: setting.Database.SlowQueryThreshold, Threshold: setting.Database.SlowQueryThreshold,
Logger: log.GetLogger("xorm"), Logger: log.GetLogger("xorm"),
}) })

@ -171,3 +171,9 @@
user_id: 40 user_id: 40
repo_id: 61 repo_id: 61
mode: 4 mode: 4
-
id: 30
user_id: 40
repo_id: 1
mode: 2

@ -22,6 +22,7 @@
content_type: 1 # json content_type: 1 # json
events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}' events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}'
is_active: true is_active: true
- -
id: 4 id: 4
repo_id: 2 repo_id: 2
@ -29,3 +30,23 @@
content_type: 1 # json content_type: 1 # json
events: '{"push_only":true,"branch_filter":"{master,feature*}"}' events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
is_active: true is_active: true
-
id: 5
repo_id: 0
owner_id: 0
url: www.example.com/url5
content_type: 1 # json
events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
is_active: true
is_system_webhook: true
-
id: 6
repo_id: 0
owner_id: 0
url: www.example.com/url6
content_type: 1 # json
events: '{"push_only":true,"branch_filter":"{master,feature*}"}'
is_active: true
is_system_webhook: false

@ -167,6 +167,9 @@ func GetBranch(ctx context.Context, repoID int64, branchName string) (*Branch, e
BranchName: branchName, BranchName: branchName,
} }
} }
// FIXME: this design is not right: it doesn't check `branch.IsDeleted`, it doesn't make sense to make callers to check IsDeleted again and again.
// It causes inconsistency with `GetBranches` and `git.GetBranch`, and will lead to strange bugs
// In the future, there should be 2 functions: `GetBranchExisting` and `GetBranchWithDeleted`
return &branch, nil return &branch, nil
} }
@ -440,6 +443,8 @@ type FindRecentlyPushedNewBranchesOptions struct {
} }
type RecentlyPushedNewBranch struct { type RecentlyPushedNewBranch struct {
BranchRepo *repo_model.Repository
BranchName string
BranchDisplayName string BranchDisplayName string
BranchLink string BranchLink string
BranchCompareURL string BranchCompareURL string
@ -540,7 +545,9 @@ func FindRecentlyPushedNewBranches(ctx context.Context, doer *user_model.User, o
branchDisplayName = fmt.Sprintf("%s:%s", branch.Repo.FullName(), branchDisplayName) branchDisplayName = fmt.Sprintf("%s:%s", branch.Repo.FullName(), branchDisplayName)
} }
newBranches = append(newBranches, &RecentlyPushedNewBranch{ newBranches = append(newBranches, &RecentlyPushedNewBranch{
BranchRepo: branch.Repo,
BranchDisplayName: branchDisplayName, BranchDisplayName: branchDisplayName,
BranchName: branch.Name,
BranchLink: fmt.Sprintf("%s/src/branch/%s", branch.Repo.Link(), util.PathEscapeSegments(branch.Name)), BranchLink: fmt.Sprintf("%s/src/branch/%s", branch.Repo.Link(), util.PathEscapeSegments(branch.Name)),
BranchCompareURL: branch.Repo.ComposeBranchCompareURL(opts.BaseRepo, branch.Name), BranchCompareURL: branch.Repo.ComposeBranchCompareURL(opts.BaseRepo, branch.Name),
CommitTime: branch.CommitTime, CommitTime: branch.CommitTime,

@ -112,8 +112,8 @@ const (
CommentTypePRScheduledToAutoMerge // 34 pr was scheduled to auto merge when checks succeed CommentTypePRScheduledToAutoMerge // 34 pr was scheduled to auto merge when checks succeed
CommentTypePRUnScheduledToAutoMerge // 35 pr was un scheduled to auto merge when checks succeed CommentTypePRUnScheduledToAutoMerge // 35 pr was un scheduled to auto merge when checks succeed
CommentTypePin // 36 pin Issue CommentTypePin // 36 pin Issue/PullRequest
CommentTypeUnpin // 37 unpin Issue CommentTypeUnpin // 37 unpin Issue/PullRequest
CommentTypeChangeTimeEstimate // 38 Change time estimate CommentTypeChangeTimeEstimate // 38 Change time estimate
) )

@ -46,11 +46,6 @@ func (s Stopwatch) Seconds() int64 {
return int64(timeutil.TimeStampNow() - s.CreatedUnix) return int64(timeutil.TimeStampNow() - s.CreatedUnix)
} }
// Duration returns a human-readable duration string based on local server time
func (s Stopwatch) Duration() string {
return util.SecToTime(s.Seconds())
}
func getStopwatch(ctx context.Context, userID, issueID int64) (sw *Stopwatch, exists bool, err error) { func getStopwatch(ctx context.Context, userID, issueID int64) (sw *Stopwatch, exists bool, err error) {
sw = new(Stopwatch) sw = new(Stopwatch)
exists, err = db.GetEngine(ctx). exists, err = db.GetEngine(ctx).
@ -201,7 +196,7 @@ func FinishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Iss
Doer: user, Doer: user,
Issue: issue, Issue: issue,
Repo: issue.Repo, Repo: issue.Repo,
Content: util.SecToTime(timediff), Content: util.SecToHours(timediff),
Type: CommentTypeStopTracking, Type: CommentTypeStopTracking,
TimeID: tt.ID, TimeID: tt.ID,
}); err != nil { }); err != nil {

@ -175,10 +175,14 @@ func (p *Permission) LogString() string {
return fmt.Sprintf(format, args...) return fmt.Sprintf(format, args...)
} }
func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) { func finalProcessRepoUnitPermission(user *user_model.User, perm *Permission) {
if user == nil || user.ID <= 0 { if user == nil || user.ID <= 0 {
// for anonymous access, it could be:
// AccessMode is None or Read, units has repo units, unitModes is nil
return return
} }
// apply everyone access permissions
for _, u := range perm.units { for _, u := range perm.units {
if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.everyoneAccessMode[u.Type] { if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.everyoneAccessMode[u.Type] {
if perm.everyoneAccessMode == nil { if perm.everyoneAccessMode == nil {
@ -187,17 +191,40 @@ func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
} }
} }
if perm.unitsMode == nil {
// if unitsMode is not set, then it means that the default p.AccessMode applies to all units
return
}
// remove no permission units
origPermUnits := perm.units
perm.units = make([]*repo_model.RepoUnit, 0, len(perm.units))
for _, u := range origPermUnits {
shouldKeep := false
for t := range perm.unitsMode {
if shouldKeep = u.Type == t; shouldKeep {
break
}
}
for t := range perm.everyoneAccessMode {
if shouldKeep = shouldKeep || u.Type == t; shouldKeep {
break
}
}
if shouldKeep {
perm.units = append(perm.units, u)
}
}
} }
// GetUserRepoPermission returns the user permissions to the repository // GetUserRepoPermission returns the user permissions to the repository
func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (perm Permission, err error) { func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (perm Permission, err error) {
defer func() { defer func() {
if err == nil { if err == nil {
applyEveryoneRepoPermission(user, &perm) finalProcessRepoUnitPermission(user, &perm)
} }
if log.IsTrace() {
log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm) log.Trace("Permission Loaded for user %-v in repo %-v, permissions: %-+v", user, repo, perm)
}
}() }()
if err = repo.LoadUnits(ctx); err != nil { if err = repo.LoadUnits(ctx); err != nil {
@ -294,16 +321,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
} }
} }
// remove no permission units
perm.units = make([]*repo_model.RepoUnit, 0, len(repo.Units))
for t := range perm.unitsMode {
for _, u := range repo.Units {
if u.Type == t {
perm.units = append(perm.units, u)
}
}
}
return perm, err return perm, err
} }

@ -50,7 +50,7 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead}, {Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
}, },
} }
applyEveryoneRepoPermission(nil, &perm) finalProcessRepoUnitPermission(nil, &perm)
assert.False(t, perm.CanRead(unit.TypeWiki)) assert.False(t, perm.CanRead(unit.TypeWiki))
perm = Permission{ perm = Permission{
@ -59,7 +59,7 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead}, {Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
}, },
} }
applyEveryoneRepoPermission(&user_model.User{ID: 0}, &perm) finalProcessRepoUnitPermission(&user_model.User{ID: 0}, &perm)
assert.False(t, perm.CanRead(unit.TypeWiki)) assert.False(t, perm.CanRead(unit.TypeWiki))
perm = Permission{ perm = Permission{
@ -68,7 +68,7 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead}, {Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
}, },
} }
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm) finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
assert.True(t, perm.CanRead(unit.TypeWiki)) assert.True(t, perm.CanRead(unit.TypeWiki))
perm = Permission{ perm = Permission{
@ -77,20 +77,22 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead}, {Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
}, },
} }
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm) finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
// it should work the same as "EveryoneAccessMode: none" because the default AccessMode should be applied to units // it should work the same as "EveryoneAccessMode: none" because the default AccessMode should be applied to units
assert.True(t, perm.CanWrite(unit.TypeWiki)) assert.True(t, perm.CanWrite(unit.TypeWiki))
perm = Permission{ perm = Permission{
units: []*repo_model.RepoUnit{ units: []*repo_model.RepoUnit{
{Type: unit.TypeCode}, // will be removed
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead}, {Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
}, },
unitsMode: map[unit.Type]perm_model.AccessMode{ unitsMode: map[unit.Type]perm_model.AccessMode{
unit.TypeWiki: perm_model.AccessModeWrite, unit.TypeWiki: perm_model.AccessModeWrite,
}, },
} }
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm) finalProcessRepoUnitPermission(&user_model.User{ID: 1}, &perm)
assert.True(t, perm.CanWrite(unit.TypeWiki)) assert.True(t, perm.CanWrite(unit.TypeWiki))
assert.Len(t, perm.units, 1)
} }
func TestUnitAccessMode(t *testing.T) { func TestUnitAccessMode(t *testing.T) {

@ -56,16 +56,11 @@ func repoArchiverForRelativePath(relativePath string) (*RepoArchiver, error) {
if err != nil { if err != nil {
return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument} return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument}
} }
nameExts := strings.SplitN(parts[2], ".", 2) commitID, archiveType := git.SplitArchiveNameType(parts[2])
if len(nameExts) != 2 { if archiveType == git.ArchiveUnknown {
return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument} return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument}
} }
return &RepoArchiver{RepoID: repoID, CommitID: commitID, Type: archiveType}, nil
return &RepoArchiver{
RepoID: repoID,
CommitID: parts[1] + nameExts[0],
Type: git.ToArchiveType(nameExts[1]),
}, nil
} }
// GetRepoArchiver get an archiver // GetRepoArchiver get an archiver

@ -54,6 +54,7 @@ func UpdateRepoLicenses(ctx context.Context, repo *Repository, commitID string,
for _, o := range oldLicenses { for _, o := range oldLicenses {
// Update already existing license // Update already existing license
if o.License == license { if o.License == license {
o.CommitID = commitID
if _, err := db.GetEngine(ctx).ID(o.ID).Cols("`commit_id`").Update(o); err != nil { if _, err := db.GetEngine(ctx).ID(o.ID).Cols("`commit_id`").Update(o); err != nil {
return err return err
} }

@ -11,35 +11,13 @@ import (
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
) )
// Copy copies file from source to target path. // SyncFile synchronizes the two files. This is skipped if both files
func Copy(src, dest string) error {
// Gather file information to set back later.
si, err := os.Lstat(src)
if err != nil {
return err
}
// Handle symbolic link.
if si.Mode()&os.ModeSymlink != 0 {
target, err := os.Readlink(src)
if err != nil {
return err
}
// NOTE: os.Chmod and os.Chtimes don't recognize symbolic link,
// which will lead "no such file or directory" error.
return os.Symlink(target, dest)
}
return util.CopyFile(src, dest)
}
// Sync synchronizes the two files. This is skipped if both files
// exist and the size, modtime, and mode match. // exist and the size, modtime, and mode match.
func Sync(srcPath, destPath string) error { func SyncFile(srcPath, destPath string) error {
dest, err := os.Stat(destPath) dest, err := os.Stat(destPath)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return Copy(srcPath, destPath) return util.CopyFile(srcPath, destPath)
} }
return err return err
} }
@ -55,7 +33,7 @@ func Sync(srcPath, destPath string) error {
return nil return nil
} }
return Copy(srcPath, destPath) return util.CopyFile(srcPath, destPath)
} }
// SyncDirs synchronizes files recursively from source to target directory. // SyncDirs synchronizes files recursively from source to target directory.
@ -66,6 +44,10 @@ func SyncDirs(srcPath, destPath string) error {
return err return err
} }
// the keep file is used to keep the directory in a git repository, it doesn't need to be synced
// and go-git doesn't work with the ".keep" file (it would report errors like "ref is empty")
const keepFile = ".keep"
// find and delete all untracked files // find and delete all untracked files
destFiles, err := util.ListDirRecursively(destPath, &util.ListDirOptions{IncludeDir: true}) destFiles, err := util.ListDirRecursively(destPath, &util.ListDirOptions{IncludeDir: true})
if err != nil { if err != nil {
@ -73,13 +55,17 @@ func SyncDirs(srcPath, destPath string) error {
} }
for _, destFile := range destFiles { for _, destFile := range destFiles {
destFilePath := filepath.Join(destPath, destFile) destFilePath := filepath.Join(destPath, destFile)
shouldRemove := filepath.Base(destFilePath) == keepFile
if _, err = os.Stat(filepath.Join(srcPath, destFile)); err != nil { if _, err = os.Stat(filepath.Join(srcPath, destFile)); err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
// if src file does not exist, remove dest file shouldRemove = true
if err = os.RemoveAll(destFilePath); err != nil { } else {
return err return err
} }
} else { }
// if src file does not exist, remove dest file
if shouldRemove {
if err = os.RemoveAll(destFilePath); err != nil {
return err return err
} }
} }
@ -95,8 +81,8 @@ func SyncDirs(srcPath, destPath string) error {
// util.ListDirRecursively appends a slash to the directory name // util.ListDirRecursively appends a slash to the directory name
if strings.HasSuffix(srcFile, "/") { if strings.HasSuffix(srcFile, "/") {
err = os.MkdirAll(destFilePath, os.ModePerm) err = os.MkdirAll(destFilePath, os.ModePerm)
} else { } else if filepath.Base(destFilePath) != keepFile {
err = Sync(filepath.Join(srcPath, srcFile), destFilePath) err = SyncFile(filepath.Join(srcPath, srcFile), destFilePath)
} }
if err != nil { if err != nil {
return err return err

@ -11,6 +11,19 @@ import (
"code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/optional"
) )
// GetSystemOrDefaultWebhooks returns webhooks by given argument or all if argument is missing.
func GetSystemOrDefaultWebhooks(ctx context.Context, isSystemWebhook optional.Option[bool]) ([]*Webhook, error) {
webhooks := make([]*Webhook, 0, 5)
if !isSystemWebhook.Has() {
return webhooks, db.GetEngine(ctx).Where("repo_id=? AND owner_id=?", 0, 0).
Find(&webhooks)
}
return webhooks, db.GetEngine(ctx).
Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, isSystemWebhook.Value()).
Find(&webhooks)
}
// GetDefaultWebhooks returns all admin-default webhooks. // GetDefaultWebhooks returns all admin-default webhooks.
func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) { func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) {
webhooks := make([]*Webhook, 0, 5) webhooks := make([]*Webhook, 0, 5)

@ -0,0 +1,37 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package webhook
import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/optional"
"github.com/stretchr/testify/assert"
)
func TestGetSystemOrDefaultWebhooks(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hooks, err := GetSystemOrDefaultWebhooks(db.DefaultContext, optional.None[bool]())
assert.NoError(t, err)
if assert.Len(t, hooks, 2) {
assert.Equal(t, int64(5), hooks[0].ID)
assert.Equal(t, int64(6), hooks[1].ID)
}
hooks, err = GetSystemOrDefaultWebhooks(db.DefaultContext, optional.Some(true))
assert.NoError(t, err)
if assert.Len(t, hooks, 1) {
assert.Equal(t, int64(5), hooks[0].ID)
}
hooks, err = GetSystemOrDefaultWebhooks(db.DefaultContext, optional.Some(false))
assert.NoError(t, err)
if assert.Len(t, hooks, 1) {
assert.Equal(t, int64(6), hooks[0].ID)
}
}

@ -38,9 +38,14 @@ func Init() error {
const ( const (
testCacheKey = "DefaultCache.TestKey" testCacheKey = "DefaultCache.TestKey"
SlowCacheThreshold = 100 * time.Microsecond // SlowCacheThreshold marks cache tests as slow
// set to 30ms per discussion: https://github.com/go-gitea/gitea/issues/33190
// TODO: Replace with metrics histogram
SlowCacheThreshold = 30 * time.Millisecond
) )
// Test performs delete, put and get operations on a predefined key
// returns
func Test() (time.Duration, error) { func Test() (time.Duration, error) {
if defaultCache == nil { if defaultCache == nil {
return 0, fmt.Errorf("default cache not initialized") return 0, fmt.Errorf("default cache not initialized")

@ -43,7 +43,8 @@ func TestTest(t *testing.T) {
elapsed, err := Test() elapsed, err := Test()
assert.NoError(t, err) assert.NoError(t, err)
// mem cache should take from 300ns up to 1ms on modern hardware ... // mem cache should take from 300ns up to 1ms on modern hardware ...
assert.Less(t, elapsed, time.Millisecond) assert.Positive(t, elapsed)
assert.Less(t, elapsed, SlowCacheThreshold)
} }
func TestGetCache(t *testing.T) { func TestGetCache(t *testing.T) {

@ -18,6 +18,7 @@ import (
"time" "time"
"code.gitea.io/gitea/modules/git/internal" //nolint:depguard // only this file can use the internal type CmdArg, other files and packages should use AddXxx functions "code.gitea.io/gitea/modules/git/internal" //nolint:depguard // only this file can use the internal type CmdArg, other files and packages should use AddXxx functions
"code.gitea.io/gitea/modules/gtprof"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
@ -54,7 +55,7 @@ func logArgSanitize(arg string) string {
} else if filepath.IsAbs(arg) { } else if filepath.IsAbs(arg) {
base := filepath.Base(arg) base := filepath.Base(arg)
dir := filepath.Dir(arg) dir := filepath.Dir(arg)
return filepath.Join(filepath.Base(dir), base) return ".../" + filepath.Join(filepath.Base(dir), base)
} }
return arg return arg
} }
@ -295,15 +296,20 @@ func (c *Command) run(skip int, opts *RunOpts) error {
timeout = defaultCommandExecutionTimeout timeout = defaultCommandExecutionTimeout
} }
var desc string cmdLogString := c.LogString()
callerInfo := util.CallerFuncName(1 /* util */ + 1 /* this */ + skip /* parent */) callerInfo := util.CallerFuncName(1 /* util */ + 1 /* this */ + skip /* parent */)
if pos := strings.LastIndex(callerInfo, "/"); pos >= 0 { if pos := strings.LastIndex(callerInfo, "/"); pos >= 0 {
callerInfo = callerInfo[pos+1:] callerInfo = callerInfo[pos+1:]
} }
// these logs are for debugging purposes only, so no guarantee of correctness or stability // these logs are for debugging purposes only, so no guarantee of correctness or stability
desc = fmt.Sprintf("git.Run(by:%s, repo:%s): %s", callerInfo, logArgSanitize(opts.Dir), c.LogString()) desc := fmt.Sprintf("git.Run(by:%s, repo:%s): %s", callerInfo, logArgSanitize(opts.Dir), cmdLogString)
log.Debug("git.Command: %s", desc) log.Debug("git.Command: %s", desc)
_, span := gtprof.GetTracer().Start(c.parentContext, gtprof.TraceSpanGitRun)
defer span.End()
span.SetAttributeString(gtprof.TraceAttrFuncCaller, callerInfo)
span.SetAttributeString(gtprof.TraceAttrGitCommand, cmdLogString)
var ctx context.Context var ctx context.Context
var cancel context.CancelFunc var cancel context.CancelFunc
var finished context.CancelFunc var finished context.CancelFunc

@ -58,5 +58,5 @@ func TestCommandString(t *testing.T) {
assert.EqualValues(t, cmd.prog+` a "-m msg" "it's a test" "say \"hello\""`, cmd.LogString()) assert.EqualValues(t, cmd.prog+` a "-m msg" "it's a test" "say \"hello\""`, cmd.LogString())
cmd = NewCommandContextNoGlobals(context.Background(), "url: https://a:b@c/", "/root/dir-a/dir-b") cmd = NewCommandContextNoGlobals(context.Background(), "url: https://a:b@c/", "/root/dir-a/dir-b")
assert.EqualValues(t, cmd.prog+` "url: https://sanitized-credential@c/" dir-a/dir-b`, cmd.LogString()) assert.EqualValues(t, cmd.prog+` "url: https://sanitized-credential@c/" .../dir-a/dir-b`, cmd.LogString())
} }

@ -476,8 +476,12 @@ func (c *Commit) GetRepositoryDefaultPublicGPGKey(forceUpdate bool) (*GPGSetting
} }
func IsStringLikelyCommitID(objFmt ObjectFormat, s string, minLength ...int) bool { func IsStringLikelyCommitID(objFmt ObjectFormat, s string, minLength ...int) bool {
minLen := util.OptionalArg(minLength, objFmt.FullLength()) maxLen := 64 // sha256
if len(s) < minLen || len(s) > objFmt.FullLength() { if objFmt != nil {
maxLen = objFmt.FullLength()
}
minLen := util.OptionalArg(minLength, maxLen)
if len(s) < minLen || len(s) > maxLen {
return false return false
} }
for _, c := range s { for _, c := range s {

@ -64,7 +64,10 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
} else if commit.ParentCount() == 0 { } else if commit.ParentCount() == 0 {
cmd.AddArguments("show").AddDynamicArguments(endCommit).AddDashesAndList(files...) cmd.AddArguments("show").AddDynamicArguments(endCommit).AddDashesAndList(files...)
} else { } else {
c, _ := commit.Parent(0) c, err := commit.Parent(0)
if err != nil {
return err
}
cmd.AddArguments("diff", "-M").AddDynamicArguments(c.ID.String(), endCommit).AddDashesAndList(files...) cmd.AddArguments("diff", "-M").AddDynamicArguments(c.ID.String(), endCommit).AddDashesAndList(files...)
} }
case RawDiffPatch: case RawDiffPatch:
@ -74,7 +77,10 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
} else if commit.ParentCount() == 0 { } else if commit.ParentCount() == 0 {
cmd.AddArguments("format-patch", "--no-signature", "--stdout", "--root").AddDynamicArguments(endCommit).AddDashesAndList(files...) cmd.AddArguments("format-patch", "--no-signature", "--stdout", "--root").AddDynamicArguments(endCommit).AddDashesAndList(files...)
} else { } else {
c, _ := commit.Parent(0) c, err := commit.Parent(0)
if err != nil {
return err
}
query := fmt.Sprintf("%s...%s", endCommit, c.ID.String()) query := fmt.Sprintf("%s...%s", endCommit, c.ID.String())
cmd.AddArguments("format-patch", "--no-signature", "--stdout").AddDynamicArguments(query).AddDashesAndList(files...) cmd.AddArguments("format-patch", "--no-signature", "--stdout").AddDynamicArguments(query).AddDashesAndList(files...)
} }

@ -80,6 +80,10 @@ func RefNameFromTag(shortName string) RefName {
return RefName(TagPrefix + shortName) return RefName(TagPrefix + shortName)
} }
func RefNameFromCommit(shortName string) RefName {
return RefName(shortName)
}
func (ref RefName) String() string { func (ref RefName) String() string {
return string(ref) return string(ref)
} }
@ -181,32 +185,38 @@ func (ref RefName) RefGroup() string {
return "" return ""
} }
// RefType is a simple ref type of the reference, it is used for UI and webhooks
type RefType string
const (
RefTypeBranch RefType = "branch"
RefTypeTag RefType = "tag"
RefTypeCommit RefType = "commit"
)
// RefType returns the simple ref type of the reference, e.g. branch, tag // RefType returns the simple ref type of the reference, e.g. branch, tag
// It's different from RefGroup, which is using the name of the directory under .git/refs // It's different from RefGroup, which is using the name of the directory under .git/refs
// Here we using branch but not heads, using tag but not tags func (ref RefName) RefType() RefType {
func (ref RefName) RefType() string { switch {
var refType string case ref.IsBranch():
if ref.IsBranch() { return RefTypeBranch
refType = "branch" case ref.IsTag():
} else if ref.IsTag() { return RefTypeTag
refType = "tag" case IsStringLikelyCommitID(nil, string(ref), 6):
return RefTypeCommit
} }
return refType return ""
} }
// RefURL returns the absolute URL for a ref in a repository // RefWebLinkPath returns a path for the reference that can be used in a web link:
func RefURL(repoURL, ref string) string { // * "branch/<branch_name>"
refFullName := RefName(ref) // * "tag/<tag_name>"
refName := util.PathEscapeSegments(refFullName.ShortName()) // * "commit/<commit_id>"
switch { // It returns an empty string if the reference is not a branch, tag or commit.
case refFullName.IsBranch(): func (ref RefName) RefWebLinkPath() string {
return repoURL + "/src/branch/" + refName refType := ref.RefType()
case refFullName.IsTag(): if refType == "" {
return repoURL + "/src/tag/" + refName return ""
case !Sha1ObjectFormat.IsValid(ref):
// assume they mean a branch
return repoURL + "/src/branch/" + refName
default:
return repoURL + "/src/commit/" + refName
} }
return string(refType) + "/" + util.PathEscapeSegments(ref.ShortName())
} }

@ -20,6 +20,8 @@ func TestRefName(t *testing.T) {
// Test pull names // Test pull names
assert.Equal(t, "1", RefName("refs/pull/1/head").PullName()) assert.Equal(t, "1", RefName("refs/pull/1/head").PullName())
assert.True(t, RefName("refs/pull/1/head").IsPull())
assert.True(t, RefName("refs/pull/1/merge").IsPull())
assert.Equal(t, "my/pull", RefName("refs/pull/my/pull/head").PullName()) assert.Equal(t, "my/pull", RefName("refs/pull/my/pull/head").PullName())
// Test for branch names // Test for branch names
@ -30,9 +32,8 @@ func TestRefName(t *testing.T) {
assert.Equal(t, "c0ffee", RefName("c0ffee").ShortName()) assert.Equal(t, "c0ffee", RefName("c0ffee").ShortName())
} }
func TestRefURL(t *testing.T) { func TestRefWebLinkPath(t *testing.T) {
repoURL := "/user/repo" assert.Equal(t, "branch/foo", RefName("refs/heads/foo").RefWebLinkPath())
assert.Equal(t, repoURL+"/src/branch/foo", RefURL(repoURL, "refs/heads/foo")) assert.Equal(t, "tag/foo", RefName("refs/tags/foo").RefWebLinkPath())
assert.Equal(t, repoURL+"/src/tag/foo", RefURL(repoURL, "refs/tags/foo")) assert.Equal(t, "commit/c0ffee", RefName("c0ffee").RefWebLinkPath())
assert.Equal(t, repoURL+"/src/commit/c0ffee", RefURL(repoURL, "c0ffee"))
} }

@ -16,37 +16,35 @@ import (
type ArchiveType int type ArchiveType int
const ( const (
// ZIP zip archive type ArchiveUnknown ArchiveType = iota
ZIP ArchiveType = iota + 1 ArchiveZip // 1
// TARGZ tar gz archive type ArchiveTarGz // 2
TARGZ ArchiveBundle // 3
// BUNDLE bundle archive type
BUNDLE
) )
// String converts an ArchiveType to string // String converts an ArchiveType to string: the extension of the archive file without prefix dot
func (a ArchiveType) String() string { func (a ArchiveType) String() string {
switch a { switch a {
case ZIP: case ArchiveZip:
return "zip" return "zip"
case TARGZ: case ArchiveTarGz:
return "tar.gz" return "tar.gz"
case BUNDLE: case ArchiveBundle:
return "bundle" return "bundle"
} }
return "unknown" return "unknown"
} }
func ToArchiveType(s string) ArchiveType { func SplitArchiveNameType(s string) (string, ArchiveType) {
switch s { switch {
case "zip": case strings.HasSuffix(s, ".zip"):
return ZIP return strings.TrimSuffix(s, ".zip"), ArchiveZip
case "tar.gz": case strings.HasSuffix(s, ".tar.gz"):
return TARGZ return strings.TrimSuffix(s, ".tar.gz"), ArchiveTarGz
case "bundle": case strings.HasSuffix(s, ".bundle"):
return BUNDLE return strings.TrimSuffix(s, ".bundle"), ArchiveBundle
} }
return 0 return s, ArchiveUnknown
} }
// CreateArchive create archive content to the target path // CreateArchive create archive content to the target path

@ -0,0 +1,32 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestArchiveType(t *testing.T) {
name, archiveType := SplitArchiveNameType("test.tar.gz")
assert.Equal(t, "test", name)
assert.Equal(t, "tar.gz", archiveType.String())
name, archiveType = SplitArchiveNameType("a/b/test.zip")
assert.Equal(t, "a/b/test", name)
assert.Equal(t, "zip", archiveType.String())
name, archiveType = SplitArchiveNameType("1234.bundle")
assert.Equal(t, "1234", name)
assert.Equal(t, "bundle", archiveType.String())
name, archiveType = SplitArchiveNameType("test")
assert.Equal(t, "test", name)
assert.Equal(t, "unknown", archiveType.String())
name, archiveType = SplitArchiveNameType("test.xz")
assert.Equal(t, "test.xz", name)
assert.Equal(t, "unknown", archiveType.String())
}

@ -57,7 +57,7 @@ func (repo *Repository) IsBranchExist(name string) bool {
// GetBranches returns branches from the repository, skipping "skip" initial branches and // GetBranches returns branches from the repository, skipping "skip" initial branches and
// returning at most "limit" branches, or all branches if "limit" is 0. // returning at most "limit" branches, or all branches if "limit" is 0.
// Branches are returned with sort of `-commiterdate` as the nogogit // Branches are returned with sort of `-committerdate` as the nogogit
// implementation. This requires full fetch, sort and then the // implementation. This requires full fetch, sort and then the
// skip/limit applies later as gogit returns in undefined order. // skip/limit applies later as gogit returns in undefined order.
func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) { func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {

@ -0,0 +1,32 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package gtprof
type EventConfig struct {
attributes []*TraceAttribute
}
type EventOption interface {
applyEvent(*EventConfig)
}
type applyEventFunc func(*EventConfig)
func (f applyEventFunc) applyEvent(cfg *EventConfig) {
f(cfg)
}
func WithAttributes(attrs ...*TraceAttribute) EventOption {
return applyEventFunc(func(cfg *EventConfig) {
cfg.attributes = append(cfg.attributes, attrs...)
})
}
func eventConfigFromOptions(options ...EventOption) *EventConfig {
cfg := &EventConfig{}
for _, opt := range options {
opt.applyEvent(cfg)
}
return cfg
}

@ -0,0 +1,175 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package gtprof
import (
"context"
"fmt"
"sync"
"time"
"code.gitea.io/gitea/modules/util"
)
type contextKey struct {
name string
}
var contextKeySpan = &contextKey{"span"}
type traceStarter interface {
start(ctx context.Context, traceSpan *TraceSpan, internalSpanIdx int) (context.Context, traceSpanInternal)
}
type traceSpanInternal interface {
addEvent(name string, cfg *EventConfig)
recordError(err error, cfg *EventConfig)
end()
}
type TraceSpan struct {
// immutable
parent *TraceSpan
internalSpans []traceSpanInternal
internalContexts []context.Context
// mutable, must be protected by mutex
mu sync.RWMutex
name string
statusCode uint32
statusDesc string
startTime time.Time
endTime time.Time
attributes []*TraceAttribute
children []*TraceSpan
}
type TraceAttribute struct {
Key string
Value TraceValue
}
type TraceValue struct {
v any
}
func (t *TraceValue) AsString() string {
return fmt.Sprint(t.v)
}
func (t *TraceValue) AsInt64() int64 {
v, _ := util.ToInt64(t.v)
return v
}
func (t *TraceValue) AsFloat64() float64 {
v, _ := util.ToFloat64(t.v)
return v
}
var globalTraceStarters []traceStarter
type Tracer struct {
starters []traceStarter
}
func (s *TraceSpan) SetName(name string) {
s.mu.Lock()
defer s.mu.Unlock()
s.name = name
}
func (s *TraceSpan) SetStatus(code uint32, desc string) {
s.mu.Lock()
defer s.mu.Unlock()
s.statusCode, s.statusDesc = code, desc
}
func (s *TraceSpan) AddEvent(name string, options ...EventOption) {
cfg := eventConfigFromOptions(options...)
for _, tsp := range s.internalSpans {
tsp.addEvent(name, cfg)
}
}
func (s *TraceSpan) RecordError(err error, options ...EventOption) {
cfg := eventConfigFromOptions(options...)
for _, tsp := range s.internalSpans {
tsp.recordError(err, cfg)
}
}
func (s *TraceSpan) SetAttributeString(key, value string) *TraceSpan {
s.mu.Lock()
defer s.mu.Unlock()
s.attributes = append(s.attributes, &TraceAttribute{Key: key, Value: TraceValue{v: value}})
return s
}
func (t *Tracer) Start(ctx context.Context, spanName string) (context.Context, *TraceSpan) {
starters := t.starters
if starters == nil {
starters = globalTraceStarters
}
ts := &TraceSpan{name: spanName, startTime: time.Now()}
parentSpan := GetContextSpan(ctx)
if parentSpan != nil {
parentSpan.mu.Lock()
parentSpan.children = append(parentSpan.children, ts)
parentSpan.mu.Unlock()
ts.parent = parentSpan
}
parentCtx := ctx
for internalSpanIdx, tsp := range starters {
var internalSpan traceSpanInternal
if parentSpan != nil {
parentCtx = parentSpan.internalContexts[internalSpanIdx]
}
ctx, internalSpan = tsp.start(parentCtx, ts, internalSpanIdx)
ts.internalContexts = append(ts.internalContexts, ctx)
ts.internalSpans = append(ts.internalSpans, internalSpan)
}
ctx = context.WithValue(ctx, contextKeySpan, ts)
return ctx, ts
}
type mutableContext interface {
context.Context
SetContextValue(key, value any)
GetContextValue(key any) any
}
// StartInContext starts a trace span in Gitea's mutable context (usually the web request context).
// Due to the design limitation of Gitea's web framework, it can't use `context.WithValue` to bind a new span into a new context.
// So here we use our "reqctx" framework to achieve the same result: web request context could always see the latest "span".
func (t *Tracer) StartInContext(ctx mutableContext, spanName string) (*TraceSpan, func()) {
curTraceSpan := GetContextSpan(ctx)
_, newTraceSpan := GetTracer().Start(ctx, spanName)
ctx.SetContextValue(contextKeySpan, newTraceSpan)
return newTraceSpan, func() {
newTraceSpan.End()
ctx.SetContextValue(contextKeySpan, curTraceSpan)
}
}
func (s *TraceSpan) End() {
s.mu.Lock()
s.endTime = time.Now()
s.mu.Unlock()
for _, tsp := range s.internalSpans {
tsp.end()
}
}
func GetTracer() *Tracer {
return &Tracer{}
}
func GetContextSpan(ctx context.Context) *TraceSpan {
ts, _ := ctx.Value(contextKeySpan).(*TraceSpan)
return ts
}

@ -0,0 +1,96 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package gtprof
import (
"context"
"fmt"
"strings"
"sync/atomic"
"time"
"code.gitea.io/gitea/modules/tailmsg"
)
type traceBuiltinStarter struct{}
type traceBuiltinSpan struct {
ts *TraceSpan
internalSpanIdx int
}
func (t *traceBuiltinSpan) addEvent(name string, cfg *EventConfig) {
// No-op because builtin tracer doesn't need it.
// In the future we might use it to mark the time point between backend logic and network response.
}
func (t *traceBuiltinSpan) recordError(err error, cfg *EventConfig) {
// No-op because builtin tracer doesn't need it.
// Actually Gitea doesn't handle err this way in most cases
}
func (t *traceBuiltinSpan) toString(out *strings.Builder, indent int) {
t.ts.mu.RLock()
defer t.ts.mu.RUnlock()
out.WriteString(strings.Repeat(" ", indent))
out.WriteString(t.ts.name)
if t.ts.endTime.IsZero() {
out.WriteString(" duration: (not ended)")
} else {
out.WriteString(fmt.Sprintf(" duration=%.4fs", t.ts.endTime.Sub(t.ts.startTime).Seconds()))
}
for _, a := range t.ts.attributes {
out.WriteString(" ")
out.WriteString(a.Key)
out.WriteString("=")
value := a.Value.AsString()
if strings.ContainsAny(value, " \t\r\n") {
quoted := false
for _, c := range "\"'`" {
if quoted = !strings.Contains(value, string(c)); quoted {
value = string(c) + value + string(c)
break
}
}
if !quoted {
value = fmt.Sprintf("%q", value)
}
}
out.WriteString(value)
}
out.WriteString("\n")
for _, c := range t.ts.children {
span := c.internalSpans[t.internalSpanIdx].(*traceBuiltinSpan)
span.toString(out, indent+2)
}
}
func (t *traceBuiltinSpan) end() {
if t.ts.parent == nil {
// TODO: debug purpose only
// TODO: it should distinguish between http response network lag and actual processing time
threshold := time.Duration(traceBuiltinThreshold.Load())
if threshold != 0 && t.ts.endTime.Sub(t.ts.startTime) > threshold {
sb := &strings.Builder{}
t.toString(sb, 0)
tailmsg.GetManager().GetTraceRecorder().Record(sb.String())
}
}
}
func (t *traceBuiltinStarter) start(ctx context.Context, traceSpan *TraceSpan, internalSpanIdx int) (context.Context, traceSpanInternal) {
return ctx, &traceBuiltinSpan{ts: traceSpan, internalSpanIdx: internalSpanIdx}
}
func init() {
globalTraceStarters = append(globalTraceStarters, &traceBuiltinStarter{})
}
var traceBuiltinThreshold atomic.Int64
func EnableBuiltinTracer(threshold time.Duration) {
traceBuiltinThreshold.Store(int64(threshold))
}

@ -0,0 +1,19 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package gtprof
// Some interesting names could be found in https://github.com/open-telemetry/opentelemetry-go/tree/main/semconv
const (
TraceSpanHTTP = "http"
TraceSpanGitRun = "git-run"
TraceSpanDatabase = "database"
)
const (
TraceAttrFuncCaller = "func.caller"
TraceAttrDbSQL = "db.sql"
TraceAttrGitCommand = "git.command"
TraceAttrHTTPRoute = "http.route"
)

@ -0,0 +1,93 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package gtprof
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
)
// "vendor span" is a simple demo for a span from a vendor library
var vendorContextKey any = "vendorContextKey"
type vendorSpan struct {
name string
children []*vendorSpan
}
func vendorTraceStart(ctx context.Context, name string) (context.Context, *vendorSpan) {
span := &vendorSpan{name: name}
parentSpan, ok := ctx.Value(vendorContextKey).(*vendorSpan)
if ok {
parentSpan.children = append(parentSpan.children, span)
}
ctx = context.WithValue(ctx, vendorContextKey, span)
return ctx, span
}
// below "testTrace*" integrate the vendor span into our trace system
type testTraceSpan struct {
vendorSpan *vendorSpan
}
func (t *testTraceSpan) addEvent(name string, cfg *EventConfig) {}
func (t *testTraceSpan) recordError(err error, cfg *EventConfig) {}
func (t *testTraceSpan) end() {}
type testTraceStarter struct{}
func (t *testTraceStarter) start(ctx context.Context, traceSpan *TraceSpan, internalSpanIdx int) (context.Context, traceSpanInternal) {
ctx, span := vendorTraceStart(ctx, traceSpan.name)
return ctx, &testTraceSpan{span}
}
func TestTraceStarter(t *testing.T) {
globalTraceStarters = []traceStarter{&testTraceStarter{}}
ctx := context.Background()
ctx, span := GetTracer().Start(ctx, "root")
defer span.End()
func(ctx context.Context) {
ctx, span := GetTracer().Start(ctx, "span1")
defer span.End()
func(ctx context.Context) {
_, span := GetTracer().Start(ctx, "spanA")
defer span.End()
}(ctx)
func(ctx context.Context) {
_, span := GetTracer().Start(ctx, "spanB")
defer span.End()
}(ctx)
}(ctx)
func(ctx context.Context) {
_, span := GetTracer().Start(ctx, "span2")
defer span.End()
}(ctx)
var spanFullNames []string
var collectSpanNames func(parentFullName string, s *vendorSpan)
collectSpanNames = func(parentFullName string, s *vendorSpan) {
fullName := parentFullName + "/" + s.name
spanFullNames = append(spanFullNames, fullName)
for _, c := range s.children {
collectSpanNames(fullName, c)
}
}
collectSpanNames("", span.internalSpans[0].(*testTraceSpan).vendorSpan)
assert.Equal(t, []string{
"/root",
"/root/span1",
"/root/span1/spanA",
"/root/span1/spanB",
"/root/span2",
}, spanFullNames)
}

@ -46,7 +46,7 @@ func (Renderer) Render(ctx *markup.RenderContext, _ io.Reader, output io.Writer)
setting.AppSubURL, setting.AppSubURL,
url.PathEscape(ctx.RenderOptions.Metas["user"]), url.PathEscape(ctx.RenderOptions.Metas["user"]),
url.PathEscape(ctx.RenderOptions.Metas["repo"]), url.PathEscape(ctx.RenderOptions.Metas["repo"]),
ctx.RenderOptions.Metas["BranchNameSubURL"], ctx.RenderOptions.Metas["RefTypeNameSubURL"],
url.PathEscape(ctx.RenderOptions.RelativePath), url.PathEscape(ctx.RenderOptions.RelativePath),
) )
return ctx.RenderInternal.FormatWithSafeAttrs(output, `<div class="%s" %s="%s"></div>`, playerClassName, playerSrcAttr, rawURL) return ctx.RenderInternal.FormatWithSafeAttrs(output, `<div class="%s" %s="%s"></div>`, playerClassName, playerSrcAttr, rawURL)

@ -44,7 +44,7 @@ type RenderOptions struct {
MarkupType string MarkupType string
// user&repo, format&style&regexp (for external issue pattern), teams&org (for mention) // user&repo, format&style&regexp (for external issue pattern), teams&org (for mention)
// BranchNameSubURL (for iframe&asciicast) // RefTypeNameSubURL (for iframe&asciicast)
// markupAllowShortIssuePattern // markupAllowShortIssuePattern
// markdownLineBreakStyle (comment, document) // markdownLineBreakStyle (comment, document)
Metas map[string]string Metas map[string]string
@ -170,7 +170,7 @@ sandbox="allow-scripts"
setting.AppSubURL, setting.AppSubURL,
url.PathEscape(ctx.RenderOptions.Metas["user"]), url.PathEscape(ctx.RenderOptions.Metas["user"]),
url.PathEscape(ctx.RenderOptions.Metas["repo"]), url.PathEscape(ctx.RenderOptions.Metas["repo"]),
ctx.RenderOptions.Metas["BranchNameSubURL"], ctx.RenderOptions.Metas["RefTypeNameSubURL"],
url.PathEscape(ctx.RenderOptions.RelativePath), url.PathEscape(ctx.RenderOptions.RelativePath),
)) ))
return err return err

@ -6,7 +6,6 @@ package repository
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git" git_model "code.gitea.io/gitea/models/git"
@ -52,9 +51,6 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository,
{ {
branches, _, err := gitRepo.GetBranchNames(0, 0) branches, _, err := gitRepo.GetBranchNames(0, 0)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "ref file is empty") {
return 0, nil
}
return 0, err return 0, err
} }
log.Trace("SyncRepoBranches[%s]: branches[%d]: %v", repo.FullName(), len(branches), branches) log.Trace("SyncRepoBranches[%s]: branches[%d]: %v", repo.FullName(), len(branches), branches)

@ -0,0 +1,73 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package tailmsg
import (
"sync"
"time"
)
type MsgRecord struct {
Time time.Time
Content string
}
type MsgRecorder interface {
Record(content string)
GetRecords() []*MsgRecord
}
type memoryMsgRecorder struct {
mu sync.RWMutex
msgs []*MsgRecord
limit int
}
// TODO: use redis for a clustered environment
func (m *memoryMsgRecorder) Record(content string) {
m.mu.Lock()
defer m.mu.Unlock()
m.msgs = append(m.msgs, &MsgRecord{
Time: time.Now(),
Content: content,
})
if len(m.msgs) > m.limit {
m.msgs = m.msgs[len(m.msgs)-m.limit:]
}
}
func (m *memoryMsgRecorder) GetRecords() []*MsgRecord {
m.mu.RLock()
defer m.mu.RUnlock()
ret := make([]*MsgRecord, len(m.msgs))
copy(ret, m.msgs)
return ret
}
func NewMsgRecorder(limit int) MsgRecorder {
return &memoryMsgRecorder{
limit: limit,
}
}
type Manager struct {
traceRecorder MsgRecorder
logRecorder MsgRecorder
}
func (m *Manager) GetTraceRecorder() MsgRecorder {
return m.traceRecorder
}
func (m *Manager) GetLogRecorder() MsgRecorder {
return m.logRecorder
}
var GetManager = sync.OnceValue(func() *Manager {
return &Manager{
traceRecorder: NewMsgRecorder(100),
logRecorder: NewMsgRecorder(1000),
}
})

@ -69,7 +69,7 @@ func NewFuncMap() template.FuncMap {
// time / number / format // time / number / format
"FileSize": base.FileSize, "FileSize": base.FileSize,
"CountFmt": countFmt, "CountFmt": countFmt,
"Sec2Time": util.SecToTime, "Sec2Time": util.SecToHours,
"TimeEstimateString": timeEstimateString, "TimeEstimateString": timeEstimateString,

@ -8,59 +8,17 @@ import (
"strings" "strings"
) )
// SecToTime converts an amount of seconds to a human-readable string. E.g. // SecToHours converts an amount of seconds to a human-readable hours string.
// 66s -> 1 minute 6 seconds // This is stable for planning and managing timesheets.
// 52410s -> 14 hours 33 minutes // Here it only supports hours and minutes, because a work day could contain 6 or 7 or 8 hours.
// 563418 -> 6 days 12 hours func SecToHours(durationVal any) string {
// 1563418 -> 2 weeks 4 days
// 3937125s -> 1 month 2 weeks
// 45677465s -> 1 year 6 months
func SecToTime(durationVal any) string {
duration, _ := ToInt64(durationVal) duration, _ := ToInt64(durationVal)
hours := duration / 3600
formattedTime := ""
// The following four variables are calculated by taking
// into account the previously calculated variables, this avoids
// pitfalls when using remainders. As that could lead to incorrect
// results when the calculated number equals the quotient number.
remainingDays := duration / (60 * 60 * 24)
years := remainingDays / 365
remainingDays -= years * 365
months := remainingDays * 12 / 365
remainingDays -= months * 365 / 12
weeks := remainingDays / 7
remainingDays -= weeks * 7
days := remainingDays
// The following three variables are calculated without depending
// on the previous calculated variables.
hours := (duration / 3600) % 24
minutes := (duration / 60) % 60 minutes := (duration / 60) % 60
seconds := duration % 60
// Extract only the relevant information of the time formattedTime := ""
// If the time is greater than a year, it makes no sense to display seconds.
switch {
case years > 0:
formattedTime = formatTime(years, "year", formattedTime)
formattedTime = formatTime(months, "month", formattedTime)
case months > 0:
formattedTime = formatTime(months, "month", formattedTime)
formattedTime = formatTime(weeks, "week", formattedTime)
case weeks > 0:
formattedTime = formatTime(weeks, "week", formattedTime)
formattedTime = formatTime(days, "day", formattedTime)
case days > 0:
formattedTime = formatTime(days, "day", formattedTime)
formattedTime = formatTime(hours, "hour", formattedTime)
case hours > 0:
formattedTime = formatTime(hours, "hour", formattedTime) formattedTime = formatTime(hours, "hour", formattedTime)
formattedTime = formatTime(minutes, "minute", formattedTime) formattedTime = formatTime(minutes, "minute", formattedTime)
default:
formattedTime = formatTime(minutes, "minute", formattedTime)
formattedTime = formatTime(seconds, "second", formattedTime)
}
// The formatTime() function always appends a space at the end. This will be trimmed // The formatTime() function always appends a space at the end. This will be trimmed
return strings.TrimRight(formattedTime, " ") return strings.TrimRight(formattedTime, " ")
@ -76,6 +34,5 @@ func formatTime(value int64, name, formattedTime string) string {
} else if value > 1 { } else if value > 1 {
formattedTime = fmt.Sprintf("%s%d %ss ", formattedTime, value, name) formattedTime = fmt.Sprintf("%s%d %ss ", formattedTime, value, name)
} }
return formattedTime return formattedTime
} }

@ -9,22 +9,17 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestSecToTime(t *testing.T) { func TestSecToHours(t *testing.T) {
second := int64(1) second := int64(1)
minute := 60 * second minute := 60 * second
hour := 60 * minute hour := 60 * minute
day := 24 * hour day := 24 * hour
year := 365 * day
assert.Equal(t, "1 minute 6 seconds", SecToTime(minute+6*second)) assert.Equal(t, "1 minute", SecToHours(minute+6*second))
assert.Equal(t, "1 hour", SecToTime(hour)) assert.Equal(t, "1 hour", SecToHours(hour))
assert.Equal(t, "1 hour", SecToTime(hour+second)) assert.Equal(t, "1 hour", SecToHours(hour+second))
assert.Equal(t, "14 hours 33 minutes", SecToTime(14*hour+33*minute+30*second)) assert.Equal(t, "14 hours 33 minutes", SecToHours(14*hour+33*minute+30*second))
assert.Equal(t, "6 days 12 hours", SecToTime(6*day+12*hour+30*minute+18*second)) assert.Equal(t, "156 hours 30 minutes", SecToHours(6*day+12*hour+30*minute+18*second))
assert.Equal(t, "2 weeks 4 days", SecToTime((2*7+4)*day+2*hour+16*minute+58*second)) assert.Equal(t, "98 hours 16 minutes", SecToHours(4*day+2*hour+16*minute+58*second))
assert.Equal(t, "4 weeks", SecToTime(4*7*day)) assert.Equal(t, "672 hours", SecToHours(4*7*day))
assert.Equal(t, "4 weeks 1 day", SecToTime((4*7+1)*day))
assert.Equal(t, "1 month 2 weeks", SecToTime((6*7+3)*day+13*hour+38*minute+45*second))
assert.Equal(t, "11 months", SecToTime(year-25*day))
assert.Equal(t, "1 year 5 months", SecToTime(year+163*day+10*hour+11*minute+5*second))
} }

@ -121,7 +121,7 @@ func wrapHandlerProvider[T http.Handler](hp func(next http.Handler) T, funcInfo
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
routing.UpdateFuncInfo(req.Context(), funcInfo) defer routing.RecordFuncInfo(req.Context(), funcInfo)()
h.ServeHTTP(resp, req) h.ServeHTTP(resp, req)
}) })
} }
@ -157,7 +157,7 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
return // it's doing pre-check, just return return // it's doing pre-check, just return
} }
routing.UpdateFuncInfo(req.Context(), funcInfo) defer routing.RecordFuncInfo(req.Context(), funcInfo)()
ret := fn.Call(argsIn) ret := fn.Call(argsIn)
// handle the return value (no-op at the moment) // handle the return value (no-op at the moment)

@ -6,23 +6,30 @@ package routing
import ( import (
"context" "context"
"net/http" "net/http"
"code.gitea.io/gitea/modules/gtprof"
"code.gitea.io/gitea/modules/reqctx"
) )
type contextKeyType struct{} type contextKeyType struct{}
var contextKey contextKeyType var contextKey contextKeyType
// UpdateFuncInfo updates a context's func info // RecordFuncInfo records a func info into context
func UpdateFuncInfo(ctx context.Context, funcInfo *FuncInfo) { func RecordFuncInfo(ctx context.Context, funcInfo *FuncInfo) (end func()) {
record, ok := ctx.Value(contextKey).(*requestRecord) end = func() {}
if !ok { if reqCtx := reqctx.FromContext(ctx); reqCtx != nil {
return var traceSpan *gtprof.TraceSpan
traceSpan, end = gtprof.GetTracer().StartInContext(reqCtx, "http.func")
traceSpan.SetAttributeString("func", funcInfo.shortName)
} }
if record, ok := ctx.Value(contextKey).(*requestRecord); ok {
record.lock.Lock() record.lock.Lock()
record.funcInfo = funcInfo record.funcInfo = funcInfo
record.lock.Unlock() record.lock.Unlock()
} }
return end
}
// MarkLongPolling marks the request is a long-polling request, and the logger may output different message for it // MarkLongPolling marks the request is a long-polling request, and the logger may output different message for it
func MarkLongPolling(resp http.ResponseWriter, req *http.Request) { func MarkLongPolling(resp http.ResponseWriter, req *http.Request) {

@ -104,6 +104,12 @@ dist
.temp .temp
.cache .cache
# vitepress build output
**/.vitepress/dist
# vitepress cache directory
**/.vitepress/cache
# Docusaurus cache and generated files # Docusaurus cache and generated files
.docusaurus .docusaurus

@ -167,5 +167,8 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/ #.idea/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file # PyPI configuration file
.pypirc .pypirc

@ -3,10 +3,6 @@
debug/ debug/
target/ target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt # These are backup files generated by rustfmt
**/*.rs.bk **/*.rs.bk

@ -1115,9 +1115,6 @@ blame.ignore_revs=Ignorování revizí v <a href="%s">.git-blame-ignorerevs</a>.
blame.ignore_revs.failed=Nepodařilo se ignorovat revize v <a href="%s">.git-blame-ignore-revs</a>. blame.ignore_revs.failed=Nepodařilo se ignorovat revize v <a href="%s">.git-blame-ignore-revs</a>.
user_search_tooltip=Zobrazí maximálně 30 uživatelů user_search_tooltip=Zobrazí maximálně 30 uživatelů
tree_path_not_found_commit=Cesta %[1]s v commitu %[2]s neexistuje
tree_path_not_found_branch=Cesta %[1]s ve větvi %[2]s neexistuje
tree_path_not_found_tag=Cesta %[1]s ve značce %[2]s neexistuje
transfer.accept=Přijmout převod transfer.accept=Přijmout převod
transfer.accept_desc=Převést do „%s“ transfer.accept_desc=Převést do „%s“
@ -1651,7 +1648,7 @@ issues.attachment.open_tab=`Klikněte pro zobrazení „%s“ v nové záložce`
issues.attachment.download=`Klikněte pro stažení „%s“` issues.attachment.download=`Klikněte pro stažení „%s“`
issues.subscribe=Odebírat issues.subscribe=Odebírat
issues.unsubscribe=Zrušit odběr issues.unsubscribe=Zrušit odběr
issues.unpin_issue=Odepnout úkol issues.unpin=Odepnout
issues.max_pinned=Nemůžete připnout další úkoly issues.max_pinned=Nemůžete připnout další úkoly
issues.pin_comment=připnuto %s issues.pin_comment=připnuto %s
issues.unpin_comment=odepnul/a tento %s issues.unpin_comment=odepnul/a tento %s
@ -1686,16 +1683,13 @@ issues.timetracker_timer_manually_add=Přidat čas
issues.time_estimate_set=Nastavit odhadovaný čas issues.time_estimate_set=Nastavit odhadovaný čas
issues.time_estimate_display=Odhad: %s issues.time_estimate_display=Odhad: %s
issues.change_time_estimate_at=změnil/a odhad času na <b>%s</b> %s
issues.remove_time_estimate_at=odstranil/a odhad času %s issues.remove_time_estimate_at=odstranil/a odhad času %s
issues.time_estimate_invalid=Formát odhadu času je neplatný issues.time_estimate_invalid=Formát odhadu času je neplatný
issues.start_tracking_history=započal/a práci %s issues.start_tracking_history=započal/a práci %s
issues.tracker_auto_close=Časovač se automaticky zastaví po zavření tohoto úkolu issues.tracker_auto_close=Časovač se automaticky zastaví po zavření tohoto úkolu
issues.tracking_already_started=`Již jste spustili sledování času na <a href="%s">jiném úkolu</a>!` issues.tracking_already_started=`Již jste spustili sledování času na <a href="%s">jiném úkolu</a>!`
issues.stop_tracking_history=pracoval/a <b>%s</b> %s
issues.cancel_tracking_history=`zrušil/a sledování času %s` issues.cancel_tracking_history=`zrušil/a sledování času %s`
issues.del_time=Odstranit tento časový záznam issues.del_time=Odstranit tento časový záznam
issues.add_time_history=přidal/a strávený čas <b>%s</b> %s
issues.del_time_history=`odstranil/a strávený čas %s` issues.del_time_history=`odstranil/a strávený čas %s`
issues.add_time_manually=Přidat čas ručně issues.add_time_manually=Přidat čas ručně
issues.add_time_hours=Hodiny issues.add_time_hours=Hodiny
@ -2158,7 +2152,6 @@ settings.advanced_settings=Pokročilá nastavení
settings.wiki_desc=Povolit Wiki repozitáře settings.wiki_desc=Povolit Wiki repozitáře
settings.use_internal_wiki=Používat vestavěnou Wiki settings.use_internal_wiki=Používat vestavěnou Wiki
settings.default_wiki_branch_name=Výchozí název větve Wiki settings.default_wiki_branch_name=Výchozí název větve Wiki
settings.default_wiki_everyone_access=Výchozí přístupová práva pro přihlášené uživatele:
settings.failed_to_change_default_wiki_branch=Změna výchozí větve wiki se nezdařila. settings.failed_to_change_default_wiki_branch=Změna výchozí větve wiki se nezdařila.
settings.use_external_wiki=Používat externí Wiki settings.use_external_wiki=Používat externí Wiki
settings.external_wiki_url=URL externí Wiki settings.external_wiki_url=URL externí Wiki
@ -3373,7 +3366,6 @@ monitor.execute_time=Doba provádění
monitor.last_execution_result=Výsledek monitor.last_execution_result=Výsledek
monitor.process.cancel=Zrušit proces monitor.process.cancel=Zrušit proces
monitor.process.cancel_desc=Zrušení procesu může způsobit ztrátu dat monitor.process.cancel_desc=Zrušení procesu může způsobit ztrátu dat
monitor.process.cancel_notices=Zrušit: <strong>%s</strong>?
monitor.process.children=Potomek monitor.process.children=Potomek
monitor.queues=Fronty monitor.queues=Fronty
@ -3570,7 +3562,6 @@ conda.install=Pro instalaci balíčku pomocí Conda spusťte následující př
container.details.type=Typ obrazu container.details.type=Typ obrazu
container.details.platform=Platforma container.details.platform=Platforma
container.pull=Stáhněte obraz z příkazové řádky: container.pull=Stáhněte obraz z příkazové řádky:
container.digest=Výběr:
container.multi_arch=OS/architektura container.multi_arch=OS/architektura
container.layers=Vrstvy obrazů container.layers=Vrstvy obrazů
container.labels=Štítky container.labels=Štítky

@ -1111,9 +1111,6 @@ blame.ignore_revs=Revisionen in <a href="%s">.git-blame-ignore-revs</a> werden i
blame.ignore_revs.failed=Fehler beim Ignorieren der Revisionen in <a href="%s">.git-blame-ignore-revs</a>. blame.ignore_revs.failed=Fehler beim Ignorieren der Revisionen in <a href="%s">.git-blame-ignore-revs</a>.
user_search_tooltip=Zeigt maximal 30 Benutzer user_search_tooltip=Zeigt maximal 30 Benutzer
tree_path_not_found_commit=Pfad %[1]s existiert nicht in Commit%[2]s
tree_path_not_found_branch=Pfad %[1]s existiert nicht in Branch %[2]s
tree_path_not_found_tag=Pfad %[1]s existiert nicht in Tag %[2]s
transfer.accept=Übertragung Akzeptieren transfer.accept=Übertragung Akzeptieren
transfer.accept_desc=`Übertragung nach "%s"` transfer.accept_desc=`Übertragung nach "%s"`
@ -1646,7 +1643,7 @@ issues.attachment.open_tab=`Klicken, um „%s“ in einem neuen Tab zu öffnen`
issues.attachment.download=`Klicken, um „%s“ herunterzuladen` issues.attachment.download=`Klicken, um „%s“ herunterzuladen`
issues.subscribe=Abonnieren issues.subscribe=Abonnieren
issues.unsubscribe=Abbestellen issues.unsubscribe=Abbestellen
issues.unpin_issue=Issue abheften issues.unpin=Loslösen
issues.max_pinned=Du kannst keine weiteren Issues anheften issues.max_pinned=Du kannst keine weiteren Issues anheften
issues.pin_comment=hat das %s angeheftet issues.pin_comment=hat das %s angeheftet
issues.unpin_comment=hat das %s abgeheftet issues.unpin_comment=hat das %s abgeheftet
@ -1681,16 +1678,13 @@ issues.timetracker_timer_manually_add=Zeit hinzufügen
issues.time_estimate_set=Geschätzte Zeit festlegen issues.time_estimate_set=Geschätzte Zeit festlegen
issues.time_estimate_display=Schätzung: %s issues.time_estimate_display=Schätzung: %s
issues.change_time_estimate_at=Zeitschätzung geändert zu <b>%s</b> %s
issues.remove_time_estimate_at=Zeitschätzung %s entfernt issues.remove_time_estimate_at=Zeitschätzung %s entfernt
issues.time_estimate_invalid=Format der Zeitschätzung ist ungültig issues.time_estimate_invalid=Format der Zeitschätzung ist ungültig
issues.start_tracking_history=hat die Zeiterfassung %s gestartet issues.start_tracking_history=hat die Zeiterfassung %s gestartet
issues.tracker_auto_close=Der Timer wird automatisch gestoppt, wenn dieser Issue geschlossen wird issues.tracker_auto_close=Der Timer wird automatisch gestoppt, wenn dieser Issue geschlossen wird
issues.tracking_already_started=`Du hast die Zeiterfassung bereits in <a href="%s">diesem Issue</a> gestartet!` issues.tracking_already_started=`Du hast die Zeiterfassung bereits in <a href="%s">diesem Issue</a> gestartet!`
issues.stop_tracking_history=hat für <b>%s</b> gearbeitet %s
issues.cancel_tracking_history=`hat die Zeiterfassung %s abgebrochen` issues.cancel_tracking_history=`hat die Zeiterfassung %s abgebrochen`
issues.del_time=Diese Zeiterfassung löschen issues.del_time=Diese Zeiterfassung löschen
issues.add_time_history=hat <b>%s</b> gearbeitete Zeit hinzugefügt %s
issues.del_time_history=`hat %s gearbeitete Zeit gelöscht` issues.del_time_history=`hat %s gearbeitete Zeit gelöscht`
issues.add_time_manually=Zeit manuell hinzufügen issues.add_time_manually=Zeit manuell hinzufügen
issues.add_time_hours=Stunden issues.add_time_hours=Stunden
@ -2154,7 +2148,6 @@ settings.advanced_settings=Erweiterte Einstellungen
settings.wiki_desc=Repository-Wiki aktivieren settings.wiki_desc=Repository-Wiki aktivieren
settings.use_internal_wiki=Eingebautes Wiki verwenden settings.use_internal_wiki=Eingebautes Wiki verwenden
settings.default_wiki_branch_name=Standardbezeichnung für Wiki-Branch settings.default_wiki_branch_name=Standardbezeichnung für Wiki-Branch
settings.default_wiki_everyone_access=Standard-Zugriffsberechtigung für angemeldete Benutzer:
settings.failed_to_change_default_wiki_branch=Das Ändern des Standard-Wiki-Branches ist fehlgeschlagen. settings.failed_to_change_default_wiki_branch=Das Ändern des Standard-Wiki-Branches ist fehlgeschlagen.
settings.use_external_wiki=Externes Wiki verwenden settings.use_external_wiki=Externes Wiki verwenden
settings.external_wiki_url=Externe Wiki-URL settings.external_wiki_url=Externe Wiki-URL
@ -3363,7 +3356,6 @@ monitor.execute_time=Ausführungszeit
monitor.last_execution_result=Ergebnis monitor.last_execution_result=Ergebnis
monitor.process.cancel=Prozess abbrechen monitor.process.cancel=Prozess abbrechen
monitor.process.cancel_desc=Abbrechen eines Prozesses kann Datenverlust verursachen monitor.process.cancel_desc=Abbrechen eines Prozesses kann Datenverlust verursachen
monitor.process.cancel_notices=Abbrechen: <strong>%s</strong>?
monitor.process.children=Subprozesse monitor.process.children=Subprozesse
monitor.queues=Warteschlangen monitor.queues=Warteschlangen
@ -3559,7 +3551,6 @@ conda.install=Um das Paket mit Conda zu installieren, führe den folgenden Befeh
container.details.type=Container-Image Typ container.details.type=Container-Image Typ
container.details.platform=Plattform container.details.platform=Plattform
container.pull=Downloade das Container-Image aus der Kommandozeile: container.pull=Downloade das Container-Image aus der Kommandozeile:
container.digest=Digest:
container.multi_arch=Betriebsystem / Architektur container.multi_arch=Betriebsystem / Architektur
container.layers=Container-Image Ebenen container.layers=Container-Image Ebenen
container.labels=Labels container.labels=Labels

@ -992,9 +992,6 @@ blame_prior=Προβολή ευθύνης πριν από αυτή την αλλ
blame.ignore_revs=Αγνόηση των αναθεωρήσεων στο <a href="%s">.git-blame-ignore-revs</a>. Πατήστε <a href="%s">εδώ</a> για να το παρακάμψετε και να δείτε την κανονική προβολή ευθυνών. blame.ignore_revs=Αγνόηση των αναθεωρήσεων στο <a href="%s">.git-blame-ignore-revs</a>. Πατήστε <a href="%s">εδώ</a> για να το παρακάμψετε και να δείτε την κανονική προβολή ευθυνών.
blame.ignore_revs.failed=Αποτυχία αγνόησης των αναθεωρήσεων στο <a href="%s">.git-blame-ignore-revs</a>. blame.ignore_revs.failed=Αποτυχία αγνόησης των αναθεωρήσεων στο <a href="%s">.git-blame-ignore-revs</a>.
tree_path_not_found_commit=Η διαδρομή %[1]s δεν υπάρχει στην υποβολή %[2]s
tree_path_not_found_branch=Η διαδρομή %[1]s δεν υπάρχει στον κλάδο %[2]s
tree_path_not_found_tag=Η διαδρομή %[1]s δεν υπάρχει στην ετικέτα %[2]s
transfer.accept=Αποδοχή Μεταφοράς transfer.accept=Αποδοχή Μεταφοράς
transfer.reject=Απόρριψη Μεταφοράς transfer.reject=Απόρριψη Μεταφοράς
@ -1495,7 +1492,7 @@ issues.attachment.open_tab=`Κάντε κλικ για να δείτε το "%s"
issues.attachment.download=`Κάντε κλικ για να λάβετε το "%s"` issues.attachment.download=`Κάντε κλικ για να λάβετε το "%s"`
issues.subscribe=Εγγραφή issues.subscribe=Εγγραφή
issues.unsubscribe=Διαγραφή issues.unsubscribe=Διαγραφή
issues.unpin_issue=Άφεση Ζητήματος issues.unpin=Άφεση
issues.max_pinned=Δεν μπορείτε να διατηρήσετε περισσότερα ζητήματα issues.max_pinned=Δεν μπορείτε να διατηρήσετε περισσότερα ζητήματα
issues.pin_comment=διατήρησε αυτό %s issues.pin_comment=διατήρησε αυτό %s
issues.unpin_comment=άφησε αυτό %s issues.unpin_comment=άφησε αυτό %s
@ -3239,7 +3236,6 @@ conda.install=Για να εγκαταστήσετε το πακέτο χρησ
container.details.type=Τύπος Εικόνας container.details.type=Τύπος Εικόνας
container.details.platform=Πλατφόρμα container.details.platform=Πλατφόρμα
container.pull=Κατεβάστε την εικόνα από τη γραμμή εντολών: container.pull=Κατεβάστε την εικόνα από τη γραμμή εντολών:
container.digest=Σύνοψη:
container.multi_arch=ΛΣ / Αρχιτεκτονική container.multi_arch=ΛΣ / Αρχιτεκτονική
container.layers=Στρώματα Εικόνας container.layers=Στρώματα Εικόνας
container.labels=Ετικέτες container.labels=Ετικέτες

@ -1115,9 +1115,7 @@ blame.ignore_revs = Ignoring revisions in <a href="%s">.git-blame-ignore-revs</a
blame.ignore_revs.failed = Failed to ignore revisions in <a href="%s">.git-blame-ignore-revs</a>. blame.ignore_revs.failed = Failed to ignore revisions in <a href="%s">.git-blame-ignore-revs</a>.
user_search_tooltip = Shows a maximum of 30 users user_search_tooltip = Shows a maximum of 30 users
tree_path_not_found_commit = Path %[1]s doesn't exist in commit %[2]s tree_path_not_found = Path %[1]s doesn't exist in %[2]s
tree_path_not_found_branch = Path %[1]s doesn't exist in branch %[2]s
tree_path_not_found_tag = Path %[1]s doesn't exist in tag %[2]s
transfer.accept = Accept Transfer transfer.accept = Accept Transfer
transfer.accept_desc = Transfer to "%s" transfer.accept_desc = Transfer to "%s"
@ -1652,7 +1650,7 @@ issues.attachment.open_tab = `Click to see "%s" in a new tab`
issues.attachment.download = `Click to download "%s"` issues.attachment.download = `Click to download "%s"`
issues.subscribe = Subscribe issues.subscribe = Subscribe
issues.unsubscribe = Unsubscribe issues.unsubscribe = Unsubscribe
issues.unpin_issue = Unpin Issue issues.unpin = Unpin
issues.max_pinned = "You can't pin more issues" issues.max_pinned = "You can't pin more issues"
issues.pin_comment = "pinned this %s" issues.pin_comment = "pinned this %s"
issues.unpin_comment = "unpinned this %s" issues.unpin_comment = "unpinned this %s"
@ -1687,16 +1685,16 @@ issues.timetracker_timer_manually_add = Add Time
issues.time_estimate_set = Set estimated time issues.time_estimate_set = Set estimated time
issues.time_estimate_display = Estimate: %s issues.time_estimate_display = Estimate: %s
issues.change_time_estimate_at = changed time estimate to <b>%s</b> %s issues.change_time_estimate_at = changed time estimate to <b>%[1]s</b> %[2]s
issues.remove_time_estimate_at = removed time estimate %s issues.remove_time_estimate_at = removed time estimate %s
issues.time_estimate_invalid = Time estimate format is invalid issues.time_estimate_invalid = Time estimate format is invalid
issues.start_tracking_history = started working %s issues.start_tracking_history = started working %s
issues.tracker_auto_close = Timer will be stopped automatically when this issue gets closed issues.tracker_auto_close = Timer will be stopped automatically when this issue gets closed
issues.tracking_already_started = `You have already started time tracking on <a href="%s">another issue</a>!` issues.tracking_already_started = `You have already started time tracking on <a href="%s">another issue</a>!`
issues.stop_tracking_history = worked for <b>%s</b> %s issues.stop_tracking_history = worked for <b>%[1]s</b> %[2]s
issues.cancel_tracking_history = `canceled time tracking %s` issues.cancel_tracking_history = `canceled time tracking %s`
issues.del_time = Delete this time log issues.del_time = Delete this time log
issues.add_time_history = added spent time <b>%s</b> %s issues.add_time_history = added spent time <b>%[1]s</b> %[2]s
issues.del_time_history= `deleted spent time %s` issues.del_time_history= `deleted spent time %s`
issues.add_time_manually = Manually Add Time issues.add_time_manually = Manually Add Time
issues.add_time_hours = Hours issues.add_time_hours = Hours
@ -1955,6 +1953,7 @@ pulls.upstream_diverging_prompt_behind_1 = This branch is %[1]d commit behind %[
pulls.upstream_diverging_prompt_behind_n = This branch is %[1]d commits behind %[2]s pulls.upstream_diverging_prompt_behind_n = This branch is %[1]d commits behind %[2]s
pulls.upstream_diverging_prompt_base_newer = The base branch %s has new changes pulls.upstream_diverging_prompt_base_newer = The base branch %s has new changes
pulls.upstream_diverging_merge = Sync fork pulls.upstream_diverging_merge = Sync fork
pulls.upstream_diverging_merge_confirm = Would you like to merge "%[1]s" onto "%[2]s"?
pull.deleted_branch = (deleted):%s pull.deleted_branch = (deleted):%s
pull.agit_documentation = Review documentation about AGit pull.agit_documentation = Review documentation about AGit
@ -2160,7 +2159,7 @@ settings.advanced_settings = Advanced Settings
settings.wiki_desc = Enable Repository Wiki settings.wiki_desc = Enable Repository Wiki
settings.use_internal_wiki = Use Built-In Wiki settings.use_internal_wiki = Use Built-In Wiki
settings.default_wiki_branch_name = Default Wiki Branch Name settings.default_wiki_branch_name = Default Wiki Branch Name
settings.default_wiki_everyone_access = Default Access Permission for signed-in users: settings.default_permission_everyone_access = Default access permission for all signed-in users:
settings.failed_to_change_default_wiki_branch = Failed to change the default wiki branch. settings.failed_to_change_default_wiki_branch = Failed to change the default wiki branch.
settings.use_external_wiki = Use External Wiki settings.use_external_wiki = Use External Wiki
settings.external_wiki_url = External Wiki URL settings.external_wiki_url = External Wiki URL
@ -2715,6 +2714,8 @@ branch.create_branch_operation = Create branch
branch.new_branch = Create new branch branch.new_branch = Create new branch
branch.new_branch_from = Create new branch from "%s" branch.new_branch_from = Create new branch from "%s"
branch.renamed = Branch %s was renamed to %s. branch.renamed = Branch %s was renamed to %s.
branch.rename_default_or_protected_branch_error = Only admins can rename default or protected branches.
branch.rename_protected_branch_failed = This branch is protected by glob-based protection rules.
tag.create_tag = Create tag %s tag.create_tag = Create tag %s
tag.create_tag_operation = Create tag tag.create_tag_operation = Create tag
@ -3367,6 +3368,8 @@ monitor.previous = Previous Time
monitor.execute_times = Executions monitor.execute_times = Executions
monitor.process = Running Processes monitor.process = Running Processes
monitor.stacktrace = Stacktrace monitor.stacktrace = Stacktrace
monitor.trace = Trace
monitor.performance_logs = Performance Logs
monitor.processes_count = %d Processes monitor.processes_count = %d Processes
monitor.download_diagnosis_report = Download diagnosis report monitor.download_diagnosis_report = Download diagnosis report
monitor.desc = Description monitor.desc = Description
@ -3375,7 +3378,6 @@ monitor.execute_time = Execution Time
monitor.last_execution_result = Result monitor.last_execution_result = Result
monitor.process.cancel = Cancel process monitor.process.cancel = Cancel process
monitor.process.cancel_desc = Cancelling a process may cause data loss monitor.process.cancel_desc = Cancelling a process may cause data loss
monitor.process.cancel_notices = Cancel: <strong>%s</strong>?
monitor.process.children = Children monitor.process.children = Children
monitor.queues = Queues monitor.queues = Queues
@ -3572,7 +3574,8 @@ conda.install = To install the package using Conda, run the following command:
container.details.type = Image Type container.details.type = Image Type
container.details.platform = Platform container.details.platform = Platform
container.pull = Pull the image from the command line: container.pull = Pull the image from the command line:
container.digest = Digest: container.images = Images
container.digest = Digest
container.multi_arch = OS / Arch container.multi_arch = OS / Arch
container.layers = Image Layers container.layers = Image Layers
container.labels = Labels container.labels = Labels

@ -982,9 +982,6 @@ blame_prior=Ver la culpa antes de este cambio
blame.ignore_revs=Ignorando revisiones en <a href="%s">.git-blame-ignore-revs</a>. Haga clic <a href="%s">aquí para saltar</a> y para a la vista normal. blame.ignore_revs=Ignorando revisiones en <a href="%s">.git-blame-ignore-revs</a>. Haga clic <a href="%s">aquí para saltar</a> y para a la vista normal.
blame.ignore_revs.failed=No se pudieron ignorar las revisiones en <a href="%s">.git-blame-ignore-revs</a>. blame.ignore_revs.failed=No se pudieron ignorar las revisiones en <a href="%s">.git-blame-ignore-revs</a>.
tree_path_not_found_commit=La ruta %[1]s no existe en el commit %[2]s
tree_path_not_found_branch=La ruta %[1]s no existe en la rama %[2]s
tree_path_not_found_tag=La ruta %[1]s no existe en la etiqueta %[2]s
transfer.accept=Aceptar transferencia transfer.accept=Aceptar transferencia
transfer.reject=Rechazar transferencia transfer.reject=Rechazar transferencia
@ -1485,7 +1482,7 @@ issues.attachment.open_tab='Haga clic para ver "%s" en una pestaña nueva'
issues.attachment.download=`Haga clic para descargar "%s"` issues.attachment.download=`Haga clic para descargar "%s"`
issues.subscribe=Suscribir issues.subscribe=Suscribir
issues.unsubscribe=Desuscribirse issues.unsubscribe=Desuscribirse
issues.unpin_issue=Desanclar incidencia issues.unpin=Desanclar
issues.max_pinned=No puedes anclar más incidencias issues.max_pinned=No puedes anclar más incidencias
issues.pin_comment=anclado este %s issues.pin_comment=anclado este %s
issues.unpin_comment=desanclado este %s issues.unpin_comment=desanclado este %s
@ -3218,7 +3215,6 @@ conda.install=Para instalar el paquete usando Conda, ejecute el siguiente comand
container.details.type=Tipo de imagen container.details.type=Tipo de imagen
container.details.platform=Plataforma container.details.platform=Plataforma
container.pull=Arrastra la imagen desde la línea de comandos: container.pull=Arrastra la imagen desde la línea de comandos:
container.digest=Resumen:
container.multi_arch=SO / Arquitectura container.multi_arch=SO / Arquitectura
container.layers=Capas de imagen container.layers=Capas de imagen
container.labels=Etiquetas container.labels=Etiquetas

@ -46,7 +46,7 @@ webauthn_unsupported_browser=Votre navigateur ne prend actuellement pas en charg
webauthn_error_unknown=Une erreur indéterminée s'est produite. Veuillez réessayer. webauthn_error_unknown=Une erreur indéterminée s'est produite. Veuillez réessayer.
webauthn_error_insecure=`WebAuthn ne prend en charge que les connexions sécurisées. Pour les tests via HTTP, vous pouvez utiliser l'origine "localhost" ou "127.0.0.1"` webauthn_error_insecure=`WebAuthn ne prend en charge que les connexions sécurisées. Pour les tests via HTTP, vous pouvez utiliser l'origine "localhost" ou "127.0.0.1"`
webauthn_error_unable_to_process=Le serveur n'a pas pu traiter votre demande. webauthn_error_unable_to_process=Le serveur n'a pas pu traiter votre demande.
webauthn_error_duplicated=La clé de sécurité n'est pas autorisée pour cette demande. Veuillez vous assurer que la clé n'est pas déjà enregistrée. webauthn_error_duplicated=La clé de sécurité n’est pas autorisée pour cette demande. Veuillez vous assurer que la clé n’est pas déjà enregistrée.
webauthn_error_empty=Vous devez définir un nom pour cette clé. webauthn_error_empty=Vous devez définir un nom pour cette clé.
webauthn_error_timeout=Le délai d'attente imparti a été atteint avant que votre clé ne puisse être lue. Veuillez recharger la page pour réessayer. webauthn_error_timeout=Le délai d'attente imparti a été atteint avant que votre clé ne puisse être lue. Veuillez recharger la page pour réessayer.
webauthn_reload=Recharger webauthn_reload=Recharger
@ -469,7 +469,7 @@ sspi_auth_failed=Échec de l'authentification SSPI
password_pwned=Le mot de passe que vous avez choisi <a target="_blank" rel="noopener noreferrer" href="%s">fait partit des mots de passe ayant fuité</a> sur internet. Veuillez réessayer avec un mot de passe différent et considérez remplacer ce mot de passe si vous l’utilisez ailleurs. password_pwned=Le mot de passe que vous avez choisi <a target="_blank" rel="noopener noreferrer" href="%s">fait partit des mots de passe ayant fuité</a> sur internet. Veuillez réessayer avec un mot de passe différent et considérez remplacer ce mot de passe si vous l’utilisez ailleurs.
password_pwned_err=Impossible d'envoyer la demande à HaveIBeenPwned password_pwned_err=Impossible d'envoyer la demande à HaveIBeenPwned
last_admin=Vous ne pouvez pas supprimer ce compte car au moins un administrateur est requis. last_admin=Vous ne pouvez pas supprimer ce compte car au moins un administrateur est requis.
signin_passkey=Se connecter avec une clé d’identification (passkey) signin_passkey=Se connecter avec une clé d’accès (passkey)
back_to_sign_in=Revenir à la page de connexion back_to_sign_in=Revenir à la page de connexion
[mail] [mail]
@ -818,7 +818,7 @@ manage_ssh_keys=Gérer les clés SSH
manage_ssh_principals=Gérer les certificats principaux SSH manage_ssh_principals=Gérer les certificats principaux SSH
manage_gpg_keys=Gérer les clés GPG manage_gpg_keys=Gérer les clés GPG
add_key=Ajouter une clé add_key=Ajouter une clé
ssh_desc=Ces clefs SSH publiques sont associées à votre compte. Les clefs privées correspondantes permettent l'accès complet à vos repos. ssh_desc=Ces clés SSH publiques sont associées à votre compte. Les clés privées correspondantes permettent l’accès complet à vos dépôts.
principal_desc=Ces Principaux de certificats SSH sont associés à votre compte et permettent un accès complet à vos dépôts. principal_desc=Ces Principaux de certificats SSH sont associés à votre compte et permettent un accès complet à vos dépôts.
gpg_desc=Ces clés GPG sont associées à votre compte. Conservez-les en lieu sûr, car elles permettent de vérifier vos révisions. gpg_desc=Ces clés GPG sont associées à votre compte. Conservez-les en lieu sûr, car elles permettent de vérifier vos révisions.
ssh_helper=<strong>Besoin d'aide ?</strong> Consultez le guide de GitHub pour <a href="%s">créer vos propres clés SSH</a> ou <a href="%s">résoudre les problèmes courants</a> que vous pourriez rencontrer en utilisant SSH. ssh_helper=<strong>Besoin d'aide ?</strong> Consultez le guide de GitHub pour <a href="%s">créer vos propres clés SSH</a> ou <a href="%s">résoudre les problèmes courants</a> que vous pourriez rencontrer en utilisant SSH.
@ -969,7 +969,7 @@ passcode_invalid=Le mot de passe est invalide. Réessayez.
twofa_enrolled=L’authentification à deux facteurs a été activée pour votre compte. Gardez votre clé de secours (%s) en lieu sûr, car il ne vous sera montré qu'une seule fois. twofa_enrolled=L’authentification à deux facteurs a été activée pour votre compte. Gardez votre clé de secours (%s) en lieu sûr, car il ne vous sera montré qu'une seule fois.
twofa_failed_get_secret=Impossible d'obtenir le secret. twofa_failed_get_secret=Impossible d'obtenir le secret.
webauthn_desc=Les clefs de sécurité sont des dispositifs matériels contenant des clefs cryptographiques. Elles peuvent être utilisées pour l’authentification à deux facteurs. La clef de sécurité doit supporter le standard <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a>. webauthn_desc=Les clés de sécurité sont des dispositifs matériels contenant des clés cryptographiques. Elles peuvent être utilisées pour l’authentification à deux facteurs. La clé de sécurité doit supporter le standard <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a>.
webauthn_register_key=Ajouter une clé de sécurité webauthn_register_key=Ajouter une clé de sécurité
webauthn_nickname=Pseudonyme webauthn_nickname=Pseudonyme
webauthn_delete_key=Retirer la clé de sécurité webauthn_delete_key=Retirer la clé de sécurité
@ -1115,9 +1115,6 @@ blame.ignore_revs=Les révisions dans <a href="%s">.git-blame-ignore-revs</a> so
blame.ignore_revs.failed=Impossible d'ignorer les révisions dans <a href="%s">.git-blame-ignore-revs</a>. blame.ignore_revs.failed=Impossible d'ignorer les révisions dans <a href="%s">.git-blame-ignore-revs</a>.
user_search_tooltip=Affiche un maximum de 30 utilisateurs user_search_tooltip=Affiche un maximum de 30 utilisateurs
tree_path_not_found_commit=Le chemin %[1]s n’existe pas dans la révision %[2]s.
tree_path_not_found_branch=Le chemin %[1]s n’existe pas dans la branche %[2]s.
tree_path_not_found_tag=Le chemin %[1]s n’existe pas dans l’étiquette %[2]s.
transfer.accept=Accepter le transfert transfer.accept=Accepter le transfert
transfer.accept_desc=Transférer à « %s » transfer.accept_desc=Transférer à « %s »
@ -1651,7 +1648,7 @@ issues.attachment.open_tab=`Cliquez ici pour voir « %s » dans un nouvel ongl
issues.attachment.download=`Cliquez pour télécharger « %s ».` issues.attachment.download=`Cliquez pour télécharger « %s ».`
issues.subscribe=S’abonner issues.subscribe=S’abonner
issues.unsubscribe=Se désabonner issues.unsubscribe=Se désabonner
issues.unpin_issue=Désépingler le ticket issues.unpin=Désépingler
issues.max_pinned=Vous ne pouvez pas épingler plus de tickets issues.max_pinned=Vous ne pouvez pas épingler plus de tickets
issues.pin_comment=a épinglé ça %s. issues.pin_comment=a épinglé ça %s.
issues.unpin_comment=a désépinglé ça %s. issues.unpin_comment=a désépinglé ça %s.
@ -1686,16 +1683,13 @@ issues.timetracker_timer_manually_add=Pointer du temps
issues.time_estimate_set=Définir le temps estimé issues.time_estimate_set=Définir le temps estimé
issues.time_estimate_display=Estimation : %s issues.time_estimate_display=Estimation : %s
issues.change_time_estimate_at=a changé le temps estimé à <b>%s</b> %s
issues.remove_time_estimate_at=a supprimé le temps estimé %s issues.remove_time_estimate_at=a supprimé le temps estimé %s
issues.time_estimate_invalid=Le format du temps estimé est invalide issues.time_estimate_invalid=Le format du temps estimé est invalide
issues.start_tracking_history=`a commencé son travail %s.` issues.start_tracking_history=`a commencé son travail %s.`
issues.tracker_auto_close=Le minuteur sera automatiquement arrêté quand le ticket sera fermé. issues.tracker_auto_close=Le minuteur sera automatiquement arrêté quand le ticket sera fermé.
issues.tracking_already_started=`Vous avez déjà un minuteur en cours sur <a href="%s">un autre ticket</a> !` issues.tracking_already_started=`Vous avez déjà un minuteur en cours sur <a href="%s">un autre ticket</a> !`
issues.stop_tracking_history=`a fini de travailler sur <b>%s</b> %s.`
issues.cancel_tracking_history=`a abandonné son minuteur %s.` issues.cancel_tracking_history=`a abandonné son minuteur %s.`
issues.del_time=Supprimer ce minuteur du journal issues.del_time=Supprimer ce minuteur du journal
issues.add_time_history=`a pointé du temps de travail %s.`
issues.del_time_history=`a supprimé son temps de travail %s.` issues.del_time_history=`a supprimé son temps de travail %s.`
issues.add_time_manually=Temps pointé manuellement issues.add_time_manually=Temps pointé manuellement
issues.add_time_hours=Heures issues.add_time_hours=Heures
@ -2159,7 +2153,6 @@ settings.advanced_settings=Paramètres avancés
settings.wiki_desc=Activer le wiki du dépôt settings.wiki_desc=Activer le wiki du dépôt
settings.use_internal_wiki=Utiliser le wiki interne settings.use_internal_wiki=Utiliser le wiki interne
settings.default_wiki_branch_name=Nom de la branche du Wiki par défaut settings.default_wiki_branch_name=Nom de la branche du Wiki par défaut
settings.default_wiki_everyone_access=Autorisation d’accès par défaut pour les utilisateurs connectés :
settings.failed_to_change_default_wiki_branch=Impossible de modifier la branche du wiki par défaut. settings.failed_to_change_default_wiki_branch=Impossible de modifier la branche du wiki par défaut.
settings.use_external_wiki=Utiliser un wiki externe settings.use_external_wiki=Utiliser un wiki externe
settings.external_wiki_url=URL Wiki externe settings.external_wiki_url=URL Wiki externe
@ -2400,17 +2393,17 @@ settings.packagist_api_token=Jeton API
settings.packagist_package_url=URL du paquet Packagist settings.packagist_package_url=URL du paquet Packagist
settings.deploy_keys=Clés de déploiement settings.deploy_keys=Clés de déploiement
settings.add_deploy_key=Ajouter une clé de déploiement settings.add_deploy_key=Ajouter une clé de déploiement
settings.deploy_key_desc=Les clefs de déploiement ont un accès en lecture seule au dépôt. settings.deploy_key_desc=Les clés de déploiement ont un accès en lecture seule au dépôt.
settings.is_writable=Activer l'accès en écriture settings.is_writable=Activer l'accès en écriture
settings.is_writable_info=Autoriser cette clé de déploiement à <strong>soumettre</strong> sur le dépôt. settings.is_writable_info=Autoriser cette clé de déploiement à <strong>soumettre</strong> sur le dépôt.
settings.no_deploy_keys=Il n'y a pas encore de clefs de déploiement. settings.no_deploy_keys=Il n’y a pas encore de clés de déploiement.
settings.title=Titre settings.title=Titre
settings.deploy_key_content=Contenu settings.deploy_key_content=Contenu
settings.key_been_used=Une clef de déploiement identique est déjà en cours d'utilisation. settings.key_been_used=Une clé de déploiement identique est déjà en cours d’utilisation.
settings.key_name_used=Une clef de déploiement du même nom existe déjà. settings.key_name_used=Une clé de déploiement du même nom existe déjà.
settings.add_key_success=La clé de déploiement "%s" a été ajoutée. settings.add_key_success=La clé de déploiement « %s » a été ajoutée.
settings.deploy_key_deletion=Supprimer une clef de déploiement settings.deploy_key_deletion=Supprimer une clé de déploiement
settings.deploy_key_deletion_desc=La suppression d'une clef de déploiement révoque son accès à ce dépôt. Continuer ? settings.deploy_key_deletion_desc=La suppression d’une clé de déploiement révoque son accès à ce dépôt. Continuer ?
settings.deploy_key_deletion_success=La clé de déploiement a été supprimée. settings.deploy_key_deletion_success=La clé de déploiement a été supprimée.
settings.branches=Branches settings.branches=Branches
settings.protected_branch=Protection de branche settings.protected_branch=Protection de branche
@ -2627,6 +2620,9 @@ diff.image.overlay=Superposition
diff.has_escaped=Cette ligne contient des caractères Unicode cachés diff.has_escaped=Cette ligne contient des caractères Unicode cachés
diff.show_file_tree=Afficher l’arborescence des fichiers diff.show_file_tree=Afficher l’arborescence des fichiers
diff.hide_file_tree=Masquer l’arborescence des fichiers diff.hide_file_tree=Masquer l’arborescence des fichiers
diff.submodule_added=Sous-module %[1]s ajouté à %[2]s
diff.submodule_deleted=Sous-module %[1]s supprimé de %[2]s
diff.submodule_updated=Sous-module %[1]s mis-à-jour : %[2]s
releases.desc=Suivi des publications et des téléchargements. releases.desc=Suivi des publications et des téléchargements.
release.releases=Publications release.releases=Publications
@ -3116,7 +3112,7 @@ auths.attribute_username_placeholder=Laisser vide afin d'utiliser le nom d'utili
auths.attribute_name=Attribut prénom auths.attribute_name=Attribut prénom
auths.attribute_surname=Attribut nom de famille auths.attribute_surname=Attribut nom de famille
auths.attribute_mail=Attribut e-mail auths.attribute_mail=Attribut e-mail
auths.attribute_ssh_public_key=Attribut clef SSH publique auths.attribute_ssh_public_key=Attribut clé SSH publique
auths.attribute_avatar=Attribut de l'avatar auths.attribute_avatar=Attribut de l'avatar
auths.attributes_in_bind=Aller chercher les attributs dans le contexte de liaison DN auths.attributes_in_bind=Aller chercher les attributs dans le contexte de liaison DN
auths.allow_deactivate_all=Permettre à un résultat de recherche vide de désactiver tous les utilisateurs auths.allow_deactivate_all=Permettre à un résultat de recherche vide de désactiver tous les utilisateurs
@ -3240,7 +3236,7 @@ config.ssh_port=Port
config.ssh_listen_port=Port d'écoute config.ssh_listen_port=Port d'écoute
config.ssh_root_path=Emplacement racine config.ssh_root_path=Emplacement racine
config.ssh_key_test_path=Chemin de test des clés config.ssh_key_test_path=Chemin de test des clés
config.ssh_keygen_path=Chemin vers le générateur de clefs ("ssh-keygen") config.ssh_keygen_path=Chemin vers le générateur de clés (« ssh-keygen »)
config.ssh_minimum_key_size_check=Vérification de la longueur de clé minimale config.ssh_minimum_key_size_check=Vérification de la longueur de clé minimale
config.ssh_minimum_key_sizes=Tailles de clé minimales config.ssh_minimum_key_sizes=Tailles de clé minimales
@ -3371,7 +3367,6 @@ monitor.execute_time=Heure d'Éxécution
monitor.last_execution_result=Résultat monitor.last_execution_result=Résultat
monitor.process.cancel=Annuler le processus monitor.process.cancel=Annuler le processus
monitor.process.cancel_desc=L’annulation d’un processus peut entraîner une perte de données. monitor.process.cancel_desc=L’annulation d’un processus peut entraîner une perte de données.
monitor.process.cancel_notices=Annuler : <strong>%s</strong> ?
monitor.process.children=Enfant monitor.process.children=Enfant
monitor.queues=Files d'attente monitor.queues=Files d'attente
@ -3568,7 +3563,6 @@ conda.install=Pour installer le paquet en utilisant Conda, exécutez la commande
container.details.type=Type d'image container.details.type=Type d'image
container.details.platform=Plateforme container.details.platform=Plateforme
container.pull=Tirez l'image depuis un terminal : container.pull=Tirez l'image depuis un terminal :
container.digest=Empreinte :
container.multi_arch=SE / Arch container.multi_arch=SE / Arch
container.layers=Calques d'image container.layers=Calques d'image
container.labels=Labels container.labels=Labels

@ -244,6 +244,7 @@ license_desc=Téigh go bhfaighidh <a target="_blank" rel="noopener noreferrer" h
[install] [install]
install=Suiteáil install=Suiteáil
installing_desc=Suiteáil anois, fan go fóill...
title=Cumraíocht Tosaigh title=Cumraíocht Tosaigh
docker_helper=Má ritheann tú Gitea taobh istigh de Docker, léigh an <a target="_blank" rel="noopener noreferrer" href="%s">doiciméadúchán</a> roimh aon socruithe a athrú. docker_helper=Má ritheann tú Gitea taobh istigh de Docker, léigh an <a target="_blank" rel="noopener noreferrer" href="%s">doiciméadúchán</a> roimh aon socruithe a athrú.
require_db_desc=Éilíonn Gitea MySQL, PostgreSQL, MSSQL, SQLite3 nó TiDB (prótacal MySQL). require_db_desc=Éilíonn Gitea MySQL, PostgreSQL, MSSQL, SQLite3 nó TiDB (prótacal MySQL).
@ -1015,6 +1016,9 @@ new_repo_helper=Tá gach comhad tionscadail i stór, lena n-áirítear stair ath
owner=Úinéir owner=Úinéir
owner_helper=B'fhéidir nach dtaispeánfar roinnt eagraíochtaí sa anuas mar gheall ar theorainn uasta comhaireamh stórais. owner_helper=B'fhéidir nach dtaispeánfar roinnt eagraíochtaí sa anuas mar gheall ar theorainn uasta comhaireamh stórais.
repo_name=Ainm Stórais repo_name=Ainm Stórais
repo_name_profile_public_hint=Is stóras speisialta é .profile is féidir leat a úsáid chun README.md a chur le do phróifíl eagraíochta poiblí, le feiceáil ag aon duine. Cinntigh go bhfuil sé poiblí agus tosaigh é le README san eolaire próifíle le tosú.
repo_name_profile_private_hint=Is stóras speisialta é .profile-private is féidir leat a úsáid chun README.md a chur le do phróifíl bhall eagraíochta, nach féidir a fheiceáil ach ag baill eagraíochta. Cinntigh go bhfuil sé príobháideach agus tosaigh le README sa eolaire próifíle chun tús a chur leis.
repo_name_helper=Úsáideann ainmneacha maith stóras focail eochair gairide, áithnid agus uathúla. D'fhéadfaí stóras darbh ainm '.profile' nó '.profile-private' a úsáid chun README.md a chur leis an bpróifíl úsáideora/eagraíochta.
repo_size=Méid an Stóras repo_size=Méid an Stóras
template=Teimpléad template=Teimpléad
template_select=Roghnaigh teimpléad. template_select=Roghnaigh teimpléad.
@ -1111,9 +1115,7 @@ blame.ignore_revs=Ag déanamh neamhairde de leasuithe i <a href="%s">.git-blame-
blame.ignore_revs.failed=Theip ar neamhaird a dhéanamh ar leasuithe i <a href="%s">.git-blame-ignore-revs</a>. blame.ignore_revs.failed=Theip ar neamhaird a dhéanamh ar leasuithe i <a href="%s">.git-blame-ignore-revs</a>.
user_search_tooltip=Taispeáint uasmhéid de 30 úsáideoir user_search_tooltip=Taispeáint uasmhéid de 30 úsáideoir
tree_path_not_found_commit=Níl cosán %[1]s ann i dtiomantas %[2]s tree_path_not_found=Níl cosán %[1]s ann i %[2]s
tree_path_not_found_branch=Níl cosán %[1]s ann i mbrainse %[2]s
tree_path_not_found_tag=Níl cosán %[1]s ann i gclib %[2]s
transfer.accept=Glac le hAistriú transfer.accept=Glac le hAistriú
transfer.accept_desc=Aistriú chuig “%s” transfer.accept_desc=Aistriú chuig “%s”
@ -1231,6 +1233,7 @@ create_new_repo_command=Stóras nua a chruthú ar an líne ordaithe
push_exist_repo=Stóras atá ann cheana a bhrú ón líne ordaithe push_exist_repo=Stóras atá ann cheana a bhrú ón líne ordaithe
empty_message=Níl aon ábhar sa stóras seo. empty_message=Níl aon ábhar sa stóras seo.
broken_message=Ní féidir na sonraí Git atá mar bhunús leis an stóras seo a léamh. Déan teagmháil le riarthóir an chás seo nó scrios an stóras seo. broken_message=Ní féidir na sonraí Git atá mar bhunús leis an stóras seo a léamh. Déan teagmháil le riarthóir an chás seo nó scrios an stóras seo.
no_branch=Níl aon bhrainsí ag an stóras seo.
code=Cód code=Cód
code.desc=Rochtain ar chód foinse, comhaid, gealltanais agus brainsí. code.desc=Rochtain ar chód foinse, comhaid, gealltanais agus brainsí.
@ -1646,7 +1649,7 @@ issues.attachment.open_tab=`Cliceáil chun "%s" a fheiceáil i gcluaisín nua`
issues.attachment.download=`Cliceáil chun "%s" a íoslódáil issues.attachment.download=`Cliceáil chun "%s" a íoslódáil
issues.subscribe=Liostáil issues.subscribe=Liostáil
issues.unsubscribe=Díliostáil issues.unsubscribe=Díliostáil
issues.unpin_issue=Bain pionna an t-eagrán issues.unpin=Díphoráil
issues.max_pinned=Ní féidir leat níos mó saincheisteanna a phionadh issues.max_pinned=Ní féidir leat níos mó saincheisteanna a phionadh
issues.pin_comment=phionnáil an %s seo issues.pin_comment=phionnáil an %s seo
issues.unpin_comment=bain pionna an %s seo issues.unpin_comment=bain pionna an %s seo
@ -1681,16 +1684,13 @@ issues.timetracker_timer_manually_add=Cuir Am leis
issues.time_estimate_set=Socraigh am measta issues.time_estimate_set=Socraigh am measta
issues.time_estimate_display=Meastachán: %s issues.time_estimate_display=Meastachán: %s
issues.change_time_estimate_at=d'athraigh an meastachán ama go <b>%s</b> %s
issues.remove_time_estimate_at=baineadh meastachán ama %s issues.remove_time_estimate_at=baineadh meastachán ama %s
issues.time_estimate_invalid=Tá formáid meastachán ama neamhbhailí issues.time_estimate_invalid=Tá formáid meastachán ama neamhbhailí
issues.start_tracking_history=thosaigh ag obair %s issues.start_tracking_history=thosaigh ag obair %s
issues.tracker_auto_close=Stopfar ama go huathoibríoch nuair a dhúnfar an tsaincheist seo issues.tracker_auto_close=Stopfar ama go huathoibríoch nuair a dhúnfar an tsaincheist seo
issues.tracking_already_started=`Tá tús curtha agat cheana féin ag rianú ama ar <a href="%s">eagrán eile</a>!` issues.tracking_already_started=`Tá tús curtha agat cheana féin ag rianú ama ar <a href="%s">eagrán eile</a>!`
issues.stop_tracking_history=d'oibrigh do <b>%s</b> %s
issues.cancel_tracking_history=`rianú ama curtha ar ceal %s` issues.cancel_tracking_history=`rianú ama curtha ar ceal %s`
issues.del_time=Scrios an log ama seo issues.del_time=Scrios an log ama seo
issues.add_time_history=cuireadh am caite <b>%s</b> %s leis
issues.del_time_history=`an t-am caite scriosta %s` issues.del_time_history=`an t-am caite scriosta %s`
issues.add_time_manually=Cuir Am leis de Láimh issues.add_time_manually=Cuir Am leis de Láimh
issues.add_time_hours=Uaireanta issues.add_time_hours=Uaireanta
@ -2154,7 +2154,6 @@ settings.advanced_settings=Ardsocruithe
settings.wiki_desc=Cumasaigh Stór Vicí settings.wiki_desc=Cumasaigh Stór Vicí
settings.use_internal_wiki=Úsáid Vicí Insuite settings.use_internal_wiki=Úsáid Vicí Insuite
settings.default_wiki_branch_name=Ainm Brainse Réamhshocraithe Vicí settings.default_wiki_branch_name=Ainm Brainse Réamhshocraithe Vicí
settings.default_wiki_everyone_access=Cead Rochtana Réamhshocraithe d'úsáideoirí sínithe isteach:
settings.failed_to_change_default_wiki_branch=Theip ar an brainse réamhshocraithe vicí a athrú. settings.failed_to_change_default_wiki_branch=Theip ar an brainse réamhshocraithe vicí a athrú.
settings.use_external_wiki=Úsáid Vicí Seachtrach settings.use_external_wiki=Úsáid Vicí Seachtrach
settings.external_wiki_url=URL Vicí Seachtrach settings.external_wiki_url=URL Vicí Seachtrach
@ -2622,6 +2621,9 @@ diff.image.overlay=Forleagan
diff.has_escaped=Tá carachtair Unicode i bhfolach ag an líne seo diff.has_escaped=Tá carachtair Unicode i bhfolach ag an líne seo
diff.show_file_tree=Taispeáin crann comhad diff.show_file_tree=Taispeáin crann comhad
diff.hide_file_tree=Folaigh crann comhad diff.hide_file_tree=Folaigh crann comhad
diff.submodule_added=Fomhodúl %[1]s curtha leis ag %[2]s
diff.submodule_deleted=Scriosadh fomhodúl %[1]s ó %[2]s
diff.submodule_updated=Nuashonraíodh fomhodúl %[1]s: %[2]s
releases.desc=Rian leaganacha tionscadal agus íoslódálacha. releases.desc=Rian leaganacha tionscadal agus íoslódálacha.
release.releases=Eisiúintí release.releases=Eisiúintí
@ -2860,6 +2862,9 @@ teams.invite.title=Tugadh cuireadh duit dul isteach i bhfoireann <strong>%s</str
teams.invite.by=Ar cuireadh ó %s teams.invite.by=Ar cuireadh ó %s
teams.invite.description=Cliceáil ar an gcnaipe thíos le do thoil chun dul isteach san fhoireann. teams.invite.description=Cliceáil ar an gcnaipe thíos le do thoil chun dul isteach san fhoireann.
view_as_role=Féach mar: %s
view_as_public_hint=Tá tú ag féachaint ar an README mar úsáideoir poiblí.
view_as_member_hint=Tá tú ag féachaint ar an README mar bhall den eagraíocht seo.
[admin] [admin]
maintenance=Cothabháil maintenance=Cothabháil
@ -3363,7 +3368,6 @@ monitor.execute_time=Am Forghníomhaithe
monitor.last_execution_result=Toradh monitor.last_execution_result=Toradh
monitor.process.cancel=Cealaigh próiseas monitor.process.cancel=Cealaigh próiseas
monitor.process.cancel_desc=Má chuirtear próiseas ar ceal d'fhéadfadh go gcaillfí sonraí monitor.process.cancel_desc=Má chuirtear próiseas ar ceal d'fhéadfadh go gcaillfí sonraí
monitor.process.cancel_notices=Cealaigh: <strong>%s</strong>?
monitor.process.children=Leanaí monitor.process.children=Leanaí
monitor.queues=Scuaineanna monitor.queues=Scuaineanna
@ -3530,6 +3534,7 @@ versions=Leaganacha
versions.view_all=Féach ar gach versions.view_all=Féach ar gach
dependency.id=ID dependency.id=ID
dependency.version=Leagan dependency.version=Leagan
search_in_external_registry=Cuardaigh i %s
alpine.registry=Socraigh an chlár seo tríd an url a chur i do chomhad <code>/etc/apk/repositories</code>: alpine.registry=Socraigh an chlár seo tríd an url a chur i do chomhad <code>/etc/apk/repositories</code>:
alpine.registry.key=Íoslódáil eochair RSA poiblí na clárlainne isteach san fhillteán <code>/etc/apk/keys/</code> chun an síniú innéacs a fhíorú: alpine.registry.key=Íoslódáil eochair RSA poiblí na clárlainne isteach san fhillteán <code>/etc/apk/keys/</code> chun an síniú innéacs a fhíorú:
alpine.registry.info=Roghnaigh $branch agus $repository ón liosta thíos. alpine.registry.info=Roghnaigh $branch agus $repository ón liosta thíos.
@ -3559,7 +3564,6 @@ conda.install=Chun an pacáiste a shuiteáil ag úsáid Conda, reáchtáil an t-
container.details.type=Cineál Íomhá container.details.type=Cineál Íomhá
container.details.platform=Ardán container.details.platform=Ardán
container.pull=Tarraing an íomhá ón líne ordaithe: container.pull=Tarraing an íomhá ón líne ordaithe:
container.digest=Díleáigh:
container.multi_arch=Córas Oibriúcháin / Ailtireacht container.multi_arch=Córas Oibriúcháin / Ailtireacht
container.layers=Sraitheanna Íomhá container.layers=Sraitheanna Íomhá
container.labels=Lipéid container.labels=Lipéid
@ -3755,6 +3759,7 @@ workflow.not_found=Níor aimsíodh sreabhadh oibre '%s'.
workflow.run_success=Ritheann sreabhadh oibre '%s' go rathúil. workflow.run_success=Ritheann sreabhadh oibre '%s' go rathúil.
workflow.from_ref=Úsáid sreabhadh oibre ó workflow.from_ref=Úsáid sreabhadh oibre ó
workflow.has_workflow_dispatch=Tá comhoibriú ag an gcur i bhfeidhm seo le himeacht workflow_dispatch. workflow.has_workflow_dispatch=Tá comhoibriú ag an gcur i bhfeidhm seo le himeacht workflow_dispatch.
workflow.has_no_workflow_dispatch=Níl aon truicear teagmhais workflow_dispatch ag sreabhadh oibre '%s'.
need_approval_desc=Teastaíonn faomhadh chun sreafaí oibre a rith le haghaidh iarratas tarraingt forc. need_approval_desc=Teastaíonn faomhadh chun sreafaí oibre a rith le haghaidh iarratas tarraingt forc.

@ -818,6 +818,7 @@ issues.attachment.open_tab=`Klik untuk melihat "%s" di tab baru`
issues.attachment.download=`Klik untuk mengunduh "%s"` issues.attachment.download=`Klik untuk mengunduh "%s"`
issues.subscribe=Berlangganan issues.subscribe=Berlangganan
issues.unsubscribe=Berhenti berlangganan issues.unsubscribe=Berhenti berlangganan
issues.unpin=Lepas sematan
issues.delete=Hapus issues.delete=Hapus

@ -1015,6 +1015,7 @@ new_repo_helper=リポジトリには、プロジェクトのすべてのファ
owner=オーナー owner=オーナー
owner_helper=リポジトリ数の上限により、一部の組織はドロップダウンに表示されない場合があります。 owner_helper=リポジトリ数の上限により、一部の組織はドロップダウンに表示されない場合があります。
repo_name=リポジトリ名 repo_name=リポジトリ名
repo_name_helper=リポジトリ名は、短く、覚えやすく、他と重複しないキーワードを使用しましょう。 リポジトリ名を ".profile" または ".profile-private" にして README.md を追加すると、ユーザーや組織のプロフィールとなります。
repo_size=リポジトリサイズ repo_size=リポジトリサイズ
template=テンプレート template=テンプレート
template_select=テンプレートを選択してください。 template_select=テンプレートを選択してください。
@ -1033,6 +1034,8 @@ fork_to_different_account=別のアカウントにフォークする
fork_visibility_helper=フォークしたリポジトリの公開/非公開は変更できません。 fork_visibility_helper=フォークしたリポジトリの公開/非公開は変更できません。
fork_branch=フォークにクローンされるブランチ fork_branch=フォークにクローンされるブランチ
all_branches=すべてのブランチ all_branches=すべてのブランチ
view_all_branches=すべてのブランチを表示
view_all_tags=すべてのタグを表示
fork_no_valid_owners=このリポジトリには有効なオーナーがいないため、フォークできません。 fork_no_valid_owners=このリポジトリには有効なオーナーがいないため、フォークできません。
fork.blocked_user=リポジトリのオーナーがあなたをブロックしているため、リポジトリをフォークできません。 fork.blocked_user=リポジトリのオーナーがあなたをブロックしているため、リポジトリをフォークできません。
use_template=このテンプレートを使用 use_template=このテンプレートを使用
@ -1107,10 +1110,8 @@ delete_preexisting_success=%s の未登録ファイルを削除しました
blame_prior=この変更より前のBlameを表示 blame_prior=この変更より前のBlameを表示
blame.ignore_revs=<a href="%s">.git-blame-ignore-revs</a> で指定されたリビジョンは除外しています。 これを迂回して通常のBlame表示を見るには <a href="%s">ここ</a>をクリック。 blame.ignore_revs=<a href="%s">.git-blame-ignore-revs</a> で指定されたリビジョンは除外しています。 これを迂回して通常のBlame表示を見るには <a href="%s">ここ</a>をクリック。
blame.ignore_revs.failed=<a href="%s">.git-blame-ignore-revs</a> によるリビジョンの無視は失敗しました。 blame.ignore_revs.failed=<a href="%s">.git-blame-ignore-revs</a> によるリビジョンの無視は失敗しました。
user_search_tooltip=最大30人までのユーザーを表示
tree_path_not_found_commit=パス %[1]s はコミット %[2]s に存在しません
tree_path_not_found_branch=パス %[1]s はブランチ %[2]s に存在しません
tree_path_not_found_tag=パス %[1]s はタグ %[2]s に存在しません
transfer.accept=移転を承認 transfer.accept=移転を承認
transfer.accept_desc=`"%s" に移転` transfer.accept_desc=`"%s" に移転`
@ -1228,6 +1229,7 @@ create_new_repo_command=コマンドラインから新しいリポジトリを
push_exist_repo=コマンドラインから既存のリポジトリをプッシュ push_exist_repo=コマンドラインから既存のリポジトリをプッシュ
empty_message=このリポジトリの中には何もありません。 empty_message=このリポジトリの中には何もありません。
broken_message=このリポジトリの基礎となる Git のデータを読み取れません。このインスタンスの管理者に相談するか、このリポジトリを削除してください。 broken_message=このリポジトリの基礎となる Git のデータを読み取れません。このインスタンスの管理者に相談するか、このリポジトリを削除してください。
no_branch=このリポジトリにはブランチがありません。
code=コード code=コード
code.desc=ソースコード、ファイル、コミット、ブランチにアクセス。 code.desc=ソースコード、ファイル、コミット、ブランチにアクセス。
@ -1525,6 +1527,8 @@ issues.filter_assignee=担当者
issues.filter_assginee_no_select=すべての担当者 issues.filter_assginee_no_select=すべての担当者
issues.filter_assginee_no_assignee=担当者なし issues.filter_assginee_no_assignee=担当者なし
issues.filter_poster=作成者 issues.filter_poster=作成者
issues.filter_user_placeholder=ユーザーを検索
issues.filter_user_no_select=すべてのユーザー
issues.filter_type=タイプ issues.filter_type=タイプ
issues.filter_type.all_issues=すべてのイシュー issues.filter_type.all_issues=すべてのイシュー
issues.filter_type.assigned_to_you=自分が担当 issues.filter_type.assigned_to_you=自分が担当
@ -1641,7 +1645,7 @@ issues.attachment.open_tab=`クリックして新しいタブで "%s" を見る`
issues.attachment.download=`クリックして "%s" をダウンロード` issues.attachment.download=`クリックして "%s" をダウンロード`
issues.subscribe=購読する issues.subscribe=購読する
issues.unsubscribe=購読を解除 issues.unsubscribe=購読を解除
issues.unpin_issue=イシューのピン留め解除 issues.unpin=ピン留め解除
issues.max_pinned=これ以上イシューをピン留めできません issues.max_pinned=これ以上イシューをピン留めできません
issues.pin_comment=がピン留め %s issues.pin_comment=がピン留め %s
issues.unpin_comment=がピン留めを解除 %s issues.unpin_comment=がピン留めを解除 %s
@ -1676,16 +1680,16 @@ issues.timetracker_timer_manually_add=時間を追加
issues.time_estimate_set=見積時間を設定 issues.time_estimate_set=見積時間を設定
issues.time_estimate_display=見積時間: %s issues.time_estimate_display=見積時間: %s
issues.change_time_estimate_at=が見積時間を <b>%s</b> に変更 %s issues.change_time_estimate_at=が見積時間を <b>%[1]s</b> に変更 %[2]s
issues.remove_time_estimate_at=が見積時間を削除 %s issues.remove_time_estimate_at=が見積時間を削除 %s
issues.time_estimate_invalid=見積時間のフォーマットが不正です issues.time_estimate_invalid=見積時間のフォーマットが不正です
issues.start_tracking_history=が作業を開始 %s issues.start_tracking_history=が作業を開始 %s
issues.tracker_auto_close=タイマーは、このイシューがクローズされると自動的に終了します issues.tracker_auto_close=タイマーは、このイシューがクローズされると自動的に終了します
issues.tracking_already_started=`<a href="%s">別のイシュー</a>で既にタイムトラッキングを開始しています!` issues.tracking_already_started=`<a href="%s">別のイシュー</a>で既にタイムトラッキングを開始しています!`
issues.stop_tracking_history=が <b>%s</b> の作業を終了 %s issues.stop_tracking_history=が <b>%[1]s</b> の作業を終了 %[2]s
issues.cancel_tracking_history=`がタイムトラッキングを中止 %s` issues.cancel_tracking_history=`がタイムトラッキングを中止 %s`
issues.del_time=このタイムログを削除 issues.del_time=このタイムログを削除
issues.add_time_history=が作業時間 <b>%s</b> を追加 %s issues.add_time_history=が作業時間 <b>%[1]s</b> を追加 %[2]s
issues.del_time_history=`が作業時間を削除 %s` issues.del_time_history=`が作業時間を削除 %s`
issues.add_time_manually=時間の手入力 issues.add_time_manually=時間の手入力
issues.add_time_hours=時間 issues.add_time_hours=時間
@ -1940,6 +1944,8 @@ pulls.delete.title=このプルリクエストを削除しますか?
pulls.delete.text=本当にこのプルリクエストを削除しますか? (これはすべてのコンテンツを完全に削除します。 保存しておきたい場合は、代わりにクローズすることを検討してください) pulls.delete.text=本当にこのプルリクエストを削除しますか? (これはすべてのコンテンツを完全に削除します。 保存しておきたい場合は、代わりにクローズすることを検討してください)
pulls.recently_pushed_new_branches=%[2]s 、あなたはブランチ <strong>%[1]s</strong> にプッシュしました pulls.recently_pushed_new_branches=%[2]s 、あなたはブランチ <strong>%[1]s</strong> にプッシュしました
pulls.upstream_diverging_prompt_behind_1=このブランチは %[2]s よりも %[1]d コミット遅れています
pulls.upstream_diverging_prompt_behind_n=このブランチは %[2]s よりも %[1]d コミット遅れています
pulls.upstream_diverging_prompt_base_newer=ベースブランチ %s に新しい変更があります pulls.upstream_diverging_prompt_base_newer=ベースブランチ %s に新しい変更があります
pulls.upstream_diverging_merge=フォークを同期 pulls.upstream_diverging_merge=フォークを同期
@ -2147,7 +2153,6 @@ settings.advanced_settings=拡張設定
settings.wiki_desc=Wikiを有効にする settings.wiki_desc=Wikiを有効にする
settings.use_internal_wiki=ビルトインのWikiを使用する settings.use_internal_wiki=ビルトインのWikiを使用する
settings.default_wiki_branch_name=デフォルトのWikiブランチ名 settings.default_wiki_branch_name=デフォルトのWikiブランチ名
settings.default_wiki_everyone_access=サインインユーザーのデフォルトのアクセス権限:
settings.failed_to_change_default_wiki_branch=デフォルトのWikiブランチを変更できませんでした。 settings.failed_to_change_default_wiki_branch=デフォルトのWikiブランチを変更できませんでした。
settings.use_external_wiki=外部のWikiを使用する settings.use_external_wiki=外部のWikiを使用する
settings.external_wiki_url=外部WikiのURL settings.external_wiki_url=外部WikiのURL
@ -2624,6 +2629,7 @@ release.new_release=新しいリリース
release.draft=下書き release.draft=下書き
release.prerelease=プレリリース release.prerelease=プレリリース
release.stable=安定版 release.stable=安定版
release.latest=最新
release.compare=比較 release.compare=比較
release.edit=編集 release.edit=編集
release.ahead.commits=<strong>%d</strong>件のコミット release.ahead.commits=<strong>%d</strong>件のコミット
@ -2852,6 +2858,9 @@ teams.invite.title=あなたは組織 <strong>%[2]s</strong> 内のチーム <st
teams.invite.by=%s からの招待 teams.invite.by=%s からの招待
teams.invite.description=下のボタンをクリックしてチームに参加してください。 teams.invite.description=下のボタンをクリックしてチームに参加してください。
view_as_role=表示: %s
view_as_public_hint=READMEを公開ユーザーとして見ています。
view_as_member_hint=READMEをこの組織のメンバーとして見ています。
[admin] [admin]
maintenance=メンテナンス maintenance=メンテナンス
@ -3355,7 +3364,6 @@ monitor.execute_time=実行時間
monitor.last_execution_result=結果 monitor.last_execution_result=結果
monitor.process.cancel=処理をキャンセル monitor.process.cancel=処理をキャンセル
monitor.process.cancel_desc=処理をキャンセルするとデータが失われる可能性があります monitor.process.cancel_desc=処理をキャンセルするとデータが失われる可能性があります
monitor.process.cancel_notices=キャンセル: <strong>%s</strong>?
monitor.process.children=子プロセス monitor.process.children=子プロセス
monitor.queues=キュー monitor.queues=キュー
@ -3530,6 +3538,8 @@ alpine.repository=リポジトリ情報
alpine.repository.branches=Branches alpine.repository.branches=Branches
alpine.repository.repositories=Repositories alpine.repository.repositories=Repositories
alpine.repository.architectures=Architectures alpine.repository.architectures=Architectures
arch.registry=<code>/etc/pacman.conf</code> にリポジトリとアーキテクチャを含めてサーバーを追加します:
arch.install=pacmanでパッケージを同期します:
arch.repository=リポジトリ情報 arch.repository=リポジトリ情報
arch.repository.repositories=リポジトリ arch.repository.repositories=リポジトリ
arch.repository.architectures=Architectures arch.repository.architectures=Architectures
@ -3549,7 +3559,7 @@ conda.install=Conda を使用してパッケージをインストールするに
container.details.type=イメージタイプ container.details.type=イメージタイプ
container.details.platform=プラットフォーム container.details.platform=プラットフォーム
container.pull=コマンドラインでイメージを取得します: container.pull=コマンドラインでイメージを取得します:
container.digest=ダイジェスト: container.digest=ダイジェスト
container.multi_arch=OS / アーキテクチャ container.multi_arch=OS / アーキテクチャ
container.layers=イメージレイヤー container.layers=イメージレイヤー
container.labels=ラベル container.labels=ラベル
@ -3712,6 +3722,7 @@ runners.status.active=稼働中
runners.status.offline=オフライン runners.status.offline=オフライン
runners.version=バージョン runners.version=バージョン
runners.reset_registration_token=登録トークンをリセット runners.reset_registration_token=登録トークンをリセット
runners.reset_registration_token_confirm=現在のトークンを無効にして、新しいトークンを生成しますか?
runners.reset_registration_token_success=ランナー登録トークンをリセットしました runners.reset_registration_token_success=ランナー登録トークンをリセットしました
runs.all_workflows=すべてのワークフロー runs.all_workflows=すべてのワークフロー

@ -997,9 +997,6 @@ blame_prior=Aplūkot vainīgo par izmaiņām pirms šīs revīzijas
blame.ignore_revs=Neņem vērā izmaiņas no <a href="%s">.git-blame-ignore-revs</a>. Nospiediet <a href="%s">šeit, lai to apietu</a> un redzētu visu izmaiņu skatu. blame.ignore_revs=Neņem vērā izmaiņas no <a href="%s">.git-blame-ignore-revs</a>. Nospiediet <a href="%s">šeit, lai to apietu</a> un redzētu visu izmaiņu skatu.
blame.ignore_revs.failed=Neizdevās neņemt vērā izmaiņas no <a href="%s">.git-blam-ignore-revs</a>. blame.ignore_revs.failed=Neizdevās neņemt vērā izmaiņas no <a href="%s">.git-blam-ignore-revs</a>.
tree_path_not_found_commit=Revīzijā %[2]s neeksistē ceļš %[1]s
tree_path_not_found_branch=Atzarā %[2]s nepastāv ceļš %[1]s
tree_path_not_found_tag=Tagā %[2]s nepastāv ceļš %[1]s
transfer.accept=Apstiprināt īpašnieka maiņu transfer.accept=Apstiprināt īpašnieka maiņu
transfer.reject=Noraidīt īpašnieka maiņu transfer.reject=Noraidīt īpašnieka maiņu
@ -1501,7 +1498,7 @@ issues.attachment.open_tab=`Noklikšķiniet, lai apskatītos "%s" jaunā logā`
issues.attachment.download=`Noklikšķiniet, lai lejupielādētu "%s"` issues.attachment.download=`Noklikšķiniet, lai lejupielādētu "%s"`
issues.subscribe=Abonēt issues.subscribe=Abonēt
issues.unsubscribe=Atrakstīties issues.unsubscribe=Atrakstīties
issues.unpin_issue=Atspraust problēmu issues.unpin=Atspraust
issues.max_pinned=Nevar piespraust vairāk problēmas issues.max_pinned=Nevar piespraust vairāk problēmas
issues.pin_comment=piesprauda šo %s issues.pin_comment=piesprauda šo %s
issues.unpin_comment=atsprauda šo %s issues.unpin_comment=atsprauda šo %s
@ -3242,7 +3239,6 @@ conda.install=Lai instalētu Conda pakotni, izpildiet sekojošu komandu:
container.details.type=Attēla formāts container.details.type=Attēla formāts
container.details.platform=Platforma container.details.platform=Platforma
container.pull=Atgādājiet šo attēlu no komandrindas: container.pull=Atgādājiet šo attēlu no komandrindas:
container.digest=Īssavilkums:
container.multi_arch=OS / arhitektūra container.multi_arch=OS / arhitektūra
container.layers=Attēla slāņi container.layers=Attēla slāņi
container.labels=Iezīmes container.labels=Iezīmes

@ -2310,7 +2310,6 @@ monitor.start=Czas rozpoczęcia
monitor.execute_time=Czas wykonania monitor.execute_time=Czas wykonania
monitor.process.cancel=Anuluj proces monitor.process.cancel=Anuluj proces
monitor.process.cancel_desc=Anulowanie procesu może spowodować utratę danych monitor.process.cancel_desc=Anulowanie procesu może spowodować utratę danych
monitor.process.cancel_notices=Anuluj: <strong>%s</strong>?
monitor.queues=Kolejki monitor.queues=Kolejki
monitor.queue=Kolejka: %s monitor.queue=Kolejka: %s

@ -1491,7 +1491,7 @@ issues.attachment.open_tab=`Clique para ver "%s" em uma nova aba`
issues.attachment.download=`Clique para baixar "%s"` issues.attachment.download=`Clique para baixar "%s"`
issues.subscribe=Inscrever-se issues.subscribe=Inscrever-se
issues.unsubscribe=Desinscrever issues.unsubscribe=Desinscrever
issues.unpin_issue=Desfixar issue issues.unpin=Desfixar
issues.max_pinned=Você não pode fixar mais issues issues.max_pinned=Você não pode fixar mais issues
issues.pin_comment=fixou isto %s issues.pin_comment=fixou isto %s
issues.unpin_comment=desafixou isto %s issues.unpin_comment=desafixou isto %s
@ -3180,7 +3180,6 @@ conda.install=Para instalar o pacote usando o Conda, execute o seguinte comando:
container.details.type=Tipo de Imagem container.details.type=Tipo de Imagem
container.details.platform=Plataforma container.details.platform=Plataforma
container.pull=Puxe a imagem pela linha de comando: container.pull=Puxe a imagem pela linha de comando:
container.digest=Digest:
container.multi_arch=S.O. / Arquitetura container.multi_arch=S.O. / Arquitetura
container.layers=Camadas da Imagem container.layers=Camadas da Imagem
container.labels=Rótulos container.labels=Rótulos

@ -261,7 +261,7 @@ path=Localização
sqlite_helper=Localização do ficheiro da base de dados em SQLite3.<br>Insira um caminho absoluto se corre o Gitea como um serviço. sqlite_helper=Localização do ficheiro da base de dados em SQLite3.<br>Insira um caminho absoluto se corre o Gitea como um serviço.
reinstall_error=Está a tentar instalar numa base de dados do Gitea já existente reinstall_error=Está a tentar instalar numa base de dados do Gitea já existente
reinstall_confirm_message=Reinstalar com uma base de dados do Gitea já existente pode causar múltiplos problemas. Na maioria dos casos deve usar o seu "app.ini" existente para correr o Gitea. Se souber o que está a fazer, confirme o seguinte: reinstall_confirm_message=Reinstalar com uma base de dados do Gitea já existente pode causar múltiplos problemas. Na maioria dos casos deve usar o seu "app.ini" existente para correr o Gitea. Se souber o que está a fazer, confirme o seguinte:
reinstall_confirm_check_1=Os dados encriptados pela chave secreta (SECRET_KEY) no ficheiro app.ini poderão ser perdidos: utilizadores poderão não ser capazes de iniciar a sessão com autenticação em dois passos (2FA) ou com chaves de utilização única (OTP) e as réplicas poderão deixar de funcionar em condições. Ao marcar esta opção estará a confirmar que o ficheiro app.ini vigente contém a SECRET_KEY certa. reinstall_confirm_check_1=Os dados encriptados pela chave secreta (SECRET_KEY) no ficheiro app.ini poderão ser perdidos: utilizadores poderão não ser capazes de iniciar a sessão com autenticação em dois passos (2FA) ou com chaves de utilização única (OTP) e as réplicas poderão deixar de funcionar em boas condições. Ao marcar esta opção estará a confirmar que o ficheiro app.ini vigente contém a SECRET_KEY certa.
reinstall_confirm_check_2=Os repositórios e as configurações poderão ter de voltar a ser sincronizados. Ao marcar esta opção estará a confirmar que vai voltar a sincronizar manualmente os automatismos para os repositórios e o ficheiro authorized_keys. Estará também a confirmar que vai assegurar que as configurações do repositório e das réplicas estão em condições. reinstall_confirm_check_2=Os repositórios e as configurações poderão ter de voltar a ser sincronizados. Ao marcar esta opção estará a confirmar que vai voltar a sincronizar manualmente os automatismos para os repositórios e o ficheiro authorized_keys. Estará também a confirmar que vai assegurar que as configurações do repositório e das réplicas estão em condições.
reinstall_confirm_check_3=Você confirma que tem a certeza absoluta de que este Gitea está a correr com a localização certa do ficheiro app.ini e que tem a certeza de que tem de voltar a instalar. Você confirma que tomou conhecimento dos riscos acima descritos. reinstall_confirm_check_3=Você confirma que tem a certeza absoluta de que este Gitea está a correr com a localização certa do ficheiro app.ini e que tem a certeza de que tem de voltar a instalar. Você confirma que tomou conhecimento dos riscos acima descritos.
err_empty_db_path=A localização da base de dados SQLite3 não pode estar vazia. err_empty_db_path=A localização da base de dados SQLite3 não pode estar vazia.
@ -1051,7 +1051,7 @@ generate_from=Gerar a partir de
repo_desc=Descrição repo_desc=Descrição
repo_desc_helper=Insira uma descrição curta (opcional) repo_desc_helper=Insira uma descrição curta (opcional)
repo_no_desc=Descrição não fornecida repo_no_desc=Descrição não fornecida
repo_lang=Idiomas repo_lang=Linguagens
repo_gitignore_helper=Escolher modelos .gitignore. repo_gitignore_helper=Escolher modelos .gitignore.
repo_gitignore_helper_desc=Escolha os ficheiros que não são para rastrear, a partir de uma lista de modelos de linguagens comuns. Serão incluídos no ficheiro .gitignore, logo à partida, artefactos típicos gerados pelas ferramentas de construção de cada uma das linguagens. repo_gitignore_helper_desc=Escolha os ficheiros que não são para rastrear, a partir de uma lista de modelos de linguagens comuns. Serão incluídos no ficheiro .gitignore, logo à partida, artefactos típicos gerados pelas ferramentas de construção de cada uma das linguagens.
issue_labels=Rótulos para as questões issue_labels=Rótulos para as questões
@ -1115,9 +1115,7 @@ blame.ignore_revs=Ignorando as revisões em <a href="%s">.git-blame-ignore-revs<
blame.ignore_revs.failed=Falhou ao ignorar as revisões em <a href="%s">.git-blame-ignore-revs</a>. blame.ignore_revs.failed=Falhou ao ignorar as revisões em <a href="%s">.git-blame-ignore-revs</a>.
user_search_tooltip=Mostra um máximo de 30 utilizadores user_search_tooltip=Mostra um máximo de 30 utilizadores
tree_path_not_found_commit=A localização %[1]s não existe no cometimento %[2]s tree_path_not_found=A localização %[1]s não existe em %[2]s
tree_path_not_found_branch=A localização %[1]s não existe no ramo %[2]s
tree_path_not_found_tag=A localização %[1]s não existe na etiqueta %[2]s
transfer.accept=Aceitar transferência transfer.accept=Aceitar transferência
transfer.accept_desc=`Transferir para "%s"` transfer.accept_desc=`Transferir para "%s"`
@ -1651,7 +1649,7 @@ issues.attachment.open_tab=`Clique para ver "%s" num separador novo`
issues.attachment.download=`Clique para descarregar "%s"` issues.attachment.download=`Clique para descarregar "%s"`
issues.subscribe=Subscrever issues.subscribe=Subscrever
issues.unsubscribe=Anular subscrição issues.unsubscribe=Anular subscrição
issues.unpin_issue=Desafixar questão issues.unpin=Desafixar
issues.max_pinned=Já não pode fixar mais questões issues.max_pinned=Já não pode fixar mais questões
issues.pin_comment=fixou isto %s issues.pin_comment=fixou isto %s
issues.unpin_comment=desafixou isto %s issues.unpin_comment=desafixou isto %s
@ -1686,16 +1684,16 @@ issues.timetracker_timer_manually_add=Adicionar tempo
issues.time_estimate_set=Definir tempo estimado issues.time_estimate_set=Definir tempo estimado
issues.time_estimate_display=Estimativa: %s issues.time_estimate_display=Estimativa: %s
issues.change_time_estimate_at=alterou a estimativa de tempo para <b>%s</b> %s issues.change_time_estimate_at=alterou a estimativa de tempo para <b>%[1]s</b> %[2]s
issues.remove_time_estimate_at=removeu a estimativa de tempo %s issues.remove_time_estimate_at=removeu a estimativa de tempo %s
issues.time_estimate_invalid=O formato da estimativa de tempo é inválido issues.time_estimate_invalid=O formato da estimativa de tempo é inválido
issues.start_tracking_history=começou a trabalhar %s issues.start_tracking_history=começou a trabalhar %s
issues.tracker_auto_close=O cronómetro será parado automaticamente quando esta questão for fechada issues.tracker_auto_close=O cronómetro será parado automaticamente quando esta questão for fechada
issues.tracking_already_started=`Você já iniciou a contagem de tempo <a href="%s">noutra questão</a>!` issues.tracking_already_started=`Você já iniciou a contagem de tempo <a href="%s">noutra questão</a>!`
issues.stop_tracking_history=trabalhou durante <b>%s</b> %s issues.stop_tracking_history=trabalhou durante <b>%[1]s</b> %[2]s
issues.cancel_tracking_history=`cancelou a contagem de tempo %s` issues.cancel_tracking_history=`cancelou a contagem de tempo %s`
issues.del_time=Eliminar este registo de tempo issues.del_time=Eliminar este registo de tempo
issues.add_time_history=adicionou <b>%s</b> de tempo gasto %s issues.add_time_history=adicionou <b>%[1]s</b> de tempo gasto %[2]s
issues.del_time_history=`eliminou o tempo gasto nesta questão %s` issues.del_time_history=`eliminou o tempo gasto nesta questão %s`
issues.add_time_manually=Adicionar tempo manualmente issues.add_time_manually=Adicionar tempo manualmente
issues.add_time_hours=Horas issues.add_time_hours=Horas
@ -2159,7 +2157,7 @@ settings.advanced_settings=Configurações avançadas
settings.wiki_desc=Habilitar wiki do repositório settings.wiki_desc=Habilitar wiki do repositório
settings.use_internal_wiki=Usar o wiki integrado settings.use_internal_wiki=Usar o wiki integrado
settings.default_wiki_branch_name=Nome do ramo predefinido do wiki settings.default_wiki_branch_name=Nome do ramo predefinido do wiki
settings.default_wiki_everyone_access=Permissão de acesso predefinida para utilizadores registados: settings.default_permission_everyone_access=Permissão de acesso predefinida para todos os utilizadores registados:
settings.failed_to_change_default_wiki_branch=Falhou ao mudar o nome do ramo predefinido do wiki. settings.failed_to_change_default_wiki_branch=Falhou ao mudar o nome do ramo predefinido do wiki.
settings.use_external_wiki=Usar um wiki externo settings.use_external_wiki=Usar um wiki externo
settings.external_wiki_url=URL do wiki externo settings.external_wiki_url=URL do wiki externo
@ -2714,6 +2712,8 @@ branch.create_branch_operation=Criar ramo
branch.new_branch=Criar um novo ramo branch.new_branch=Criar um novo ramo
branch.new_branch_from=`Criar um novo ramo a partir do ramo "%s"` branch.new_branch_from=`Criar um novo ramo a partir do ramo "%s"`
branch.renamed=O ramo %s foi renomeado para %s. branch.renamed=O ramo %s foi renomeado para %s.
branch.rename_default_or_protected_branch_error=Só os administradores é que podem renomear o ramo principal ou ramos protegidos.
branch.rename_protected_branch_failed=Este ramo está protegido por regras de salvaguarda baseadas em padrões glob.
tag.create_tag=Criar etiqueta %s tag.create_tag=Criar etiqueta %s
tag.create_tag_operation=Criar etiqueta tag.create_tag_operation=Criar etiqueta
@ -3374,7 +3374,6 @@ monitor.execute_time=Tempo de execução
monitor.last_execution_result=Resultado monitor.last_execution_result=Resultado
monitor.process.cancel=Cancelar processo monitor.process.cancel=Cancelar processo
monitor.process.cancel_desc=Cancelar um processo pode resultar na perda de dados monitor.process.cancel_desc=Cancelar um processo pode resultar na perda de dados
monitor.process.cancel_notices=Cancelar: <strong>%s</strong>?
monitor.process.children=Descendentes monitor.process.children=Descendentes
monitor.queues=Filas monitor.queues=Filas
@ -3571,7 +3570,8 @@ conda.install=Para instalar o pacote usando o Conda, execute o seguinte comando:
container.details.type=Tipo de imagem container.details.type=Tipo de imagem
container.details.platform=Plataforma container.details.platform=Plataforma
container.pull=Puxar a imagem usando a linha de comandos: container.pull=Puxar a imagem usando a linha de comandos:
container.digest=Resumo: container.images=Imagens
container.digest=Resumo
container.multi_arch=S.O. / Arquit. container.multi_arch=S.O. / Arquit.
container.layers=Camadas de imagem container.layers=Camadas de imagem
container.labels=Rótulos container.labels=Rótulos

@ -978,8 +978,6 @@ delete_preexisting_content=Удалить файлы из %s
delete_preexisting_success=Удалены непринятые файлы в %s delete_preexisting_success=Удалены непринятые файлы в %s
blame_prior=Показать авторство предшествующих изменений blame_prior=Показать авторство предшествующих изменений
tree_path_not_found_commit=Путь %[1]s не существует в коммите %[2]s
tree_path_not_found_branch=Путь %[1]s не существует в ветке %[2]s
transfer.accept=Принять трансфер transfer.accept=Принять трансфер
transfer.reject=Отказаться от перемещения transfer.reject=Отказаться от перемещения
@ -1471,7 +1469,7 @@ issues.attachment.open_tab=`Нажмите, чтобы увидеть «%s» в
issues.attachment.download=`Нажмите, чтобы скачать «%s»` issues.attachment.download=`Нажмите, чтобы скачать «%s»`
issues.subscribe=Подписаться issues.subscribe=Подписаться
issues.unsubscribe=Отказаться от подписки issues.unsubscribe=Отказаться от подписки
issues.unpin_issue=Открепить задачу issues.unpin=Открепить
issues.max_pinned=Нельзя закрепить больше задач issues.max_pinned=Нельзя закрепить больше задач
issues.pin_comment=закрепил(а) эту задачу %s issues.pin_comment=закрепил(а) эту задачу %s
issues.unpin_comment=открепил(а) эту задачу %s issues.unpin_comment=открепил(а) эту задачу %s
@ -3178,7 +3176,6 @@ conda.install=Чтобы установить пакет с помощью Conda
container.details.type=Тип образа container.details.type=Тип образа
container.details.platform=Платформа container.details.platform=Платформа
container.pull=Загрузите образ из командной строки: container.pull=Загрузите образ из командной строки:
container.digest=Отпечаток:
container.multi_arch=ОС / архитектура container.multi_arch=ОС / архитектура
container.layers=Слои образа container.layers=Слои образа
container.labels=Метки container.labels=Метки

@ -1068,6 +1068,7 @@ issues.dismiss_review=Zamietnuť revíziu
issues.dismiss_review_warning=Naozaj chcete zrušiť túto revíziu? issues.dismiss_review_warning=Naozaj chcete zrušiť túto revíziu?
issues.cancel=Zrušiť issues.cancel=Zrušiť
issues.save=Uložiť issues.save=Uložiť
issues.unpin=Odopnúť

@ -1083,9 +1083,6 @@ blame_prior=Bu değişiklikten önceki suçu görüntüle
blame.ignore_revs=<a href="%s">.git-blame-ignore-revs</a> dosyasındaki sürümler yok sayılıyor. Bunun yerine normal sorumlu görüntüsü için <a href="%s">buraya tıklayın</a>. blame.ignore_revs=<a href="%s">.git-blame-ignore-revs</a> dosyasındaki sürümler yok sayılıyor. Bunun yerine normal sorumlu görüntüsü için <a href="%s">buraya tıklayın</a>.
blame.ignore_revs.failed=<a href="%s">.git-blame-ignore-revs</a> dosyasındaki sürümler yok sayılamadı. blame.ignore_revs.failed=<a href="%s">.git-blame-ignore-revs</a> dosyasındaki sürümler yok sayılamadı.
tree_path_not_found_commit=%[1] yolu, %[2]s işlemesinde mevcut değil
tree_path_not_found_branch=%[1] yolu, %[2]s dalında mevcut değil
tree_path_not_found_tag=%[1] yolu, %[2]s etiketinde mevcut değil
transfer.accept=Aktarımı Kabul Et transfer.accept=Aktarımı Kabul Et
transfer.reject=Aktarımı Reddet transfer.reject=Aktarımı Reddet
@ -1605,7 +1602,7 @@ issues.attachment.open_tab=`Yeni bir sekmede "%s" görmek için tıkla`
issues.attachment.download=`"%s" indirmek için tıkla` issues.attachment.download=`"%s" indirmek için tıkla`
issues.subscribe=Abone Ol issues.subscribe=Abone Ol
issues.unsubscribe=Abonelikten Çık issues.unsubscribe=Abonelikten Çık
issues.unpin_issue=Konuyu Sabitlemeden Kaldır issues.unpin=Sabitlemeyi kaldır
issues.max_pinned=Daha fazla konuyu sabitleyemezsiniz issues.max_pinned=Daha fazla konuyu sabitleyemezsiniz
issues.pin_comment=%s sabitlendi issues.pin_comment=%s sabitlendi
issues.unpin_comment=%s sabitlenmesi kaldırıldı issues.unpin_comment=%s sabitlenmesi kaldırıldı
@ -2083,7 +2080,6 @@ settings.advanced_settings=Gelişmiş Ayarlar
settings.wiki_desc=Depo Wiki'sini Etkinkleştir settings.wiki_desc=Depo Wiki'sini Etkinkleştir
settings.use_internal_wiki=Dahili Wiki Kullan settings.use_internal_wiki=Dahili Wiki Kullan
settings.default_wiki_branch_name=Varsayılan Viki Dal Adı settings.default_wiki_branch_name=Varsayılan Viki Dal Adı
settings.default_wiki_everyone_access=Oturum açmış kullanıcılar için Varsayılan Erişim İzinleri:
settings.failed_to_change_default_wiki_branch=Varsayılan viki dalı değiştirilemedi. settings.failed_to_change_default_wiki_branch=Varsayılan viki dalı değiştirilemedi.
settings.use_external_wiki=Harici Wiki Kullan settings.use_external_wiki=Harici Wiki Kullan
settings.external_wiki_url=Harici Wiki bağlantısı settings.external_wiki_url=Harici Wiki bağlantısı
@ -3434,7 +3430,6 @@ conda.install=Conda ile paket kurmak için aşağıdaki komutu çalıştırın:
container.details.type=Görüntü Türü container.details.type=Görüntü Türü
container.details.platform=Platform container.details.platform=Platform
container.pull=Görüntüyü komut satırını kullanarak çekin: container.pull=Görüntüyü komut satırını kullanarak çekin:
container.digest=Özet:
container.multi_arch=İşletim Sistemi / Mimari container.multi_arch=İşletim Sistemi / Mimari
container.layers=Görüntü Katmanları container.layers=Görüntü Katmanları
container.labels=Etiketler container.labels=Etiketler

@ -1111,9 +1111,6 @@ blame.ignore_revs=忽略 <a href="%s">.git-blame-ignore-revs</a> 的修订。点
blame.ignore_revs.failed=忽略 <a href="%s">.git-blame-ignore-revs</a> 版本失败。 blame.ignore_revs.failed=忽略 <a href="%s">.git-blame-ignore-revs</a> 版本失败。
user_search_tooltip=最多显示30名用户 user_search_tooltip=最多显示30名用户
tree_path_not_found_commit=路径%[1]s 在提交 %[2]s 中不存在
tree_path_not_found_branch=路径 %[1]s 不存在于分支 %[2]s 中。
tree_path_not_found_tag=路径 %[1]s 不存在于标签 %[2]s 中
transfer.accept=接受转移 transfer.accept=接受转移
transfer.accept_desc=`转移到 "%s"` transfer.accept_desc=`转移到 "%s"`
@ -1646,7 +1643,7 @@ issues.attachment.open_tab=`在新的标签页中查看 '%s'`
issues.attachment.download=`点击下载 '%s'` issues.attachment.download=`点击下载 '%s'`
issues.subscribe=订阅 issues.subscribe=订阅
issues.unsubscribe=取消订阅 issues.unsubscribe=取消订阅
issues.unpin_issue=取消置顶 issues.unpin=取消置顶
issues.max_pinned=您不能置顶更多工单 issues.max_pinned=您不能置顶更多工单
issues.pin_comment=于 %s 被置顶 issues.pin_comment=于 %s 被置顶
issues.unpin_comment=于 %s 取消置顶 issues.unpin_comment=于 %s 取消置顶
@ -1681,16 +1678,13 @@ issues.timetracker_timer_manually_add=添加时间
issues.time_estimate_set=设置预计时间 issues.time_estimate_set=设置预计时间
issues.time_estimate_display=预计: %s issues.time_estimate_display=预计: %s
issues.change_time_estimate_at=将预计时间修改为 <b>%s</b> %s
issues.remove_time_estimate_at=删除预计时间 %s issues.remove_time_estimate_at=删除预计时间 %s
issues.time_estimate_invalid=预计时间格式无效 issues.time_estimate_invalid=预计时间格式无效
issues.start_tracking_history=`开始工作 %s` issues.start_tracking_history=`开始工作 %s`
issues.tracker_auto_close=当此工单关闭时,自动停止计时器 issues.tracker_auto_close=当此工单关闭时,自动停止计时器
issues.tracking_already_started=`你已经开始对 <a href="%s">另一个工单</a> 进行时间跟踪!` issues.tracking_already_started=`你已经开始对 <a href="%s">另一个工单</a> 进行时间跟踪!`
issues.stop_tracking_history=`停止工作 %s`
issues.cancel_tracking_history=`取消时间跟踪 %s` issues.cancel_tracking_history=`取消时间跟踪 %s`
issues.del_time=删除此时间跟踪日志 issues.del_time=删除此时间跟踪日志
issues.add_time_history=`添加计时 %s`
issues.del_time_history=`已删除时间 %s` issues.del_time_history=`已删除时间 %s`
issues.add_time_manually=手动添加时间 issues.add_time_manually=手动添加时间
issues.add_time_hours=小时 issues.add_time_hours=小时
@ -2154,7 +2148,6 @@ settings.advanced_settings=高级设置
settings.wiki_desc=启用仓库百科 settings.wiki_desc=启用仓库百科
settings.use_internal_wiki=使用内置百科 settings.use_internal_wiki=使用内置百科
settings.default_wiki_branch_name=默认百科分支名称 settings.default_wiki_branch_name=默认百科分支名称
settings.default_wiki_everyone_access=登录用户的默认访问权限:
settings.failed_to_change_default_wiki_branch=更改百科默认分支失败。 settings.failed_to_change_default_wiki_branch=更改百科默认分支失败。
settings.use_external_wiki=使用外部百科 settings.use_external_wiki=使用外部百科
settings.external_wiki_url=外部 Wiki 链接 settings.external_wiki_url=外部 Wiki 链接
@ -3363,7 +3356,6 @@ monitor.execute_time=执行时长
monitor.last_execution_result=结果 monitor.last_execution_result=结果
monitor.process.cancel=中止进程 monitor.process.cancel=中止进程
monitor.process.cancel_desc=中止一个进程可能导致数据丢失 monitor.process.cancel_desc=中止一个进程可能导致数据丢失
monitor.process.cancel_notices=中止:<strong>%s</strong> ?
monitor.process.children=子进程 monitor.process.children=子进程
monitor.queues=队列 monitor.queues=队列
@ -3559,7 +3551,6 @@ conda.install=要使用 Conda 安装软件包,请运行以下命令:
container.details.type=镜像类型 container.details.type=镜像类型
container.details.platform=平台 container.details.platform=平台
container.pull=从命令行拉取镜像: container.pull=从命令行拉取镜像:
container.digest=摘要:
container.multi_arch=OS / Arch container.multi_arch=OS / Arch
container.layers=镜像层 container.layers=镜像层
container.labels=标签 container.labels=标签

@ -1108,9 +1108,6 @@ blame.ignore_revs=忽略 <a href="%s">.git-blame-ignore-revs</a> 中的修訂。
blame.ignore_revs.failed=忽略 <a href="%s">.git-blame-ignore-revs</a> 中的修訂失敗。 blame.ignore_revs.failed=忽略 <a href="%s">.git-blame-ignore-revs</a> 中的修訂失敗。
user_search_tooltip=顯示最多 30 個使用者 user_search_tooltip=顯示最多 30 個使用者
tree_path_not_found_commit=路徑 %[1]s 在提交 %[2]s 中不存在
tree_path_not_found_branch=路徑 %[1]s 在分支 %[2]s 中不存在
tree_path_not_found_tag=路徑 %[1]s 在標籤 %[2]s 中不存在
transfer.accept=同意轉移 transfer.accept=同意轉移
transfer.accept_desc=轉移到「%s」 transfer.accept_desc=轉移到「%s」
@ -1640,7 +1637,7 @@ issues.attachment.open_tab=`在新分頁中查看「%s」`
issues.attachment.download=`點擊下載「%s」` issues.attachment.download=`點擊下載「%s」`
issues.subscribe=訂閱 issues.subscribe=訂閱
issues.unsubscribe=取消訂閱 issues.unsubscribe=取消訂閱
issues.unpin_issue=取消固定問題 issues.unpin=取消固定
issues.max_pinned=您不能固定更多問題 issues.max_pinned=您不能固定更多問題
issues.pin_comment=固定於 %s issues.pin_comment=固定於 %s
issues.unpin_comment=取消固定於 %s issues.unpin_comment=取消固定於 %s
@ -1675,16 +1672,13 @@ issues.timetracker_timer_manually_add=手動新增時間
issues.time_estimate_set=設定預估時間 issues.time_estimate_set=設定預估時間
issues.time_estimate_display=預估時間:%s issues.time_estimate_display=預估時間:%s
issues.change_time_estimate_at=將預估時間更改為 <b>%s</b> %s
issues.remove_time_estimate_at=移除預估時間 %s issues.remove_time_estimate_at=移除預估時間 %s
issues.time_estimate_invalid=預估時間格式無效 issues.time_estimate_invalid=預估時間格式無效
issues.start_tracking_history=`開始工作 %s` issues.start_tracking_history=`開始工作 %s`
issues.tracker_auto_close=當這個問題被關閉時,自動停止計時器 issues.tracker_auto_close=當這個問題被關閉時,自動停止計時器
issues.tracking_already_started=`您已在<a href="%s">另一個問題</a>上開始時間追蹤!` issues.tracking_already_started=`您已在<a href="%s">另一個問題</a>上開始時間追蹤!`
issues.stop_tracking_history=`結束工作 %s`
issues.cancel_tracking_history=`取消時間追蹤 %s` issues.cancel_tracking_history=`取消時間追蹤 %s`
issues.del_time=刪除此時間記錄 issues.del_time=刪除此時間記錄
issues.add_time_history=`加入了花費時間 %s`
issues.del_time_history=`刪除了花費時間 %s` issues.del_time_history=`刪除了花費時間 %s`
issues.add_time_manually=手動新增時間 issues.add_time_manually=手動新增時間
issues.add_time_hours=小時 issues.add_time_hours=小時
@ -2145,7 +2139,6 @@ settings.advanced_settings=進階設定
settings.wiki_desc=啟用儲存庫 Wiki settings.wiki_desc=啟用儲存庫 Wiki
settings.use_internal_wiki=使用內建 Wiki settings.use_internal_wiki=使用內建 Wiki
settings.default_wiki_branch_name=預設 Wiki 分支名稱 settings.default_wiki_branch_name=預設 Wiki 分支名稱
settings.default_wiki_everyone_access=登入使用者的預設存取權限:
settings.failed_to_change_default_wiki_branch=更改預設 Wiki 分支失敗。 settings.failed_to_change_default_wiki_branch=更改預設 Wiki 分支失敗。
settings.use_external_wiki=使用外部 Wiki settings.use_external_wiki=使用外部 Wiki
settings.external_wiki_url=外部 Wiki 連結 settings.external_wiki_url=外部 Wiki 連結
@ -3354,7 +3347,6 @@ monitor.execute_time=已執行時間
monitor.last_execution_result=結果 monitor.last_execution_result=結果
monitor.process.cancel=結束處理程序 monitor.process.cancel=結束處理程序
monitor.process.cancel_desc=結束處理程序可能造成資料遺失 monitor.process.cancel_desc=結束處理程序可能造成資料遺失
monitor.process.cancel_notices=結束: <strong>%s</strong>?
monitor.process.children=子程序 monitor.process.children=子程序
monitor.queues=佇列 monitor.queues=佇列
@ -3550,7 +3542,6 @@ conda.install=執行下列命令以使用 Conda 安裝此套件:
container.details.type=映像檔類型 container.details.type=映像檔類型
container.details.platform=平台 container.details.platform=平台
container.pull=透過下列命令拉取映像檔: container.pull=透過下列命令拉取映像檔:
container.digest=摘要:
container.multi_arch=作業系統 / 架構 container.multi_arch=作業系統 / 架構
container.layers=映像檔 Layers container.layers=映像檔 Layers
container.labels=標籤 container.labels=標籤

963
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -5,18 +5,18 @@
}, },
"dependencies": { "dependencies": {
"@citation-js/core": "0.7.14", "@citation-js/core": "0.7.14",
"@citation-js/plugin-bibtex": "0.7.16", "@citation-js/plugin-bibtex": "0.7.17",
"@citation-js/plugin-csl": "0.7.14", "@citation-js/plugin-csl": "0.7.14",
"@citation-js/plugin-software-formats": "0.6.1", "@citation-js/plugin-software-formats": "0.6.1",
"@github/markdown-toolbar-element": "2.2.3", "@github/markdown-toolbar-element": "2.2.3",
"@github/relative-time-element": "4.4.4", "@github/relative-time-element": "4.4.5",
"@github/text-expander-element": "2.8.0", "@github/text-expander-element": "2.8.0",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@primer/octicons": "19.14.0", "@primer/octicons": "19.14.0",
"@silverwind/vue3-calendar-heatmap": "2.0.6", "@silverwind/vue3-calendar-heatmap": "2.0.6",
"add-asset-webpack-plugin": "3.0.0", "add-asset-webpack-plugin": "3.0.0",
"ansi_up": "6.0.2", "ansi_up": "6.0.2",
"asciinema-player": "3.8.1", "asciinema-player": "3.8.2",
"chart.js": "4.4.7", "chart.js": "4.4.7",
"chartjs-adapter-dayjs-4": "1.0.4", "chartjs-adapter-dayjs-4": "1.0.4",
"chartjs-plugin-zoom": "2.2.0", "chartjs-plugin-zoom": "2.2.0",
@ -28,11 +28,11 @@
"easymde": "2.18.0", "easymde": "2.18.0",
"esbuild-loader": "4.2.2", "esbuild-loader": "4.2.2",
"escape-goat": "4.0.0", "escape-goat": "4.0.0",
"fast-glob": "3.3.2", "fast-glob": "3.3.3",
"htmx.org": "2.0.4", "htmx.org": "2.0.4",
"idiomorph": "0.3.0", "idiomorph": "0.4.0",
"jquery": "3.7.1", "jquery": "3.7.1",
"katex": "0.16.18", "katex": "0.16.20",
"license-checker-webpack-plugin": "0.2.1", "license-checker-webpack-plugin": "0.2.1",
"mermaid": "11.4.1", "mermaid": "11.4.1",
"mini-css-extract-plugin": "2.9.2", "mini-css-extract-plugin": "2.9.2",
@ -41,7 +41,7 @@
"monaco-editor-webpack-plugin": "7.1.0", "monaco-editor-webpack-plugin": "7.1.0",
"pdfobject": "2.3.0", "pdfobject": "2.3.0",
"perfect-debounce": "1.0.0", "perfect-debounce": "1.0.0",
"postcss": "8.4.49", "postcss": "8.5.1",
"postcss-loader": "8.1.1", "postcss-loader": "8.1.1",
"postcss-nesting": "13.0.1", "postcss-nesting": "13.0.1",
"sortablejs": "1.15.6", "sortablejs": "1.15.6",
@ -52,7 +52,7 @@
"tippy.js": "6.3.7", "tippy.js": "6.3.7",
"toastify-js": "1.12.0", "toastify-js": "1.12.0",
"tributejs": "5.1.3", "tributejs": "5.1.3",
"typescript": "5.7.2", "typescript": "5.7.3",
"uint8-to-base64": "0.2.0", "uint8-to-base64": "0.2.0",
"vanilla-colorful": "0.7.2", "vanilla-colorful": "0.7.2",
"vue": "3.5.13", "vue": "3.5.13",
@ -60,15 +60,14 @@
"vue-chartjs": "5.3.2", "vue-chartjs": "5.3.2",
"vue-loader": "17.4.2", "vue-loader": "17.4.2",
"webpack": "5.97.1", "webpack": "5.97.1",
"webpack-cli": "5.1.4", "webpack-cli": "6.0.1",
"wrap-ansi": "9.0.0" "wrap-ansi": "9.0.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint-community/eslint-plugin-eslint-comments": "4.4.1", "@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
"@playwright/test": "1.49.1", "@playwright/test": "1.49.1",
"@silverwind/vue-tsc": "2.1.13",
"@stoplight/spectral-cli": "6.14.2", "@stoplight/spectral-cli": "6.14.2",
"@stylistic/eslint-plugin-js": "2.12.1", "@stylistic/eslint-plugin-js": "2.13.0",
"@stylistic/stylelint-plugin": "3.1.1", "@stylistic/stylelint-plugin": "3.1.1",
"@types/dropzone": "5.7.9", "@types/dropzone": "5.7.9",
"@types/jquery": "3.5.32", "@types/jquery": "3.5.32",
@ -80,8 +79,8 @@
"@types/throttle-debounce": "5.0.2", "@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6", "@types/tinycolor2": "1.4.6",
"@types/toastify-js": "1.12.3", "@types/toastify-js": "1.12.3",
"@typescript-eslint/eslint-plugin": "8.18.1", "@typescript-eslint/eslint-plugin": "8.20.0",
"@typescript-eslint/parser": "8.18.1", "@typescript-eslint/parser": "8.20.0",
"@vitejs/plugin-vue": "5.2.1", "@vitejs/plugin-vue": "5.2.1",
"eslint": "8.57.0", "eslint": "8.57.0",
"eslint-import-resolver-typescript": "3.7.0", "eslint-import-resolver-typescript": "3.7.0",
@ -99,19 +98,20 @@
"eslint-plugin-vue": "9.32.0", "eslint-plugin-vue": "9.32.0",
"eslint-plugin-vue-scoped-css": "2.9.0", "eslint-plugin-vue-scoped-css": "2.9.0",
"eslint-plugin-wc": "2.2.0", "eslint-plugin-wc": "2.2.0",
"happy-dom": "15.11.7", "happy-dom": "16.6.0",
"markdownlint-cli": "0.43.0", "markdownlint-cli": "0.43.0",
"nolyfill": "1.0.43", "nolyfill": "1.0.43",
"postcss-html": "1.7.0", "postcss-html": "1.8.0",
"stylelint": "16.12.0", "stylelint": "16.13.2",
"stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0",
"stylelint-declaration-strict-value": "1.10.6", "stylelint-declaration-strict-value": "1.10.7",
"stylelint-value-no-unknown-custom-properties": "6.0.1", "stylelint-value-no-unknown-custom-properties": "6.0.1",
"svgo": "3.3.2", "svgo": "3.3.2",
"type-fest": "4.30.2", "type-fest": "4.32.0",
"updates": "16.4.1", "updates": "16.4.1",
"vite-string-plugin": "1.3.4", "vite-string-plugin": "1.3.4",
"vitest": "2.1.8" "vitest": "2.1.8",
"vue-tsc": "2.2.0"
}, },
"browserslist": [ "browserslist": [
"defaults" "defaults"

73
poetry.lock generated

@ -1,14 +1,14 @@
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. # This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
[[package]] [[package]]
name = "click" name = "click"
version = "8.1.7" version = "8.1.8"
description = "Composable command line interface toolkit" description = "Composable command line interface toolkit"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
{file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
] ]
[package.dependencies] [package.dependencies]
@ -42,33 +42,33 @@ six = ">=1.13.0"
[[package]] [[package]]
name = "djlint" name = "djlint"
version = "1.36.3" version = "1.36.4"
description = "HTML Template Linter and Formatter" description = "HTML Template Linter and Formatter"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
files = [ files = [
{file = "djlint-1.36.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ae7c620b58e16d6bf003bd7de3f71376a7a3daa79dc02e77f3726d5a75243f2"}, {file = "djlint-1.36.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2dfb60883ceb92465201bfd392291a7597c6752baede6fbb6f1980cac8d6c5c"},
{file = "djlint-1.36.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e155ce0970d4a28d0a2e9f2e106733a2ad05910eee90e056b056d48049e4a97b"}, {file = "djlint-1.36.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4bc6a1320c0030244b530ac200642f883d3daa451a115920ef3d56d08b644292"},
{file = "djlint-1.36.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e8bb0406e60cc696806aa6226df137618f3889c72f2dbdfa76c908c99151579"}, {file = "djlint-1.36.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3164a048c7bb0baf042387b1e33f9bbbf99d90d1337bb4c3d66eb0f96f5400a1"},
{file = "djlint-1.36.3-cp310-cp310-win_amd64.whl", hash = "sha256:76d32faf988ad58ef2e7a11d04046fc984b98391761bf1b61f9a6044da53d414"}, {file = "djlint-1.36.4-cp310-cp310-win_amd64.whl", hash = "sha256:3196d5277da5934962d67ad6c33a948ba77a7b6eadf064648bef6ee5f216b03c"},
{file = "djlint-1.36.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:32f7a5834000fff22e94d1d35f95aaf2e06f2af2cae18af0ed2a4e215d60e730"}, {file = "djlint-1.36.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d68da0ed10ee9ca1e32e225cbb8e9b98bf7e6f8b48a8e4836117b6605b88cc7"},
{file = "djlint-1.36.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3eb1b9c0be499e63e8822a051e7e55f188ff1ab8172a85d338a8ae21c872060e"}, {file = "djlint-1.36.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c0478d5392247f1e6ee29220bbdbf7fb4e1bc0e7e83d291fda6fb926c1787ba7"},
{file = "djlint-1.36.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c2e0dd1f26eb472b8c84eb70d6482877b6497a1fd031d7534864088f016d5ea"}, {file = "djlint-1.36.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:962f7b83aee166e499eff916d631c6dde7f1447d7610785a60ed2a75a5763483"},
{file = "djlint-1.36.3-cp311-cp311-win_amd64.whl", hash = "sha256:a06b531ab9d049c46ad4d2365d1857004a1a9dd0c23c8eae94aa0d233c6ec00d"}, {file = "djlint-1.36.4-cp311-cp311-win_amd64.whl", hash = "sha256:53cbc450aa425c832f09bc453b8a94a039d147b096740df54a3547fada77ed08"},
{file = "djlint-1.36.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e66361a865e5e5a4bbcb40f56af7f256fd02cbf9d48b763a40172749cc294084"}, {file = "djlint-1.36.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff9faffd7d43ac20467493fa71d5355b5b330a00ade1c4d1e859022f4195223b"},
{file = "djlint-1.36.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:36e102b80d83e9ac2e6be9a9ded32fb925945f6dbc7a7156e4415de1b0aa0dba"}, {file = "djlint-1.36.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:79489e262b5ac23a8dfb7ca37f1eea979674cfc2d2644f7061d95bea12c38f7e"},
{file = "djlint-1.36.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ac4b7370d80bd82281e57a470de8923ac494ffb571b89d8787cef57c738c69a"}, {file = "djlint-1.36.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e58c5fa8c6477144a0be0a87273706a059e6dd0d6efae01146ae8c29cdfca675"},
{file = "djlint-1.36.3-cp312-cp312-win_amd64.whl", hash = "sha256:107cc56bbef13d60cc0ae774a4d52881bf98e37c02412e573827a3e549217e3a"}, {file = "djlint-1.36.4-cp312-cp312-win_amd64.whl", hash = "sha256:bb6903777bf3124f5efedcddf1f4716aef097a7ec4223fc0fa54b865829a6e08"},
{file = "djlint-1.36.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2a9f51971d6e63c41ea9b3831c928e1f21ae6fe57e87a3452cfe672d10232433"}, {file = "djlint-1.36.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ead475013bcac46095b1bbc8cf97ed2f06e83422335734363f8a76b4ba7e47c2"},
{file = "djlint-1.36.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:080c98714b55d8f0fef5c42beaee8247ebb2e3d46b0936473bd6c47808bb6302"}, {file = "djlint-1.36.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6c601dfa68ea253311deb4a29a7362b7a64933bdfcfb5a06618f3e70ad1fa835"},
{file = "djlint-1.36.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f65a80e0b5cb13d357ea51ca6570b34c2d9d18974c1e57142de760ea27d49ed0"}, {file = "djlint-1.36.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bda5014f295002363381969864addeb2db13955f1b26e772657c3b273ed7809f"},
{file = "djlint-1.36.3-cp313-cp313-win_amd64.whl", hash = "sha256:95ef6b67ef7f2b90d9434bba37d572031079001dc8524add85c00ef0386bda1e"}, {file = "djlint-1.36.4-cp313-cp313-win_amd64.whl", hash = "sha256:16ce37e085afe5a30953b2bd87cbe34c37843d94c701fc68a2dda06c1e428ff4"},
{file = "djlint-1.36.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e2317a32094d525bc41cd11c8dc064bf38d1b442c99cc3f7c4a2616b5e6ce6e"}, {file = "djlint-1.36.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:89678661888c03d7bc6cadd75af69db29962b5ecbf93a81518262f5c48329f04"},
{file = "djlint-1.36.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e82266c28793cd15f97b93535d72bfbc77306eaaf6b210dd90910383a814ee6c"}, {file = "djlint-1.36.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b01a98df3e1ab89a552793590875bc6e954cad661a9304057db75363d519fa0"},
{file = "djlint-1.36.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01b2101c2d1b079e8d545e6d9d03487fcca14d2371e44cbfdedee15b0bf4567c"}, {file = "djlint-1.36.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dabbb4f7b93223d471d09ae34ed515fef98b2233cbca2449ad117416c44b1351"},
{file = "djlint-1.36.3-cp39-cp39-win_amd64.whl", hash = "sha256:15cde63ef28beb5194ff4137883025f125676ece1b574b64a3e1c6daed734639"}, {file = "djlint-1.36.4-cp39-cp39-win_amd64.whl", hash = "sha256:7a483390d17e44df5bc23dcea29bdf6b63f3ed8b4731d844773a4829af4f5e0b"},
{file = "djlint-1.36.3-py3-none-any.whl", hash = "sha256:0c05cd5b76785de2c41a2420c06ffd112800bfc0f9c0f399cc7cea7c42557f4c"}, {file = "djlint-1.36.4-py3-none-any.whl", hash = "sha256:e9699b8ac3057a6ed04fb90835b89bee954ed1959c01541ce4f8f729c938afdd"},
{file = "djlint-1.36.3.tar.gz", hash = "sha256:d85735da34bc7ac93ad8ef9b4822cc2a23d5f0ce33f25438737b8dca1d404f78"}, {file = "djlint-1.36.4.tar.gz", hash = "sha256:17254f218b46fe5a714b224c85074c099bcb74e3b2e1f15c2ddc2cf415a408a1"},
] ]
[package.dependencies] [package.dependencies]
@ -82,15 +82,17 @@ pyyaml = ">=6"
regex = ">=2023" regex = ">=2023"
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
tqdm = ">=4.62.2" tqdm = ">=4.62.2"
typing-extensions = {version = ">=3.6.6", markers = "python_version < \"3.11\""}
[[package]] [[package]]
name = "editorconfig" name = "editorconfig"
version = "0.12.4" version = "0.17.0"
description = "EditorConfig File Locator and Interpreter for Python" description = "EditorConfig File Locator and Interpreter for Python"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
{file = "EditorConfig-0.12.4.tar.gz", hash = "sha256:24857fa1793917dd9ccf0c7810a07e05404ce9b823521c7dce22a4fb5d125f80"}, {file = "EditorConfig-0.17.0-py3-none-any.whl", hash = "sha256:fe491719c5f65959ec00b167d07740e7ffec9a3f362038c72b289330b9991dfc"},
{file = "editorconfig-0.17.0.tar.gz", hash = "sha256:8739052279699840065d3a9f5c125d7d5a98daeefe53b0e5274261d77cb49aa2"},
] ]
[[package]] [[package]]
@ -370,6 +372,17 @@ notebook = ["ipywidgets (>=6)"]
slack = ["slack-sdk"] slack = ["slack-sdk"]
telegram = ["requests"] telegram = ["requests"]
[[package]]
name = "typing-extensions"
version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
]
[[package]] [[package]]
name = "yamllint" name = "yamllint"
version = "1.35.1" version = "1.35.1"
@ -391,4 +404,4 @@ dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.10" python-versions = "^3.10"
content-hash = "01b1e2f910276dd20a70ebb665c83415c37531709d90874f5b7a86a5305e2369" content-hash = "f2e8260efe6e25f77ef387daff9551e41d25027e4794b42bc7a851ed0dfafd85"

@ -5,7 +5,7 @@ package-mode = false
python = "^3.10" python = "^3.10"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
djlint = "1.36.3" djlint = "1.36.4"
yamllint = "1.35.1" yamllint = "1.35.1"
[tool.djlint] [tool.djlint]

@ -1,14 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package runner
import (
"testing"
"code.gitea.io/gitea/models/unittest"
)
func TestMain(m *testing.M) {
unittest.MainTest(m)
}

@ -8,14 +8,8 @@ import (
"fmt" "fmt"
actions_model "code.gitea.io/gitea/models/actions" actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
secret_model "code.gitea.io/gitea/models/secret" secret_model "code.gitea.io/gitea/models/secret"
actions_module "code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/actions"
runnerv1 "code.gitea.io/actions-proto-go/runner/v1" runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
@ -65,82 +59,16 @@ func pickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv
} }
func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct { func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct {
event := map[string]any{}
_ = json.Unmarshal([]byte(t.Job.Run.EventPayload), &event)
// TriggerEvent is added in https://github.com/go-gitea/gitea/pull/25229
// This fallback is for the old ActionRun that doesn't have the TriggerEvent field
// and should be removed in 1.22
eventName := t.Job.Run.TriggerEvent
if eventName == "" {
eventName = t.Job.Run.Event.Event()
}
baseRef := ""
headRef := ""
ref := t.Job.Run.Ref
sha := t.Job.Run.CommitSHA
if pullPayload, err := t.Job.Run.GetPullRequestEventPayload(); err == nil && pullPayload.PullRequest != nil && pullPayload.PullRequest.Base != nil && pullPayload.PullRequest.Head != nil {
baseRef = pullPayload.PullRequest.Base.Ref
headRef = pullPayload.PullRequest.Head.Ref
// if the TriggerEvent is pull_request_target, ref and sha need to be set according to the base of pull request
// In GitHub's documentation, ref should be the branch or tag that triggered workflow. But when the TriggerEvent is pull_request_target,
// the ref will be the base branch.
if t.Job.Run.TriggerEvent == actions_module.GithubEventPullRequestTarget {
ref = git.BranchPrefix + pullPayload.PullRequest.Base.Name
sha = pullPayload.PullRequest.Base.Sha
}
}
refName := git.RefName(ref)
giteaRuntimeToken, err := actions.CreateAuthorizationToken(t.ID, t.Job.RunID, t.JobID) giteaRuntimeToken, err := actions.CreateAuthorizationToken(t.ID, t.Job.RunID, t.JobID)
if err != nil { if err != nil {
log.Error("actions.CreateAuthorizationToken failed: %v", err) log.Error("actions.CreateAuthorizationToken failed: %v", err)
} }
taskContext, err := structpb.NewStruct(map[string]any{ gitCtx := actions.GenerateGiteaContext(t.Job.Run, t.Job)
// standard contexts, see https://docs.github.com/en/actions/learn-github-actions/contexts#github-context gitCtx["token"] = t.Token
"action": "", // string, The name of the action currently running, or the id of a step. GitHub removes special characters, and uses the name __run when the current step runs a script without an id. If you use the same action more than once in the same job, the name will include a suffix with the sequence number with underscore before it. For example, the first script you run will have the name __run, and the second script will be named __run_2. Similarly, the second invocation of actions/checkout will be actionscheckout2. gitCtx["gitea_runtime_token"] = giteaRuntimeToken
"action_path": "", // string, The path where an action is located. This property is only supported in composite actions. You can use this path to access files located in the same repository as the action.
"action_ref": "", // string, For a step executing an action, this is the ref of the action being executed. For example, v2. taskContext, err := structpb.NewStruct(gitCtx)
"action_repository": "", // string, For a step executing an action, this is the owner and repository name of the action. For example, actions/checkout.
"action_status": "", // string, For a composite action, the current result of the composite action.
"actor": t.Job.Run.TriggerUser.Name, // string, The username of the user that triggered the initial workflow run. If the workflow run is a re-run, this value may differ from github.triggering_actor. Any workflow re-runs will use the privileges of github.actor, even if the actor initiating the re-run (github.triggering_actor) has different privileges.
"api_url": setting.AppURL + "api/v1", // string, The URL of the GitHub REST API.
"base_ref": baseRef, // string, The base_ref or target branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target.
"env": "", // string, Path on the runner to the file that sets environment variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions."
"event": event, // object, The full event webhook payload. You can access individual properties of the event using this context. This object is identical to the webhook payload of the event that triggered the workflow run, and is different for each event. The webhooks for each GitHub Actions event is linked in "Events that trigger workflows." For example, for a workflow run triggered by the push event, this object contains the contents of the push webhook payload.
"event_name": eventName, // string, The name of the event that triggered the workflow run.
"event_path": "", // string, The path to the file on the runner that contains the full event webhook payload.
"graphql_url": "", // string, The URL of the GitHub GraphQL API.
"head_ref": headRef, // string, The head_ref or source branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target.
"job": fmt.Sprint(t.JobID), // string, The job_id of the current job.
"ref": ref, // string, The fully-formed ref of the branch or tag that triggered the workflow run. For workflows triggered by push, this is the branch or tag ref that was pushed. For workflows triggered by pull_request, this is the pull request merge branch. For workflows triggered by release, this is the release tag created. For other triggers, this is the branch or tag ref that triggered the workflow run. This is only set if a branch or tag is available for the event type. The ref given is fully-formed, meaning that for branches the format is refs/heads/<branch_name>, for pull requests it is refs/pull/<pr_number>/merge, and for tags it is refs/tags/<tag_name>. For example, refs/heads/feature-branch-1.
"ref_name": refName.ShortName(), // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1.
"ref_protected": false, // boolean, true if branch protections are configured for the ref that triggered the workflow run.
"ref_type": refName.RefType(), // string, The type of ref that triggered the workflow run. Valid values are branch or tag.
"path": "", // string, Path on the runner to the file that sets system PATH variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions."
"repository": t.Job.Run.Repo.OwnerName + "/" + t.Job.Run.Repo.Name, // string, The owner and repository name. For example, Codertocat/Hello-World.
"repository_owner": t.Job.Run.Repo.OwnerName, // string, The repository owner's name. For example, Codertocat.
"repositoryUrl": t.Job.Run.Repo.HTMLURL(), // string, The Git URL to the repository. For example, git://github.com/codertocat/hello-world.git.
"retention_days": "", // string, The number of days that workflow run logs and artifacts are kept.
"run_id": fmt.Sprint(t.Job.RunID), // string, A unique number for each workflow run within a repository. This number does not change if you re-run the workflow run.
"run_number": fmt.Sprint(t.Job.Run.Index), // string, A unique number for each run of a particular workflow in a repository. This number begins at 1 for the workflow's first run, and increments with each new run. This number does not change if you re-run the workflow run.
"run_attempt": fmt.Sprint(t.Job.Attempt), // string, A unique number for each attempt of a particular workflow run in a repository. This number begins at 1 for the workflow run's first attempt, and increments with each re-run.
"secret_source": "Actions", // string, The source of a secret used in a workflow. Possible values are None, Actions, Dependabot, or Codespaces.
"server_url": setting.AppURL, // string, The URL of the GitHub server. For example: https://github.com.
"sha": sha, // string, The commit SHA that triggered the workflow. The value of this commit SHA depends on the event that triggered the workflow. For more information, see "Events that trigger workflows." For example, ffac537e6cbbf934b08745a378932722df287a53.
"token": t.Token, // string, A token to authenticate on behalf of the GitHub App installed on your repository. This is functionally equivalent to the GITHUB_TOKEN secret. For more information, see "Automatic token authentication."
"triggering_actor": "", // string, The username of the user that initiated the workflow run. If the workflow run is a re-run, this value may differ from github.actor. Any workflow re-runs will use the privileges of github.actor, even if the actor initiating the re-run (github.triggering_actor) has different privileges.
"workflow": t.Job.Run.WorkflowID, // string, The name of the workflow. If the workflow file doesn't specify a name, the value of this property is the full path of the workflow file in the repository.
"workspace": "", // string, The default working directory on the runner for steps, and the default location of your repository when using the checkout action.
// additional contexts
"gitea_default_actions_url": setting.Actions.DefaultActionsURL.URL(),
"gitea_runtime_token": giteaRuntimeToken,
})
if err != nil { if err != nil {
log.Error("structpb.NewStruct failed: %v", err) log.Error("structpb.NewStruct failed: %v", err)
} }
@ -150,68 +78,18 @@ func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct {
func findTaskNeeds(ctx context.Context, task *actions_model.ActionTask) (map[string]*runnerv1.TaskNeed, error) { func findTaskNeeds(ctx context.Context, task *actions_model.ActionTask) (map[string]*runnerv1.TaskNeed, error) {
if err := task.LoadAttributes(ctx); err != nil { if err := task.LoadAttributes(ctx); err != nil {
return nil, fmt.Errorf("LoadAttributes: %w", err) return nil, fmt.Errorf("task LoadAttributes: %w", err)
}
if len(task.Job.Needs) == 0 {
return nil, nil
}
needs := container.SetOf(task.Job.Needs...)
jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: task.Job.RunID})
if err != nil {
return nil, fmt.Errorf("FindRunJobs: %w", err)
}
jobIDJobs := make(map[string][]*actions_model.ActionRunJob)
for _, job := range jobs {
jobIDJobs[job.JobID] = append(jobIDJobs[job.JobID], job)
} }
taskNeeds, err := actions.FindTaskNeeds(ctx, task.Job)
ret := make(map[string]*runnerv1.TaskNeed, len(needs))
for jobID, jobsWithSameID := range jobIDJobs {
if !needs.Contains(jobID) {
continue
}
var jobOutputs map[string]string
for _, job := range jobsWithSameID {
if job.TaskID == 0 || !job.Status.IsDone() {
// it shouldn't happen, or the job has been rerun
continue
}
got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID)
if err != nil { if err != nil {
return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err) return nil, err
}
outputs := make(map[string]string, len(got))
for _, v := range got {
outputs[v.OutputKey] = v.OutputValue
}
if len(jobOutputs) == 0 {
jobOutputs = outputs
} else {
jobOutputs = mergeTwoOutputs(outputs, jobOutputs)
}
} }
ret := make(map[string]*runnerv1.TaskNeed, len(taskNeeds))
for jobID, taskNeed := range taskNeeds {
ret[jobID] = &runnerv1.TaskNeed{ ret[jobID] = &runnerv1.TaskNeed{
Outputs: jobOutputs, Outputs: taskNeed.Outputs,
Result: runnerv1.Result(actions_model.AggregateJobStatus(jobsWithSameID)), Result: runnerv1.Result(taskNeed.Result),
} }
} }
return ret, nil return ret, nil
} }
// mergeTwoOutputs merges two outputs from two different ActionRunJobs
// Values with the same output name may be overridden. The user should ensure the output names are unique.
// See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#using-job-outputs-in-a-matrix-job
func mergeTwoOutputs(o1, o2 map[string]string) map[string]string {
ret := make(map[string]string, len(o1))
for k1, v1 := range o1 {
if len(v1) > 0 {
ret[k1] = v1
} else {
ret[k1] = o2[k1]
}
}
return ret
}

@ -34,11 +34,30 @@ func ListHooks(ctx *context.APIContext) {
// in: query // in: query
// description: page size of results // description: page size of results
// type: integer // type: integer
// - type: string
// enum:
// - system
// - default
// - all
// description: system, default or both kinds of webhooks
// name: type
// default: system
// in: query
//
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/HookList" // "$ref": "#/responses/HookList"
sysHooks, err := webhook.GetSystemWebhooks(ctx, optional.None[bool]()) // for compatibility the default value is true
isSystemWebhook := optional.Some(true)
typeValue := ctx.FormString("type")
if typeValue == "default" {
isSystemWebhook = optional.Some(false)
} else if typeValue == "all" {
isSystemWebhook = optional.None[bool]()
}
sysHooks, err := webhook.GetSystemOrDefaultWebhooks(ctx, isSystemWebhook)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetSystemWebhooks", err) ctx.Error(http.StatusInternalServerError, "GetSystemWebhooks", err)
return return

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git" git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/gitrepo"
@ -443,7 +444,14 @@ func UpdateBranch(ctx *context.APIContext) {
msg, err := repo_service.RenameBranch(ctx, repo, ctx.Doer, ctx.Repo.GitRepo, oldName, opt.Name) msg, err := repo_service.RenameBranch(ctx, repo, ctx.Doer, ctx.Repo.GitRepo, oldName, opt.Name)
if err != nil { if err != nil {
switch {
case repo_model.IsErrUserDoesNotHaveAccessToRepo(err):
ctx.Error(http.StatusForbidden, "", "User must be a repo or site admin to rename default or protected branches.")
case errors.Is(err, git_model.ErrBranchIsProtected):
ctx.Error(http.StatusForbidden, "", "Branch is protected by glob-based protection rules.")
default:
ctx.Error(http.StatusInternalServerError, "RenameBranch", err) ctx.Error(http.StatusInternalServerError, "RenameBranch", err)
}
return return
} }
if msg == "target_exist" { if msg == "target_exist" {

@ -17,11 +17,11 @@ func DownloadArchive(ctx *context.APIContext) {
var tp git.ArchiveType var tp git.ArchiveType
switch ballType := ctx.PathParam("ball_type"); ballType { switch ballType := ctx.PathParam("ball_type"); ballType {
case "tarball": case "tarball":
tp = git.TARGZ tp = git.ArchiveTarGz
case "zipball": case "zipball":
tp = git.ZIP tp = git.ArchiveZip
case "bundle": case "bundle":
tp = git.BUNDLE tp = git.ArchiveBundle
default: default:
ctx.Error(http.StatusBadRequest, "", fmt.Sprintf("Unknown archive type: %s", ballType)) ctx.Error(http.StatusBadRequest, "", fmt.Sprintf("Unknown archive type: %s", ballType))
return return
@ -36,7 +36,7 @@ func DownloadArchive(ctx *context.APIContext) {
} }
} }
r, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"), tp) r, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*")+"."+tp.String())
if err != nil { if err != nil {
ctx.ServerError("NewRequest", err) ctx.ServerError("NewRequest", err)
return return

@ -293,14 +293,7 @@ func GetArchive(ctx *context.APIContext) {
} }
func archiveDownload(ctx *context.APIContext) { func archiveDownload(ctx *context.APIContext) {
uri := ctx.PathParam("*") aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, ctx.PathParam("*"))
ext, tp, err := archiver_service.ParseFileName(uri)
if err != nil {
ctx.Error(http.StatusBadRequest, "ParseFileName", err)
return
}
aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, strings.TrimSuffix(uri, ext), tp)
if err != nil { if err != nil {
if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) { if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) {
ctx.Error(http.StatusBadRequest, "unknown archive format", err) ctx.Error(http.StatusBadRequest, "unknown archive format", err)

@ -23,7 +23,7 @@ func TestTestHook(t *testing.T) {
contexttest.LoadRepoCommit(t, ctx) contexttest.LoadRepoCommit(t, ctx)
contexttest.LoadUser(t, ctx, 2) contexttest.LoadUser(t, ctx, 2)
TestHook(ctx) TestHook(ctx)
assert.EqualValues(t, http.StatusNoContent, ctx.Resp.Status()) assert.EqualValues(t, http.StatusNoContent, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{ unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{
HookID: 1, HookID: 1,

@ -58,7 +58,7 @@ func TestRepoEdit(t *testing.T) {
web.SetForm(ctx, &opts) web.SetForm(ctx, &opts)
Edit(ctx) Edit(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, http.StatusOK, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
ID: 1, ID: 1,
}, unittest.Cond("name = ? AND is_archived = 1", *opts.Name)) }, unittest.Cond("name = ? AND is_archived = 1", *opts.Name))
@ -78,7 +78,7 @@ func TestRepoEditNameChange(t *testing.T) {
web.SetForm(ctx, &opts) web.SetForm(ctx, &opts)
Edit(ctx) Edit(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, http.StatusOK, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
ID: 1, ID: 1,

@ -9,6 +9,7 @@ import (
"strings" "strings"
"code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/gtprof"
"code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/reqctx" "code.gitea.io/gitea/modules/reqctx"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@ -43,14 +44,26 @@ func ProtocolMiddlewares() (handlers []any) {
func RequestContextHandler() func(h http.Handler) http.Handler { func RequestContextHandler() func(h http.Handler) http.Handler {
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(respOrig http.ResponseWriter, req *http.Request) {
profDesc := fmt.Sprintf("%s: %s", req.Method, req.RequestURI) // this response writer might not be the same as the one in context.Base.Resp
// because there might be a "gzip writer" in the middle, so the "written size" here is the compressed size
respWriter := context.WrapResponseWriter(respOrig)
profDesc := fmt.Sprintf("HTTP: %s %s", req.Method, req.RequestURI)
ctx, finished := reqctx.NewRequestContext(req.Context(), profDesc) ctx, finished := reqctx.NewRequestContext(req.Context(), profDesc)
defer finished() defer finished()
ctx, span := gtprof.GetTracer().Start(ctx, gtprof.TraceSpanHTTP)
req = req.WithContext(ctx)
defer func() {
chiCtx := chi.RouteContext(req.Context())
span.SetAttributeString(gtprof.TraceAttrHTTPRoute, chiCtx.RoutePattern())
span.End()
}()
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
RenderPanicErrorPage(resp, req, err) // it should never panic RenderPanicErrorPage(respWriter, req, err) // it should never panic
} }
}() }()
@ -62,7 +75,7 @@ func RequestContextHandler() func(h http.Handler) http.Handler {
_ = req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory _ = req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
} }
}) })
next.ServeHTTP(context.WrapResponseWriter(resp), req) next.ServeHTTP(respWriter, req)
}) })
} }
} }
@ -71,11 +84,11 @@ func ChiRoutePathHandler() func(h http.Handler) http.Handler {
// make sure chi uses EscapedPath(RawPath) as RoutePath, then "%2f" could be handled correctly // make sure chi uses EscapedPath(RawPath) as RoutePath, then "%2f" could be handled correctly
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
ctx := chi.RouteContext(req.Context()) chiCtx := chi.RouteContext(req.Context())
if req.URL.RawPath == "" { if req.URL.RawPath == "" {
ctx.RoutePath = req.URL.EscapedPath() chiCtx.RoutePath = req.URL.EscapedPath()
} else { } else {
ctx.RoutePath = req.URL.RawPath chiCtx.RoutePath = req.URL.RawPath
} }
next.ServeHTTP(resp, req) next.ServeHTTP(resp, req)
}) })

@ -213,7 +213,7 @@ func NormalRoutes() *web.Router {
} }
r.NotFound(func(w http.ResponseWriter, req *http.Request) { r.NotFound(func(w http.ResponseWriter, req *http.Request) {
routing.UpdateFuncInfo(req.Context(), routing.GetFuncInfo(http.NotFound, "GlobalNotFound")) defer routing.RecordFuncInfo(req.Context(), routing.GetFuncInfo(http.NotFound, "GlobalNotFound"))()
http.NotFound(w, req) http.NotFound(w, req)
}) })
return r return r

@ -37,6 +37,7 @@ const (
tplSelfCheck templates.TplName = "admin/self_check" tplSelfCheck templates.TplName = "admin/self_check"
tplCron templates.TplName = "admin/cron" tplCron templates.TplName = "admin/cron"
tplQueue templates.TplName = "admin/queue" tplQueue templates.TplName = "admin/queue"
tplPerfTrace templates.TplName = "admin/perftrace"
tplStacktrace templates.TplName = "admin/stacktrace" tplStacktrace templates.TplName = "admin/stacktrace"
tplQueueManage templates.TplName = "admin/queue_manage" tplQueueManage templates.TplName = "admin/queue_manage"
tplStats templates.TplName = "admin/stats" tplStats templates.TplName = "admin/stats"

@ -10,13 +10,15 @@ import (
"time" "time"
"code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/tailmsg"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
) )
func MonitorDiagnosis(ctx *context.Context) { func MonitorDiagnosis(ctx *context.Context) {
seconds := ctx.FormInt64("seconds") seconds := ctx.FormInt64("seconds")
if seconds <= 5 { if seconds <= 1 {
seconds = 5 seconds = 1
} }
if seconds > 300 { if seconds > 300 {
seconds = 300 seconds = 300
@ -65,4 +67,16 @@ func MonitorDiagnosis(ctx *context.Context) {
return return
} }
_ = pprof.Lookup("heap").WriteTo(f, 0) _ = pprof.Lookup("heap").WriteTo(f, 0)
f, err = zipWriter.CreateHeader(&zip.FileHeader{Name: "perftrace.txt", Method: zip.Deflate, Modified: time.Now()})
if err != nil {
ctx.ServerError("Failed to create zip file", err)
return
}
for _, record := range tailmsg.GetManager().GetTraceRecorder().GetRecords() {
_, _ = f.Write(util.UnsafeStringToBytes(record.Time.Format(time.RFC3339)))
_, _ = f.Write([]byte(" "))
_, _ = f.Write(util.UnsafeStringToBytes((record.Content)))
_, _ = f.Write([]byte("\n\n"))
}
} }

@ -0,0 +1,18 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package admin
import (
"net/http"
"code.gitea.io/gitea/modules/tailmsg"
"code.gitea.io/gitea/services/context"
)
func PerfTrace(ctx *context.Context) {
monitorTraceCommon(ctx)
ctx.Data["PageIsAdminMonitorPerfTrace"] = true
ctx.Data["PerfTraceRecords"] = tailmsg.GetManager().GetTraceRecorder().GetRecords()
ctx.HTML(http.StatusOK, tplPerfTrace)
}

@ -12,10 +12,17 @@ import (
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
) )
func monitorTraceCommon(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.monitor")
ctx.Data["PageIsAdminMonitorTrace"] = true
// Hide the performance trace tab in production, because it shows a lot of SQLs and is not that useful for end users.
// To avoid confusing end users, do not let them know this tab. End users should "download diagnosis report" instead.
ctx.Data["ShowAdminPerformanceTraceTab"] = !setting.IsProd
}
// Stacktrace show admin monitor goroutines page // Stacktrace show admin monitor goroutines page
func Stacktrace(ctx *context.Context) { func Stacktrace(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.monitor") monitorTraceCommon(ctx)
ctx.Data["PageIsAdminMonitorStacktrace"] = true
ctx.Data["GoroutineCount"] = runtime.NumGoroutine() ctx.Data["GoroutineCount"] = runtime.NumGoroutine()

@ -29,6 +29,7 @@ var tplLinkAccount templates.TplName = "user/auth/link_account"
// LinkAccount shows the page where the user can decide to login or create a new account // LinkAccount shows the page where the user can decide to login or create a new account
func LinkAccount(ctx *context.Context) { func LinkAccount(ctx *context.Context) {
// FIXME: these common template variables should be prepared in one common function, but not just copy-paste again and again.
ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationPassword || setting.Service.AllowOnlyExternalRegistration
ctx.Data["Title"] = ctx.Tr("link_account") ctx.Data["Title"] = ctx.Tr("link_account")
ctx.Data["LinkAccountMode"] = true ctx.Data["LinkAccountMode"] = true
@ -43,6 +44,7 @@ func LinkAccount(ctx *context.Context) {
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
ctx.Data["ShowRegistrationButton"] = false ctx.Data["ShowRegistrationButton"] = false
// use this to set the right link into the signIn and signUp templates in the link_account template // use this to set the right link into the signIn and signUp templates in the link_account template
@ -50,6 +52,11 @@ func LinkAccount(ctx *context.Context) {
ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup" ctx.Data["SignUpLink"] = setting.AppSubURL + "/user/link_account_signup"
gothUser, ok := ctx.Session.Get("linkAccountGothUser").(goth.User) gothUser, ok := ctx.Session.Get("linkAccountGothUser").(goth.User)
// If you'd like to quickly debug the "link account" page layout, just uncomment the blow line
// Don't worry, when the below line exists, the lint won't pass: ineffectual assignment to gothUser (ineffassign)
// gothUser, ok = goth.User{Email: "invalid-email", Name: "."}, true // intentionally use invalid data to avoid pass the registration check
if !ok { if !ok {
// no account in session, so just redirect to the login page, then the user could restart the process // no account in session, so just redirect to the login page, then the user could restart the process
ctx.Redirect(setting.AppSubURL + "/user/login") ctx.Redirect(setting.AppSubURL + "/user/login")
@ -135,6 +142,8 @@ func LinkAccountPostSignIn(ctx *context.Context) {
ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
ctx.Data["ShowRegistrationButton"] = false ctx.Data["ShowRegistrationButton"] = false
// use this to set the right link into the signIn and signUp templates in the link_account template // use this to set the right link into the signIn and signUp templates in the link_account template
@ -223,6 +232,8 @@ func LinkAccountPostRegister(ctx *context.Context) {
ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
ctx.Data["EnablePasswordSignInForm"] = setting.Service.EnablePasswordSignInForm
ctx.Data["ShowRegistrationButton"] = false ctx.Data["ShowRegistrationButton"] = false
// use this to set the right link into the signIn and signUp templates in the link_account template // use this to set the right link into the signIn and signUp templates in the link_account template

@ -34,7 +34,7 @@ func storageHandler(storageSetting *setting.Storage, prefix string, objStore sto
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return return
} }
routing.UpdateFuncInfo(req.Context(), funcInfo) defer routing.RecordFuncInfo(req.Context(), funcInfo)()
rPath := strings.TrimPrefix(req.URL.Path, "/"+prefix+"/") rPath := strings.TrimPrefix(req.URL.Path, "/"+prefix+"/")
rPath = util.PathJoinRelX(rPath) rPath = util.PathJoinRelX(rPath)
@ -65,7 +65,7 @@ func storageHandler(storageSetting *setting.Storage, prefix string, objStore sto
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return return
} }
routing.UpdateFuncInfo(req.Context(), funcInfo) defer routing.RecordFuncInfo(req.Context(), funcInfo)()
rPath := strings.TrimPrefix(req.URL.Path, "/"+prefix+"/") rPath := strings.TrimPrefix(req.URL.Path, "/"+prefix+"/")
rPath = util.PathJoinRelX(rPath) rPath = util.PathJoinRelX(rPath)

@ -23,7 +23,7 @@ func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType stri
} }
title := fmt.Sprintf("Latest commits for branch %s", ctx.Repo.BranchName) title := fmt.Sprintf("Latest commits for branch %s", ctx.Repo.BranchName)
link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.BranchNameSubURL()} link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.RefTypeNameSubURL()}
feed := &feeds.Feed{ feed := &feeds.Feed{
Title: title, Title: title,

@ -24,7 +24,7 @@ func ShowFileFeed(ctx *context.Context, repo *repo.Repository, formatType string
} }
commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange( commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(
git.CommitsByFileAndRangeOptions{ git.CommitsByFileAndRangeOptions{
Revision: ctx.Repo.RefName, Revision: ctx.Repo.RefFullName.ShortName(), // FIXME: legacy code used ShortName
File: fileName, File: fileName,
Page: 1, Page: 1,
}) })
@ -35,7 +35,7 @@ func ShowFileFeed(ctx *context.Context, repo *repo.Repository, formatType string
title := fmt.Sprintf("Latest commits for file %s", ctx.Repo.TreePath) title := fmt.Sprintf("Latest commits for file %s", ctx.Repo.TreePath)
link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)} link := &feeds.Link{Href: repo.HTMLURL() + "/" + ctx.Repo.RefTypeNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)}
feed := &feeds.Feed{ feed := &feeds.Feed{
Title: title, Title: title,

@ -850,7 +850,7 @@ func Run(ctx *context_module.Context) {
inputs := make(map[string]any) inputs := make(map[string]any)
if workflowDispatch := workflow.WorkflowDispatchConfig(); workflowDispatch != nil { if workflowDispatch := workflow.WorkflowDispatchConfig(); workflowDispatch != nil {
for name, config := range workflowDispatch.Inputs { for name, config := range workflowDispatch.Inputs {
value := ctx.Req.PostForm.Get(name) value := ctx.Req.PostFormValue(name)
if config.Type == "boolean" { if config.Type == "boolean" {
// https://www.w3.org/TR/html401/interact/forms.html // https://www.w3.org/TR/html401/interact/forms.html
// https://stackoverflow.com/questions/11424037/do-checkbox-inputs-only-post-data-if-theyre-checked // https://stackoverflow.com/questions/11424037/do-checkbox-inputs-only-post-data-if-theyre-checked

@ -46,9 +46,9 @@ func RefBlame(ctx *context.Context) {
return return
} }
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
treeLink := branchLink treeLink := branchLink
rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.RefTypeNameSubURL()
if len(ctx.Repo.TreePath) > 0 { if len(ctx.Repo.TreePath) > 0 {
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath) treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)

@ -37,7 +37,6 @@ const (
// Branches render repository branch page // Branches render repository branch page
func Branches(ctx *context.Context) { func Branches(ctx *context.Context) {
ctx.Data["Title"] = "Branches" ctx.Data["Title"] = "Branches"
ctx.Data["IsRepoToolbarBranches"] = true
ctx.Data["AllowsPulls"] = ctx.Repo.Repository.AllowsPulls(ctx) ctx.Data["AllowsPulls"] = ctx.Repo.Repository.AllowsPulls(ctx)
ctx.Data["IsWriter"] = ctx.Repo.CanWrite(unit.TypeCode) ctx.Data["IsWriter"] = ctx.Repo.CanWrite(unit.TypeCode)
ctx.Data["IsMirror"] = ctx.Repo.Repository.IsMirror ctx.Data["IsMirror"] = ctx.Repo.Repository.IsMirror
@ -185,7 +184,7 @@ func CreateBranch(ctx *context.Context) {
if ctx.HasError() { if ctx.HasError() {
ctx.Flash.Error(ctx.GetErrMsg()) ctx.Flash.Error(ctx.GetErrMsg())
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
return return
} }
@ -193,11 +192,11 @@ func CreateBranch(ctx *context.Context) {
if form.CreateTag { if form.CreateTag {
target := ctx.Repo.CommitID target := ctx.Repo.CommitID
if ctx.Repo.IsViewBranch { if ctx.Repo.RefFullName.IsBranch() {
target = ctx.Repo.BranchName target = ctx.Repo.BranchName
} }
err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, target, form.NewBranchName, "") err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, target, form.NewBranchName, "")
} else if ctx.Repo.IsViewBranch { } else if ctx.Repo.RefFullName.IsBranch() {
err = repo_service.CreateNewBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.BranchName, form.NewBranchName) err = repo_service.CreateNewBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.BranchName, form.NewBranchName)
} else { } else {
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.CommitID, form.NewBranchName) err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.CommitID, form.NewBranchName)
@ -205,25 +204,25 @@ func CreateBranch(ctx *context.Context) {
if err != nil { if err != nil {
if release_service.IsErrProtectedTagName(err) { if release_service.IsErrProtectedTagName(err) {
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected")) ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
return return
} }
if release_service.IsErrTagAlreadyExists(err) { if release_service.IsErrTagAlreadyExists(err) {
e := err.(release_service.ErrTagAlreadyExists) e := err.(release_service.ErrTagAlreadyExists)
ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName)) ctx.Flash.Error(ctx.Tr("repo.branch.tag_collision", e.TagName))
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
return return
} }
if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) { if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", form.NewBranchName)) ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", form.NewBranchName))
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
return return
} }
if git_model.IsErrBranchNameConflict(err) { if git_model.IsErrBranchNameConflict(err) {
e := err.(git_model.ErrBranchNameConflict) e := err.(git_model.ErrBranchNameConflict)
ctx.Flash.Error(ctx.Tr("repo.branch.branch_name_conflict", form.NewBranchName, e.BranchName)) ctx.Flash.Error(ctx.Tr("repo.branch.branch_name_conflict", form.NewBranchName, e.BranchName))
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
return return
} }
if git.IsErrPushRejected(err) { if git.IsErrPushRejected(err) {
@ -242,7 +241,7 @@ func CreateBranch(ctx *context.Context) {
} }
ctx.Flash.Error(flashError) ctx.Flash.Error(flashError)
} }
ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()) ctx.Redirect(ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL())
return return
} }

@ -57,7 +57,7 @@ func CherryPick(ctx *context.Context) {
ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx) ctx.Data["new_branch_name"] = GetUniquePatchBranchName(ctx)
ctx.Data["last_commit"] = ctx.Repo.CommitID ctx.Data["last_commit"] = ctx.Repo.CommitID
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
ctx.HTML(200, tplCherryPick) ctx.HTML(200, tplCherryPick)
} }
@ -85,7 +85,7 @@ func CherryPickPost(ctx *context.Context) {
ctx.Data["new_branch_name"] = form.NewBranchName ctx.Data["new_branch_name"] = form.NewBranchName
ctx.Data["last_commit"] = ctx.Repo.CommitID ctx.Data["last_commit"] = ctx.Repo.CommitID
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(200, tplCherryPick) ctx.HTML(200, tplCherryPick)

@ -29,7 +29,7 @@ func CodeFrequency(ctx *context.Context) {
// CodeFrequencyData returns JSON of code frequency data // CodeFrequencyData returns JSON of code frequency data
func CodeFrequencyData(ctx *context.Context) { func CodeFrequencyData(ctx *context.Context) {
if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.CommitID); err != nil { if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch); err != nil {
if errors.Is(err, contributors_service.ErrAwaitGeneration) { if errors.Is(err, contributors_service.ErrAwaitGeneration) {
ctx.Status(http.StatusAccepted) ctx.Status(http.StatusAccepted)
return return

@ -62,11 +62,7 @@ func Commits(ctx *context.Context) {
} }
ctx.Data["PageIsViewCode"] = true ctx.Data["PageIsViewCode"] = true
commitsCount, err := ctx.Repo.GetCommitsCount() commitsCount := ctx.Repo.CommitsCount
if err != nil {
ctx.ServerError("GetCommitsCount", err)
return
}
page := ctx.FormInt("page") page := ctx.FormInt("page")
if page <= 1 { if page <= 1 {
@ -129,12 +125,6 @@ func Graph(ctx *context.Context) {
ctx.Data["SelectedBranches"] = realBranches ctx.Data["SelectedBranches"] = realBranches
files := ctx.FormStrings("file") files := ctx.FormStrings("file")
commitsCount, err := ctx.Repo.GetCommitsCount()
if err != nil {
ctx.ServerError("GetCommitsCount", err)
return
}
graphCommitsCount, err := ctx.Repo.GetCommitGraphsCount(ctx, hidePRRefs, realBranches, files) graphCommitsCount, err := ctx.Repo.GetCommitGraphsCount(ctx, hidePRRefs, realBranches, files)
if err != nil { if err != nil {
log.Warn("GetCommitGraphsCount error for generate graph exclude prs: %t branches: %s in %-v, Will Ignore branches and try again. Underlying Error: %v", hidePRRefs, branches, ctx.Repo.Repository, err) log.Warn("GetCommitGraphsCount error for generate graph exclude prs: %t branches: %s in %-v, Will Ignore branches and try again. Underlying Error: %v", hidePRRefs, branches, ctx.Repo.Repository, err)
@ -171,7 +161,6 @@ func Graph(ctx *context.Context) {
ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name
ctx.Data["CommitCount"] = commitsCount
paginator := context.NewPagination(int(graphCommitsCount), setting.UI.GraphMaxCommitNum, page, 5) paginator := context.NewPagination(int(graphCommitsCount), setting.UI.GraphMaxCommitNum, page, 5)
paginator.AddParamFromRequest(ctx.Req) paginator.AddParamFromRequest(ctx.Req)
@ -191,7 +180,7 @@ func SearchCommits(ctx *context.Context) {
query := ctx.FormTrim("q") query := ctx.FormTrim("q")
if len(query) == 0 { if len(query) == 0 {
ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.BranchNameSubURL()) ctx.Redirect(ctx.Repo.RepoLink + "/commits/" + ctx.Repo.RefTypeNameSubURL())
return return
} }
@ -222,7 +211,7 @@ func FileHistory(ctx *context.Context) {
return return
} }
commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(ctx.Repo.RefName, fileName) commitsCount, err := ctx.Repo.GitRepo.FileCommitsCount(ctx.Repo.RefFullName.ShortName(), fileName) // FIXME: legacy code used ShortName
if err != nil { if err != nil {
ctx.ServerError("FileCommitsCount", err) ctx.ServerError("FileCommitsCount", err)
return return
@ -238,7 +227,7 @@ func FileHistory(ctx *context.Context) {
commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange( commits, err := ctx.Repo.GitRepo.CommitsByFileAndRange(
git.CommitsByFileAndRangeOptions{ git.CommitsByFileAndRangeOptions{
Revision: ctx.Repo.RefName, Revision: ctx.Repo.RefFullName.ShortName(), // FIXME: legacy code used ShortName
File: fileName, File: fileName,
Page: page, Page: page,
}) })
@ -390,12 +379,6 @@ func Diff(ctx *context.Context) {
} }
} }
ctx.Data["BranchName"], err = commit.GetBranchName()
if err != nil {
ctx.ServerError("commit.GetBranchName", err)
return
}
ctx.HTML(http.StatusOK, tplCommitPage) ctx.HTML(http.StatusOK, tplCommitPage)
} }

@ -26,7 +26,7 @@ func Contributors(ctx *context.Context) {
// ContributorsData renders JSON of contributors along with their weekly commit statistics // ContributorsData renders JSON of contributors along with their weekly commit statistics
func ContributorsData(ctx *context.Context) { func ContributorsData(ctx *context.Context) {
if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.CommitID); err != nil { if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch); err != nil {
if errors.Is(err, contributors_service.ErrAwaitGeneration) { if errors.Is(err, contributors_service.ErrAwaitGeneration) {
ctx.Status(http.StatusAccepted) ctx.Status(http.StatusAccepted)
return return

@ -180,7 +180,7 @@ func editFile(ctx *context.Context, isNewFile bool) {
ctx.Data["TreeNames"] = treeNames ctx.Data["TreeNames"] = treeNames
ctx.Data["TreePaths"] = treePaths ctx.Data["TreePaths"] = treePaths
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
ctx.Data["commit_summary"] = "" ctx.Data["commit_summary"] = ""
ctx.Data["commit_message"] = "" ctx.Data["commit_message"] = ""
if canCommit { if canCommit {
@ -428,7 +428,7 @@ func DiffPreviewPost(ctx *context.Context) {
// DeleteFile render delete file page // DeleteFile render delete file page
func DeleteFile(ctx *context.Context) { func DeleteFile(ctx *context.Context) {
ctx.Data["PageIsDelete"] = true ctx.Data["PageIsDelete"] = true
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
treePath := cleanUploadFileName(ctx.Repo.TreePath) treePath := cleanUploadFileName(ctx.Repo.TreePath)
if treePath != ctx.Repo.TreePath { if treePath != ctx.Repo.TreePath {
@ -462,7 +462,7 @@ func DeleteFilePost(ctx *context.Context) {
} }
ctx.Data["PageIsDelete"] = true ctx.Data["PageIsDelete"] = true
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
ctx.Data["TreePath"] = ctx.Repo.TreePath ctx.Data["TreePath"] = ctx.Repo.TreePath
ctx.Data["commit_summary"] = form.CommitSummary ctx.Data["commit_summary"] = form.CommitSummary
ctx.Data["commit_message"] = form.CommitMessage ctx.Data["commit_message"] = form.CommitMessage
@ -604,7 +604,7 @@ func UploadFile(ctx *context.Context) {
ctx.Data["TreeNames"] = treeNames ctx.Data["TreeNames"] = treeNames
ctx.Data["TreePaths"] = treePaths ctx.Data["TreePaths"] = treePaths
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
ctx.Data["commit_summary"] = "" ctx.Data["commit_summary"] = ""
ctx.Data["commit_message"] = "" ctx.Data["commit_message"] = ""
if canCommit { if canCommit {

@ -4,25 +4,14 @@
package repo package repo
import ( import (
"net/url"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
) )
func HandleGitError(ctx *context.Context, msg string, err error) { func HandleGitError(ctx *context.Context, msg string, err error) {
if git.IsErrNotExist(err) { if git.IsErrNotExist(err) {
refType := "" ctx.Data["NotFoundPrompt"] = ctx.Locale.Tr("repo.tree_path_not_found", ctx.Repo.TreePath, ctx.Repo.RefTypeNameSubURL())
switch { ctx.Data["NotFoundGoBackURL"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.RefTypeNameSubURL()
case ctx.Repo.IsViewBranch:
refType = "branch"
case ctx.Repo.IsViewTag:
refType = "tag"
case ctx.Repo.IsViewCommit:
refType = "commit"
}
ctx.Data["NotFoundPrompt"] = ctx.Locale.Tr("repo.tree_path_not_found_"+refType, ctx.Repo.TreePath, url.PathEscape(ctx.Repo.RefName))
ctx.Data["NotFoundGoBackURL"] = ctx.Repo.RepoLink + "/src/" + refType + "/" + url.PathEscape(ctx.Repo.RefName)
ctx.NotFound(msg, err) ctx.NotFound(msg, err)
} else { } else {
ctx.ServerError(msg, err) ctx.ServerError(msg, err)

@ -109,7 +109,7 @@ func RemoveDependency(ctx *context.Context) {
} }
// Dependency Type // Dependency Type
depTypeStr := ctx.Req.PostForm.Get("dependencyType") depTypeStr := ctx.Req.PostFormValue("dependencyType")
var depType issues_model.DependencyType var depType issues_model.DependencyType

@ -38,7 +38,7 @@ func TestInitializeLabels(t *testing.T) {
contexttest.LoadRepo(t, ctx, 2) contexttest.LoadRepo(t, ctx, 2)
web.SetForm(ctx, &forms.InitializeLabelsForm{TemplateName: "Default"}) web.SetForm(ctx, &forms.InitializeLabelsForm{TemplateName: "Default"})
InitializeLabels(ctx) InitializeLabels(ctx)
assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ unittest.AssertExistsAndLoadBean(t, &issues_model.Label{
RepoID: 2, RepoID: 2,
Name: "enhancement", Name: "enhancement",
@ -84,7 +84,7 @@ func TestNewLabel(t *testing.T) {
Color: "#abcdef", Color: "#abcdef",
}) })
NewLabel(ctx) NewLabel(ctx)
assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ unittest.AssertExistsAndLoadBean(t, &issues_model.Label{
Name: "newlabel", Name: "newlabel",
Color: "#abcdef", Color: "#abcdef",
@ -104,7 +104,7 @@ func TestUpdateLabel(t *testing.T) {
IsArchived: true, IsArchived: true,
}) })
UpdateLabel(ctx) UpdateLabel(ctx)
assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.Status()) assert.EqualValues(t, http.StatusSeeOther, ctx.Resp.WrittenStatus())
unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ unittest.AssertExistsAndLoadBean(t, &issues_model.Label{
ID: 2, ID: 2,
Name: "newnameforlabel", Name: "newnameforlabel",
@ -120,7 +120,7 @@ func TestDeleteLabel(t *testing.T) {
contexttest.LoadRepo(t, ctx, 1) contexttest.LoadRepo(t, ctx, 1)
ctx.Req.Form.Set("id", "2") ctx.Req.Form.Set("id", "2")
DeleteLabel(ctx) DeleteLabel(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, http.StatusOK, ctx.Resp.WrittenStatus())
unittest.AssertNotExistsBean(t, &issues_model.Label{ID: 2}) unittest.AssertNotExistsBean(t, &issues_model.Label{ID: 2})
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{LabelID: 2}) unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{LabelID: 2})
assert.EqualValues(t, ctx.Tr("repo.issues.label_deletion_success"), ctx.Flash.SuccessMsg) assert.EqualValues(t, ctx.Tr("repo.issues.label_deletion_success"), ctx.Flash.SuccessMsg)
@ -134,7 +134,7 @@ func TestUpdateIssueLabel_Clear(t *testing.T) {
ctx.Req.Form.Set("issue_ids", "1,3") ctx.Req.Form.Set("issue_ids", "1,3")
ctx.Req.Form.Set("action", "clear") ctx.Req.Form.Set("action", "clear")
UpdateIssueLabel(ctx) UpdateIssueLabel(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, http.StatusOK, ctx.Resp.WrittenStatus())
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: 1}) unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: 1})
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: 3}) unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: 3})
unittest.CheckConsistencyFor(t, &issues_model.Label{}) unittest.CheckConsistencyFor(t, &issues_model.Label{})
@ -160,7 +160,7 @@ func TestUpdateIssueLabel_Toggle(t *testing.T) {
ctx.Req.Form.Set("action", testCase.Action) ctx.Req.Form.Set("action", testCase.Action)
ctx.Req.Form.Set("id", strconv.Itoa(int(testCase.LabelID))) ctx.Req.Form.Set("id", strconv.Itoa(int(testCase.LabelID)))
UpdateIssueLabel(ctx) UpdateIssueLabel(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, http.StatusOK, ctx.Resp.WrittenStatus())
for _, issueID := range testCase.IssueIDs { for _, issueID := range testCase.IssueIDs {
if testCase.ExpectedAdd { if testCase.ExpectedAdd {
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: testCase.LabelID}) unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: testCase.LabelID})

@ -26,13 +26,9 @@ type userSearchResponse struct {
Results []*userSearchInfo `json:"results"` Results []*userSearchInfo `json:"results"`
} }
// IssuePosters get posters for current repo's issues/pull requests func IssuePullPosters(ctx *context.Context) {
func IssuePosters(ctx *context.Context) { isPullList := ctx.PathParam("type") == "pulls"
issuePosters(ctx, false) issuePosters(ctx, isPullList)
}
func PullPosters(ctx *context.Context) {
issuePosters(ctx, true)
} }
func issuePosters(ctx *context.Context, isPullList bool) { func issuePosters(ctx *context.Context, isPullList bool) {

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

Loading…
Cancel
Save