Merge branch 'go-gitea:main' into dev
This commit is contained in:
commit
1e2f9863e7
89 changed files with 754 additions and 549 deletions
4
Makefile
4
Makefile
|
@ -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
|
||||
|
|
58
cmd/dump.go
58
cmd/dump.go
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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/"),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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},
|
||||
},
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -362,6 +362,7 @@ func RepoRefForAPI(next http.Handler) http.Handler {
|
|||
return
|
||||
}
|
||||
ctx.Repo.Commit = commit
|
||||
ctx.Repo.TreePath = ctx.Params("*")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -72,6 +72,7 @@ loading=Зареждане…
|
|||
error404=Страницата, която се опитвате да достъпите, <strong>не съществува</strong> или <strong>не сте оторизирани</strong> да я достъпите.
|
||||
|
||||
|
||||
|
||||
[error]
|
||||
|
||||
[startpage]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -105,6 +105,7 @@ error404=Η σελίδα που προσπαθείτε να φτάσετε εί
|
|||
|
||||
never=Ποτέ
|
||||
|
||||
|
||||
[error]
|
||||
occurred=Παρουσιάστηκε ένα σφάλμα
|
||||
report_message=Αν είστε σίγουροι ότι πρόκειται για ένα πρόβλημα στο Gitea, παρακαλώ αναζητήστε στα ζητήματα στο <a href="https://github.com/go-gitea/gitea/issues" target="_blank">GitHub</a> ή ανοίξτε ένα νέο ζήτημα εάν είναι απαραίτητο.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -91,6 +91,7 @@ error404=صفحه موردنظر شما یا <strong>وجود ندارد</strong
|
|||
|
||||
never=هرگز
|
||||
|
||||
|
||||
[error]
|
||||
missing_csrf=درخواست بد: بلیط CSRF ندارد
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -73,6 +73,7 @@ loading=Memuat…
|
|||
|
||||
|
||||
|
||||
|
||||
[error]
|
||||
|
||||
[startpage]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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=コマンドラインで実行します:
|
||||
|
|
|
@ -74,6 +74,7 @@ loading=불러오는 중...
|
|||
|
||||
|
||||
|
||||
|
||||
[error]
|
||||
|
||||
[startpage]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -65,6 +65,7 @@ loading=ലഭ്യമാക്കുന്നു…
|
|||
|
||||
|
||||
|
||||
|
||||
[error]
|
||||
|
||||
[startpage]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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=Значение
|
||||
|
||||
|
|
|
@ -91,6 +91,7 @@ error404=ඔබ ළඟා වීමට උත්සාහ කරන පිටු
|
|||
|
||||
never=කිසි විටෙකත්
|
||||
|
||||
|
||||
[error]
|
||||
missing_csrf=නරක ඉල්ලීම: CSRF ටෝකන් නොමැත
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ error404=Сторінка, до якої ви намагаєтеся зверн
|
|||
|
||||
never=Ніколи
|
||||
|
||||
|
||||
[error]
|
||||
occurred=Сталася помилка
|
||||
missing_csrf=Некоректний запит: токен CSRF не задано
|
||||
|
|
|
@ -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> 上搜索问题,或在必要时打开一个新问题。
|
||||
|
|
|
@ -46,6 +46,7 @@ cancel=取消
|
|||
|
||||
|
||||
|
||||
|
||||
[error]
|
||||
|
||||
[startpage]
|
||||
|
|
|
@ -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> 搜尋相關的問題,如果有需要您也可以建立新問題。
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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",
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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}}>
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"}}">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,8 @@
|
|||
overflow-wrap: anywhere;
|
||||
|
||||
.ui.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 36px;
|
||||
margin-bottom: 0;
|
||||
.org-visibility .label {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Reference in a new issue