Merge branch 'go-gitea:main' into dev

This commit is contained in:
Anthony Wang 2022-04-26 17:48:26 -05:00
commit 1e2f9863e7
Signed by: a
GPG key ID: BC96B00AEC5F2D76
89 changed files with 754 additions and 549 deletions

View file

@ -646,7 +646,9 @@ release-sources: | $(DIST_DIRS)
echo $(VERSION) > $(STORED_VERSION_FILE)
# bsdtar needs a ^ to prevent matching subdirectories
$(eval EXCL := --exclude=$(shell tar --help | grep -q bsdtar && echo "^")./)
tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
# use transform to a add a release-folder prefix; in bsdtar the transform parameter equivalent is -s
$(eval TRANSFORM := $(shell tar --help | grep -q bsdtar && echo "-s '/^./gitea-src-$(VERSION)/'" || echo "--transform 's|^./|gitea-src-$(VERSION)/|'"))
tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) $(TRANSFORM) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
rm -f $(STORED_VERSION_FILE)
.PHONY: release-docs

View file

@ -7,6 +7,7 @@ package cmd
import (
"fmt"
"io"
"os"
"path"
"path/filepath"
@ -25,10 +26,21 @@ import (
"github.com/urfave/cli"
)
func addFile(w archiver.Writer, filePath, absPath string, verbose bool) error {
func addReader(w archiver.Writer, r io.ReadCloser, info os.FileInfo, customName string, verbose bool) error {
if verbose {
log.Info("Adding file %s\n", filePath)
log.Info("Adding file %s", customName)
}
return w.Write(archiver.File{
FileInfo: archiver.FileInfo{
FileInfo: info,
CustomName: customName,
},
ReadCloser: r,
})
}
func addFile(w archiver.Writer, filePath, absPath string, verbose bool) error {
file, err := os.Open(absPath)
if err != nil {
return err
@ -39,13 +51,7 @@ func addFile(w archiver.Writer, filePath, absPath string, verbose bool) error {
return err
}
return w.Write(archiver.File{
FileInfo: archiver.FileInfo{
FileInfo: fileInfo,
CustomName: filePath,
},
ReadCloser: file,
})
return addReader(w, file, fileInfo, filePath, verbose)
}
func isSubdir(upper, lower string) (bool, error) {
@ -136,6 +142,10 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
Name: "skip-attachment-data",
Usage: "Skip attachment data",
},
cli.BoolFlag{
Name: "skip-package-data",
Usage: "Skip package data",
},
cli.GenericFlag{
Name: "type",
Value: outputTypeEnum,
@ -241,13 +251,7 @@ func runDump(ctx *cli.Context) error {
return err
}
return w.Write(archiver.File{
FileInfo: archiver.FileInfo{
FileInfo: info,
CustomName: path.Join("data", "lfs", objPath),
},
ReadCloser: object,
})
return addReader(w, object, info, path.Join("data", "lfs", objPath), verbose)
}); err != nil {
fatal("Failed to dump LFS objects: %v", err)
}
@ -326,6 +330,7 @@ func runDump(ctx *cli.Context) error {
excludes = append(excludes, setting.RepoRootPath)
excludes = append(excludes, setting.LFS.Path)
excludes = append(excludes, setting.Attachment.Path)
excludes = append(excludes, setting.Packages.Path)
excludes = append(excludes, setting.LogRootPath)
excludes = append(excludes, absFileName)
if err := addRecursiveExclude(w, "data", setting.AppDataPath, excludes, verbose); err != nil {
@ -341,17 +346,24 @@ func runDump(ctx *cli.Context) error {
return err
}
return w.Write(archiver.File{
FileInfo: archiver.FileInfo{
FileInfo: info,
CustomName: path.Join("data", "attachments", objPath),
},
ReadCloser: object,
})
return addReader(w, object, info, path.Join("data", "attachments", objPath), verbose)
}); err != nil {
fatal("Failed to dump attachments: %v", err)
}
if ctx.IsSet("skip-package-data") && ctx.Bool("skip-package-data") {
log.Info("Skip dumping package data")
} else if err := storage.Packages.IterateObjects(func(objPath string, object storage.Object) error {
info, err := object.Stat()
if err != nil {
return err
}
return addReader(w, object, info, path.Join("data", "packages", objPath), verbose)
}); err != nil {
fatal("Failed to dump packages: %v", err)
}
// Doesn't check if LogRootPath exists before processing --skip-log intentionally,
// ensuring that it's clear the dump is skipped whether the directory's initialized
// yet or not.

View file

@ -398,6 +398,7 @@ INTERNAL_TOKEN=
;; By modifying the Gitea database, users can gain Gitea administrator privileges.
;; It also enables them to access other resources available to the user on the operating system that is running the Gitea instance and perform arbitrary actions in the name of the Gitea OS user.
;; WARNING: This maybe harmful to you website or your operating system.
;; WARNING: Setting this to true does not change existing hooks in git repos; adjust it before if necessary.
;DISABLE_GIT_HOOKS = true
;;
;; Set to true to disable webhooks feature.

View file

@ -497,6 +497,7 @@ Certain queues have defaults that override the defaults set in `[queue]` (this o
It also enables them to access other resources available to the user on the operating system that is running the
Gitea instance and perform arbitrary actions in the name of the Gitea OS user.
This maybe harmful to you website or your operating system.
Setting this to true does not change existing hooks in git repos; adjust it before if necessary.
- `DISABLE_WEBHOOKS`: **false**: Set to `true` to disable webhooks feature.
- `ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET`: **true**: Set to `false` to allow local users to push to gitea-repositories without setting up the Gitea environment. This is not recommended and if you want local users to push to Gitea repositories you should set the environment appropriately.
- `IMPORT_LOCAL_PATHS`: **false**: Set to `false` to prevent all users (including admin) from importing local path on server.

View file

@ -43,7 +43,7 @@ Vous devriez avoir une instance fonctionnelle de Gitea. Pour accèder à l'inter
## Named Volumes
Ce guide aboutira à une installation avec les données Gita et PostgreSQL stockées dans des volumes nommés. Cela permet une sauvegarde, une restauration et des mises à niveau en toute simplicité.
Ce guide aboutira à une installation avec les données Gitea et PostgreSQL stockées dans des volumes nommés. Cela permet une sauvegarde, une restauration et des mises à niveau en toute simplicité.
### The Database

View file

@ -313,8 +313,13 @@ in the current directory.
- `--tempdir path`, `-t path`: Path to the temporary directory used. Optional. (default: /tmp).
- `--skip-repository`, `-R`: Skip the repository dumping. Optional.
- `--skip-custom-dir`: Skip dumping of the custom dir. Optional.
- `--skip-lfs-data`: Skip dumping of LFS data. Optional.
- `--skip-attachment-data`: Skip dumping of attachment data. Optional.
- `--skip-package-data`: Skip dumping of package data. Optional.
- `--skip-log`: Skip dumping of log data. Optional.
- `--database`, `-d`: Specify the database SQL syntax. Optional.
- `--verbose`, `-V`: If provided, shows additional details. Optional.
- `--type`: Set the dump output format. Optional. (default: zip)
- Examples:
- `gitea dump`
- `gitea dump --verbose`

View file

@ -112,6 +112,13 @@ func TestMain(m *testing.M) {
}
}
os.Unsetenv("GIT_AUTHOR_NAME")
os.Unsetenv("GIT_AUTHOR_EMAIL")
os.Unsetenv("GIT_AUTHOR_DATE")
os.Unsetenv("GIT_COMMITTER_NAME")
os.Unsetenv("GIT_COMMITTER_EMAIL")
os.Unsetenv("GIT_COMMITTER_DATE")
err := unittest.InitFixtures(
unittest.FixturesOptions{
Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"),

View file

@ -1194,7 +1194,8 @@ func GetIssuesByIDs(issueIDs []int64) ([]*Issue, error) {
// IssuesOptions represents options of an issue.
type IssuesOptions struct {
db.ListOptions
RepoIDs []int64 // include all repos if empty
RepoID int64 // overwrites RepoCond if not 0
RepoCond builder.Cond
AssigneeID int64
PosterID int64
MentionedID int64
@ -1285,15 +1286,15 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
sess.In("issue.id", opts.IssueIDs)
}
if len(opts.RepoIDs) > 0 {
applyReposCondition(sess, opts.RepoIDs)
if opts.RepoID != 0 {
opts.RepoCond = builder.Eq{"issue.repo_id": opts.RepoID}
}
if opts.RepoCond != nil {
sess.And(opts.RepoCond)
}
switch opts.IsClosed {
case util.OptionalBoolTrue:
sess.And("issue.is_closed=?", true)
case util.OptionalBoolFalse:
sess.And("issue.is_closed=?", false)
if !opts.IsClosed.IsNone() {
sess.And("issue.is_closed=?", opts.IsClosed.IsTrue())
}
if opts.AssigneeID > 0 {
@ -1412,10 +1413,6 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organizati
return cond
}
func applyReposCondition(sess *xorm.Session, repoIDs []int64) *xorm.Session {
return sess.In("issue.repo_id", repoIDs)
}
func applyAssigneeCondition(sess *xorm.Session, assigneeID int64) *xorm.Session {
return sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
And("issue_assignees.assignee_id = ?", assigneeID)
@ -1510,20 +1507,10 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
func CountIssues(opts *IssuesOptions) (int64, error) {
e := db.GetEngine(db.DefaultContext)
countsSlice := make([]*struct {
Count int64
}, 0, 1)
sess := e.Select("COUNT(issue.id) AS count").Table("issue")
sess.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
opts.setupSessionNoLimit(sess)
if err := sess.Find(&countsSlice); err != nil {
return 0, fmt.Errorf("unable to CountIssues: %w", err)
}
if len(countsSlice) != 1 {
return 0, fmt.Errorf("unable to get one row result when CountIssues, row count=%d", len(countsSlice))
}
return countsSlice[0].Count, nil
return sess.Count()
}
// GetParticipantsIDsByIssueID returns the IDs of all users who participated in comments of an issue,

View file

@ -57,12 +57,9 @@ func (label *Label) CalOpenIssues() {
// CalOpenOrgIssues calculates the open issues of a label for a specific repo
func (label *Label) CalOpenOrgIssues(repoID, labelID int64) {
repoIDs := []int64{repoID}
labelIDs := []int64{labelID}
counts, _ := CountIssuesByRepo(&IssuesOptions{
RepoIDs: repoIDs,
LabelIDs: labelIDs,
RepoID: repoID,
LabelIDs: []int64{labelID},
})
for _, count := range counts {

View file

@ -66,6 +66,38 @@ func getStopwatch(ctx context.Context, userID, issueID int64) (sw *Stopwatch, ex
return
}
// UserIDCount is a simple coalition of UserID and Count
type UserStopwatch struct {
UserID int64
StopWatches []*Stopwatch
}
// GetUIDsAndNotificationCounts between the two provided times
func GetUIDsAndStopwatch() ([]*UserStopwatch, error) {
sws := []*Stopwatch{}
if err := db.GetEngine(db.DefaultContext).Find(&sws); err != nil {
return nil, err
}
if len(sws) == 0 {
return []*UserStopwatch{}, nil
}
lastUserID := int64(-1)
res := []*UserStopwatch{}
for _, sw := range sws {
if lastUserID == sw.UserID {
lastUserStopwatch := res[len(res)-1]
lastUserStopwatch.StopWatches = append(lastUserStopwatch.StopWatches, sw)
} else {
res = append(res, &UserStopwatch{
UserID: sw.UserID,
StopWatches: []*Stopwatch{sw},
})
}
}
return res, nil
}
// GetUserStopwatches return list of all stopwatches of a user
func GetUserStopwatches(userID int64, listOptions db.ListOptions) ([]*Stopwatch, error) {
sws := make([]*Stopwatch, 0, 8)

View file

@ -21,6 +21,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
"xorm.io/builder"
)
func TestIssue_ReplaceLabels(t *testing.T) {
@ -157,7 +158,7 @@ func TestIssues(t *testing.T) {
},
{
IssuesOptions{
RepoIDs: []int64{1, 3},
RepoCond: builder.In("repo_id", 1, 3),
SortType: "oldest",
ListOptions: db.ListOptions{
Page: 1,
@ -344,7 +345,7 @@ func TestGetRepoIDsForIssuesOptions(t *testing.T) {
},
{
IssuesOptions{
RepoIDs: []int64{1, 2},
RepoCond: builder.In("repo_id", 1, 2),
},
[]int64{1, 2},
},

View file

@ -460,7 +460,7 @@ Please try upgrading to a lower version first (suggested v1.6.4), then upgrade t
// Downgrading Gitea's database version not supported
if int(v-minDBVersion) > len(migrations) {
msg := fmt.Sprintf("Your database (migration version: %d) is for a newer Gita, you can not use the newer database for this old Gitea release (%d).", v, minDBVersion+len(migrations))
msg := fmt.Sprintf("Your database (migration version: %d) is for a newer Gitea, you can not use the newer database for this old Gitea release (%d).", v, minDBVersion+len(migrations))
msg += "\nGitea will exit to keep your database safe and unchanged. Please use the correct Gitea release, do not change the migration version manually (incorrect manual operation may lose data)."
if !setting.IsProd {
msg += fmt.Sprintf("\nIf you are in development and really know what you're doing, you can force changing the migration version by executing: UPDATE version SET version=%d WHERE id=1;", minDBVersion+len(migrations))

View file

@ -362,6 +362,7 @@ func RepoRefForAPI(next http.Handler) http.Handler {
return
}
ctx.Repo.Commit = commit
ctx.Repo.TreePath = ctx.Params("*")
return
}

View file

@ -72,8 +72,8 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e
"authorized_keys file %q is out of date.\nRegenerate it with:\n\t\"%s\"\nor\n\t\"%s\"",
fPath,
"gitea admin regenerate keys",
"gitea doctor --run authorized_keys --fix")
return fmt.Errorf(`authorized_keys is out of date and should be regenerated with "gitea admin regenerate keys" or "gitea doctor --run authorized_keys --fix"`)
"gitea doctor --run authorized-keys --fix")
return fmt.Errorf(`authorized_keys is out of date and should be regenerated with "gitea admin regenerate keys" or "gitea doctor --run authorized-keys --fix"`)
}
logger.Warn("authorized_keys is out of date. Attempting rewrite...")
err = asymkey_model.RewriteAllPublicKeys()

View file

@ -9,7 +9,9 @@ import (
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
@ -80,6 +82,31 @@ loop:
})
}
then = now
if setting.Service.EnableTimetracking {
usersStopwatches, err := models.GetUIDsAndStopwatch()
if err != nil {
log.Error("Unable to get GetUIDsAndStopwatch: %v", err)
return
}
for _, userStopwatches := range usersStopwatches {
apiSWs, err := convert.ToStopWatches(userStopwatches.StopWatches)
if err != nil {
log.Error("Unable to APIFormat stopwatches: %v", err)
continue
}
dataBs, err := json.Marshal(apiSWs)
if err != nil {
log.Error("Unable to marshal stopwatches: %v", err)
continue
}
m.SendMessage(userStopwatches.UserID, &Event{
Name: "stopwatches",
Data: string(dataBs),
})
}
}
}
}
m.UnregisterAll()

View file

@ -57,6 +57,12 @@ func CatFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError,
<-closed
}
// Ensure cancel is called as soon as the provided context is cancelled
go func() {
<-ctx.Done()
cancel()
}()
_, filename, line, _ := runtime.Caller(2)
filename = strings.TrimPrefix(filename, callerPrefix)
@ -101,6 +107,12 @@ func CatFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
<-closed
}
// Ensure cancel is called as soon as the provided context is cancelled
go func() {
<-ctx.Done()
cancel()
}()
_, filename, line, _ := runtime.Caller(2)
filename = strings.TrimPrefix(filename, callerPrefix)

View file

@ -28,8 +28,8 @@ const (
)
// GetRawDiff dumps diff results of repository in given commit ID to io.Writer.
func GetRawDiff(ctx context.Context, repoPath, commitID string, diffType RawDiffType, writer io.Writer) error {
return GetRawDiffForFile(ctx, repoPath, "", commitID, diffType, "", writer)
func GetRawDiff(repo *Repository, commitID string, diffType RawDiffType, writer io.Writer) error {
return GetRepoRawDiffForFile(repo, "", commitID, diffType, "", writer)
}
// GetReverseRawDiff dumps the reverse diff results of repository in given commit ID to io.Writer.
@ -46,17 +46,6 @@ func GetReverseRawDiff(ctx context.Context, repoPath, commitID string, writer io
return nil
}
// GetRawDiffForFile dumps diff results of file in given commit ID to io.Writer.
func GetRawDiffForFile(ctx context.Context, repoPath, startCommit, endCommit string, diffType RawDiffType, file string, writer io.Writer) error {
repo, closer, err := RepositoryFromContextOrOpen(ctx, repoPath)
if err != nil {
return fmt.Errorf("RepositoryFromContextOrOpen: %v", err)
}
defer closer.Close()
return GetRepoRawDiffForFile(repo, startCommit, endCommit, diffType, file, writer)
}
// GetRepoRawDiffForFile dumps diff results of file in given commit ID to io.Writer according given repository
func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diffType RawDiffType, file string, writer io.Writer) error {
commit, err := repo.GetCommit(endCommit)

View file

@ -321,7 +321,7 @@ func populateIssueIndexer(ctx context.Context) {
// UpdateRepoIndexer add/update all issues of the repositories
func UpdateRepoIndexer(repo *repo_model.Repository) {
is, err := models.Issues(&models.IssuesOptions{
RepoIDs: []int64{repo.ID},
RepoID: repo.ID,
IsClosed: util.OptionalBoolNone,
IsPull: util.OptionalBoolNone,
})

View file

@ -1,104 +0,0 @@
// Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package sync
// UniqueQueue is a queue which guarantees only one instance of same
// identity is in the line. Instances with same identity will be
// discarded if there is already one in the line.
//
// This queue is particularly useful for preventing duplicated task
// of same purpose.
type UniqueQueue struct {
table *StatusTable
queue chan string
closed chan struct{}
}
// NewUniqueQueue initializes and returns a new UniqueQueue object.
func NewUniqueQueue(queueLength int) *UniqueQueue {
if queueLength <= 0 {
queueLength = 100
}
return &UniqueQueue{
table: NewStatusTable(),
queue: make(chan string, queueLength),
closed: make(chan struct{}),
}
}
// Close closes this queue
func (q *UniqueQueue) Close() {
select {
case <-q.closed:
default:
q.table.lock.Lock()
select {
case <-q.closed:
default:
close(q.closed)
}
q.table.lock.Unlock()
}
}
// IsClosed returns a channel that is closed when this Queue is closed
func (q *UniqueQueue) IsClosed() <-chan struct{} {
return q.closed
}
// IDs returns the current ids in the pool
func (q *UniqueQueue) IDs() []string {
q.table.lock.Lock()
defer q.table.lock.Unlock()
ids := make([]string, 0, len(q.table.pool))
for id := range q.table.pool {
ids = append(ids, id)
}
return ids
}
// Queue returns channel of queue for retrieving instances.
func (q *UniqueQueue) Queue() <-chan string {
return q.queue
}
// Exist returns true if there is an instance with given identity
// exists in the queue.
func (q *UniqueQueue) Exist(id string) bool {
return q.table.IsRunning(id)
}
// AddFunc adds new instance to the queue with a custom runnable function,
// the queue is blocked until the function exits.
func (q *UniqueQueue) AddFunc(id string, fn func()) {
q.table.lock.Lock()
if _, ok := q.table.pool[id]; ok {
q.table.lock.Unlock()
return
}
q.table.pool[id] = struct{}{}
if fn != nil {
fn()
}
q.table.lock.Unlock()
select {
case <-q.closed:
return
case q.queue <- id:
return
}
}
// Add adds new instance to the queue.
func (q *UniqueQueue) Add(id string) {
q.AddFunc(id, nil)
}
// Remove removes instance from the queue.
func (q *UniqueQueue) Remove(id string) {
q.table.Stop(id)
}

View file

@ -13,32 +13,10 @@ import (
"code.gitea.io/gitea/modules/setting"
)
var loopbackIPBlocks []*net.IPNet
var externalTrackerRegex = regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`)
func init() {
for _, cidr := range []string{
"127.0.0.0/8", // IPv4 loopback
"::1/128", // IPv6 loopback
} {
if _, block, err := net.ParseCIDR(cidr); err == nil {
loopbackIPBlocks = append(loopbackIPBlocks, block)
}
}
}
func isLoopbackIP(ip string) bool {
pip := net.ParseIP(ip)
if pip == nil {
return false
}
for _, block := range loopbackIPBlocks {
if block.Contains(pip) {
return true
}
}
return false
return net.ParseIP(ip).IsLoopback()
}
// IsValidURL checks if URL is valid

View file

@ -72,6 +72,7 @@ loading=Зареждане…
error404=Страницата, която се опитвате да достъпите, <strong>не съществува</strong> или <strong>не сте оторизирани</strong> да я достъпите.
[error]
[startpage]

View file

@ -86,6 +86,7 @@ error404=Stránka, kterou se snažíte zobrazit, buď <strong>neexistuje</strong
never=Nikdy
[error]
missing_csrf=Špatný požadavek: Neexistuje CSRF token

View file

@ -103,6 +103,7 @@ error404=Die Seite, die du gerade versuchst aufzurufen, <strong>existiert entwed
never=Niemals
[error]
occurred=Ein Fehler ist aufgetreten
report_message=Wenn du dir sicher bist, dass dies ein Fehler von Gitea ist, suche bitte auf <a href="https://github.com/go-gitea/gitea/issues">GitHub</a> nach diesem Fehler und erstelle gegebenenfalls ein neues Issue.

View file

@ -105,6 +105,7 @@ error404=Η σελίδα που προσπαθείτε να φτάσετε εί
never=Ποτέ
[error]
occurred=Παρουσιάστηκε ένα σφάλμα
report_message=Αν είστε σίγουροι ότι πρόκειται για ένα πρόβλημα στο Gitea, παρακαλώ αναζητήστε στα ζητήματα στο <a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a> ή ανοίξτε ένα νέο ζήτημα εάν είναι απαραίτητο.

View file

@ -105,6 +105,8 @@ error404 = The page you are trying to reach either <strong>does not exist</stron
never = Never
rss_feed = RSS Feed
[error]
occurred = An error occurred
report_message = If you are sure this is a Gitea bug, please search for issues on <a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a> or open a new issue if necessary.
@ -1039,6 +1041,7 @@ line_unicode = `This line has hidden unicode characters`
escape_control_characters = Escape
unescape_control_characters = Unescape
file_copy_permalink = Copy Permalink
view_git_blame = View Git Blame
video_not_supported_in_browser = Your browser does not support the HTML5 'video' tag.
audio_not_supported_in_browser = Your browser does not support the HTML5 'audio' tag.
stored_lfs = Stored with Git LFS
@ -3086,7 +3089,7 @@ settings.link = Link this package to a repository
settings.link.description = If you link a package with a repository, the package is listed in the repository's package list.
settings.link.select = Select Repository
settings.link.button = Update Repository Link
settings.link.success = Repository link was successfully updated.
settings.link.success = Repository link was successfully updated.
settings.link.error = Failed to update repository link.
settings.delete = Delete package
settings.delete.description = Deleting a package is permanent and cannot be undone.

View file

@ -105,6 +105,7 @@ error404=La página a la que está intentando acceder o <strong>no existe</stron
never=Nunca
[error]
occurred=Ha ocurrido un error
report_message=Si estás seguro de que este es un error de Gitea, por favor busca un problema en <a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a> y abre un nuevo problema si es necesario.

View file

@ -91,6 +91,7 @@ error404=صفحه موردنظر شما یا <strong>وجود ندارد</strong
never=هرگز
[error]
missing_csrf=درخواست بد: بلیط CSRF ندارد

View file

@ -75,6 +75,7 @@ loading=Ladataan…
error404=Sivu, jota yrität nähdä, joko <strong>ei löydy</strong> tai <strong>et ole oikeutettu</strong> katsomaan sitä.
[error]
[startpage]

View file

@ -91,6 +91,7 @@ error404=La page que vous essayez d'atteindre <strong>n'existe pas</strong> ou <
never=Jamais
[error]
missing_csrf=Requête incorrecte: aucun jeton CSRF présent

View file

@ -80,6 +80,7 @@ step2=2. lépés:
error404=Az elérni kívánt oldal vagy <strong>nem létezik</strong>, vagy <strong>nincs jogosultsága</strong> a megtekintéséhez.
[error]
[startpage]

View file

@ -73,6 +73,7 @@ loading=Memuat…
[error]
[startpage]

View file

@ -105,6 +105,7 @@ error404=Síðan sem þú ert að reyna að fá annað hvort <strong>er ekki til
never=Aldrei
[error]
occurred=Villa kom upp
report_message=Ef þú ert viss um að þetta sé villa í Gitea þá skaltu leita að vandamálum á <a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a> eða opna nýtt vandamál ef þörf krefst.

View file

@ -83,6 +83,7 @@ step2=Passo 2:
error404=La pagina che stai cercando di raggiungere <strong>non esiste</strong> oppure <strong>non sei autorizzato</strong> a visualizzarla.
[error]
[startpage]

View file

@ -105,6 +105,7 @@ error404=アクセスしようとしたページは<strong>存在しない</stro
never=無し
[error]
occurred=エラーが発生しました.
report_message=Gitea のバグが疑われる場合は、<a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a>でIssueを検索して、見つからなければ新しいIssueを作成してください。
@ -3051,6 +3052,9 @@ container.labels.key=キー
container.labels.value=
generic.download=コマンドラインでパッケージをダウンロードします:
generic.documentation=汎用 レジストリの詳細については、<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/generic">ドキュメント</a> を参照してください。
helm.registry=このレジストリをコマンドラインからセットアップします:
helm.install=パッケージをインストールするには、次のコマンドを実行します:
helm.documentation=Helm レジストリの詳細については、 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/helm/">ドキュメント</a> を参照してください。
maven.registry=あなたのプロジェクトの <code>pom.xml</code> ファイルに、このレジストリをセットアップします:
maven.install=パッケージを使用するため <code>pom.xml</code> ファイル内の <code>dependencies</code> ブロックに以下を含めます:
maven.install2=コマンドラインで実行します:

View file

@ -74,6 +74,7 @@ loading=불러오는 중...
[error]
[startpage]

View file

@ -105,6 +105,7 @@ error404=Lapa, ko vēlaties atvērt, <strong>neeksistē</strong> vai arī <stron
never=Nekad
[error]
occurred=Radusies kļūda
report_message=Ja esat pārliecināts, ka šī ir Gitea kļūda, pārbaudiet, ka tā jau nav zināma meklējot <a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a> vai ziņojiet par jaunu kļūdu, ja nepieciešams.

View file

@ -65,6 +65,7 @@ loading=ലഭ്യമാക്കുന്നു…
[error]
[startpage]

View file

@ -90,6 +90,7 @@ error404=De pagina die u probeert te bereiken <strong>bestaat niet</strong> of <
never=Nooit
[error]
missing_csrf=Foutief verzoek: geen CSRF-token aanwezig

View file

@ -105,6 +105,7 @@ error404=Strona, do której próbujesz dotrzeć <strong>nie istnieje</strong> lu
never=Nigdy
[error]
occurred=Wystąpił błąd
report_message=Jeśli jesteś pewien, że jest to błąd Gitea, poszukaj już istniejącego zgłoszenia na <a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a> lub w razie potrzeby otwórz nowy problem.

View file

@ -1,4 +1,4 @@
home=Página inicial
home=Inicio
dashboard=Painel
explore=Explorar
help=Ajuda
@ -105,6 +105,7 @@ error404=A página que você está tentando acessar <strong>não existe</strong>
never=Nunca
[error]
occurred=Ocorreu um erro
report_message=Se você tem certeza de que é um bug do Gitea, procure por issues no <a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a> ou abra uma nova issue, se necessário.
@ -226,8 +227,8 @@ default_keep_email_private=Ocultar endereços de e-mail por padrão
default_keep_email_private_popup=Ocultar endereços de e-mail de novas contas de usuário por padrão.
default_allow_create_organization=Permitir a criação de organizações por padrão
default_allow_create_organization_popup=Permitir que novas contas de usuários criem organizações por padrão.
default_enable_timetracking=Habilitar o contador de tempo por padrão
default_enable_timetracking_popup=Habilitar o contador de tempo para novos repositórios por padrão.
default_enable_timetracking=Habilitar o Cronômetro por Padrão
default_enable_timetracking_popup=Habilitar o cronômetro para novos repositórios por padrão.
no_reply_address=Domínio de e-mail oculto
no_reply_address_helper=Nome de domínio para usuários com um endereço de e-mail oculto. Por exemplo, o nome de usuário 'joe' será registrado no Git como 'joe@noreply.example.org' se o domínio de e-mail oculto estiver definido como 'noreply.example.org'.
password_algorithm=Algoritmo Hash de Senha
@ -267,6 +268,7 @@ organizations=Organizações
search=Pesquisar
code=Código
search.fuzzy=Similar
search.match=Correspondência
code_search_unavailable=A pesquisa por código não está disponível no momento. Entre em contato com o administrador do site.
repo_no_results=Nenhum repositório correspondente foi encontrado.
user_no_results=Nenhum usuário correspondente foi encontrado.
@ -451,6 +453,7 @@ lang_select_error=Selecione um idioma da lista.
username_been_taken=O nome de usuário já está sendo usado.
username_change_not_local_user=Usuários não-locais não são autorizados a alterar nome de usuário.
repo_name_been_taken=O nome de repositório já está sendo usado.
repository_force_private=Forçar Privado está ativado: repositórios privados não podem ser tornados públicos.
repository_files_already_exist=Arquivos já existem neste repositório. Contate o administrador.
repository_files_already_exist.adopt=Arquivos já existem neste repositório e só podem ser adotados.
repository_files_already_exist.delete=Arquivos já existem neste repositório. Você deve deletá-los.
@ -555,14 +558,17 @@ hidden_comment_types=Tipos de comentários ocultos
comment_type_group_reference=Referência
comment_type_group_label=Rótulo
comment_type_group_milestone=Marco
comment_type_group_assignee=Atribuído
comment_type_group_title=Título
comment_type_group_branch=Branch
comment_type_group_time_tracking=Contador de tempo
comment_type_group_deadline=Prazo final
comment_type_group_dependency=Dependência
comment_type_group_lock=Status de Bloqueio
comment_type_group_review_request=Revisar solicitação
comment_type_group_pull_request_push=Commits adicionados
comment_type_group_project=Projeto
comment_type_group_issue_ref=Referência do issue
saved_successfully=Suas configurações foram salvas com sucesso.
privacy=Privacidade
keep_activity_private=Ocultar a atividade da página de perfil
@ -764,6 +770,7 @@ twofa_failed_get_secret=Falha ao obter o segredo.
webauthn_desc=Chaves de segurança são dispositivos de hardware que contém chaves de criptografia. Elas podem ser usadas para autenticação de dois fatores. A chave de segurança deve suportar o padrão <a rel="noreferrer" target="_blank" href="https://w3c.github.io/webauthn/#webauthn-authenticator">WebAuthnn Authenticator</a>.
webauthn_register_key=Adicionar chave de segurança
webauthn_nickname=Apelido
webauthn_delete_key=Remover chave de segurança
webauthn_delete_key_desc=Se você remover uma chave de segurança, não poderá mais entrar com ela. Continuar?
@ -1375,13 +1382,14 @@ issues.delete.title=Apagar esta issue?
issues.delete.text=Você realmente deseja excluir esta issue? (Isto irá remover permanentemente todo o conteúdo. Considere fechá-la em vez disso, se você pretende mantê-la arquivado)
issues.tracker=Contador de tempo
issues.start_tracking_short=Iniciar Cronômetro
issues.start_tracking=Iniciar contador de tempo
issues.start_tracking=Iniciar Cronômetro
issues.start_tracking_history=`começou a trabalhar %s`
issues.tracker_auto_close=Contador de tempo será parado automaticamente quando esta issue for fechada
issues.stop_tracking=Parar cronômetro
issues.tracking_already_started=`Você já iniciou o cronômetro em <a href="%s">outra issue</a>!`
issues.stop_tracking=Parar Cronômetro
issues.stop_tracking_history=`parou de trabalhar %s`
issues.cancel_tracking=Descartar
issues.cancel_tracking_history=`cancelou contador de tempo %s`
issues.cancel_tracking_history=`cancelou o cronômetro %s`
issues.add_time=Adicionar tempo manualmente
issues.del_time=Apagar este registro de tempo
issues.add_time_short=Adicionar tempo
@ -1774,7 +1782,7 @@ settings.tracker_issue_style=Formato de número do issue tracker externo
settings.tracker_issue_style.numeric=Numérico
settings.tracker_issue_style.alphanumeric=Alfanumérico
settings.tracker_url_format_desc=Use os espaços reservados <code>{user}</code>, <code>{repo}</code> e <code>{index}</code> para o nome de usuário, nome do repositório e o índice de problemas.
settings.enable_timetracker=Habilitar contador de tempo
settings.enable_timetracker=Habilitar Cronômetro
settings.allow_only_contributors_to_track_time=Permitir que apenas os colaboradores acompanhem o contador de tempo
settings.pulls_desc=Habilitar pull requests no repositório
settings.pulls.ignore_whitespace=Ignorar espaço em branco em conflitos
@ -1944,6 +1952,7 @@ settings.event_pull_request_review_desc=Pull request aprovado, rejeitado ou revi
settings.event_pull_request_sync=Pull Request Sincronizado
settings.event_pull_request_sync_desc=Pull request sincronizado.
settings.event_package=Pacote
settings.event_package_desc=Pacote criado ou excluído em um repositório.
settings.branch_filter=Filtro de branch
settings.branch_filter_desc=Lista dos branches a serem considerados nos eventos push, criação de branch e exclusão de branch, especificados como padrão glob. Se estiver vazio ou for <code>*</code>, eventos para todos os branches serão relatados. Veja <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> documentação da sintaxe. Exemplos: <code>master</code>, <code>{master,release*}</code>.
settings.active=Ativo
@ -2025,6 +2034,7 @@ settings.dismiss_stale_approvals_desc=Quando novos commits que mudam o conteúdo
settings.require_signed_commits=Exibir commits assinados
settings.require_signed_commits_desc=Rejeitar pushes para este branch se não estiverem assinados ou não forem validáveis.
settings.protect_protected_file_patterns=Padrões de arquivos protegidos (separados usando ponto e vírgula '\;'):
settings.protect_protected_file_patterns_desc=Arquivos protegidos que não têm permissão para serem alterados diretamente, mesmo se o usuário tiver permissão para adicionar, editar ou apagar arquivos neste branch. Vários padrões podem ser separados usando ponto e vírgula ('\;'). Veja <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> documentação para sintaxe de padrões. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.protect_unprotected_file_patterns=Padrões de arquivos desprotegidos (separados usando ponto e vírgula '\;'):
settings.protect_unprotected_file_patterns_desc=Arquivos não protegidos que podem ser alterados diretamente se o usuário tiver acesso de gravação, ignorando as restrições de push. Vários padrões podem ser separados usando ponto e vírgula ('\;'). Veja <a href="https://pkg.go.dev/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> documentação para sintaxe de padrões. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.add_protected_branch=Habilitar proteção
@ -2152,6 +2162,7 @@ diff.review.placeholder=Comentário da revisão
diff.review.comment=Comentar
diff.review.approve=Aprovar
diff.review.reject=Solicitar alterações
diff.committed_by=commit de
diff.protected=Protegido
diff.image.side_by_side=Lado a Lado
diff.image.swipe=Deslizar
@ -2414,7 +2425,9 @@ dashboard.check_repo_stats=Verificar estatísticas de todos os repositórios
dashboard.archive_cleanup=Apagar arquivos antigos de repositório
dashboard.deleted_branches_cleanup=Realizar limpeza de branches apagados
dashboard.git_gc_repos=Coleta de lixo em todos os repositórios
dashboard.resync_all_sshkeys=Atualizar o arquivo '.ssh/authorized_keys' com as chaves SSH do Gitea.
dashboard.resync_all_sshkeys.desc=(Não necessário para o servidor SSH embutido.)
dashboard.resync_all_sshprincipals=Atualizar o arquivo '.ssh/authorized_principals' com os diretores do Gitea SSH.
dashboard.resync_all_sshprincipals.desc=(Não necessário para o servidor SSH embutido.)
dashboard.resync_all_hooks=Ressincronizar hooks pre-receive, update e post-receive de todos os repositórios.
dashboard.reinit_missing_repos=Reinicializar todos os repositórios Git perdidos cujos registros existem
@ -2483,12 +2496,14 @@ users.prohibit_login=Desabilitar acesso
users.is_admin=É administrador
users.is_restricted=Está restrito
users.allow_git_hook=Pode criar hooks Git
users.allow_git_hook_tooltip=Hooks Git são executados como o usuário do SO que executa Gitea e terá o mesmo nível de acesso ao servidor. Como resultado, os usuários com esse privilégio especial de Hook do Git podem acessar e modificar todos os repositórios do Gitea, bem como o banco de dados usado pelo Gitea. Por conseguinte, podem também obter privilégios de administrador do Gitea.
users.allow_import_local=Pode importar repositórios locais
users.allow_create_organization=Pode criar organizações
users.update_profile=Atualizar conta de usuário
users.delete_account=Excluir conta de usuário
users.still_own_repo=Este usuário ainda possui um ou mais repositórios. Exclua ou transfira esses repositórios primeiro.
users.still_has_org=Este usuário é membro de uma organização. Remova o usuário de qualquer organização primeiro.
users.still_own_packages=Este usuário ainda possui um ou mais pacotes. Exclua esses pacotes primeiro.
users.deletion_success=A conta de usuário foi excluída.
users.reset_2fa=Reinicializar 2FA
users.list_status_filter.menu_text=Filtro
@ -2525,6 +2540,7 @@ orgs.new_orga=Nova organização
repos.repo_manage_panel=Gerenciamento do repositório
repos.unadopted=Repositórios Não Adotados
repos.unadopted.no_more=Não foram encontrados mais repositórios não adotados
repos.owner=Proprietário
repos.name=Nome
repos.private=Privado
@ -2543,6 +2559,7 @@ packages.version=Versão
packages.type=Tipo
packages.repository=Repositório
packages.size=Tamanho
packages.published=Publicado
defaulthooks=Webhooks Padrões
defaulthooks.desc=Webhooks automaticamente fazem requisições HTTP POST para um servidor quando acionados por determinados eventos do Gitea. Webhooks definidos aqui são os padrões e serão copiados para todos os novos repositórios. Leia mais no <a target="_blank" rel="noopener" href="https://docs.gitea.io/en-us/webhooks/">guia de webhooks</a>.
@ -2713,8 +2730,8 @@ config.active_code_lives=Ativar Code Lives
config.reset_password_code_lives=Tempo de expiração do código de recuperação de conta
config.default_keep_email_private=Ocultar endereços de e-mail por padrão
config.default_allow_create_organization=Permitir a criação de organizações por padrão
config.enable_timetracking=Habilitar contador de tempo
config.default_enable_timetracking=Habilitar o contador de tempo por padrão
config.enable_timetracking=Habilitar Cronômetro
config.default_enable_timetracking=Habilitar o Cronômetro por Padrão
config.default_allow_only_contributors_to_track_time=Permitir que apenas os colaboradores acompanhem o contador de tempo
config.no_reply_address=Ocultar domínio de e-mail
config.default_visibility_organization=Visibilidade padrão para novas organizações
@ -2970,9 +2987,14 @@ error.unit_not_allowed=Você não tem permissão para acessar esta seção do re
title=Pacotes
desc=Gerenciar pacotes do repositório.
empty=Não há pacotes ainda.
empty.documentation=Para obter mais informações sobre o registro de pacote, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/overview">a documentação</a>.
filter.type=Tipo
filter.type.all=Todos
filter.no_result=Seu filtro não produziu resultados.
filter.container.tagged=Marcado
filter.container.untagged=Desmarcado
published_by=Publicado %[1]s por <a href="%[2]s">%[3]s</a>
published_by_in=Publicado %[1]s por <a href="%[2]s">%[3]s</a> em <a href="%[4]s"><strong>%[5]s</strong></a>
installation=Instalação
about=Sobre este pacote
requirements=Requisitos
@ -2982,27 +3004,64 @@ details=Detalhes
details.author=Autor
details.project_site=Site do Projeto
details.license=Licença
assets=Recursos
versions=Versões
versions.on=em
versions.view_all=Ver todas
dependency.id=ID
dependency.version=Versão
composer.registry=Configure este registro em seu arquivo <code>~/.composer/config.json</code>:
composer.install=Para instalar o pacote usando o Composer, execute o seguinte comando:
composer.documentation=Para obter mais informações sobre o registro do Composer, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/composer/">a documentação</a>.
composer.dependencies=Dependências
composer.dependencies.development=Dependências de Desenvolvimento
conan.details.repository=Repositório
conan.registry=Configure este registro pela linha de comando:
conan.install=Para instalar o pacote usando o Conan, execute o seguinte comando:
conan.documentation=Para obter mais informações sobre o registro Conan, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/conan/">a documentação</a>.
container.details.type=Tipo de Imagem
container.details.platform=Plataforma
container.details.repository_site=Site do Repositório
container.details.documentation_site=Site da Documentação
container.pull=Puxe a imagem pela linha de comando:
container.documentation=Para obter mais informações sobre o registro de Container, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/container/">a documentação</a>.
container.multi_arch=S.O. / Arquitetura
container.layers=Camadas da Imagem
container.labels=Rótulos
container.labels.key=Chave
container.labels.value=Valor
generic.download=Baixar pacote pela linha de comando:
generic.documentation=Para obter mais informações sobre o registro genérico, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/generic">a documentação</a>.
helm.registry=Configurar este registro pela linha de comando:
helm.install=Para instalar o pacote, execute o seguinte comando:
helm.documentation=Para obter mais informações sobre o registro Helm, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/helm/">a documentação</a>.
maven.registry=Configure este registro no arquivo <code>pom.xml</code> do seu projeto:
maven.install=Para usar o pacote inclua o seguinte no bloco de <code>dependencies</code> no arquivo <code>pom.xml</code>:
maven.install2=Executar via linha de comando:
maven.download=Para baixar a dependência, execute via linha de comando:
maven.documentation=Para obter mais informações sobre o registro Maven, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/maven/">a documentação</a>.
nuget.registry=Configurar este registro pela linha de comando:
nuget.install=Para instalar o pacote usando NuGet, execute o seguinte comando:
nuget.documentation=Para obter mais informações sobre o registro Nuget, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/nuget/">a documentação</a>.
npm.registry=Configure este registro no arquivo <code>.npmrc</code> do seu projeto:
npm.install=Para instalar o pacote usando o npm, execute o seguinte comando:
npm.install2=ou adicione-o ao arquivo package.json:
npm.documentation=Para obter mais informações sobre o registro npm, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/npm/">a documentação</a>.
npm.dependencies=Dependências
npm.dependencies.development=Dependências de Desenvolvimento
npm.dependencies.peer=Dependências Peer
npm.dependencies.optional=Dependências Opcionais
npm.details.tag=Tag
pypi.requires=Requer Python
pypi.install=Para instalar o pacote usando pip, execute o seguinte comando:
pypi.documentation=Para obter mais informações sobre o registro PyPI, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/pypi/">a documentação</a>.
rubygems.install=Para instalar o pacote usando gem, execute o seguinte comando:
rubygems.install2=ou adicione-o ao Gemfile:
rubygems.dependencies.runtime=Dependências de Execução
rubygems.dependencies.development=Dependências de Desenvolvimento
rubygems.required.ruby=Requer o Ruby versão
rubygems.required.rubygems=Requer o RubyGem versão
rubygems.documentation=Para obter mais informações sobre o registro do RubyGems, consulte <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/rubygems/">a documentação</a>.
settings.link=Vincular este pacote a um repositório
settings.link.description=Se você vincular um pacote a um repositório, o pacote será listado na lista de pacotes do repositório.
settings.link.select=Selecionar Repositório

View file

@ -105,6 +105,8 @@ error404=A página que pretende aceder <strong>não existe</strong> ou <strong>n
never=Nunca
rss_feed=Fonte RSS
[error]
occurred=Ocorreu um erro
report_message=Se tiver certeza de que se trata de um erro do Gitea, procure, por favor, questões relacionadas no <a href="https://github.com/go-gitea/gitea/issues">GitHub</a> ou abra uma nova questão, se necessário.

View file

@ -45,6 +45,8 @@ webauthn_error_insecure=WebAuthn поддерживает только безо
webauthn_error_unable_to_process=Сервер не смог обработать ваш запрос.
webauthn_error_duplicated=Представленный ключ не подходит для этого запроса. Если вы пытаетесь зарегистрировать его, убедитесь, что ключ ещё не зарегистрирован.
webauthn_error_empty=Вы должны указать имя для этого ключа.
webauthn_error_timeout=Тайм-аут достигнут до того, как ваш ключ был прочитан. Перезагрузите эту страницу и повторите попытку.
webauthn_reload=Обновить
repository=Репозиторий
organization=Организация
@ -102,8 +104,13 @@ error404=Страница, которую вы пытаетесь открыть
never=Никогда
rss_feed=RSS-лента
[error]
occurred=Произошла ошибка
missing_csrf=Некорректный запрос: CSRF токен отсутствует
invalid_csrf=Некорректный запрос: неверный CSRF токен
network_error=Ошибка сети
[startpage]
app_desc=Удобный сервис собственного хостинга репозиториев Git
@ -260,6 +267,7 @@ search=Поиск
code=Код
search.fuzzy=Неточный
search.match=Соответствие
code_search_unavailable=В настоящее время поиск по коду недоступен. Обратитесь к администратору сайта.
repo_no_results=Подходящие репозитории не найдены.
user_no_results=Подходящие пользователи не найдены.
org_no_results=Подходящие организации не найдены.
@ -273,6 +281,7 @@ register_helper_msg=Уже есть аккаунт? Авторизуйтесь!
social_register_helper_msg=Уже есть аккаунт? Свяжите его сейчас!
disable_register_prompt=Извините, возможность регистрации отключена. Пожалуйста, свяжитесь с администратором сайта.
disable_register_mail=Подтверждение регистрации по электронной почте отключено.
manual_activation_only=Обратитесь к администратору сайта для завершения активации.
remember_me=Запомнить это устройство
forgot_password_title=Восстановить пароль
forgot_password=Забыли пароль?
@ -311,6 +320,9 @@ oauth_signup_submit=Полная учётная запись
oauth_signin_tab=Ссылка на существующую учётную запись
oauth_signin_title=Войдите, чтобы авторизовать связанную учётную запись
oauth_signin_submit=Привязать учётную запись
oauth.signin.error=Произошла ошибка при обработке запроса авторизации. Если эта ошибка повторяется, обратитесь к администратору сайта.
oauth.signin.error.access_denied=Запрос на авторизацию был отклонен.
oauth.signin.error.temporarily_unavailable=Ошибка авторизации, так как сервер временно недоступен. Пожалуйста, повторите попытку позже.
openid_connect_submit=Подключить
openid_connect_title=Подключение к существующей учетной записи
openid_connect_desc=Выбранный OpenID URI неизвестен. Свяжите с новой учетной записью здесь.
@ -475,7 +487,9 @@ auth_failed=Ошибка аутентификации: %v
still_own_repo=Ваша учётная запись владеет одним или несколькими репозиториями; сначала удалите или перенесите их.
still_has_org=Ваша учётная запись является членом одной или нескольких организаций; сначала выйдите из них.
still_own_packages=Ваша учётная запись владеет одним или несколькими пакетами, сначала удалите их.
org_still_own_repo=Эта организация по-прежнему владеет одним или несколькими репозиториями; сначала удалите или перенесите их.
org_still_own_packages=Эта организация всё ещё имеет пакеты, сначала удалите их.
target_branch_not_exist=Целевая ветка не существует.
@ -516,6 +530,7 @@ twofa=Двухфакторная аутентификация
account_link=Привязанные аккаунты
organization=Организации
uid=UID
webauthn=Ключи безопасности
public_profile=Открытый профиль
biography_placeholder=Расскажите немного о себе
@ -537,6 +552,19 @@ continue=Далее
cancel=Отмена
language=Язык
ui=Тема
hidden_comment_types=Скрытые типы комментариев
comment_type_group_reference=Упоминания
comment_type_group_label=Операции с метками
comment_type_group_milestone=Этап
comment_type_group_assignee=Назначения
comment_type_group_title=Правки заголовков
comment_type_group_branch=Операции с ветками
comment_type_group_time_tracking=Отслеживание времени
comment_type_group_deadline=Модификации сроков выполнения
comment_type_group_dependency=Модификации зависимостей
comment_type_group_lock=Смена статуса ограничения на обсуждение
comment_type_group_review_request=Запросы на рецензию
saved_successfully=Ваши настройки успешно сохранены.
privacy=Приватность
keep_activity_private=Скрыть активность со страницы профиля
keep_activity_private_popup=Делает активность видимой только для вас и администраторов
@ -736,6 +764,10 @@ twofa_enrolled=Для вашего аккаунта была включена д
twofa_failed_get_secret=Не удалось получить ключ.
webauthn_desc=Ключи безопасности - это аппаратные устройства, содержащие криптографические ключи. Они могут использоваться для двухфакторной аутентификации. Ключи безопасности должны поддерживать стандарт <a rel="noreferrer" target="_blank" href="https://w3c.github.io/webauthn/#webauthn-authenticator">WebAuthn Authenticator</a>.
webauthn_register_key=Добавить ключ безопасности
webauthn_nickname=Имя пользователя
webauthn_delete_key=Удалить ключ безопасности
webauthn_delete_key_desc=Если вы удалите ключ безопасности, вы больше не сможете войти с его помощью. Продолжить?
manage_account_links=Управление привязанными аккаунтами
manage_account_links_desc=Эти внешние аккаунты привязаны к вашему аккаунту Gitea.
@ -786,6 +818,7 @@ visibility_fork_helper=(Изменение этого повлияет на вс
clone_helper=Нужна помощь в клонировании? Посетите страницу <a target="_blank" rel="noopener noreferrer" href="%s">помощи</a>.
fork_repo=Форкнуть репозиторий
fork_from=Форк от
already_forked=Вы уже форкнули %s
fork_visibility_helper=Видимость форкнутого репозитория изменить нельзя.
use_template=Использовать этот шаблон
clone_in_vsc=Клонировать в VS Code
@ -920,6 +953,7 @@ migrate.migrating=Перенос из <b>%s</b>...
migrate.migrating_failed=Перенос из <b>%s</b> не удался.
migrate.migrating_failed.error=Ошибка: %s
migrate.migrating_failed_no_addr=Миграция не удалась.
migrate.github.description=Переносите данные с github.com или других серверов GitHub.
migrate.git.description=Перенести только репозиторий из любого Git сервиса.
migrate.gitlab.description=Перенести данные с gitlab.com или других экземпляров GitLab.
migrate.gitea.description=Перенести данные с gitea.com или других экземпляров Gitea.
@ -929,6 +963,7 @@ migrate.codebase.description=Перенос данных с codebasehq.com.
migrate.gitbucket.description=Перенести данные из экземпляров GitBucket.
migrate.migrating_git=Перенос Git данных
migrate.migrating_topics=Миграция тем
migrate.migrating_milestones=Перенос этапов
migrate.migrating_labels=Миграция меток
migrate.migrating_issues=Миграция задач
migrate.migrating_pulls=Миграция запросов на слияние
@ -1025,6 +1060,8 @@ editor.add_tmpl=Добавить '<filename>'
editor.add=Создал(а) '%s'
editor.update=Изменил(а) на '%s'
editor.delete=Удалить '%s'
editor.patch=Применить патч
editor.new_patch=Новый патч
editor.commit_message_desc=Добавьте необязательное расширенное описание…
editor.signoff_desc=Добавить Signed-off-by коммитом в конце сообщения журнала коммитов.
editor.commit_directly_to_this_branch=Сделайте коммит прямо в ветку <strong class="branch-name">%s</strong>.
@ -1077,6 +1114,12 @@ commits.signed_by_untrusted_user=Подписано ненадежным пол
commits.signed_by_untrusted_user_unmatched=Подписан ненадежным пользователем, который не соответствует коммиту
commits.gpg_key_id=Идентификатор GPG ключа
commit.revert=Откатить
commit.revert-header=Откат: %s
commit.revert-content=Выбрать ветку для отката:
commit.cherry-pick=Cherry-pick
commit.cherry-pick-header=Cherry-pick: %s
commit.cherry-pick-content=Выбрать ветку для cherry-pick:
ext_issues.desc=Ссылка на внешнюю систему отслеживания ошибок.
@ -1302,6 +1345,9 @@ issues.lock.reason=Причина для ограничения
issues.lock.title=Ограничить обсуждение данной задачи.
issues.unlock.title=Снять ограничение обсуждения данной задачи.
issues.comment_on_locked=Вы не можете оставить комментарий по задаче, ограниченной для обсуждения.
issues.delete=Удалить
issues.delete.title=Удалить эту задачу?
issues.delete.text=Вы действительно хотите удалить эту задачу? Это навсегда удалит всё содержимое. Возможно лучше закрыть её в архивных целях.
issues.tracker=Отслеживание времени
issues.start_tracking_short=Запустить таймер
issues.start_tracking=Начать отслеживание времени
@ -1342,6 +1388,8 @@ issues.due_date_remove=удалён срок выполнения %s %s
issues.due_date_overdue=Просроченные
issues.due_date_invalid=Срок действия недействителен или находится за пределами допустимого диапазона. Пожалуйста, используйте формат 'гггг-мм-дд'.
issues.dependency.title=Зависимости
issues.dependency.issue_no_dependencies=Зависимостей нет.
issues.dependency.pr_no_dependencies=Зависимостей нет.
issues.dependency.add=Добавить зависимость…
issues.dependency.cancel=Отменить
issues.dependency.remove=Удалить
@ -1407,7 +1455,7 @@ pulls.new=Новый запрос на слияние
pulls.view=Просмотр запроса на слияние
pulls.compare_changes=Новый запрос на слияние
pulls.compare_changes_desc=Сравнить две ветки и создать запрос на слияние для изменений.
pulls.compare_base=родительская ветка
pulls.compare_base=базовая ветка
pulls.compare_compare=взять из
pulls.switch_comparison_type=Переключить тип сравнения
pulls.switch_head_and_base=Поменять исходную и целевую ветки местами
@ -1482,7 +1530,9 @@ pulls.rebase_conflict_summary=Сообщение об ошибке
; </summary><code>%[2]s<br>%[3]s</code></details>
pulls.unrelated_histories=Слияние не удалось: У источника и цели слияния нет общей истории. Совет: попробуйте другую стратегию
pulls.merge_out_of_date=Ошибка слияния: при создании слияния база данных была обновлена. Подсказка: попробуйте ещё раз.
pulls.push_rejected=Слияние не удалось: push был отклонён. Проверьте Git-хуки для этого репозитория.
pulls.push_rejected_summary=Полная ошибка отклонения
pulls.push_rejected_no_message=Слияние не удалось: push был отклонён, но сервер не указал причину.<br>Проверьте Git-хуки для этого репозитория
pulls.open_unmerged_pull_exists=`Вы не можете снова открыть, поскольку уже существует запрос на слияние (#%d) из того же репозитория с той же информацией о слиянии и ожидающий слияния.`
pulls.status_checking=Выполняются некоторые проверки
pulls.status_checks_success=Все проверки выполнены успешно
@ -1646,6 +1696,8 @@ search.search_repo=Поиск по репозиторию
search.fuzzy=Неточный
search.match=Соответствие
search.results=Результаты поиска "%s" в <a href="%s">%s</a>
search.code_no_results=Не найдено исходного кода, соответствующего поисковому запросу.
search.code_search_unavailable=В настоящее время поиск по коду недоступен. Обратитесь к администратору сайта.
settings=Настройки
settings.desc=В настройках вы можете менять различные параметры этого репозитория
@ -1805,6 +1857,7 @@ settings.webhook.response=Ответ
settings.webhook.headers=Заголовки
settings.webhook.payload=Содержимое
settings.webhook.body=Тело ответа
settings.webhook.replay.description=Повторить этот веб-хук.
settings.githook_edit_desc=Если хук не активен, будет подставлен пример содержимого. Пустое значение в этом поле приведёт к отключению хука.
settings.githook_name=Название Hook'a
settings.githook_content=Содержание hook'а
@ -1862,6 +1915,8 @@ settings.event_pull_request_review=Запрос на слияние рассмо
settings.event_pull_request_review_desc=Запрос на слияние утвержден, отклонён или оставлен комментарий.
settings.event_pull_request_sync=Синхронизация запроса на слияние
settings.event_pull_request_sync_desc=Запрос на слияние синхронизирован.
settings.event_package=Пакеты
settings.event_package_desc=Пакет создан или удален в репозитории.
settings.branch_filter=Фильтр веток
settings.branch_filter_desc=Белый список ветвей для событий Push, создания ветвей и удаления ветвей, указанных в виде глоб-шаблона. Если пустой или <code>*</code>, то все событий для всех ветвей будут зарегистрированы. Перейдите по ссылке <a href="https://godoc.org/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> на документацию по синтаксису. Примеры: <code>master</code>, <code>{master,release*}</code>.
settings.active=Активный
@ -1875,6 +1930,13 @@ settings.hook_type=Тип hook'а
settings.slack_token=Slack токен
settings.slack_domain=Домен
settings.slack_channel=Канал
settings.web_hook_name_gitea=Gitea
settings.web_hook_name_gogs=Gogs
settings.web_hook_name_slack=Slack
settings.web_hook_name_discord=Discord
settings.web_hook_name_dingtalk=DingTalk
settings.web_hook_name_msteams=Microsoft Teams
settings.web_hook_name_feishu_or_larksuite=Feishu / Lark Suite
settings.deploy_keys=Ключи развертывания
settings.add_deploy_key=Добавить ключ развертывания
settings.deploy_key_desc=Ключи развёртывания доступны только для чтения. Это не то же самое что и SSH-ключи аккаунта.
@ -2129,11 +2191,14 @@ branch.included_desc=Эта ветка является частью ветки
branch.included=Включено
branch.create_new_branch=Создать ветку из ветви:
branch.confirm_create_branch=Создать ветку
branch.create_branch_operation=Создать ветку
branch.new_branch=Создать новую ветку
branch.new_branch_from=Создать новую ветку из '%s'
branch.renamed=Ветка %s была переименована в %s.
tag.create_tag=Создать тег <strong>%s</strong>
tag.create_tag_operation=Создать тег
tag.confirm_create_tag=Создать тег
tag.create_success=Тег '%s' был создан.
@ -2221,7 +2286,9 @@ teams.leave=Выйти
teams.leave.detail=Покинуть %s?
teams.can_create_org_repo=Создать репозитории
teams.can_create_org_repo_helper=Участники могут создавать новые репозитории в организации. Создатель получит администраторский доступ к новому репозиторию.
teams.read_access=Чтение
teams.read_access_helper=Участники могут просматривать и клонировать командные репозитории.
teams.write_access=Запись
teams.write_access_helper=Участники могут читать и выполнять push в командные репозитории.
teams.admin_access=Доступ администратора
teams.admin_access_helper=Участники могут выполнять pull, push в командные репозитории и добавлять соавторов в команду.
@ -2345,6 +2412,7 @@ dashboard.last_gc_pause=Последняя пауза сборщика мусо
dashboard.gc_times=Количество сборок мусора
dashboard.delete_old_actions=Удалите все старые действия из базы данных
dashboard.delete_old_actions.started=Удалите все старые действия из запущенной базы данных.
dashboard.update_checker=Проверка обновлений
users.user_manage_panel=Панель управления пользователями
users.new_account=Создать новый аккаунт
@ -2427,6 +2495,14 @@ repos.forks=Форки
repos.issues=Задачи
repos.size=Размер
packages.owner=Владелец
packages.creator=Автор
packages.name=Наименование
packages.version=Версия
packages.type=Тип
packages.repository=Репозиторий
packages.size=Размер
packages.published=Опубликовано
defaulthooks=Стандартные Веб-хуки
defaulthooks.desc=Вебхуки автоматически делают HTTP-POST запросы на сервер, когда вызываются определенные события Gitea. Вебхуки, определённые здесь, по умолчанию и будут скопированы во все новые репозитории. Подробнее читайте в <a target="_blank" rel="noopener" href="https://docs.gitea.io/en-us/webhooks/">руководстве по вебхукам</a>.
@ -2683,9 +2759,11 @@ monitor.next=Следующий раз
monitor.previous=Предыдущий раз
monitor.execute_times=Количество выполнений
monitor.process=Запущенные процессы
monitor.goroutines=%d горутин
monitor.desc=Описание
monitor.start=Время начала
monitor.execute_time=Время выполнения
monitor.last_execution_result=Результат
monitor.process.cancel=Отменить процесс
monitor.process.cancel_desc=Отмена процесса может привести к потере данных
monitor.process.cancel_notices=Отменить: <strong>%s</strong>?
@ -2716,6 +2794,11 @@ monitor.queue.pool.flush.title=Очистить очередь
monitor.queue.pool.flush.desc=При сбросе будет добавлен работник, который будет закрыт, когда очередь будет пустой, или истечет время время.
monitor.queue.pool.flush.submit=Добавить чистящего работника
monitor.queue.pool.flush.added=Добавлен чистящий рабочий на %[1]s
monitor.queue.pool.pause.desc=Приостановка очереди приостановит обработку данных
monitor.queue.pool.pause.submit=Приостановить очередь
monitor.queue.pool.resume.title=Возобновить очередь
monitor.queue.pool.resume.desc=Эта очередь возобновит работу
monitor.queue.pool.resume.submit=Возобновить очередь
monitor.queue.settings.title=Настройки пула
monitor.queue.settings.desc=Пулы динамично растут с ускорением в ответ на блокировку их рабочих очередей. Эти изменения не повлияют на текущие рабочие группы.
@ -2845,4 +2928,18 @@ error.no_unit_allowed_repo=У вас нет доступа ни к одному
error.unit_not_allowed=У вас нет доступа к этому разделу репозитория.
[packages]
title=Пакеты
desc=Управление пакетами репозитория.
empty=Пока нет пакетов.
empty.documentation=Дополнительную информацию о реестре пакетов можно найти в <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/overview">документации</a>.
filter.type=Тип
filter.type.all=Все
filter.no_result=Фильтр не дал результатов.
installation=Установка
about=Об этом пакете
requirements=Требования
dependencies=Зависимости
container.multi_arch=ОС / архитектура
container.labels.key=Ключ
container.labels.value=Значение

View file

@ -91,6 +91,7 @@ error404=ඔබ ළඟා වීමට උත්සාහ කරන පිටු
never=කිසි විටෙකත්
[error]
missing_csrf=නරක ඉල්ලීම: CSRF ටෝකන් නොමැත

View file

@ -79,6 +79,7 @@ loading=Laddar…
error404=Sidan du försöker nå <strong>finns inte</strong> eller så <strong>har du inte behörighet</strong> att se den.
[error]
[startpage]

View file

@ -91,6 +91,7 @@ error404=Ulaşmaya çalıştığınız sayfa <strong>mevcut değil</strong> veya
never=Asla
[error]
missing_csrf=Hatalı İstek: CSRF anahtarı yok

View file

@ -92,6 +92,7 @@ error404=Сторінка, до якої ви намагаєтеся зверн
never=Ніколи
[error]
occurred=Сталася помилка
missing_csrf=Некоректний запит: токен CSRF не задано

View file

@ -105,6 +105,7 @@ error404=您正尝试访问的页面 <strong>不存在</strong> 或 <strong>您
never=从不
[error]
occurred=发生了一个错误
report_message=如果您确定这是一个 Gitea bug请在 <a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a> 上搜索问题,或在必要时打开一个新问题。

View file

@ -46,6 +46,7 @@ cancel=取消
[error]
[startpage]

View file

@ -105,6 +105,7 @@ error404=您正嘗試訪問的頁面 <strong>不存在</strong> 或 <strong>您
never=從來沒有
[error]
occurred=發生錯誤
report_message=如果您確定這是一個 Gitea 的 bug請到 <a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a> 搜尋相關的問題,如果有需要您也可以建立新問題。

View file

@ -11,7 +11,6 @@ import (
"net/http"
"strconv"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
@ -268,17 +267,12 @@ func DownloadCommitDiffOrPatch(ctx *context.APIContext) {
// "$ref": "#/responses/string"
// "404":
// "$ref": "#/responses/notFound"
repoPath := repo_model.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
// TODO: use gitRepo from context
if err := git.GetRawDiff(
ctx,
repoPath,
ctx.Params(":sha"),
git.RawDiffType(ctx.Params(":diffType")),
ctx.Resp,
); err != nil {
sha := ctx.Params(":sha")
diffType := git.RawDiffType(ctx.Params(":diffType"))
if err := git.GetRawDiff(ctx.Repo.GitRepo, sha, diffType, ctx.Resp); err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound(ctx.Params(":sha"))
ctx.NotFound(sha)
return
}
ctx.Error(http.StatusInternalServerError, "DownloadCommitDiffOrPatch", err)

View file

@ -175,6 +175,7 @@ func SearchIssues(ctx *context.APIContext) {
opts.TeamID = team.ID
}
repoCond := models.SearchRepositoryCondition(opts)
repoIDs, _, err := models.SearchRepositoryIDs(opts)
if err != nil {
ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err)
@ -235,7 +236,7 @@ func SearchIssues(ctx *context.APIContext) {
Page: ctx.FormInt("page"),
PageSize: limit,
},
RepoIDs: repoIDs,
RepoCond: repoCond,
IsClosed: isClosed,
IssueIDs: issueIDs,
IncludedLabelNames: includedLabelNames,
@ -462,7 +463,7 @@ func ListIssues(ctx *context.APIContext) {
if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
issuesOpt := &models.IssuesOptions{
ListOptions: listOptions,
RepoIDs: []int64{ctx.Repo.Repository.ID},
RepoID: ctx.Repo.Repository.ID,
IsClosed: isClosed,
IssueIDs: issueIDs,
LabelIDs: labelIDs,

View file

@ -9,10 +9,15 @@ import (
"testing"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
webhook_service "code.gitea.io/gitea/services/webhook"
)
func TestMain(m *testing.M) {
setting.LoadForTest()
setting.NewQueueService()
unittest.MainTest(m, &unittest.TestOptions{
GiteaRootPath: filepath.Join("..", "..", "..", ".."),
SetUp: webhook_service.Init,
})
}

View file

@ -22,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
@ -217,7 +218,6 @@ func Search(ctx *context.APIContext) {
}
results[i] = convert.ToRepo(repo, accessMode)
}
ctx.SetLinkHeader(int(count), opts.PageSize)
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, api.SearchResults{
@ -248,7 +248,8 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
if repo_model.IsErrRepoAlreadyExist(err) {
ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.")
} else if db.IsErrNameReserved(err) ||
db.IsErrNamePatternNotAllowed(err) {
db.IsErrNamePatternNotAllowed(err) ||
repo_module.IsErrIssueLabelTemplateLoad(err) {
ctx.Error(http.StatusUnprocessableEntity, "", err)
} else {
ctx.Error(http.StatusInternalServerError, "CreateRepository", err)

View file

@ -145,7 +145,7 @@ func GlobalInitInstalled(ctx context.Context) {
mustInit(stats_indexer.Init)
mirror_service.InitSyncMirrors()
webhook.InitDeliverHooks()
mustInit(webhook.Init)
mustInit(pull_service.Init)
mustInit(task.Init)
mustInit(repo_migrations.Init)

View file

@ -8,15 +8,10 @@ import (
"net/http"
"time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/eventsource"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/routers/web/auth"
)
@ -71,8 +66,6 @@ func Events(ctx *context.Context) {
timer := time.NewTicker(30 * time.Second)
stopwatchTimer := time.NewTicker(setting.UI.Notification.MinTimeout)
loop:
for {
select {
@ -93,32 +86,6 @@ loop:
case <-shutdownCtx.Done():
go unregister()
break loop
case <-stopwatchTimer.C:
sws, err := models.GetUserStopwatches(ctx.Doer.ID, db.ListOptions{})
if err != nil {
log.Error("Unable to GetUserStopwatches: %v", err)
continue
}
apiSWs, err := convert.ToStopWatches(sws)
if err != nil {
log.Error("Unable to APIFormat stopwatches: %v", err)
continue
}
dataBs, err := json.Marshal(apiSWs)
if err != nil {
log.Error("Unable to marshal stopwatches: %v", err)
continue
}
_, err = (&eventsource.Event{
Name: "stopwatches",
Data: string(dataBs),
}).WriteTo(ctx.Resp)
if err != nil {
log.Error("Unable to write to EventStream for user %s: %v", ctx.Doer.Name, err)
go unregister()
break loop
}
ctx.Resp.Flush()
case event, ok := <-messageChan:
if !ok {
break loop

View file

@ -151,7 +151,7 @@ func CherryPickPost(ctx *context.Context) {
return
}
} else {
if err := git.GetRawDiff(ctx, ctx.Repo.Repository.RepoPath(), sha, git.RawDiffType("patch"), buf); err != nil {
if err := git.GetRawDiff(ctx.Repo.GitRepo, sha, git.RawDiffType("patch"), buf); err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound("GetRawDiff", errors.New("commit "+ctx.Params(":sha")+" does not exist."))
return

View file

@ -7,13 +7,13 @@ package repo
import (
"errors"
"fmt"
"net/http"
"strings"
"code.gitea.io/gitea/models"
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
@ -381,15 +381,24 @@ func Diff(ctx *context.Context) {
// RawDiff dumps diff results of repository in given commit ID to io.Writer
func RawDiff(ctx *context.Context) {
var repoPath string
var gitRepo *git.Repository
if ctx.Data["PageIsWiki"] != nil {
repoPath = ctx.Repo.Repository.WikiPath()
wikiRepo, err := git.OpenRepository(ctx, ctx.Repo.Repository.WikiPath())
if err != nil {
ctx.ServerError("OpenRepository", err)
return
}
defer wikiRepo.Close()
gitRepo = wikiRepo
} else {
repoPath = repo_model.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
gitRepo = ctx.Repo.GitRepo
if gitRepo == nil {
ctx.ServerError("GitRepo not open", fmt.Errorf("no open git repo for '%s'", ctx.Repo.Repository.FullName()))
return
}
}
if err := git.GetRawDiff(
ctx,
repoPath,
gitRepo,
ctx.Params(":sha"),
git.RawDiffType(ctx.Params(":ext")),
ctx.Resp,

View file

@ -232,7 +232,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
Page: pager.Paginater.Current(),
PageSize: setting.UI.IssuePagingNum,
},
RepoIDs: []int64{repo.ID},
RepoID: repo.ID,
AssigneeID: assigneeID,
PosterID: posterID,
MentionedID: mentionedID,
@ -269,14 +269,15 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
}
}
commitStatus, err := pull_service.GetIssuesLastCommitStatus(ctx, issues)
commitStatuses, lastStatus, err := pull_service.GetIssuesAllCommitStatus(ctx, issues)
if err != nil {
ctx.ServerError("GetIssuesLastCommitStatus", err)
ctx.ServerError("GetIssuesAllCommitStatus", err)
return
}
ctx.Data["Issues"] = issues
ctx.Data["CommitStatus"] = commitStatus
ctx.Data["CommitLastStatus"] = lastStatus
ctx.Data["CommitStatuses"] = commitStatuses
// Get assignees.
ctx.Data["Assignees"], err = models.GetRepoAssignees(repo)
@ -2167,6 +2168,7 @@ func SearchIssues(ctx *context.Context) {
opts.TeamID = team.ID
}
repoCond := models.SearchRepositoryCondition(opts)
repoIDs, _, err := models.SearchRepositoryIDs(opts)
if err != nil {
ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err.Error())
@ -2227,7 +2229,7 @@ func SearchIssues(ctx *context.Context) {
Page: ctx.FormInt("page"),
PageSize: limit,
},
RepoIDs: repoIDs,
RepoCond: repoCond,
IsClosed: isClosed,
IssueIDs: issueIDs,
IncludedLabelNames: includedLabelNames,
@ -2403,7 +2405,7 @@ func ListIssues(ctx *context.Context) {
if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
issuesOpt := &models.IssuesOptions{
ListOptions: listOptions,
RepoIDs: []int64{ctx.Repo.Repository.ID},
RepoID: ctx.Repo.Repository.ID,
IsClosed: isClosed,
IssueIDs: issueIDs,
LabelIDs: labelIDs,

View file

@ -9,7 +9,9 @@ import (
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/eventsource"
)
// IssueStopwatch creates or stops a stopwatch for the given issue.
@ -59,6 +61,18 @@ func CancelStopwatch(c *context.Context) {
return
}
stopwatches, err := models.GetUserStopwatches(c.Doer.ID, db.ListOptions{})
if err != nil {
c.ServerError("GetUserStopwatches", err)
return
}
if len(stopwatches) == 0 {
eventsource.GetManager().SendMessage(c.Doer.ID, &eventsource.Event{
Name: "stopwatches",
Data: "{}",
})
}
url := issue.HTMLURL()
c.Redirect(url, http.StatusSeeOther)
}

View file

@ -590,26 +590,28 @@ func SearchRepo(ctx *context.Context) {
return
}
results := make([]*api.Repository, len(repos))
for i, repo := range repos {
if err = repo.GetOwner(ctx); err != nil {
ctx.JSON(http.StatusInternalServerError, api.SearchError{
OK: false,
Error: err.Error(),
})
return
}
accessMode, err := models.AccessLevel(ctx.Doer, repo)
if err != nil {
ctx.JSON(http.StatusInternalServerError, api.SearchError{
OK: false,
Error: err.Error(),
})
}
results[i] = convert.ToRepo(repo, accessMode)
ctx.SetTotalCountHeader(count)
// To improve performance when only the count is requested
if ctx.FormBool("count_only") {
return
}
results := make([]*api.Repository, len(repos))
for i, repo := range repos {
results[i] = &api.Repository{
ID: repo.ID,
FullName: repo.FullName(),
Fork: repo.IsFork,
Private: repo.IsPrivate,
Template: repo.IsTemplate,
Mirror: repo.IsMirror,
Stars: repo.NumStars,
HTMLURL: repo.HTMLURL(),
Internal: !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePrivate,
}
}
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, api.SearchResults{
OK: true,
Data: results,

View file

@ -146,6 +146,21 @@ func renderDirectory(ctx *context.Context, treeLink string) {
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
}
// Check permission to add or upload new file.
if ctx.Repo.CanWrite(unit_model.TypeCode) && ctx.Repo.IsViewBranch {
ctx.Data["CanAddFile"] = !ctx.Repo.Repository.IsArchived
ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled && !ctx.Repo.Repository.IsArchived
}
readmeFile, readmeTreelink := findReadmeFile(ctx, entries, treeLink)
if ctx.Written() || readmeFile == nil {
return
}
renderReadmeFile(ctx, readmeFile, readmeTreelink)
}
func findReadmeFile(ctx *context.Context, entries git.Entries, treeLink string) (*namedBlob, string) {
// 3 for the extensions in exts[] in order
// the last one is for a readme that doesn't
// strictly match an extension
@ -183,7 +198,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
target, err = entry.FollowLinks()
if err != nil && !git.IsErrBadLink(err) {
ctx.ServerError("FollowLinks", err)
return
return nil, ""
}
}
log.Debug("%t", target == nil)
@ -205,7 +220,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
entry, err = entry.FollowLinks()
if err != nil && !git.IsErrBadLink(err) {
ctx.ServerError("FollowLinks", err)
return
return nil, ""
}
}
if entry != nil && (entry.IsExecutable() || entry.IsRegular()) {
@ -236,7 +251,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
readmeFile, err = getReadmeFileFromPath(ctx.Repo.Commit, entry.GetSubJumpablePathName())
if err != nil {
ctx.ServerError("getReadmeFileFromPath", err)
return
return nil, ""
}
if readmeFile != nil {
readmeFile.name = entry.Name() + "/" + readmeFile.name
@ -245,129 +260,127 @@ func renderDirectory(ctx *context.Context, treeLink string) {
}
}
}
return readmeFile, readmeTreelink
}
if readmeFile != nil {
ctx.Data["RawFileLink"] = ""
ctx.Data["ReadmeInList"] = true
ctx.Data["ReadmeExist"] = true
ctx.Data["FileIsSymlink"] = readmeFile.isSymlink
func renderReadmeFile(ctx *context.Context, readmeFile *namedBlob, readmeTreelink string) {
ctx.Data["RawFileLink"] = ""
ctx.Data["ReadmeInList"] = true
ctx.Data["ReadmeExist"] = true
ctx.Data["FileIsSymlink"] = readmeFile.isSymlink
dataRc, err := readmeFile.blob.DataAsync()
if err != nil {
ctx.ServerError("Data", err)
return
}
defer dataRc.Close()
dataRc, err := readmeFile.blob.DataAsync()
if err != nil {
ctx.ServerError("Data", err)
return
}
defer dataRc.Close()
buf := make([]byte, 1024)
n, _ := util.ReadAtMost(dataRc, buf)
buf = buf[:n]
buf := make([]byte, 1024)
n, _ := util.ReadAtMost(dataRc, buf)
buf = buf[:n]
st := typesniffer.DetectContentType(buf)
isTextFile := st.IsText()
st := typesniffer.DetectContentType(buf)
isTextFile := st.IsText()
ctx.Data["FileIsText"] = isTextFile
ctx.Data["FileName"] = readmeFile.name
fileSize := int64(0)
isLFSFile := false
ctx.Data["IsLFSFile"] = false
ctx.Data["FileIsText"] = isTextFile
ctx.Data["FileName"] = readmeFile.name
fileSize := int64(0)
isLFSFile := false
ctx.Data["IsLFSFile"] = false
// FIXME: what happens when README file is an image?
if isTextFile && setting.LFS.StartServer {
pointer, _ := lfs.ReadPointerFromBuffer(buf)
if pointer.IsValid() {
meta, err := models.GetLFSMetaObjectByOid(ctx.Repo.Repository.ID, pointer.Oid)
if err != nil && err != models.ErrLFSObjectNotExist {
ctx.ServerError("GetLFSMetaObject", err)
// FIXME: what happens when README file is an image?
if isTextFile && setting.LFS.StartServer {
pointer, _ := lfs.ReadPointerFromBuffer(buf)
if pointer.IsValid() {
meta, err := models.GetLFSMetaObjectByOid(ctx.Repo.Repository.ID, pointer.Oid)
if err != nil && err != models.ErrLFSObjectNotExist {
ctx.ServerError("GetLFSMetaObject", err)
return
}
if meta != nil {
ctx.Data["IsLFSFile"] = true
isLFSFile = true
// OK read the lfs object
var err error
dataRc, err = lfs.ReadMetaObject(pointer)
if err != nil {
ctx.ServerError("ReadMetaObject", err)
return
}
if meta != nil {
ctx.Data["IsLFSFile"] = true
isLFSFile = true
defer dataRc.Close()
// OK read the lfs object
var err error
dataRc, err = lfs.ReadMetaObject(pointer)
if err != nil {
ctx.ServerError("ReadMetaObject", err)
return
}
defer dataRc.Close()
buf = make([]byte, 1024)
n, err = util.ReadAtMost(dataRc, buf)
if err != nil {
ctx.ServerError("Data", err)
return
}
buf = buf[:n]
st = typesniffer.DetectContentType(buf)
isTextFile = st.IsText()
ctx.Data["IsTextFile"] = isTextFile
fileSize = meta.Size
ctx.Data["FileSize"] = meta.Size
filenameBase64 := base64.RawURLEncoding.EncodeToString([]byte(readmeFile.name))
ctx.Data["RawFileLink"] = fmt.Sprintf("%s.git/info/lfs/objects/%s/%s", ctx.Repo.Repository.HTMLURL(), url.PathEscape(meta.Oid), url.PathEscape(filenameBase64))
buf = make([]byte, 1024)
n, err = util.ReadAtMost(dataRc, buf)
if err != nil {
ctx.ServerError("Data", err)
return
}
}
}
buf = buf[:n]
if !isLFSFile {
fileSize = readmeFile.blob.Size()
}
st = typesniffer.DetectContentType(buf)
isTextFile = st.IsText()
ctx.Data["IsTextFile"] = isTextFile
if isTextFile {
if fileSize >= setting.UI.MaxDisplayFileSize {
// Pretend that this is a normal text file to display 'This file is too large to be shown'
ctx.Data["IsFileTooLarge"] = true
ctx.Data["IsTextFile"] = true
ctx.Data["FileSize"] = fileSize
} else {
rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc))
if markupType := markup.Type(readmeFile.name); markupType != "" {
ctx.Data["IsMarkup"] = true
ctx.Data["MarkupType"] = string(markupType)
var result strings.Builder
err := markup.Render(&markup.RenderContext{
Ctx: ctx,
Filename: readmeFile.name,
URLPrefix: readmeTreelink,
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
GitRepo: ctx.Repo.GitRepo,
}, rd, &result)
if err != nil {
log.Error("Render failed: %v then fallback", err)
buf := &bytes.Buffer{}
ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, buf)
ctx.Data["FileContent"] = strings.ReplaceAll(
gotemplate.HTMLEscapeString(buf.String()), "\n", `<br>`,
)
} else {
ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlString(result.String())
}
} else {
ctx.Data["IsRenderedHTML"] = true
buf := &bytes.Buffer{}
ctx.Data["EscapeStatus"], err = charset.EscapeControlReader(rd, buf)
if err != nil {
log.Error("Read failed: %v", err)
}
ctx.Data["FileContent"] = strings.ReplaceAll(
gotemplate.HTMLEscapeString(buf.String()), "\n", `<br>`,
)
}
fileSize = meta.Size
ctx.Data["FileSize"] = meta.Size
filenameBase64 := base64.RawURLEncoding.EncodeToString([]byte(readmeFile.name))
ctx.Data["RawFileLink"] = fmt.Sprintf("%s.git/info/lfs/objects/%s/%s", ctx.Repo.Repository.HTMLURL(), url.PathEscape(meta.Oid), url.PathEscape(filenameBase64))
}
}
}
// Check permission to add or upload new file.
if ctx.Repo.CanWrite(unit_model.TypeCode) && ctx.Repo.IsViewBranch {
ctx.Data["CanAddFile"] = !ctx.Repo.Repository.IsArchived
ctx.Data["CanUploadFile"] = setting.Repository.Upload.Enabled && !ctx.Repo.Repository.IsArchived
if !isTextFile {
return
}
if !isLFSFile {
fileSize = readmeFile.blob.Size()
}
if fileSize >= setting.UI.MaxDisplayFileSize {
// Pretend that this is a normal text file to display 'This file is too large to be shown'
ctx.Data["IsFileTooLarge"] = true
ctx.Data["IsTextFile"] = true
ctx.Data["FileSize"] = fileSize
return
}
rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc))
if markupType := markup.Type(readmeFile.name); markupType != "" {
ctx.Data["IsMarkup"] = true
ctx.Data["MarkupType"] = string(markupType)
var result strings.Builder
err := markup.Render(&markup.RenderContext{
Ctx: ctx,
Filename: readmeFile.name,
URLPrefix: readmeTreelink,
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
GitRepo: ctx.Repo.GitRepo,
}, rd, &result)
if err != nil {
log.Error("Render failed: %v then fallback", err)
buf := &bytes.Buffer{}
ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, buf)
ctx.Data["FileContent"] = strings.ReplaceAll(
gotemplate.HTMLEscapeString(buf.String()), "\n", `<br>`,
)
} else {
ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlString(result.String())
}
} else {
ctx.Data["IsRenderedHTML"] = true
buf := &bytes.Buffer{}
ctx.Data["EscapeStatus"], err = charset.EscapeControlReader(rd, buf)
if err != nil {
log.Error("Read failed: %v", err)
}
ctx.Data["FileContent"] = strings.ReplaceAll(
gotemplate.HTMLEscapeString(buf.String()), "\n", `<br>`,
)
}
}

View file

@ -463,13 +463,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
// to check if it's in the team(which possible isn't the case).
opts.User = nil
}
userRepoIDs, _, err := models.SearchRepositoryIDs(repoOpts)
if err != nil {
ctx.ServerError("models.SearchRepositoryIDs: %v", err)
return
}
opts.RepoIDs = userRepoIDs
opts.RepoCond = models.SearchRepositoryCondition(repoOpts)
}
// keyword holds the search term entered into the search field.
@ -533,7 +527,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
// Gets set when clicking filters on the issues overview page.
repoIDs := getRepoIDs(ctx.FormString("repos"))
if len(repoIDs) > 0 {
opts.RepoIDs = repoIDs
opts.RepoCond = builder.In("issue.repo_id", repoIDs)
}
// ------------------------------
@ -579,7 +573,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
}
}
commitStatus, err := pull_service.GetIssuesLastCommitStatus(ctx, issues)
commitStatuses, lastStatus, err := pull_service.GetIssuesAllCommitStatus(ctx, issues)
if err != nil {
ctx.ServerError("GetIssuesLastCommitStatus", err)
return
@ -656,7 +650,8 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
}
return 0
}
ctx.Data["CommitStatus"] = commitStatus
ctx.Data["CommitLastStatus"] = lastStatus
ctx.Data["CommitStatuses"] = commitStatuses
ctx.Data["Repos"] = showRepos
ctx.Data["Counts"] = issueCountByRepo
ctx.Data["IssueStats"] = issueStats

View file

@ -553,7 +553,7 @@ func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head
}
if ok {
_, _, err = git.NewCommand(g.ctx, "fetch", remote, pr.Head.Ref).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()})
_, _, err = git.NewCommand(g.ctx, "fetch", "--no-tags", "--", remote, pr.Head.Ref).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()})
if err != nil {
log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err)
} else {

View file

@ -105,7 +105,7 @@ func TestGiteaUploadRepo(t *testing.T) {
assert.Len(t, releases, 1)
issues, err := models.Issues(&models.IssuesOptions{
RepoIDs: []int64{repo.ID},
RepoID: repo.ID,
IsPull: util.OptionalBoolFalse,
SortType: "oldest",
})

View file

@ -726,18 +726,25 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *models.PullRequest) s
return stringBuilder.String()
}
// GetIssuesLastCommitStatus returns a map
// GetIssuesLastCommitStatus returns a map of issue ID to the most recent commit's latest status
func GetIssuesLastCommitStatus(ctx context.Context, issues models.IssueList) (map[int64]*models.CommitStatus, error) {
_, lastStatus, err := GetIssuesAllCommitStatus(ctx, issues)
return lastStatus, err
}
// GetIssuesAllCommitStatus returns a map of issue ID to a list of all statuses for the most recent commit as well as a map of issue ID to only the commit's latest status
func GetIssuesAllCommitStatus(ctx context.Context, issues models.IssueList) (map[int64][]*models.CommitStatus, map[int64]*models.CommitStatus, error) {
if err := issues.LoadPullRequests(); err != nil {
return nil, err
return nil, nil, err
}
if _, err := issues.LoadRepositories(); err != nil {
return nil, err
return nil, nil, err
}
var (
gitRepos = make(map[int64]*git.Repository)
res = make(map[int64]*models.CommitStatus)
res = make(map[int64][]*models.CommitStatus)
lastRes = make(map[int64]*models.CommitStatus)
err error
)
defer func() {
@ -760,28 +767,27 @@ func GetIssuesLastCommitStatus(ctx context.Context, issues models.IssueList) (ma
gitRepos[issue.RepoID] = gitRepo
}
status, err := getLastCommitStatus(gitRepo, issue.PullRequest)
statuses, lastStatus, err := getAllCommitStatus(gitRepo, issue.PullRequest)
if err != nil {
log.Error("getLastCommitStatus: cant get last commit of pull [%d]: %v", issue.PullRequest.ID, err)
log.Error("getAllCommitStatus: cant get commit statuses of pull [%d]: %v", issue.PullRequest.ID, err)
continue
}
res[issue.PullRequest.ID] = status
res[issue.PullRequest.ID] = statuses
lastRes[issue.PullRequest.ID] = lastStatus
}
return res, nil
return res, lastRes, nil
}
// getLastCommitStatus get pr's last commit status. PR's last commit status is the head commit id's last commit status
func getLastCommitStatus(gitRepo *git.Repository, pr *models.PullRequest) (status *models.CommitStatus, err error) {
sha, err := gitRepo.GetRefCommitID(pr.GetGitRefName())
if err != nil {
return nil, err
// getAllCommitStatus get pr's commit statuses.
func getAllCommitStatus(gitRepo *git.Repository, pr *models.PullRequest) (statuses []*models.CommitStatus, lastStatus *models.CommitStatus, err error) {
sha, shaErr := gitRepo.GetRefCommitID(pr.GetGitRefName())
if shaErr != nil {
return nil, nil, shaErr
}
statusList, _, err := models.GetLatestCommitStatus(pr.BaseRepo.ID, sha, db.ListOptions{})
if err != nil {
return nil, err
}
return models.CalcCommitStatus(statusList), nil
statuses, _, err = models.GetLatestCommitStatus(pr.BaseRepo.ID, sha, db.ListOptions{})
lastStatus = models.CalcCommitStatus(statuses)
return statuses, lastStatus, err
}
// IsHeadEqualWithBranch returns if the commits of branchName are available in pull request head

View file

@ -15,7 +15,6 @@ import (
"io"
"net/http"
"net/url"
"strconv"
"strings"
"sync"
"time"
@ -26,6 +25,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/queue"
"code.gitea.io/gitea/modules/setting"
"github.com/gobwas/glob"
@ -202,10 +202,8 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error {
return nil
}
// DeliverHooks checks and delivers undelivered hooks.
// FIXME: graceful: This would likely benefit from either a worker pool with dummy queue
// or a full queue. Then more hooks could be sent at same time.
func DeliverHooks(ctx context.Context) {
// populateDeliverHooks checks and delivers undelivered hooks.
func populateDeliverHooks(ctx context.Context) {
select {
case <-ctx.Done():
return
@ -226,42 +224,9 @@ func DeliverHooks(ctx context.Context) {
return
default:
}
if err = Deliver(ctx, t); err != nil {
log.Error("deliver: %v", err)
}
}
// Start listening on new hook requests.
for {
select {
case <-ctx.Done():
hookQueue.Close()
return
case repoIDStr := <-hookQueue.Queue():
log.Trace("DeliverHooks [repo_id: %v]", repoIDStr)
hookQueue.Remove(repoIDStr)
repoID, err := strconv.ParseInt(repoIDStr, 10, 64)
if err != nil {
log.Error("Invalid repo ID: %s", repoIDStr)
continue
}
tasks, err := webhook_model.FindRepoUndeliveredHookTasks(repoID)
if err != nil {
log.Error("Get repository [%d] hook tasks: %v", repoID, err)
continue
}
for _, t := range tasks {
select {
case <-ctx.Done():
return
default:
}
if err = Deliver(ctx, t); err != nil {
log.Error("deliver: %v", err)
}
}
if err := addToTask(t.RepoID); err != nil {
log.Error("DeliverHook failed [%d]: %v", t.RepoID, err)
}
}
}
@ -297,8 +262,8 @@ func webhookProxy() func(req *http.Request) (*url.URL, error) {
}
}
// InitDeliverHooks starts the hooks delivery thread
func InitDeliverHooks() {
// Init starts the hooks delivery thread
func Init() error {
timeout := time.Duration(setting.Webhook.DeliverTimeout) * time.Second
allowedHostListValue := setting.Webhook.AllowedHostList
@ -316,5 +281,13 @@ func InitDeliverHooks() {
},
}
go graceful.GetManager().RunWithShutdownContext(DeliverHooks)
hookQueue = queue.CreateUniqueQueue("webhook_sender", handle, "")
if hookQueue == nil {
return fmt.Errorf("Unable to create webhook_sender Queue")
}
go graceful.GetManager().RunWithShutdownFns(hookQueue.Run)
populateDeliverHooks(graceful.GetManager().HammerContext())
return nil
}

View file

@ -9,12 +9,16 @@ import (
"testing"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
_ "code.gitea.io/gitea/models"
)
func TestMain(m *testing.M) {
setting.LoadForTest()
setting.NewQueueService()
unittest.MainTest(m, &unittest.TestOptions{
GiteaRootPath: filepath.Join("..", ".."),
SetUp: Init,
})
}

View file

@ -12,10 +12,11 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
webhook_model "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/queue"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/util"
"github.com/gobwas/glob"
@ -80,7 +81,7 @@ func IsValidHookTaskType(name string) bool {
}
// hookQueue is a global queue of web hooks
var hookQueue = sync.NewUniqueQueue(setting.Webhook.QueueLength)
var hookQueue queue.UniqueQueue
// getPayloadBranch returns branch for hook event, if applicable.
func getPayloadBranch(p api.Payloader) string {
@ -101,14 +102,47 @@ func getPayloadBranch(p api.Payloader) string {
return ""
}
// handle passed PR IDs and test the PRs
func handle(data ...queue.Data) []queue.Data {
for _, datum := range data {
repoIDStr := datum.(string)
log.Trace("DeliverHooks [repo_id: %v]", repoIDStr)
repoID, err := strconv.ParseInt(repoIDStr, 10, 64)
if err != nil {
log.Error("Invalid repo ID: %s", repoIDStr)
continue
}
tasks, err := webhook_model.FindRepoUndeliveredHookTasks(repoID)
if err != nil {
log.Error("Get repository [%d] hook tasks: %v", repoID, err)
continue
}
for _, t := range tasks {
if err = Deliver(graceful.GetManager().HammerContext(), t); err != nil {
log.Error("deliver: %v", err)
}
}
}
return nil
}
func addToTask(repoID int64) error {
err := hookQueue.PushFunc(strconv.FormatInt(repoID, 10), nil)
if err != nil && err != queue.ErrAlreadyInQueue {
return err
}
return nil
}
// PrepareWebhook adds special webhook to task queue for given payload.
func PrepareWebhook(w *webhook_model.Webhook, repo *repo_model.Repository, event webhook_model.HookEventType, p api.Payloader) error {
if err := prepareWebhook(w, repo, event, p); err != nil {
return err
}
go hookQueue.Add(strconv.FormatInt(repo.ID, 10))
return nil
return addToTask(repo.ID)
}
func checkBranch(w *webhook_model.Webhook, branch string) bool {
@ -188,8 +222,7 @@ func PrepareWebhooks(repo *repo_model.Repository, event webhook_model.HookEventT
return err
}
go hookQueue.Add(strconv.FormatInt(repo.ID, 10))
return nil
return addToTask(repo.ID)
}
func prepareWebhooks(repo *repo_model.Repository, event webhook_model.HookEventType, p api.Payloader) error {
@ -240,7 +273,5 @@ func ReplayHookTask(w *webhook_model.Webhook, uuid string) error {
return err
}
go hookQueue.Add(strconv.FormatInt(t.RepoID, 10))
return nil
return addToTask(t.RepoID)
}

View file

@ -116,7 +116,7 @@
<input name="restricted" type="checkbox" {{if .User.IsRestricted}}checked{{end}}>
</div>
</div>
<div class="inline field">
<div class="inline field"{{if DisableGitHooks}} hidden{{end}}>
<div class="ui checkbox tooltip" data-content="{{.i18n.Tr "admin.users.allow_git_hook_tooltip"}}" data-variation="very wide">
<label><strong>{{.i18n.Tr "admin.users.allow_git_hook"}}</strong></label>
<input name="allow_git_hook" type="checkbox" {{if .User.CanEditGitHook}}checked{{end}} {{if DisableGitHooks}}disabled{{end}}>

View file

@ -85,7 +85,7 @@
{{template "custom/body_inner_pre" .}}
{{if not .PageIsInstall}}
<div class="ui top secondary stackable main menu following bar light">
<div class="ui top secondary stackable main menu following bar light no-vertical-tabs">
{{template "base/head_navbar" .}}
</div><!-- end bar -->
{{end}}

View file

@ -5,6 +5,7 @@
<div id="org-info">
<div class="ui header">
{{.Org.DisplayName}}
<a href="{{.Org.HomeLink}}.rss"><i class="ui grey icon tooltip ml-3" data-content="{{.i18n.Tr "rss_feed"}}" data-position="top center">{{svg "octicon-rss" 36}}</i></a>
<span class="org-visibility">
{{if .Org.Visibility.IsLimited}}<div class="ui large basic horizontal label">{{.i18n.Tr "org.settings.visibility.limited_shortname"}}</div>{{end}}
{{if .Org.Visibility.IsPrivate}}<div class="ui large basic horizontal label">{{.i18n.Tr "org.settings.visibility.private_shortname"}}</div>{{end}}

View file

@ -191,7 +191,7 @@
<div class="twelve wide column issue-title">
{{.i18n.Tr "repo.pulls.has_pull_request" (Escape $.RepoLink) (Escape $.RepoRelPath) .PullRequest.Index | Safe}}
<h1>
<span id="issue-title">{{RenderIssueTitle .PullRequest.Issue.Title $.RepoLink $.Repository.ComposeMetas}}</span>
<span id="issue-title">{{RenderIssueTitle $.Context .PullRequest.Issue.Title $.RepoLink $.Repository.ComposeMetas}}</span>
<span class="index">#{{.PullRequest.Issue.Index}}</span>
</h1>
</div>

View file

@ -13,6 +13,7 @@
<a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a>
<div class="mx-2">/</div>
<a href="{{$.RepoLink}}">{{.Name}}</a>
<a href="{{$.RepoLink}}.rss"><i class="ui grey icon tooltip ml-3" data-content="{{$.i18n.Tr "rss_feed"}}" data-position="top center">{{svg "octicon-rss" 18}}</i></a>
<div class="labels df ac fw">
{{if .IsTemplate}}
{{if .IsPrivate}}

View file

@ -60,7 +60,7 @@
</div>
{{end}}
{{template "repo/sub_menu" .}}
<div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins">
<div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins no-vertical-tabs">
{{template "repo/branch_dropdown" dict "root" .}}
{{ $n := len .TreeNames}}
{{ $l := Subtract $n 1}}

View file

@ -128,6 +128,9 @@
<a class="item ref-in-new-issue" href="{{.RepoLink}}/issues/new?body={{.Repository.HTMLURL}}{{printf "/src/commit/" }}{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}" rel="nofollow noindex">{{.i18n.Tr "repo.issues.context.reference_issue"}}</a>
</div>
{{end}}
<div class="ui link list">
<a class="item view_git_blame" href="{{.Repository.HTMLURL}}/blame/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}">{{.i18n.Tr "repo.view_git_blame"}}</a>
</div>
<div class="ui link list">
<a data-clipboard-text="{{.Repository.HTMLURL}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}" class="item copy-line-permalink">{{.i18n.Tr "repo.file_copy_permalink"}}</a>
</div>

View file

@ -3,7 +3,7 @@
{{template "repo/header" .}}
{{ $title := .title}}
<div class="ui container">
<div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins">
<div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins no-vertical-tabs">
<div class="fitted item">
<div class="choose page">
<div class="ui floating filter dropdown" data-no-results="{{.i18n.Tr "repo.pulls.no_results"}}">

View file

@ -33,8 +33,8 @@
<div class="issue-item-top-row">
<a class="title tdn" href="{{if .HTMLURL}}{{.HTMLURL}}{{else}}{{$.Link}}/{{.Index}}{{end}}">{{RenderEmoji .Title}}</a>
{{if .IsPull}}
{{if (index $.CommitStatus .PullRequest.ID)}}
{{template "repo/commit_status" (index $.CommitStatus .PullRequest.ID)}}
{{if (index $.CommitStatuses .PullRequest.ID)}}
{{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID) "root" $}}
{{end}}
{{end}}
<span class="labels-list ml-2">

View file

@ -16,6 +16,7 @@
<div class="content word-break profile-avatar-name">
{{if .Owner.FullName}}<span class="header text center">{{.Owner.FullName}}</span>{{end}}
<span class="username text center">{{.Owner.Name}}</span>
<a href="{{.Owner.HomeLink}}.rss"><i class="ui grey icon tooltip ml-3" data-content="{{.i18n.Tr "rss_feed"}}" data-position="bottom center">{{svg "octicon-rss" 18}}</i></a>
</div>
<div class="extra content word-break">
<ul>

View file

@ -298,36 +298,41 @@ function initVueComponents() {
this.searchRepos();
},
searchRepos() {
async searchRepos() {
this.isLoading = true;
if (!this.reposTotalCount) {
const totalCountSearchURL = `${this.subUrl}/repo/search?sort=updated&order=desc&uid=${this.uid}&team_id=${this.teamId}&q=&page=1&mode=`;
$.getJSON(totalCountSearchURL, (_result, _textStatus, request) => {
this.reposTotalCount = request.getResponseHeader('X-Total-Count');
});
}
const searchedMode = this.repoTypes[this.reposFilter].searchMode;
const searchedURL = this.searchURL;
const searchedQuery = this.searchQuery;
$.getJSON(searchedURL, (result, _textStatus, request) => {
if (searchedURL === this.searchURL) {
this.repos = result.data;
const count = request.getResponseHeader('X-Total-Count');
if (searchedQuery === '' && searchedMode === '' && this.archivedFilter === 'both') {
this.reposTotalCount = count;
}
Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, count);
this.finalPage = Math.ceil(count / this.searchLimit);
this.updateHistory();
let response, json;
try {
if (!this.reposTotalCount) {
const totalCountSearchURL = `${this.subUrl}/repo/search?count_only=1&uid=${this.uid}&team_id=${this.teamId}&q=&page=1&mode=`;
response = await fetch(totalCountSearchURL);
this.reposTotalCount = response.headers.get('X-Total-Count');
}
}).always(() => {
response = await fetch(searchedURL);
json = await response.json();
} catch {
if (searchedURL === this.searchURL) {
this.isLoading = false;
}
});
return;
}
if (searchedURL === this.searchURL) {
this.repos = json.data;
const count = response.headers.get('X-Total-Count');
if (searchedQuery === '' && searchedMode === '' && this.archivedFilter === 'both') {
this.reposTotalCount = count;
}
Vue.set(this.counts, `${this.reposFilter}:${this.archivedFilter}:${this.privateFilter}`, count);
this.finalPage = Math.ceil(count / this.searchLimit);
this.updateHistory();
this.isLoading = false;
}
},
repoIcon(repo) {

View file

@ -4,10 +4,12 @@ function getDefaultSvgBoundsIfUndefined(svgXml, src) {
const DefaultSize = 300;
const MaxSize = 99999;
const svg = svgXml.rootElement;
const width = svg.width.baseVal;
const height = svg.height.baseVal;
const svg = svgXml.documentElement;
const width = svg?.width?.baseVal;
const height = svg?.height?.baseVal;
if (width === undefined || height === undefined) {
return null; // in case some svg is invalid or doesn't have the width/height
}
if (width.unitType === SVGLength.SVG_LENGTHTYPE_PERCENTAGE || height.unitType === SVGLength.SVG_LENGTHTYPE_PERCENTAGE) {
const img = new Image();
img.src = src;
@ -29,6 +31,7 @@ function getDefaultSvgBoundsIfUndefined(svgXml, src) {
height: DefaultSize
};
}
return null;
}
export default function initImageDiff() {
@ -88,6 +91,10 @@ export default function initImageDiff() {
info.$image.on('load', () => {
info.loaded = true;
setReadyIfLoaded();
}).on('error', () => {
info.loaded = true;
setReadyIfLoaded();
info.$boundsInfo.text('(image error)');
});
info.$image.attr('src', info.path);

View file

@ -15,10 +15,7 @@ function selectRange($list, $select, $from) {
// add hashchange to permalink
const $issue = $('a.ref-in-new-issue');
const $copyPermalink = $('a.copy-line-permalink');
if ($copyPermalink.length === 0) {
return;
}
const $viewGitBlame = $('a.view_git_blame');
const updateIssueHref = function (anchor) {
if ($issue.length === 0) {
@ -29,7 +26,22 @@ function selectRange($list, $select, $from) {
$issue.attr('href', href);
};
const updateViewGitBlameFragment = function (anchor) {
if ($viewGitBlame.length === 0) {
return;
}
let href = $viewGitBlame.attr('href');
href = `${href.replace(/#L\d+$|#L\d+-L\d+$/, '')}`;
if (anchor.length !== 0) {
href = `${href}#${anchor}`;
}
$viewGitBlame.attr('href', href);
};
const updateCopyPermalinkHref = function(anchor) {
if ($copyPermalink.length === 0) {
return;
}
let link = $copyPermalink.attr('data-clipboard-text');
link = `${link.replace(/#L\d+$|#L\d+-L\d+$/, '')}#${anchor}`;
$copyPermalink.attr('data-clipboard-text', link);
@ -53,6 +65,7 @@ function selectRange($list, $select, $from) {
changeHash(`#L${a}-L${b}`);
updateIssueHref(`L${a}-L${b}`);
updateViewGitBlameFragment(`L${a}-L${b}`);
updateCopyPermalinkHref(`L${a}-L${b}`);
return;
}
@ -61,6 +74,7 @@ function selectRange($list, $select, $from) {
changeHash(`#${$select.attr('rel')}`);
updateIssueHref($select.attr('rel'));
updateViewGitBlameFragment($select.attr('rel'));
updateCopyPermalinkHref($select.attr('rel'));
}

View file

@ -49,3 +49,16 @@ export function initRepoCommitLastCommitLoader() {
});
});
}
export function initCommitStatuses() {
$('.commit-statuses-trigger').each(function () {
const positionRight = $('.repository.file.list').length > 0 || $('.repository.diff').length > 0;
const popupPosition = positionRight ? 'right center' : 'left center';
$(this)
.popup({
on: 'click',
lastResort: popupPosition, // prevent error message "Popup does not fit within the boundaries of the viewport"
position: popupPosition,
});
});
}

View file

@ -423,18 +423,6 @@ export function initRepository() {
}
// Commit statuses
$('.commit-statuses-trigger').each(function () {
const positionRight = $('.repository.file.list').length > 0 || $('.repository.diff').length > 0;
const popupPosition = positionRight ? 'right center' : 'left center';
$(this)
.popup({
on: 'click',
lastResort: popupPosition, // prevent error message "Popup does not fit within the boundaries of the viewport"
position: popupPosition,
});
});
// File list and commits
if ($('.repository.file.list').length > 0 || $('.branch-dropdown').length > 0 ||
$('.repository.commits').length > 0 || $('.repository.release').length > 0) {

View file

@ -127,6 +127,10 @@ function updateStopwatchData(data) {
const watch = data[0];
const btnEl = $('.active-stopwatch-trigger');
if (!watch) {
if (updateTimeInterval) {
clearInterval(updateTimeInterval);
updateTimeInterval = null;
}
btnEl.addClass('hidden');
} else {
const {repo_owner_name, repo_name, issue_index, seconds} = watch;

View file

@ -38,7 +38,11 @@ import {
initRepoPullRequestMergeInstruction,
initRepoPullRequestReview,
} from './features/repo-issue.js';
import {initRepoEllipsisButton, initRepoCommitLastCommitLoader} from './features/repo-commit.js';
import {
initRepoEllipsisButton,
initRepoCommitLastCommitLoader,
initCommitStatuses,
} from './features/repo-commit.js';
import {
checkAppUrl,
initFootLanguageMenu,
@ -165,6 +169,8 @@ $(document).ready(() => {
initRepoWikiForm();
initRepository();
initCommitStatuses();
initUserAuthLinkAccountView();
initUserAuthOauth2();
initUserAuthWebAuthn();

View file

@ -1350,12 +1350,6 @@ footer {
overflow: auto;
}
@media @mediaSm {
.ui.menu.new-menu {
overflow: visible !important;
}
}
.ui.menu.new-menu .new-menu-inner {
display: flex;
margin-left: auto;
@ -1363,14 +1357,6 @@ footer {
overflow-x: auto;
}
@media @mediaSm {
.ui.menu.new-menu .new-menu-inner {
flex-wrap: wrap;
margin-left: 0;
margin-right: 0;
}
}
.ui.menu.new-menu::after {
position: absolute;
display: block;
@ -2194,3 +2180,16 @@ table th[data-sortt-desc] {
height: 15px;
}
}
@media @mediaSm {
.ui.stackable.menu:not(.no-vertical-tabs) {
overflow-y: hidden;
overflow-x: auto;
flex-direction: row;
flex-wrap: nowrap !important;
.item {
width: initial !important;
}
}
}

View file

@ -50,6 +50,8 @@
overflow-wrap: anywhere;
.ui.header {
display: flex;
align-items: center;
font-size: 36px;
margin-bottom: 0;
.org-visibility .label {

View file

@ -2956,11 +2956,21 @@ tbody.commit-list {
justify-content: space-between;
flex-wrap: wrap;
word-break: break-word;
@media @mediaSm {
+ .container {
margin-top: 7px;
}
}
}
.repo-buttons {
display: flex;
align-items: center;
@media @mediaSm {
margin-top: 1em;
}
}
.repo-buttons .ui.labeled.button > .label:hover {

View file

@ -3,12 +3,8 @@
.user {
&.profile {
.ui.card {
.header,
.username {
display: block;
}
.header {
display: block;
font-weight: 600;
font-size: 1.3rem;
margin-top: -.2rem;
@ -17,6 +13,7 @@
.profile-avatar-name {
border-top: none;
text-align: center;
}
.extra.content {