diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 08708948940f..ac0c9e9c8670 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -313,6 +313,7 @@ USER = root ;DB_TYPE = sqlite3 ;PATH= ; defaults to data/gitea.db ;SQLITE_TIMEOUT = ; Query timeout defaults to: 500 +;SQLITE_JOURNAL_MODE = ; defaults to sqlite database default (often DELETE), can be used to enable WAL mode. https://www.sqlite.org/pragma.html#pragma_journal_mode ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; @@ -878,6 +879,9 @@ ROUTER = console ;; Allow deletion of unadopted repositories ;ALLOW_DELETION_OF_UNADOPTED_REPOSITORIES = false +;; Don't allow download source archive files from UI +;DISABLE_DOWNLOAD_SOURCE_ARCHIVES = false + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;[repository.editor] diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 31a294e1c933..e4e7ad7b1986 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -78,6 +78,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. - `DEFAULT_BRANCH`: **main**: Default branch name of all repositories. - `ALLOW_ADOPTION_OF_UNADOPTED_REPOSITORIES`: **false**: Allow non-admin users to adopt unadopted repositories - `ALLOW_DELETION_OF_UNADOPTED_REPOSITORIES`: **false**: Allow non-admin users to delete unadopted repositories +- `DISABLE_DOWNLOAD_SOURCE_ARCHIVES`: **false**: Don't allow download source archive files from UI ### Repository - Editor (`repository.editor`) @@ -382,6 +383,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a - `verify-ca`: Enable TLS with verification of the database server certificate against its root certificate. - `verify-full`: Enable TLS and verify the database server name matches the given certificate in either the `Common Name` or `Subject Alternative Name` fields. - `SQLITE_TIMEOUT`: **500**: Query timeout for SQLite3 only. +- `SQLITE_JOURNAL_MODE`: **""**: Change journal mode for SQlite3. Can be used to enable [WAL mode](https://www.sqlite.org/wal.html) when high load causes write congestion. See [SQlite3 docs](https://www.sqlite.org/pragma.html#pragma_journal_mode) for possible values. Defaults to the default for the database file, often DELETE. - `ITERATE_BUFFER_SIZE`: **50**: Internal buffer size for iterating. - `CHARSET`: **utf8mb4**: For MySQL only, either "utf8" or "utf8mb4". NOTICE: for "utf8mb4" you must use MySQL InnoDB > 5.6. Gitea is unable to check this. - `PATH`: **data/gitea.db**: For SQLite3 only, the database file path. diff --git a/docs/content/doc/installation/from-binary.en-us.md b/docs/content/doc/installation/from-binary.en-us.md index 91a6b2a9d6d2..f603fe37cfd0 100644 --- a/docs/content/doc/installation/from-binary.en-us.md +++ b/docs/content/doc/installation/from-binary.en-us.md @@ -24,13 +24,29 @@ embedded assets. This can be different for older releases. ## Download -Choose the file matching your platform from the [downloads page](https://dl.gitea.io/gitea/), copy the URL and replace the URL within the commands below: +You can find the file matching your platform from the [downloads page](https://dl.gitea.io/gitea/) after navigating to the version you want to download. + +### Choosing the right file + +**For Linux**, you will likely want `linux-amd64`. It's for 64-bit Intel/AMD platforms, but there are other platforms available, including `arm64` (e.g. Raspberry PI 4), `386` (i.e. 32-bit), `arm-5`, and `arm-6`. + +**For Windows**, you will likely want `windows-4.0-amd64`. It's for all modern versions of Windows, but there is also a `386` platform available designed for older, 32-bit versions of Windows. + +*Note: there is also a `gogit-windows` file available that was created to help with some [performance problems](https://github.com/go-gitea/gitea/pull/15482) reported by some Windows users on older systems/versions. You should consider using this file if you're experiencing performance issues, and let us know if it improves performance.* + +**For macOS**, you should choose `darwin-arm64` if your hardware uses Apple Silicon, or `darwin-amd64` for Intel. + +### Downloading with wget + +Copy the commands below and replace the URL within the one you wish to download. ```sh wget -O gitea https://dl.gitea.io/gitea/{{< version >}}/gitea-{{< version >}}-linux-amd64 chmod +x gitea ``` +Note that the above command will download Gitea {{< version >}} for 64-bit Linux. + ## Verify GPG signature Gitea signs all binaries with a [GPG key](https://keys.openpgp.org/search?q=teabot%40gitea.io) to prevent against unwanted modification of binaries. diff --git a/docs/content/doc/installation/with-docker.en-us.md b/docs/content/doc/installation/with-docker.en-us.md index fb60b97118b9..895f04804e2b 100644 --- a/docs/content/doc/installation/with-docker.en-us.md +++ b/docs/content/doc/installation/with-docker.en-us.md @@ -303,7 +303,23 @@ services: - GITEA__mailer__PASSWD="""${GITEA__mailer__PASSWD:?GITEA__mailer__PASSWD not set}""" ``` -To set required TOKEN and SECRET values, consider using Gitea's built-in [generate utility functions](https://docs.gitea.io/en-us/command-line/#generate). +Gitea will generate new secrets/tokens for every new installation automatically and write them into the app.ini. If you want to set the secrets/tokens manually, you can use the following docker commands to use of Gitea's built-in [generate utility functions](https://docs.gitea.io/en-us/command-line/#generate). Do not lose/change your SECRET_KEY after the installation, otherwise the encrypted data can not be decrypted anymore. + +The following commands will output a new `SECRET_KEY` and `INTERNAL_TOKEN` to `stdout`, which you can then place in your environment variables. + +```bash +docker run -it --rm gitea/gitea:1 gitea generate secret SECRET_KEY +docker run -it --rm gitea/gitea:1 gitea generate secret INTERNAL_TOKEN +``` + +```yaml +... +services: + server: + environment: + - GITEA__security__SECRET_KEY=[value returned by generate secret SECRET_KEY] + - GITEA__security__INTERNAL_TOKEN=[value returned by generate secret INTERNAL_TOKEN] +``` ## SSH Container Passthrough diff --git a/docs/content/doc/packages/composer.en-us.md b/docs/content/doc/packages/composer.en-us.md index 2502ee45b509..47b03781f06e 100644 --- a/docs/content/doc/packages/composer.en-us.md +++ b/docs/content/doc/packages/composer.en-us.md @@ -60,6 +60,8 @@ curl --user your_username:your_password_or_token \ https://gitea.example.com/api/packages/testuser/composer?version=1.0.3 ``` +If you are using 2FA or OAuth use a [personal access token]({{< relref "doc/developers/api-usage.en-us.md#authentication" >}}) instead of the password. + The server responds with the following HTTP Status codes. | HTTP Status Code | Meaning | diff --git a/docs/content/doc/packages/conan.en-us.md b/docs/content/doc/packages/conan.en-us.md index c650e9d7ea49..fb104f34f43b 100644 --- a/docs/content/doc/packages/conan.en-us.md +++ b/docs/content/doc/packages/conan.en-us.md @@ -37,7 +37,7 @@ conan user --remote {remote} --password {password} {username} | -----------| ----------- | | `remote` | The remote name. | | `username` | Your Gitea username. | -| `password` | Your Gitea password or a personal access token. | +| `password` | Your Gitea password. If you are using 2FA or OAuth use a [personal access token]({{< relref "doc/developers/api-usage.en-us.md#authentication" >}}) instead of the password. | | `owner` | The owner of the package. | For example: diff --git a/docs/content/doc/packages/container.en-us.md b/docs/content/doc/packages/container.en-us.md index 28559eb22b8d..77dbbafd02cb 100644 --- a/docs/content/doc/packages/container.en-us.md +++ b/docs/content/doc/packages/container.en-us.md @@ -34,6 +34,8 @@ To push an image or if the image is in a private registry, you have to authentic docker login gitea.example.com ``` +If you are using 2FA or OAuth use a [personal access token]({{< relref "doc/developers/api-usage.en-us.md#authentication" >}}) instead of the password. + ## Image naming convention Images must follow this naming convention: diff --git a/docs/content/doc/packages/generic.en-us.md b/docs/content/doc/packages/generic.en-us.md index afef323938d4..a82058da8ac5 100644 --- a/docs/content/doc/packages/generic.en-us.md +++ b/docs/content/doc/packages/generic.en-us.md @@ -37,7 +37,7 @@ PUT https://gitea.example.com/api/packages/{owner}/generic/{package_name}/{packa | ----------------- | ----------- | | `owner` | The owner of the package. | | `package_name` | The package name. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`). | -| `package_version` | The package version as described in the [SemVer](https://semver.org/) spec. | +| `package_version` | The package version, a non-empty string. | | `file_name` | The filename. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`). | Example request using HTTP Basic authentication: @@ -48,6 +48,8 @@ curl --user your_username:your_password_or_token \ https://gitea.example.com/api/packages/testuser/generic/test_package/1.0.0/file.bin ``` +If you are using 2FA or OAuth use a [personal access token]({{< relref "doc/developers/api-usage.en-us.md#authentication" >}}) instead of the password. + The server reponds with the following HTTP Status codes. | HTTP Status Code | Meaning | diff --git a/docs/content/doc/packages/helm.en-us.md b/docs/content/doc/packages/helm.en-us.md index 9c43b08bf429..89b929f9bc84 100644 --- a/docs/content/doc/packages/helm.en-us.md +++ b/docs/content/doc/packages/helm.en-us.md @@ -42,7 +42,7 @@ helm cm-push ./{chart_file}.tgz {repo} | Parameter | Description | | ------------ | ----------- | | `username` | Your Gitea username. | -| `password` | Your Gitea password or a personal access token. | +| `password` | Your Gitea password. If you are using 2FA or OAuth use a [personal access token]({{< relref "doc/developers/api-usage.en-us.md#authentication" >}}) instead of the password. | | `repo` | The name for the repository. | | `chart_file` | The Helm Chart archive. | | `owner` | The owner of the package. | diff --git a/docs/content/doc/packages/nuget.en-us.md b/docs/content/doc/packages/nuget.en-us.md index 421faf9ee650..a4435fa99f01 100644 --- a/docs/content/doc/packages/nuget.en-us.md +++ b/docs/content/doc/packages/nuget.en-us.md @@ -38,7 +38,7 @@ dotnet nuget add source --name {source_name} --username {username} --password {p | ------------- | ----------- | | `source_name` | The desired source name. | | `username` | Your Gitea username. | -| `password` | Your Gitea password or a personal access token. | +| `password` | Your Gitea password. If you are using 2FA or OAuth use a [personal access token]({{< relref "doc/developers/api-usage.en-us.md#authentication" >}}) instead of the password. | | `owner` | The owner of the package. | For example: diff --git a/docs/content/doc/packages/pypi.en-us.md b/docs/content/doc/packages/pypi.en-us.md index 148257507237..af17fe83101c 100644 --- a/docs/content/doc/packages/pypi.en-us.md +++ b/docs/content/doc/packages/pypi.en-us.md @@ -42,7 +42,7 @@ password = {password} | ------------ | ----------- | | `owner` | The owner of the package. | | `username` | Your Gitea username. | -| `password` | Your Gitea password or a [personal access token]({{< relref "doc/developers/api-usage.en-us.md#authentication" >}}). | +| `password` | Your Gitea password. If you are using 2FA or OAuth use a [personal access token]({{< relref "doc/developers/api-usage.en-us.md#authentication" >}}) instead of the password. | ## Publish a package diff --git a/docs/content/doc/packages/rubygems.en-us.md b/docs/content/doc/packages/rubygems.en-us.md index 98b3feafcd3d..dd7ac9ee7ed0 100644 --- a/docs/content/doc/packages/rubygems.en-us.md +++ b/docs/content/doc/packages/rubygems.en-us.md @@ -36,7 +36,7 @@ https://gitea.example.com/api/packages/{owner}/rubygems: Bearer {token} | Parameter | Description | | ------------- | ----------- | | `owner` | The owner of the package. | -| `token` | Your personal access token. | +| `token` | Your [personal access token]({{< relref "doc/developers/api-usage.en-us.md#authentication" >}}). | For example: diff --git a/go.mod b/go.mod index 78495cc6a252..6d41af507d35 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( gitea.com/go-chi/cache v0.2.0 gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5 gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8 - gitea.com/lunny/levelqueue v0.4.1 + gitea.com/lunny/levelqueue v0.4.2-0.20220729054728-f020868cc2f7 github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 github.com/NYTimes/gziphandler v1.1.1 github.com/PuerkitoBio/goquery v1.8.0 diff --git a/go.sum b/go.sum index dca68d9a8e7d..124e65a727ad 100644 --- a/go.sum +++ b/go.sum @@ -80,8 +80,8 @@ gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5 h1:J/1i8u40TbcLP/w2w gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5/go.mod h1:hQ9SYHKdOX968wJglb/NMQ+UqpOKwW4L+EYdvkWjHSo= gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8 h1:tJQRXgZigkLeeW9LPlps9G9aMoE6LAmqigLA+wxmd1Q= gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8/go.mod h1:fc/pjt5EqNKgqQXYzcas1Z5L5whkZHyOvTA7OzWVJck= -gitea.com/lunny/levelqueue v0.4.1 h1:RZ+AFx5gBsZuyqCvofhAkPQ9uaVDPJnsULoJZIYaJNw= -gitea.com/lunny/levelqueue v0.4.1/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU= +gitea.com/lunny/levelqueue v0.4.2-0.20220729054728-f020868cc2f7 h1:Zc3RQWC2xOVglLciQH/ZIC5IqSk3Jn96LflGQLv18Rg= +gitea.com/lunny/levelqueue v0.4.2-0.20220729054728-f020868cc2f7/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE= diff --git a/integrations/api_packages_container_test.go b/integrations/api_packages_container_test.go index bdb8e2e90e39..5e073f313f7a 100644 --- a/integrations/api_packages_container_test.go +++ b/integrations/api_packages_container_test.go @@ -27,6 +27,7 @@ import ( func TestPackageContainer(t *testing.T) { defer prepareTestEnv(t)() + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) has := func(l packages_model.PackagePropertyList, name string) bool { @@ -37,6 +38,15 @@ func TestPackageContainer(t *testing.T) { } return false } + getAllByName := func(l packages_model.PackagePropertyList, name string) []string { + values := make([]string, 0, len(l)) + for _, pp := range l { + if pp.Name == name { + values = append(values, pp.Value) + } + } + return values + } images := []string{"test", "te/st"} tags := []string{"latest", "main"} @@ -67,7 +77,7 @@ func TestPackageContainer(t *testing.T) { Token string `json:"token"` } - authenticate := []string{`Bearer realm="` + setting.AppURL + `v2/token"`} + authenticate := []string{`Bearer realm="` + setting.AppURL + `v2/token",service="container_registry",scope="*"`} t.Run("Anonymous", func(t *testing.T) { defer PrintCurrentTest(t)() @@ -237,7 +247,8 @@ func TestPackageContainer(t *testing.T) { assert.Nil(t, pd.SemVer) assert.Equal(t, image, pd.Package.Name) assert.Equal(t, tag, pd.Version.Version) - assert.True(t, has(pd.Properties, container_module.PropertyManifestTagged)) + assert.ElementsMatch(t, []string{strings.ToLower(user.LowerName + "/" + image)}, getAllByName(pd.PackageProperties, container_module.PropertyRepository)) + assert.True(t, has(pd.VersionProperties, container_module.PropertyManifestTagged)) assert.IsType(t, &container_module.Metadata{}, pd.Metadata) metadata := pd.Metadata.(*container_module.Metadata) @@ -331,7 +342,8 @@ func TestPackageContainer(t *testing.T) { assert.Nil(t, pd.SemVer) assert.Equal(t, image, pd.Package.Name) assert.Equal(t, untaggedManifestDigest, pd.Version.Version) - assert.False(t, has(pd.Properties, container_module.PropertyManifestTagged)) + assert.ElementsMatch(t, []string{strings.ToLower(user.LowerName + "/" + image)}, getAllByName(pd.PackageProperties, container_module.PropertyRepository)) + assert.False(t, has(pd.VersionProperties, container_module.PropertyManifestTagged)) assert.IsType(t, &container_module.Metadata{}, pd.Metadata) @@ -363,18 +375,10 @@ func TestPackageContainer(t *testing.T) { assert.Nil(t, pd.SemVer) assert.Equal(t, image, pd.Package.Name) assert.Equal(t, multiTag, pd.Version.Version) - assert.True(t, has(pd.Properties, container_module.PropertyManifestTagged)) + assert.ElementsMatch(t, []string{strings.ToLower(user.LowerName + "/" + image)}, getAllByName(pd.PackageProperties, container_module.PropertyRepository)) + assert.True(t, has(pd.VersionProperties, container_module.PropertyManifestTagged)) - getAllByName := func(l packages_model.PackagePropertyList, name string) []string { - values := make([]string, 0, len(l)) - for _, pp := range l { - if pp.Name == name { - values = append(values, pp.Value) - } - } - return values - } - assert.ElementsMatch(t, []string{manifestDigest, untaggedManifestDigest}, getAllByName(pd.Properties, container_module.PropertyManifestReference)) + assert.ElementsMatch(t, []string{manifestDigest, untaggedManifestDigest}, getAllByName(pd.VersionProperties, container_module.PropertyManifestReference)) assert.IsType(t, &container_module.Metadata{}, pd.Metadata) metadata := pd.Metadata.(*container_module.Metadata) @@ -536,4 +540,56 @@ func TestPackageContainer(t *testing.T) { }) }) } + + t.Run("OwnerNameChange", func(t *testing.T) { + defer PrintCurrentTest(t)() + + checkCatalog := func(owner string) func(t *testing.T) { + return func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := NewRequest(t, "GET", fmt.Sprintf("%sv2/_catalog", setting.AppURL)) + addTokenAuthHeader(req, userToken) + resp := MakeRequest(t, req, http.StatusOK) + + type RepositoryList struct { + Repositories []string `json:"repositories"` + } + + repoList := &RepositoryList{} + DecodeJSON(t, resp, &repoList) + + assert.Len(t, repoList.Repositories, len(images)) + names := make([]string, 0, len(images)) + for _, image := range images { + names = append(names, strings.ToLower(owner+"/"+image)) + } + assert.ElementsMatch(t, names, repoList.Repositories) + } + } + + t.Run(fmt.Sprintf("Catalog[%s]", user.LowerName), checkCatalog(user.LowerName)) + + session := loginUser(t, user.Name) + + newOwnerName := "newUsername" + + req := NewRequestWithValues(t, "POST", "/user/settings", map[string]string{ + "_csrf": GetCSRF(t, session, "/user/settings"), + "name": newOwnerName, + "email": "user2@example.com", + "language": "en-US", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + t.Run(fmt.Sprintf("Catalog[%s]", newOwnerName), checkCatalog(newOwnerName)) + + req = NewRequestWithValues(t, "POST", "/user/settings", map[string]string{ + "_csrf": GetCSRF(t, session, "/user/settings"), + "name": user.Name, + "email": "user2@example.com", + "language": "en-US", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + }) } diff --git a/integrations/api_packages_generic_test.go b/integrations/api_packages_generic_test.go index c507702eaafd..adaf99e981ae 100644 --- a/integrations/api_packages_generic_test.go +++ b/integrations/api_packages_generic_test.go @@ -42,7 +42,6 @@ func TestPackageGeneric(t *testing.T) { pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) assert.NoError(t, err) - assert.NotNil(t, pd.SemVer) assert.Nil(t, pd.Metadata) assert.Equal(t, packageName, pd.Package.Name) assert.Equal(t, packageVersion, pd.Version.Version) diff --git a/integrations/api_packages_npm_test.go b/integrations/api_packages_npm_test.go index 28a371193982..ad88ac5da676 100644 --- a/integrations/api_packages_npm_test.go +++ b/integrations/api_packages_npm_test.go @@ -85,9 +85,9 @@ func TestPackageNpm(t *testing.T) { assert.IsType(t, &npm.Metadata{}, pd.Metadata) assert.Equal(t, packageName, pd.Package.Name) assert.Equal(t, packageVersion, pd.Version.Version) - assert.Len(t, pd.Properties, 1) - assert.Equal(t, npm.TagProperty, pd.Properties[0].Name) - assert.Equal(t, packageTag, pd.Properties[0].Value) + assert.Len(t, pd.VersionProperties, 1) + assert.Equal(t, npm.TagProperty, pd.VersionProperties[0].Name) + assert.Equal(t, packageTag, pd.VersionProperties[0].Value) pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID) assert.NoError(t, err) diff --git a/integrations/api_repo_file_create_test.go b/integrations/api_repo_file_create_test.go index eb550f1d28d7..fd460ce5643f 100644 --- a/integrations/api_repo_file_create_test.go +++ b/integrations/api_repo_file_create_test.go @@ -50,7 +50,7 @@ func getCreateFileOptions() api.CreateFileOptions { } } -func getExpectedFileResponseForCreate(repoFullName, commitID, treePath string) *api.FileResponse { +func getExpectedFileResponseForCreate(repoFullName, commitID, treePath, latestCommitSHA string) *api.FileResponse { sha := "a635aa942442ddfdba07468cf9661c08fbdf0ebf" encoding := "base64" content := "VGhpcyBpcyBuZXcgdGV4dA==" @@ -60,17 +60,18 @@ func getExpectedFileResponseForCreate(repoFullName, commitID, treePath string) * downloadURL := setting.AppURL + repoFullName + "/raw/branch/master/" + treePath return &api.FileResponse{ Content: &api.ContentsResponse{ - Name: filepath.Base(treePath), - Path: treePath, - SHA: sha, - Size: 16, - Type: "file", - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: filepath.Base(treePath), + Path: treePath, + SHA: sha, + LastCommitSHA: latestCommitSHA, + Size: 16, + Type: "file", + Encoding: &encoding, + Content: &content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, @@ -170,7 +171,8 @@ func TestAPICreateFile(t *testing.T) { resp := session.MakeRequest(t, req, http.StatusCreated) gitRepo, _ := git.OpenRepository(stdCtx.Background(), repo1.RepoPath()) commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName) - expectedFileResponse := getExpectedFileResponseForCreate("user2/repo1", commitID, treePath) + latestCommit, _ := gitRepo.GetCommitByPath(treePath) + expectedFileResponse := getExpectedFileResponseForCreate("user2/repo1", commitID, treePath, latestCommit.ID.String()) var fileResponse api.FileResponse DecodeJSON(t, resp, &fileResponse) assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) @@ -289,7 +291,8 @@ func TestAPICreateFile(t *testing.T) { emptyRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: "empty-repo"}).(*repo_model.Repository) // public repo gitRepo, _ := git.OpenRepository(stdCtx.Background(), emptyRepo.RepoPath()) commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName) - expectedFileResponse := getExpectedFileResponseForCreate("user2/empty-repo", commitID, treePath) + latestCommit, _ := gitRepo.GetCommitByPath(treePath) + expectedFileResponse := getExpectedFileResponseForCreate("user2/empty-repo", commitID, treePath, latestCommit.ID.String()) DecodeJSON(t, resp, &fileResponse) assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) diff --git a/integrations/api_repo_file_update_test.go b/integrations/api_repo_file_update_test.go index 0c9c0763f467..e39d83e49e13 100644 --- a/integrations/api_repo_file_update_test.go +++ b/integrations/api_repo_file_update_test.go @@ -48,7 +48,7 @@ func getUpdateFileOptions() *api.UpdateFileOptions { } } -func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileResponse { +func getExpectedFileResponseForUpdate(commitID, treePath, lastCommitSHA string) *api.FileResponse { sha := "08bd14b2e2852529157324de9c226b3364e76136" encoding := "base64" content := "VGhpcyBpcyB1cGRhdGVkIHRleHQ=" @@ -58,17 +58,18 @@ func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileRespon downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath return &api.FileResponse{ Content: &api.ContentsResponse{ - Name: filepath.Base(treePath), - Path: treePath, - SHA: sha, - Type: "file", - Size: 20, - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: filepath.Base(treePath), + Path: treePath, + SHA: sha, + LastCommitSHA: lastCommitSHA, + Type: "file", + Size: 20, + Encoding: &encoding, + Content: &content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, @@ -137,7 +138,8 @@ func TestAPIUpdateFile(t *testing.T) { resp := session.MakeRequest(t, req, http.StatusOK) gitRepo, _ := git.OpenRepository(stdCtx.Background(), repo1.RepoPath()) commitID, _ := gitRepo.GetBranchCommitID(updateFileOptions.NewBranchName) - expectedFileResponse := getExpectedFileResponseForUpdate(commitID, treePath) + lasCommit, _ := gitRepo.GetCommitByPath(treePath) + expectedFileResponse := getExpectedFileResponseForUpdate(commitID, treePath, lasCommit.ID.String()) var fileResponse api.FileResponse DecodeJSON(t, resp, &fileResponse) assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) diff --git a/integrations/api_repo_get_contents_list_test.go b/integrations/api_repo_get_contents_list_test.go index 42227a9c4b72..97b152bf1b34 100644 --- a/integrations/api_repo_get_contents_list_test.go +++ b/integrations/api_repo_get_contents_list_test.go @@ -21,7 +21,7 @@ import ( "github.com/stretchr/testify/assert" ) -func getExpectedContentsListResponseForContents(ref, refType string) []*api.ContentsResponse { +func getExpectedContentsListResponseForContents(ref, refType, lastCommitSHA string) []*api.ContentsResponse { treePath := "README.md" sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f" selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + treePath + "?ref=" + ref @@ -30,15 +30,16 @@ func getExpectedContentsListResponseForContents(ref, refType string) []*api.Cont downloadURL := setting.AppURL + "user2/repo1/raw/" + refType + "/" + ref + "/" + treePath return []*api.ContentsResponse{ { - Name: filepath.Base(treePath), - Path: treePath, - SHA: sha, - Type: "file", - Size: 30, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: filepath.Base(treePath), + Path: treePath, + SHA: sha, + LastCommitSHA: lastCommitSHA, + Type: "file", + Size: 30, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, @@ -94,7 +95,9 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { var contentsListResponse []*api.ContentsResponse DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) - expectedContentsListResponse := getExpectedContentsListResponseForContents(ref, refType) + lastCommit, err := gitRepo.GetCommitByPath("README.md") + assert.NoError(t, err) + expectedContentsListResponse := getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String()) assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) // No ref @@ -103,17 +106,22 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) - expectedContentsListResponse = getExpectedContentsListResponseForContents(repo1.DefaultBranch, refType) + + expectedContentsListResponse = getExpectedContentsListResponseForContents(repo1.DefaultBranch, refType, lastCommit.ID.String()) assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) - // ref is the branch we created above in setup + // ref is the branch we created above in setup ref = newBranch refType = "branch" req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/contents/%s?ref=%s", user2.Name, repo1.Name, treePath, ref) resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) - expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType) + branchCommit, err := gitRepo.GetBranchCommit(ref) + assert.NoError(t, err) + lastCommit, err = branchCommit.GetCommitByPath("README.md") + assert.NoError(t, err) + expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String()) assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) // ref is the new tag we created above in setup @@ -123,7 +131,11 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) - expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType) + tagCommit, err := gitRepo.GetTagCommit(ref) + assert.NoError(t, err) + lastCommit, err = tagCommit.GetCommitByPath("README.md") + assert.NoError(t, err) + expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType, lastCommit.ID.String()) assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) // ref is a commit @@ -133,7 +145,7 @@ func testAPIGetContentsList(t *testing.T, u *url.URL) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsListResponse) assert.NotNil(t, contentsListResponse) - expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType) + expectedContentsListResponse = getExpectedContentsListResponseForContents(ref, refType, commitID) assert.EqualValues(t, expectedContentsListResponse, contentsListResponse) // Test file contents a file with a bad ref diff --git a/integrations/api_repo_get_contents_test.go b/integrations/api_repo_get_contents_test.go index 67f2cb83625a..56f5336b7bd9 100644 --- a/integrations/api_repo_get_contents_test.go +++ b/integrations/api_repo_get_contents_test.go @@ -20,7 +20,7 @@ import ( "github.com/stretchr/testify/assert" ) -func getExpectedContentsResponseForContents(ref, refType string) *api.ContentsResponse { +func getExpectedContentsResponseForContents(ref, refType, lastCommitSHA string) *api.ContentsResponse { treePath := "README.md" sha := "4b4851ad51df6a7d9f25c979345979eaeb5b349f" encoding := "base64" @@ -30,17 +30,18 @@ func getExpectedContentsResponseForContents(ref, refType string) *api.ContentsRe gitURL := setting.AppURL + "api/v1/repos/user2/repo1/git/blobs/" + sha downloadURL := setting.AppURL + "user2/repo1/raw/" + refType + "/" + ref + "/" + treePath return &api.ContentsResponse{ - Name: treePath, - Path: treePath, - SHA: sha, - Type: "file", - Size: 30, - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: treePath, + Path: treePath, + SHA: sha, + LastCommitSHA: lastCommitSHA, + Type: "file", + Size: 30, + Encoding: &encoding, + Content: &content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, @@ -96,7 +97,8 @@ func testAPIGetContents(t *testing.T, u *url.URL) { var contentsResponse api.ContentsResponse DecodeJSON(t, resp, &contentsResponse) assert.NotNil(t, contentsResponse) - expectedContentsResponse := getExpectedContentsResponseForContents(ref, refType) + lastCommit, _ := gitRepo.GetCommitByPath("README.md") + expectedContentsResponse := getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String()) assert.EqualValues(t, *expectedContentsResponse, contentsResponse) // No ref @@ -105,7 +107,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsResponse) assert.NotNil(t, contentsResponse) - expectedContentsResponse = getExpectedContentsResponseForContents(repo1.DefaultBranch, refType) + expectedContentsResponse = getExpectedContentsResponseForContents(repo1.DefaultBranch, refType, lastCommit.ID.String()) assert.EqualValues(t, *expectedContentsResponse, contentsResponse) // ref is the branch we created above in setup @@ -115,7 +117,9 @@ func testAPIGetContents(t *testing.T, u *url.URL) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsResponse) assert.NotNil(t, contentsResponse) - expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType) + branchCommit, _ := gitRepo.GetBranchCommit(ref) + lastCommit, _ = branchCommit.GetCommitByPath("README.md") + expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String()) assert.EqualValues(t, *expectedContentsResponse, contentsResponse) // ref is the new tag we created above in setup @@ -125,7 +129,9 @@ func testAPIGetContents(t *testing.T, u *url.URL) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsResponse) assert.NotNil(t, contentsResponse) - expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType) + tagCommit, _ := gitRepo.GetTagCommit(ref) + lastCommit, _ = tagCommit.GetCommitByPath("README.md") + expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType, lastCommit.ID.String()) assert.EqualValues(t, *expectedContentsResponse, contentsResponse) // ref is a commit @@ -135,7 +141,7 @@ func testAPIGetContents(t *testing.T, u *url.URL) { resp = session.MakeRequest(t, req, http.StatusOK) DecodeJSON(t, resp, &contentsResponse) assert.NotNil(t, contentsResponse) - expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType) + expectedContentsResponse = getExpectedContentsResponseForContents(ref, refType, commitID) assert.EqualValues(t, *expectedContentsResponse, contentsResponse) // Test file contents a file with a bad ref diff --git a/integrations/mirror_push_test.go b/integrations/mirror_push_test.go index a73b69e7869d..3a22b0075423 100644 --- a/integrations/mirror_push_test.go +++ b/integrations/mirror_push_test.go @@ -13,6 +13,7 @@ import ( "testing" "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -47,7 +48,7 @@ func testMirrorPush(t *testing.T, u *url.URL) { doCreatePushMirror(ctx, fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(ctx.Username), url.PathEscape(mirrorRepo.Name)), user.LowerName, userPassword)(t) - mirrors, err := repo_model.GetPushMirrorsByRepoID(srcRepo.ID) + mirrors, _, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{}) assert.NoError(t, err) assert.Len(t, mirrors, 1) @@ -72,7 +73,7 @@ func testMirrorPush(t *testing.T, u *url.URL) { // Cleanup doRemovePushMirror(ctx, fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(ctx.Username), url.PathEscape(mirrorRepo.Name)), user.LowerName, userPassword, int(mirrors[0].ID))(t) - mirrors, err = repo_model.GetPushMirrorsByRepoID(srcRepo.ID) + mirrors, _, err = repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{}) assert.NoError(t, err) assert.Len(t, mirrors, 0) } diff --git a/integrations/repofiles_update_test.go b/integrations/repofiles_update_test.go index 2add99cc86ff..ac9b0509eae4 100644 --- a/integrations/repofiles_update_test.go +++ b/integrations/repofiles_update_test.go @@ -47,7 +47,7 @@ func getUpdateRepoFileOptions(repo *repo_model.Repository) *files_service.Update } } -func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileResponse { +func getExpectedFileResponseForRepofilesCreate(commitID, lastCommitSHA string) *api.FileResponse { treePath := "new/file.txt" encoding := "base64" content := "VGhpcyBpcyBhIE5FVyBmaWxl" @@ -57,17 +57,18 @@ func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileRespons downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath return &api.FileResponse{ Content: &api.ContentsResponse{ - Name: filepath.Base(treePath), - Path: treePath, - SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885", - Type: "file", - Size: 18, - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: filepath.Base(treePath), + Path: treePath, + SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885", + LastCommitSHA: lastCommitSHA, + Type: "file", + Size: 18, + Encoding: &encoding, + Content: &content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, @@ -115,7 +116,7 @@ func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileRespons } } -func getExpectedFileResponseForRepofilesUpdate(commitID, filename string) *api.FileResponse { +func getExpectedFileResponseForRepofilesUpdate(commitID, filename, lastCommitSHA string) *api.FileResponse { encoding := "base64" content := "VGhpcyBpcyBVUERBVEVEIGNvbnRlbnQgZm9yIHRoZSBSRUFETUUgZmlsZQ==" selfURL := setting.AppURL + "api/v1/repos/user2/repo1/contents/" + filename + "?ref=master" @@ -124,17 +125,18 @@ func getExpectedFileResponseForRepofilesUpdate(commitID, filename string) *api.F downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + filename return &api.FileResponse{ Content: &api.ContentsResponse{ - Name: filename, - Path: filename, - SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647", - Type: "file", - Size: 43, - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: filename, + Path: filename, + SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647", + LastCommitSHA: lastCommitSHA, + Type: "file", + Size: 43, + Encoding: &encoding, + Content: &content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, @@ -206,7 +208,8 @@ func TestCreateOrUpdateRepoFileForCreate(t *testing.T) { defer gitRepo.Close() commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) - expectedFileResponse := getExpectedFileResponseForRepofilesCreate(commitID) + lastCommit, _ := gitRepo.GetCommitByPath("new/file.txt") + expectedFileResponse := getExpectedFileResponseForRepofilesCreate(commitID, lastCommit.ID.String()) assert.NotNil(t, expectedFileResponse) if expectedFileResponse != nil { assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) @@ -241,8 +244,9 @@ func TestCreateOrUpdateRepoFileForUpdate(t *testing.T) { gitRepo, _ := git.OpenRepository(git.DefaultContext, repo.RepoPath()) defer gitRepo.Close() - commitID, _ := gitRepo.GetBranchCommitID(opts.NewBranch) - expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID, opts.TreePath) + commit, _ := gitRepo.GetBranchCommit(opts.NewBranch) + lastCommit, _ := commit.GetCommitByPath(opts.TreePath) + expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.TreePath, lastCommit.ID.String()) assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) assert.EqualValues(t, expectedFileResponse.Commit.SHA, fileResponse.Commit.SHA) assert.EqualValues(t, expectedFileResponse.Commit.HTMLURL, fileResponse.Commit.HTMLURL) @@ -277,7 +281,8 @@ func TestCreateOrUpdateRepoFileForUpdateWithFileMove(t *testing.T) { defer gitRepo.Close() commit, _ := gitRepo.GetBranchCommit(opts.NewBranch) - expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.TreePath) + lastCommit, _ := commit.GetCommitByPath(opts.TreePath) + expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.TreePath, lastCommit.ID.String()) // assert that the old file no longer exists in the last commit of the branch fromEntry, err := commit.GetTreeEntryByPath(opts.FromTreePath) switch err.(type) { @@ -326,8 +331,9 @@ func TestCreateOrUpdateRepoFileWithoutBranchNames(t *testing.T) { gitRepo, _ := git.OpenRepository(git.DefaultContext, repo.RepoPath()) defer gitRepo.Close() - commitID, _ := gitRepo.GetBranchCommitID(repo.DefaultBranch) - expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commitID, opts.TreePath) + commit, _ := gitRepo.GetBranchCommit(repo.DefaultBranch) + lastCommit, _ := commit.GetCommitByPath(opts.TreePath) + expectedFileResponse := getExpectedFileResponseForRepofilesUpdate(commit.ID.String(), opts.TreePath, lastCommit.ID.String()) assert.EqualValues(t, expectedFileResponse.Content, fileResponse.Content) }) } diff --git a/models/auth/webauthn.go b/models/auth/webauthn.go index 2dc304378010..d3062342f545 100644 --- a/models/auth/webauthn.go +++ b/models/auth/webauthn.go @@ -6,7 +6,6 @@ package auth import ( "context" - "encoding/base32" "fmt" "strings" @@ -20,14 +19,14 @@ import ( // ErrWebAuthnCredentialNotExist represents a "ErrWebAuthnCRedentialNotExist" kind of error. type ErrWebAuthnCredentialNotExist struct { ID int64 - CredentialID string + CredentialID []byte } func (err ErrWebAuthnCredentialNotExist) Error() string { - if err.CredentialID == "" { + if len(err.CredentialID) == 0 { return fmt.Sprintf("WebAuthn credential does not exist [id: %d]", err.ID) } - return fmt.Sprintf("WebAuthn credential does not exist [credential_id: %s]", err.CredentialID) + return fmt.Sprintf("WebAuthn credential does not exist [credential_id: %x]", err.CredentialID) } // IsErrWebAuthnCredentialNotExist checks if an error is a ErrWebAuthnCredentialNotExist. @@ -43,7 +42,7 @@ type WebAuthnCredential struct { Name string LowerName string `xorm:"unique(s)"` UserID int64 `xorm:"INDEX unique(s)"` - CredentialID string `xorm:"INDEX VARCHAR(410)"` + CredentialID []byte `xorm:"INDEX VARBINARY(1024)"` PublicKey []byte AttestationType string AAGUID []byte @@ -94,9 +93,8 @@ type WebAuthnCredentialList []*WebAuthnCredential func (list WebAuthnCredentialList) ToCredentials() []webauthn.Credential { creds := make([]webauthn.Credential, 0, len(list)) for _, cred := range list { - credID, _ := base32.HexEncoding.DecodeString(cred.CredentialID) creds = append(creds, webauthn.Credential{ - ID: credID, + ID: cred.CredentialID, PublicKey: cred.PublicKey, AttestationType: cred.AttestationType, Authenticator: webauthn.Authenticator{ @@ -164,11 +162,11 @@ func HasWebAuthnRegistrationsByUID(uid int64) (bool, error) { } // GetWebAuthnCredentialByCredID returns WebAuthn credential by credential ID -func GetWebAuthnCredentialByCredID(userID int64, credID string) (*WebAuthnCredential, error) { +func GetWebAuthnCredentialByCredID(userID int64, credID []byte) (*WebAuthnCredential, error) { return getWebAuthnCredentialByCredID(db.DefaultContext, userID, credID) } -func getWebAuthnCredentialByCredID(ctx context.Context, userID int64, credID string) (*WebAuthnCredential, error) { +func getWebAuthnCredentialByCredID(ctx context.Context, userID int64, credID []byte) (*WebAuthnCredential, error) { cred := new(WebAuthnCredential) if found, err := db.GetEngine(ctx).Where("user_id = ? AND credential_id = ?", userID, credID).Get(cred); err != nil { return nil, err @@ -187,7 +185,7 @@ func createCredential(ctx context.Context, userID int64, name string, cred *weba c := &WebAuthnCredential{ UserID: userID, Name: name, - CredentialID: base32.HexEncoding.EncodeToString(cred.ID), + CredentialID: cred.ID, PublicKey: cred.PublicKey, AttestationType: cred.AttestationType, AAGUID: cred.Authenticator.AAGUID, diff --git a/models/auth/webauthn_test.go b/models/auth/webauthn_test.go index 216bf110806e..cc39691ce2d3 100644 --- a/models/auth/webauthn_test.go +++ b/models/auth/webauthn_test.go @@ -5,7 +5,6 @@ package auth import ( - "encoding/base32" "testing" "code.gitea.io/gitea/models/unittest" @@ -61,9 +60,7 @@ func TestCreateCredential(t *testing.T) { res, err := CreateCredential(1, "WebAuthn Created Credential", &webauthn.Credential{ID: []byte("Test")}) assert.NoError(t, err) assert.Equal(t, "WebAuthn Created Credential", res.Name) - bs, err := base32.HexEncoding.DecodeString(res.CredentialID) - assert.NoError(t, err) - assert.Equal(t, []byte("Test"), bs) + assert.Equal(t, []byte("Test"), res.CredentialID) unittest.AssertExistsIf(t, true, &WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1}) } diff --git a/models/db/common.go b/models/db/common.go new file mode 100644 index 000000000000..1a59a8b5c697 --- /dev/null +++ b/models/db/common.go @@ -0,0 +1,23 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package db + +import ( + "strings" + + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" + + "xorm.io/builder" +) + +// BuildCaseInsensitiveLike returns a condition to check if the given value is like the given key case-insensitively. +// Handles especially SQLite correctly as UPPER there only transforms ASCII letters. +func BuildCaseInsensitiveLike(key, value string) builder.Cond { + if setting.Database.UseSQLite3 { + return builder.Like{"UPPER(" + key + ")", util.ToUpperASCII(value)} + } + return builder.Like{"UPPER(" + key + ")", strings.ToUpper(value)} +} diff --git a/models/issues/issue.go b/models/issues/issue.go index 064f0d22abd0..5bdb60f7c08c 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -27,7 +27,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/references" - "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -1903,23 +1902,17 @@ func GetRepoIssueStats(repoID, uid int64, filterMode int, isPull bool) (numOpen, func SearchIssueIDsByKeyword(ctx context.Context, kw string, repoIDs []int64, limit, start int) (int64, []int64, error) { repoCond := builder.In("repo_id", repoIDs) subQuery := builder.Select("id").From("issue").Where(repoCond) - // SQLite's UPPER function only transforms ASCII letters. - if setting.Database.UseSQLite3 { - kw = util.ToUpperASCII(kw) - } else { - kw = strings.ToUpper(kw) - } cond := builder.And( repoCond, builder.Or( - builder.Like{"UPPER(name)", kw}, - builder.Like{"UPPER(content)", kw}, + db.BuildCaseInsensitiveLike("name", kw), + db.BuildCaseInsensitiveLike("content", kw), builder.In("id", builder.Select("issue_id"). From("comment"). Where(builder.And( builder.Eq{"type": CommentTypeComment}, builder.In("issue_id", subQuery), - builder.Like{"UPPER(content)", kw}, + db.BuildCaseInsensitiveLike("content", kw), )), ), ), diff --git a/models/issues/milestone.go b/models/issues/milestone.go index c49799f391dc..1021938b205a 100644 --- a/models/issues/milestone.go +++ b/models/issues/milestone.go @@ -361,7 +361,7 @@ func (opts GetMilestonesOption) toCond() builder.Cond { } if len(opts.Name) != 0 { - cond = cond.And(builder.Like{"UPPER(name)", strings.ToUpper(opts.Name)}) + cond = cond.And(db.BuildCaseInsensitiveLike("name", opts.Name)) } return cond diff --git a/models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml b/models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml new file mode 100644 index 000000000000..55a237a0d633 --- /dev/null +++ b/models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml @@ -0,0 +1,9 @@ +- + id: 1 + credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG=" +- + id: 2 + credential_id: "051CLMMKB62S6M9M2A4H54K7MMCQALFJ36G4TGB2S9A47APLTILU6C6744CEBG4EKCGV357N21BSLH8JD33GQMFAR6DQ70S76P34J6FR=" +- + id: 4 + credential_id: "APU4B1NDTEVTEM60V4T0FRL7SRJMO9KIE2AKFQ8JDGTQ7VHFI41FDEFTDLBVQEAE4ER49QV2GTGVFDNBO31BPOA3OQN6879OT6MTU3G=" diff --git a/models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/webauthn_credential.yml b/models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/webauthn_credential.yml new file mode 100644 index 000000000000..c02a76e3742a --- /dev/null +++ b/models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/webauthn_credential.yml @@ -0,0 +1,31 @@ +- + id: 1 + lower_name: "u2fkey-correctly-migrated" + name: "u2fkey-correctly-migrated" + user_id: 1 + credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG=" + public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2 + attestation_type: 'fido-u2f' + sign_count: 1 + clone_warning: false +- + id: 2 + lower_name: "non-u2f-key" + name: "non-u2f-key" + user_id: 1 + credential_id: "051CLMMKB62S6M9M2A4H54K7MMCQALFJ36G4TGB2S9A47APLTILU6C6744CEBG4EKCGV357N21BSLH8JD33GQMFAR6DQ70S76P34J6FR" + public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2 + attestation_type: 'none' + sign_count: 1 + clone_warning: false +- + id: 4 + lower_name: "packed-key" + name: "packed-key" + user_id: 1 + credential_id: "APU4B1NDTEVTEM60V4T0FRL7SRJMO9KIE2AKFQ8JDGTQ7VHFI41FDEFTDLBVQEAE4ER49QV2GTGVFDNBO31BPOA3OQN6879OT6MTU3G=" + public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2 + attestation_type: 'fido-u2f' + sign_count: 1 + clone_warning: false + diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 1b2a743b6d35..2719f45efbbb 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -398,6 +398,14 @@ var migrations = []Migration{ NewMigration("Improve Action table indices v2", improveActionTableIndices), // v219 -> v220 NewMigration("Add sync_on_commit column to push_mirror table", addSyncOnCommitColForPushMirror), + // v220 -> v221 + NewMigration("Add container repository property", addContainerRepositoryProperty), + // v221 -> v222 + NewMigration("Store WebAuthentication CredentialID as bytes and increase size to at least 1024", storeWebauthnCredentialIDAsBytes), + // v222 -> v223 + NewMigration("Drop old CredentialID column", dropOldCredentialIDColumn), + // v223 -> v224 + NewMigration("Rename CredentialIDBytes column to CredentialID", renameCredentialIDBytes), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v220.go b/models/migrations/v220.go new file mode 100644 index 000000000000..f5983582a30f --- /dev/null +++ b/models/migrations/v220.go @@ -0,0 +1,29 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + packages_model "code.gitea.io/gitea/models/packages" + container_module "code.gitea.io/gitea/modules/packages/container" + + "xorm.io/xorm" + "xorm.io/xorm/schemas" +) + +func addContainerRepositoryProperty(x *xorm.Engine) error { + switch x.Dialect().URI().DBType { + case schemas.SQLITE: + _, err := x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, u.lower_name || '/' || p.lower_name FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?", packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer) + if err != nil { + return err + } + default: + _, err := x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, CONCAT(u.lower_name, '/', p.lower_name) FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?", packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer) + if err != nil { + return err + } + } + return nil +} diff --git a/models/migrations/v221.go b/models/migrations/v221.go new file mode 100644 index 000000000000..f3bcfcdf1de2 --- /dev/null +++ b/models/migrations/v221.go @@ -0,0 +1,75 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "encoding/base32" + "fmt" + + "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" +) + +func storeWebauthnCredentialIDAsBytes(x *xorm.Engine) error { + // Create webauthnCredential table + type webauthnCredential struct { + ID int64 `xorm:"pk autoincr"` + Name string + LowerName string `xorm:"unique(s)"` + UserID int64 `xorm:"INDEX unique(s)"` + CredentialID string `xorm:"INDEX VARCHAR(410)"` + // Note the lack of INDEX here - these will be created once the column is renamed in v223.go + CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022 + PublicKey []byte + AttestationType string + AAGUID []byte + SignCount uint32 `xorm:"BIGINT"` + CloneWarning bool + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` + UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` + } + if err := x.Sync2(&webauthnCredential{}); err != nil { + return err + } + + var start int + creds := make([]*webauthnCredential, 0, 50) + for { + err := x.Select("id, credential_id").OrderBy("id").Limit(50, start).Find(&creds) + if err != nil { + return err + } + + err = func() error { + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return fmt.Errorf("unable to allow start session. Error: %w", err) + } + for _, cred := range creds { + cred.CredentialIDBytes, err = base32.HexEncoding.DecodeString(cred.CredentialID) + if err != nil { + return fmt.Errorf("unable to parse credential id %s for credential[%d]: %w", cred.CredentialID, cred.ID, err) + } + count, err := sess.ID(cred.ID).Cols("credential_id_bytes").Update(cred) + if count != 1 || err != nil { + return fmt.Errorf("unable to update credential id bytes for credential[%d]: %d,%w", cred.ID, count, err) + } + } + return sess.Commit() + }() + if err != nil { + return err + } + + if len(creds) < 50 { + break + } + start += 50 + creds = creds[:0] + } + return nil +} diff --git a/models/migrations/v221_test.go b/models/migrations/v221_test.go new file mode 100644 index 000000000000..c50ca5c87329 --- /dev/null +++ b/models/migrations/v221_test.go @@ -0,0 +1,65 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "encoding/base32" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_storeWebauthnCredentialIDAsBytes(t *testing.T) { + // Create webauthnCredential table + type WebauthnCredential struct { + ID int64 `xorm:"pk autoincr"` + Name string + LowerName string `xorm:"unique(s)"` + UserID int64 `xorm:"INDEX unique(s)"` + CredentialID string `xorm:"INDEX VARCHAR(410)"` + PublicKey []byte + AttestationType string + AAGUID []byte + SignCount uint32 `xorm:"BIGINT"` + CloneWarning bool + } + + type ExpectedWebauthnCredential struct { + ID int64 `xorm:"pk autoincr"` + CredentialID string // CredentialID is at most 1023 bytes as per spec released 20 July 2022 + } + + type ConvertedWebauthnCredential struct { + ID int64 `xorm:"pk autoincr"` + CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022 + } + + // Prepare and load the testing database + x, deferable := prepareTestEnv(t, 0, new(WebauthnCredential), new(ExpectedWebauthnCredential)) + defer deferable() + if x == nil || t.Failed() { + return + } + + if err := storeWebauthnCredentialIDAsBytes(x); err != nil { + assert.NoError(t, err) + return + } + + expected := []ExpectedWebauthnCredential{} + if err := x.Table("expected_webauthn_credential").Asc("id").Find(&expected); !assert.NoError(t, err) { + return + } + + got := []ConvertedWebauthnCredential{} + if err := x.Table("webauthn_credential").Select("id, credential_id_bytes").Asc("id").Find(&got); !assert.NoError(t, err) { + return + } + + for i, e := range expected { + credIDBytes, _ := base32.HexEncoding.DecodeString(e.CredentialID) + assert.Equal(t, credIDBytes, got[i].CredentialIDBytes) + } +} diff --git a/models/migrations/v222.go b/models/migrations/v222.go new file mode 100644 index 000000000000..99acdfd20608 --- /dev/null +++ b/models/migrations/v222.go @@ -0,0 +1,64 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "context" + "fmt" + + "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" +) + +func dropOldCredentialIDColumn(x *xorm.Engine) error { + // This migration maybe rerun so that we should check if it has been run + credentialIDExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id") + if err != nil { + return err + } + if !credentialIDExist { + // Column is already non-extant + return nil + } + credentialIDBytesExists, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id_bytes") + if err != nil { + return err + } + if !credentialIDBytesExists { + // looks like 221 hasn't properly run + return fmt.Errorf("webauthn_credential does not have a credential_id_bytes column... it is not safe to run this migration") + } + + // Create webauthnCredential table + type webauthnCredential struct { + ID int64 `xorm:"pk autoincr"` + Name string + LowerName string `xorm:"unique(s)"` + UserID int64 `xorm:"INDEX unique(s)"` + CredentialID string `xorm:"INDEX VARCHAR(410)"` + // Note the lack of the INDEX on CredentialIDBytes - we will add this in v223.go + CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022 + PublicKey []byte + AttestationType string + AAGUID []byte + SignCount uint32 `xorm:"BIGINT"` + CloneWarning bool + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` + UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` + } + if err := x.Sync2(&webauthnCredential{}); err != nil { + return err + } + + // Drop the old credential ID + sess := x.NewSession() + defer sess.Close() + + if err := dropTableColumns(sess, "webauthn_credential", "credential_id"); err != nil { + return fmt.Errorf("unable to drop old credentialID column: %w", err) + } + return sess.Commit() +} diff --git a/models/migrations/v223.go b/models/migrations/v223.go new file mode 100644 index 000000000000..d7ee4812b826 --- /dev/null +++ b/models/migrations/v223.go @@ -0,0 +1,103 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package migrations + +import ( + "context" + "fmt" + + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" +) + +func renameCredentialIDBytes(x *xorm.Engine) error { + // This migration maybe rerun so that we should check if it has been run + credentialIDExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id") + if err != nil { + return err + } + if credentialIDExist { + credentialIDBytesExists, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id_bytes") + if err != nil { + return err + } + if !credentialIDBytesExists { + return nil + } + } + + err = func() error { + // webauthnCredential table + type webauthnCredential struct { + ID int64 `xorm:"pk autoincr"` + Name string + LowerName string `xorm:"unique(s)"` + UserID int64 `xorm:"INDEX unique(s)"` + // Note the lack of INDEX here + CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022 + PublicKey []byte + AttestationType string + AAGUID []byte + SignCount uint32 `xorm:"BIGINT"` + CloneWarning bool + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` + UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` + } + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + + if err := sess.Sync2(new(webauthnCredential)); err != nil { + return fmt.Errorf("error on Sync2: %v", err) + } + + if credentialIDExist { + // if both errors and message exist, drop message at first + if err := dropTableColumns(sess, "webauthn_credential", "credential_id"); err != nil { + return err + } + } + + switch { + case setting.Database.UseMySQL: + if _, err := sess.Exec("ALTER TABLE `webauthn_credential` CHANGE credential_id_bytes credential_id VARBINARY(1024)"); err != nil { + return err + } + case setting.Database.UseMSSQL: + if _, err := sess.Exec("sp_rename 'webauthn_credential.credential_id_bytes', 'credential_id', 'COLUMN'"); err != nil { + return err + } + default: + if _, err := sess.Exec("ALTER TABLE `webauthn_credential` RENAME COLUMN credential_id_bytes TO credential_id"); err != nil { + return err + } + } + return sess.Commit() + }() + if err != nil { + return err + } + + // Create webauthnCredential table + type webauthnCredential struct { + ID int64 `xorm:"pk autoincr"` + Name string + LowerName string `xorm:"unique(s)"` + UserID int64 `xorm:"INDEX unique(s)"` + CredentialID []byte `xorm:"INDEX VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022 + PublicKey []byte + AttestationType string + AAGUID []byte + SignCount uint32 `xorm:"BIGINT"` + CloneWarning bool + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` + UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` + } + return x.Sync2(&webauthnCredential{}) +} diff --git a/models/packages/container/search.go b/models/packages/container/search.go index 972cac9528f8..a3409fe74311 100644 --- a/models/packages/container/search.go +++ b/models/packages/container/search.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/packages" + user_model "code.gitea.io/gitea/models/user" container_module "code.gitea.io/gitea/modules/packages/container" "xorm.io/builder" @@ -210,6 +211,7 @@ func SearchImageTags(ctx context.Context, opts *ImageTagsSearchOptions) ([]*pack return pvs, count, err } +// SearchExpiredUploadedBlobs gets all uploaded blobs which are older than specified func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([]*packages.PackageFile, error) { var cond builder.Cond = builder.Eq{ "package_version.is_internal": true, @@ -225,3 +227,37 @@ func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([ Where(cond). Find(&pfs) } + +// GetRepositories gets a sorted list of all repositories +func GetRepositories(ctx context.Context, actor *user_model.User, n int, last string) ([]string, error) { + var cond builder.Cond = builder.Eq{ + "package.type": packages.TypeContainer, + "package_property.ref_type": packages.PropertyTypePackage, + "package_property.name": container_module.PropertyRepository, + } + + cond = cond.And(builder.Exists( + builder. + Select("package_version.id"). + Where(builder.Eq{"package_version.is_internal": false}.And(builder.Expr("package.id = package_version.package_id"))). + From("package_version"), + )) + + if last != "" { + cond = cond.And(builder.Gt{"package_property.value": strings.ToLower(last)}) + } + + cond = cond.And(user_model.BuildCanSeeUserCondition(actor)) + + sess := db.GetEngine(ctx). + Table("package"). + Select("package_property.value"). + Join("INNER", "user", "`user`.id = package.owner_id"). + Join("INNER", "package_property", "package_property.ref_id = package.id"). + Where(cond). + Asc("package_property.value"). + Limit(n) + + repositories := make([]string, 0, n) + return repositories, sess.Find(&repositories) +} diff --git a/models/packages/descriptor.go b/models/packages/descriptor.go index fbdc40f37fbb..31819ccca1ab 100644 --- a/models/packages/descriptor.go +++ b/models/packages/descriptor.go @@ -40,15 +40,16 @@ func (l PackagePropertyList) GetByName(name string) string { // PackageDescriptor describes a package type PackageDescriptor struct { - Package *Package - Owner *user_model.User - Repository *repo_model.Repository - Version *PackageVersion - SemVer *version.Version - Creator *user_model.User - Properties PackagePropertyList - Metadata interface{} - Files []*PackageFileDescriptor + Package *Package + Owner *user_model.User + Repository *repo_model.Repository + Version *PackageVersion + SemVer *version.Version + Creator *user_model.User + PackageProperties PackagePropertyList + VersionProperties PackagePropertyList + Metadata interface{} + Files []*PackageFileDescriptor } // PackageFileDescriptor describes a package file @@ -102,6 +103,10 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc return nil, err } } + pps, err := GetProperties(ctx, PropertyTypePackage, p.ID) + if err != nil { + return nil, err + } pvps, err := GetProperties(ctx, PropertyTypeVersion, pv.ID) if err != nil { return nil, err @@ -152,15 +157,16 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc } return &PackageDescriptor{ - Package: p, - Owner: o, - Repository: repository, - Version: pv, - SemVer: semVer, - Creator: creator, - Properties: PackagePropertyList(pvps), - Metadata: metadata, - Files: pfds, + Package: p, + Owner: o, + Repository: repository, + Version: pv, + SemVer: semVer, + Creator: creator, + PackageProperties: PackagePropertyList(pps), + VersionProperties: PackagePropertyList(pvps), + Metadata: metadata, + Files: pfds, }, nil } diff --git a/models/packages/package.go b/models/packages/package.go index bdb535492bb4..97cfbc6cad20 100644 --- a/models/packages/package.go +++ b/models/packages/package.go @@ -131,6 +131,12 @@ func TryInsertPackage(ctx context.Context, p *Package) (*Package, error) { return p, nil } +// DeletePackageByID deletes a package by id +func DeletePackageByID(ctx context.Context, packageID int64) error { + _, err := db.GetEngine(ctx).ID(packageID).Delete(&Package{}) + return err +} + // SetRepositoryLink sets the linked repository func SetRepositoryLink(ctx context.Context, packageID, repoID int64) error { _, err := db.GetEngine(ctx).ID(packageID).Cols("repo_id").Update(&Package{RepoID: repoID}) @@ -192,21 +198,20 @@ func GetPackagesByType(ctx context.Context, ownerID int64, packageType Type) ([] Find(&ps) } -// DeletePackagesIfUnreferenced deletes a package if there are no associated versions -func DeletePackagesIfUnreferenced(ctx context.Context) error { +// FindUnreferencedPackages gets all packages without associated versions +func FindUnreferencedPackages(ctx context.Context) ([]*Package, error) { in := builder. Select("package.id"). From("package"). LeftJoin("package_version", "package_version.package_id = package.id"). Where(builder.Expr("package_version.id IS NULL")) - _, err := db.GetEngine(ctx). + ps := make([]*Package, 0, 10) + return ps, db.GetEngine(ctx). // double select workaround for MySQL // https://stackoverflow.com/questions/4471277/mysql-delete-from-with-subquery-as-condition Where(builder.In("package.id", builder.Select("id").From(in, "temp"))). - Delete(&Package{}) - - return err + Find(&ps) } // HasOwnerPackages tests if a user/org has packages diff --git a/models/packages/package_property.go b/models/packages/package_property.go index bf7dc346c6c9..fc1071380194 100644 --- a/models/packages/package_property.go +++ b/models/packages/package_property.go @@ -21,9 +21,11 @@ const ( PropertyTypeVersion PropertyType = iota // 0 // PropertyTypeFile means the reference is a package file PropertyTypeFile // 1 + // PropertyTypePackage means the reference is a package + PropertyTypePackage // 2 ) -// PackageProperty represents a property of a package version or file +// PackageProperty represents a property of a package, version or file type PackageProperty struct { ID int64 `xorm:"pk autoincr"` RefType PropertyType `xorm:"INDEX NOT NULL"` @@ -68,3 +70,9 @@ func DeletePropertyByID(ctx context.Context, propertyID int64) error { _, err := db.GetEngine(ctx).ID(propertyID).Delete(&PackageProperty{}) return err } + +// DeletePropertyByName deletes properties by name +func DeletePropertyByName(ctx context.Context, refType PropertyType, refID int64, name string) error { + _, err := db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ? AND name = ?", refType, refID, name).Delete(&PackageProperty{}) + return err +} diff --git a/models/repo/pushmirror.go b/models/repo/pushmirror.go index 0a7dea79c93b..02ffcbee76a5 100644 --- a/models/repo/pushmirror.go +++ b/models/repo/pushmirror.go @@ -5,12 +5,15 @@ package repo import ( + "context" "errors" "time" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/builder" ) // ErrPushMirrorNotExist mirror does not exist error @@ -29,6 +32,25 @@ type PushMirror struct { LastUpdateUnix timeutil.TimeStamp `xorm:"INDEX last_update"` LastError string `xorm:"text"` } +type PushMirrorOptions struct { + ID int64 + RepoID int64 + RemoteName string +} + +func (opts *PushMirrorOptions) toConds() builder.Cond { + cond := builder.NewCond() + if opts.RepoID > 0 { + cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) + } + if opts.RemoteName != "" { + cond = cond.And(builder.Eq{"remote_name": opts.RemoteName}) + } + if opts.ID > 0 { + cond = cond.And(builder.Eq{"id": opts.ID}) + } + return cond +} func init() { db.RegisterModel(new(PushMirror)) @@ -53,45 +75,48 @@ func (m *PushMirror) GetRemoteName() string { } // InsertPushMirror inserts a push-mirror to database -func InsertPushMirror(m *PushMirror) error { - _, err := db.GetEngine(db.DefaultContext).Insert(m) +func InsertPushMirror(ctx context.Context, m *PushMirror) error { + _, err := db.GetEngine(ctx).Insert(m) return err } // UpdatePushMirror updates the push-mirror -func UpdatePushMirror(m *PushMirror) error { - _, err := db.GetEngine(db.DefaultContext).ID(m.ID).AllCols().Update(m) - return err -} - -// DeletePushMirrorByID deletes a push-mirrors by ID -func DeletePushMirrorByID(ID int64) error { - _, err := db.GetEngine(db.DefaultContext).ID(ID).Delete(&PushMirror{}) +func UpdatePushMirror(ctx context.Context, m *PushMirror) error { + _, err := db.GetEngine(ctx).ID(m.ID).AllCols().Update(m) return err } -// DeletePushMirrorsByRepoID deletes all push-mirrors by repoID -func DeletePushMirrorsByRepoID(repoID int64) error { - _, err := db.GetEngine(db.DefaultContext).Delete(&PushMirror{RepoID: repoID}) - return err +func DeletePushMirrors(ctx context.Context, opts PushMirrorOptions) error { + if opts.RepoID > 0 { + _, err := db.GetEngine(ctx).Where(opts.toConds()).Delete(&PushMirror{}) + return err + } + return errors.New("repoID required and must be set") } -// GetPushMirrorByID returns push-mirror information. -func GetPushMirrorByID(ID int64) (*PushMirror, error) { - m := &PushMirror{} - has, err := db.GetEngine(db.DefaultContext).ID(ID).Get(m) +func GetPushMirror(ctx context.Context, opts PushMirrorOptions) (*PushMirror, error) { + mirror := &PushMirror{} + exist, err := db.GetEngine(ctx).Where(opts.toConds()).Get(mirror) if err != nil { return nil, err - } else if !has { + } else if !exist { return nil, ErrPushMirrorNotExist } - return m, nil + return mirror, nil } // GetPushMirrorsByRepoID returns push-mirror information of a repository. -func GetPushMirrorsByRepoID(repoID int64) ([]*PushMirror, error) { +func GetPushMirrorsByRepoID(ctx context.Context, repoID int64, listOptions db.ListOptions) ([]*PushMirror, int64, error) { + sess := db.GetEngine(ctx).Where("repo_id = ?", repoID) + if listOptions.Page != 0 { + sess = db.SetSessionPagination(sess, &listOptions) + mirrors := make([]*PushMirror, 0, listOptions.PageSize) + count, err := sess.FindAndCount(&mirrors) + return mirrors, count, err + } mirrors := make([]*PushMirror, 0, 10) - return mirrors, db.GetEngine(db.DefaultContext).Where("repo_id=?", repoID).Find(&mirrors) + count, err := sess.FindAndCount(&mirrors) + return mirrors, count, err } // GetPushMirrorsSyncedOnCommit returns push-mirrors for this repo that should be updated by new commits @@ -103,8 +128,8 @@ func GetPushMirrorsSyncedOnCommit(repoID int64) ([]*PushMirror, error) { } // PushMirrorsIterate iterates all push-mirror repositories. -func PushMirrorsIterate(limit int, f func(idx int, bean interface{}) error) error { - return db.GetEngine(db.DefaultContext). +func PushMirrorsIterate(ctx context.Context, limit int, f func(idx int, bean interface{}) error) error { + return db.GetEngine(ctx). Where("last_update + (`interval` / ?) <= ?", time.Second, time.Now().Unix()). And("`interval` != 0"). OrderBy("last_update ASC"). diff --git a/models/repo/pushmirror_test.go b/models/repo/pushmirror_test.go index d36a48547e1c..5087e3009577 100644 --- a/models/repo/pushmirror_test.go +++ b/models/repo/pushmirror_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/timeutil" @@ -20,20 +21,20 @@ func TestPushMirrorsIterate(t *testing.T) { now := timeutil.TimeStampNow() - repo_model.InsertPushMirror(&repo_model.PushMirror{ + repo_model.InsertPushMirror(db.DefaultContext, &repo_model.PushMirror{ RemoteName: "test-1", LastUpdateUnix: now, Interval: 1, }) long, _ := time.ParseDuration("24h") - repo_model.InsertPushMirror(&repo_model.PushMirror{ + repo_model.InsertPushMirror(db.DefaultContext, &repo_model.PushMirror{ RemoteName: "test-2", LastUpdateUnix: now, Interval: long, }) - repo_model.InsertPushMirror(&repo_model.PushMirror{ + repo_model.InsertPushMirror(db.DefaultContext, &repo_model.PushMirror{ RemoteName: "test-3", LastUpdateUnix: now, Interval: 0, @@ -41,7 +42,7 @@ func TestPushMirrorsIterate(t *testing.T) { time.Sleep(1 * time.Millisecond) - repo_model.PushMirrorsIterate(1, func(idx int, bean interface{}) error { + repo_model.PushMirrorsIterate(db.DefaultContext, 1, func(idx int, bean interface{}) error { m, ok := bean.(*repo_model.PushMirror) assert.True(t, ok) assert.Equal(t, "test-1", m.RemoteName) diff --git a/models/user/search.go b/models/user/search.go index 76ff55ea2664..f8e6c89f0680 100644 --- a/models/user/search.go +++ b/models/user/search.go @@ -58,24 +58,7 @@ func (opts *SearchUserOptions) toSearchQueryBase() *xorm.Session { cond = cond.And(builder.In("visibility", opts.Visible)) } - if opts.Actor != nil { - // If Admin - they see all users! - if !opts.Actor.IsAdmin { - // Users can see an organization they are a member of - accessCond := builder.In("id", builder.Select("org_id").From("org_user").Where(builder.Eq{"uid": opts.Actor.ID})) - if !opts.Actor.IsRestricted { - // Not-Restricted users can see public and limited users/organizations - accessCond = accessCond.Or(builder.In("visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited)) - } - // Don't forget about self - accessCond = accessCond.Or(builder.Eq{"id": opts.Actor.ID}) - cond = cond.And(accessCond) - } - } else { - // Force visibility for privacy - // Not logged in - only public users - cond = cond.And(builder.In("visibility", structs.VisibleTypePublic)) - } + cond = cond.And(BuildCanSeeUserCondition(opts.Actor)) if opts.UID > 0 { cond = cond.And(builder.Eq{"id": opts.UID}) @@ -163,3 +146,26 @@ func IterateUser(f func(user *User) error) error { } } } + +// BuildCanSeeUserCondition creates a condition which can be used to restrict results to users/orgs the actor can see +func BuildCanSeeUserCondition(actor *User) builder.Cond { + if actor != nil { + // If Admin - they see all users! + if !actor.IsAdmin { + // Users can see an organization they are a member of + cond := builder.In("`user`.id", builder.Select("org_id").From("org_user").Where(builder.Eq{"uid": actor.ID})) + if !actor.IsRestricted { + // Not-Restricted users can see public and limited users/organizations + cond = cond.Or(builder.In("`user`.visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited)) + } + // Don't forget about self + return cond.Or(builder.Eq{"`user`.id": actor.ID}) + } + + return nil + } + + // Force visibility for privacy + // Not logged in - only public users + return builder.In("`user`.visibility", structs.VisibleTypePublic) +} diff --git a/models/user/user.go b/models/user/user.go index fbd8df947247..91eeeb896252 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -64,12 +64,14 @@ var AvailableHashAlgorithms = []string{ } const ( - // EmailNotificationsEnabled indicates that the user would like to receive all email notifications + // EmailNotificationsEnabled indicates that the user would like to receive all email notifications except your own EmailNotificationsEnabled = "enabled" // EmailNotificationsOnMention indicates that the user would like to be notified via email when mentioned. EmailNotificationsOnMention = "onmention" // EmailNotificationsDisabled indicates that the user would not like to be notified via email. EmailNotificationsDisabled = "disabled" + // EmailNotificationsEnabled indicates that the user would like to receive all email notifications and your own + EmailNotificationsAndYourOwn = "andyourown" ) // User represents the object of individual and member of organization. @@ -1045,7 +1047,7 @@ func GetMaileableUsersByIDs(ids []int64, isMention bool) ([]*User, error) { Where("`type` = ?", UserTypeIndividual). And("`prohibit_login` = ?", false). And("`is_active` = ?", true). - And("`email_notifications_preference` IN ( ?, ?)", EmailNotificationsEnabled, EmailNotificationsOnMention). + In("`email_notifications_preference`", EmailNotificationsEnabled, EmailNotificationsOnMention, EmailNotificationsAndYourOwn). Find(&ous) } @@ -1053,7 +1055,7 @@ func GetMaileableUsersByIDs(ids []int64, isMention bool) ([]*User, error) { Where("`type` = ?", UserTypeIndividual). And("`prohibit_login` = ?", false). And("`is_active` = ?", true). - And("`email_notifications_preference` = ?", EmailNotificationsEnabled). + In("`email_notifications_preference`", EmailNotificationsEnabled, EmailNotificationsAndYourOwn). Find(&ous) } diff --git a/models/user/user_test.go b/models/user/user_test.go index 4994ac53ab3d..489ee3b05da3 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -153,6 +153,9 @@ func TestEmailNotificationPreferences(t *testing.T) { assert.NoError(t, user_model.SetEmailNotifications(user, user_model.EmailNotificationsDisabled)) assert.Equal(t, user_model.EmailNotificationsDisabled, user.EmailNotifications()) + + assert.NoError(t, user_model.SetEmailNotifications(user, user_model.EmailNotificationsAndYourOwn)) + assert.Equal(t, user_model.EmailNotificationsAndYourOwn, user.EmailNotifications()) } } diff --git a/modules/context/package.go b/modules/context/package.go index 4c52907dc529..92a97831ddc0 100644 --- a/modules/context/package.go +++ b/modules/context/package.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models/organization" packages_model "code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/models/perm" + "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/structs" ) @@ -52,14 +53,30 @@ func packageAssignment(ctx *Context, errCb func(int, string, interface{})) { } if ctx.Package.Owner.IsOrganization() { + org := organization.OrgFromUser(ctx.Package.Owner) + // 1. Get user max authorize level for the org (may be none, if user is not member of the org) if ctx.Doer != nil { var err error - ctx.Package.AccessMode, err = organization.OrgFromUser(ctx.Package.Owner).GetOrgUserMaxAuthorizeLevel(ctx.Doer.ID) + ctx.Package.AccessMode, err = org.GetOrgUserMaxAuthorizeLevel(ctx.Doer.ID) if err != nil { errCb(http.StatusInternalServerError, "GetOrgUserMaxAuthorizeLevel", err) return } + // If access mode is less than write check every team for more permissions + if ctx.Package.AccessMode < perm.AccessModeWrite { + teams, err := organization.GetUserOrgTeams(ctx, org.ID, ctx.Doer.ID) + if err != nil { + errCb(http.StatusInternalServerError, "GetUserOrgTeams", err) + return + } + for _, t := range teams { + perm := t.UnitAccessModeCtx(ctx, unit.TypePackages) + if ctx.Package.AccessMode < perm { + ctx.Package.AccessMode = perm + } + } + } } // 2. If authorize level is none, check if org is visible to user if ctx.Package.AccessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, ctx.Package.Owner, ctx.Doer) { diff --git a/modules/context/repo.go b/modules/context/repo.go index 1d9f98158e19..ea4054206999 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -393,7 +393,7 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) { } } - pushMirrors, err := repo_model.GetPushMirrorsByRepoID(repo.ID) + pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, db.ListOptions{}) if err != nil { ctx.ServerError("GetPushMirrorsByRepoID", err) return diff --git a/modules/convert/mirror.go b/modules/convert/mirror.go new file mode 100644 index 000000000000..b2414f46774c --- /dev/null +++ b/modules/convert/mirror.go @@ -0,0 +1,39 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package convert + +import ( + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/git" + api "code.gitea.io/gitea/modules/structs" +) + +// ToPushMirror convert from repo_model.PushMirror and remoteAddress to api.TopicResponse +func ToPushMirror(pm *repo_model.PushMirror) (*api.PushMirror, error) { + repo := pm.GetRepository() + remoteAddress, err := getRemoteAddress(repo, pm.RemoteName) + if err != nil { + return nil, err + } + return &api.PushMirror{ + RepoName: repo.Name, + RemoteName: pm.RemoteName, + RemoteAddress: remoteAddress, + CreatedUnix: pm.CreatedUnix.FormatLong(), + LastUpdateUnix: pm.LastUpdateUnix.FormatLong(), + LastError: pm.LastError, + Interval: pm.Interval.String(), + }, nil +} + +func getRemoteAddress(repo *repo_model.Repository, remoteName string) (string, error) { + url, err := git.GetRemoteURL(git.DefaultContext, repo.RepoPath(), remoteName) + if err != nil { + return "", err + } + // remove confidential information + url.User = nil + return url.String(), nil +} diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go index e6fec4d1a32e..7ff23af42eb8 100644 --- a/modules/git/repo_commit.go +++ b/modules/git/repo_commit.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" + "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/setting" ) @@ -434,3 +435,20 @@ func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err e } return len(stdout) > 0, err } + +func (repo *Repository) AddLastCommitCache(cacheKey, fullName, sha string) error { + if repo.LastCommitCache == nil { + commitsCount, err := cache.GetInt64(cacheKey, func() (int64, error) { + commit, err := repo.GetCommit(sha) + if err != nil { + return 0, err + } + return commit.CommitsCount() + }) + if err != nil { + return err + } + repo.LastCommitCache = NewLastCommitCache(commitsCount, fullName, repo, cache.GetCache()) + } + return nil +} diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go index 6832207c0fc7..af3376e8d712 100644 --- a/modules/highlight/highlight.go +++ b/modules/highlight/highlight.go @@ -10,6 +10,7 @@ import ( "bytes" "fmt" gohtml "html" + "io" "path/filepath" "strings" "sync" @@ -26,7 +27,7 @@ import ( ) // don't index files larger than this many bytes for performance purposes -const sizeLimit = 1000000 +const sizeLimit = 1024 * 1024 var ( // For custom user mapping @@ -46,7 +47,6 @@ func NewContext() { highlightMapping[keys[i].Name()] = keys[i].Value() } } - // The size 512 is simply a conservative rule of thumb c, err := lru.New2Q(512) if err != nil { @@ -60,7 +60,7 @@ func NewContext() { func Code(fileName, language, code string) string { NewContext() - // diff view newline will be passed as empty, change to literal \n so it can be copied + // diff view newline will be passed as empty, change to literal '\n' so it can be copied // preserve literal newline in blame view if code == "" || code == "\n" { return "\n" @@ -128,36 +128,32 @@ func CodeFromLexer(lexer chroma.Lexer, code string) string { return code } - htmlw.Flush() + _ = htmlw.Flush() // Chroma will add newlines for certain lexers in order to highlight them properly - // Once highlighted, strip them here so they don't cause copy/paste trouble in HTML output + // Once highlighted, strip them here, so they don't cause copy/paste trouble in HTML output return strings.TrimSuffix(htmlbuf.String(), "\n") } -// File returns a slice of chroma syntax highlighted lines of code -func File(numLines int, fileName, language string, code []byte) []string { +// File returns a slice of chroma syntax highlighted HTML lines of code +func File(fileName, language string, code []byte) ([]string, error) { NewContext() if len(code) > sizeLimit { - return plainText(string(code), numLines) + return PlainText(code), nil } + formatter := html.New(html.WithClasses(true), html.WithLineNumbers(false), html.PreventSurroundingPre(true), ) - if formatter == nil { - log.Error("Couldn't create chroma formatter") - return plainText(string(code), numLines) - } - - htmlbuf := bytes.Buffer{} - htmlw := bufio.NewWriter(&htmlbuf) + htmlBuf := bytes.Buffer{} + htmlWriter := bufio.NewWriter(&htmlBuf) var lexer chroma.Lexer // provided language overrides everything - if len(language) > 0 { + if language != "" { lexer = lexers.Get(language) } @@ -168,9 +164,9 @@ func File(numLines int, fileName, language string, code []byte) []string { } if lexer == nil { - language := analyze.GetCodeLanguage(fileName, code) + guessLanguage := analyze.GetCodeLanguage(fileName, code) - lexer = lexers.Get(language) + lexer = lexers.Get(guessLanguage) if lexer == nil { lexer = lexers.Match(fileName) if lexer == nil { @@ -181,54 +177,43 @@ func File(numLines int, fileName, language string, code []byte) []string { iterator, err := lexer.Tokenise(nil, string(code)) if err != nil { - log.Error("Can't tokenize code: %v", err) - return plainText(string(code), numLines) + return nil, fmt.Errorf("can't tokenize code: %w", err) } - err = formatter.Format(htmlw, styles.GitHub, iterator) + err = formatter.Format(htmlWriter, styles.GitHub, iterator) if err != nil { - log.Error("Can't format code: %v", err) - return plainText(string(code), numLines) + return nil, fmt.Errorf("can't format code: %w", err) } - htmlw.Flush() - finalNewLine := false - if len(code) > 0 { - finalNewLine = code[len(code)-1] == '\n' - } + _ = htmlWriter.Flush() - m := make([]string, 0, numLines) - for _, v := range strings.SplitN(htmlbuf.String(), "\n", numLines) { - content := v - // need to keep lines that are only \n so copy/paste works properly in browser - if content == "" { - content = "\n" - } else if content == `` { - content += "\n" - } else if content == `` { - content += "\n" - } - content = strings.TrimSuffix(content, ``) - content = strings.TrimPrefix(content, ``) - m = append(m, content) + // at the moment, Chroma generates stable output `...\n` for each line + htmlStr := htmlBuf.String() + lines := strings.Split(htmlStr, ``) + m := make([]string, 0, len(lines)) + for i := 1; i < len(lines); i++ { + line := lines[i] + line = strings.TrimSuffix(line, "") + m = append(m, line) } - if finalNewLine { - m = append(m, "\n") - } - - return m + return m, nil } -// return unhiglighted map -func plainText(code string, numLines int) []string { - m := make([]string, 0, numLines) - for _, v := range strings.SplitN(code, "\n", numLines) { - content := v - // need to keep lines that are only \n so copy/paste works properly in browser - if content == "" { - content = "\n" +// PlainText returns non-highlighted HTML for code +func PlainText(code []byte) []string { + r := bufio.NewReader(bytes.NewReader(code)) + m := make([]string, 0, bytes.Count(code, []byte{'\n'})+1) + for { + content, err := r.ReadString('\n') + if err != nil && err != io.EOF { + log.Error("failed to read string from buffer: %v", err) + break + } + if content == "" && err == io.EOF { + break } - m = append(m, gohtml.EscapeString(content)) + s := gohtml.EscapeString(content) + m = append(m, s) } return m } diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go index e5dfedd2b3c8..8f83f4a2f612 100644 --- a/modules/highlight/highlight_test.go +++ b/modules/highlight/highlight_test.go @@ -8,97 +8,146 @@ import ( "strings" "testing" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" - "github.com/stretchr/testify/assert" - "gopkg.in/ini.v1" ) +func lines(s string) []string { + return strings.Split(strings.ReplaceAll(strings.TrimSpace(s), `\n`, "\n"), "\n") +} + func TestFile(t *testing.T) { - setting.Cfg = ini.Empty() tests := []struct { - name string - numLines int - fileName string - code string - want string + name string + code string + want []string }{ { - name: ".drone.yml", - numLines: 12, - fileName: ".drone.yml", - code: util.Dedent(` - kind: pipeline - name: default + name: "empty.py", + code: "", + want: lines(""), + }, + { + name: "tags.txt", + code: "<>", + want: lines("<>"), + }, + { + name: "tags.py", + code: "<>", + want: lines(`<>`), + }, + { + name: "eol-no.py", + code: "a=1", + want: lines(`a=1`), + }, + { + name: "eol-newline1.py", + code: "a=1\n", + want: lines(`a=1\n`), + }, + { + name: "eol-newline2.py", + code: "a=1\n\n", + want: lines(` +a=1\n +\n + `, + ), + }, + { + name: "empty-line-with-space.py", + code: strings.ReplaceAll(strings.TrimSpace(` +def: + a=1 - steps: - - name: test - image: golang:1.13 - environment: - GOPROXY: https://goproxy.cn - commands: - - go get -u - - go build -v - - go test -v -race -coverprofile=coverage.txt -covermode=atomic - `), - want: util.Dedent(` - kind: pipeline - name: default - - steps: - - name: test - image: golang:1.13 - environment: - GOPROXY: https://goproxy.cn - commands: - - go get -u - - go build -v - - go test -v -race -coverprofile=coverage.txt -covermode=atomic +b='' +{space} +c=2 + `), "{space}", " "), + want: lines(` +def:\n + a=1\n +\n +b=''\n + \n +c=2`, + ), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + out, err := File(tt.name, "", []byte(tt.code)) + assert.NoError(t, err) + expected := strings.Join(tt.want, "\n") + actual := strings.Join(out, "\n") + assert.Equal(t, strings.Count(actual, "")) + assert.EqualValues(t, expected, actual) + }) + } +} + +func TestPlainText(t *testing.T) { + tests := []struct { + name string + code string + want []string + }{ + { + name: "empty.py", + code: "", + want: lines(""), + }, + { + name: "tags.py", + code: "<>", + want: lines("<>"), + }, + { + name: "eol-no.py", + code: "a=1", + want: lines(`a=1`), + }, + { + name: "eol-newline1.py", + code: "a=1\n", + want: lines(`a=1\n`), + }, + { + name: "eol-newline2.py", + code: "a=1\n\n", + want: lines(` +a=1\n +\n `), }, { - name: ".drone.yml - trailing space", - numLines: 13, - fileName: ".drone.yml", - code: strings.Replace(util.Dedent(` - kind: pipeline - name: default + name: "empty-line-with-space.py", + code: strings.ReplaceAll(strings.TrimSpace(` +def: + a=1 - steps: - - name: test - image: golang:1.13 - environment: - GOPROXY: https://goproxy.cn - commands: - - go get -u - - go build -v - - go test -v -race -coverprofile=coverage.txt -covermode=atomic - `)+"\n", "name: default", "name: default ", 1), - want: util.Dedent(` - kind: pipeline - name: default - - steps: - - name: test - image: golang:1.13 - environment: - GOPROXY: https://goproxy.cn - commands: - - go get -u - - go build -v - - go test -v -race -coverprofile=coverage.txt -covermode=atomic - - - - `), +b='' +{space} +c=2 + `), "{space}", " "), + want: lines(` +def:\n + a=1\n +\n +b=''\n + \n +c=2`), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := strings.Join(File(tt.numLines, tt.fileName, "", []byte(tt.code)), "\n") - assert.Equal(t, tt.want, got) + out := PlainText([]byte(tt.code)) + expected := strings.Join(tt.want, "\n") + actual := strings.Join(out, "\n") + assert.EqualValues(t, expected, actual) }) } } diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index e88fa311875d..9278d4c65d4a 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -310,14 +310,9 @@ func IsMarkupFile(name, markup string) bool { } // IsReadmeFile reports whether name looks like a README file -// based on its name. If an extension is provided, it will strictly -// match that extension. -// Note that the '.' should be provided in ext, e.g ".md" -func IsReadmeFile(name string, ext ...string) bool { +// based on its name. +func IsReadmeFile(name string) bool { name = strings.ToLower(name) - if len(ext) > 0 { - return name == "readme"+ext[0] - } if len(name) < 6 { return false } else if len(name) == 6 { @@ -325,3 +320,27 @@ func IsReadmeFile(name string, ext ...string) bool { } return name[:7] == "readme." } + +// IsReadmeFileExtension reports whether name looks like a README file +// based on its name. It will look through the provided extensions and check if the file matches +// one of the extensions and provide the index in the extension list. +// If the filename is `readme.` with an unmatched extension it will match with the index equaling +// the length of the provided extension list. +// Note that the '.' should be provided in ext, e.g ".md" +func IsReadmeFileExtension(name string, ext ...string) (int, bool) { + if len(name) < 6 || name[:6] != "readme" { + return 0, false + } + + for i, extension := range ext { + if name[6:] == extension { + return i, true + } + } + + if name[6] == '.' { + return len(ext), true + } + + return 0, false +} diff --git a/modules/markup/renderer_test.go b/modules/markup/renderer_test.go index 4cfa02246319..1e0f7db194f7 100644 --- a/modules/markup/renderer_test.go +++ b/modules/markup/renderer_test.go @@ -40,24 +40,47 @@ func TestMisc_IsReadmeFile(t *testing.T) { assert.False(t, IsReadmeFile(testCase)) } - trueTestCasesStrict := [][]string{ - {"readme", ""}, - {"readme.md", ".md"}, - {"readme.txt", ".txt"}, - } - falseTestCasesStrict := [][]string{ - {"readme", ".md"}, - {"readme.md", ""}, - {"readme.md", ".txt"}, - {"readme.md", "md"}, - {"readmee.md", ".md"}, - {"readme.i18n.md", ".md"}, + type extensionTestcase struct { + name string + expected bool + idx int } - for _, testCase := range trueTestCasesStrict { - assert.True(t, IsReadmeFile(testCase[0], testCase[1])) + exts := []string{".md", ".txt", ""} + testCasesExtensions := []extensionTestcase{ + { + name: "readme", + expected: true, + idx: 2, + }, + { + name: "readme.md", + expected: true, + idx: 0, + }, + { + name: "readme.txt", + expected: true, + idx: 1, + }, + { + name: "readme.doc", + expected: true, + idx: 3, + }, + { + name: "readmee.md", + }, + { + name: "readme..", + expected: true, + idx: 3, + }, } - for _, testCase := range falseTestCasesStrict { - assert.False(t, IsReadmeFile(testCase[0], testCase[1])) + + for _, testCase := range testCasesExtensions { + idx, ok := IsReadmeFileExtension(testCase.name, exts...) + assert.Equal(t, testCase.expected, ok) + assert.Equal(t, testCase.idx, idx) } } diff --git a/modules/notification/mail/mail.go b/modules/notification/mail/mail.go index 1f217304b063..5085656c14a4 100644 --- a/modules/notification/mail/mail.go +++ b/modules/notification/mail/mail.go @@ -126,7 +126,7 @@ func (m *mailNotifier) NotifyPullRequestCodeComment(pr *issues_model.PullRequest func (m *mailNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) { // mail only sent to added assignees and not self-assignee - if !removed && doer.ID != assignee.ID && (assignee.EmailNotifications() == user_model.EmailNotificationsEnabled || assignee.EmailNotifications() == user_model.EmailNotificationsOnMention) { + if !removed && doer.ID != assignee.ID && assignee.EmailNotifications() != user_model.EmailNotificationsDisabled { ct := fmt.Sprintf("Assigned #%d.", issue.Index) if err := mailer.SendIssueAssignedMail(issue, doer, ct, comment, []*user_model.User{assignee}); err != nil { log.Error("Error in SendIssueAssignedMail for issue[%d] to assignee[%d]: %v", issue.ID, assignee.ID, err) @@ -135,7 +135,7 @@ func (m *mailNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue *i } func (m *mailNotifier) NotifyPullReviewRequest(doer *user_model.User, issue *issues_model.Issue, reviewer *user_model.User, isRequest bool, comment *issues_model.Comment) { - if isRequest && doer.ID != reviewer.ID && (reviewer.EmailNotifications() == user_model.EmailNotificationsEnabled || reviewer.EmailNotifications() == user_model.EmailNotificationsOnMention) { + if isRequest && doer.ID != reviewer.ID && reviewer.EmailNotifications() != user_model.EmailNotificationsDisabled { ct := fmt.Sprintf("Requested to review %s.", issue.HTMLURL()) if err := mailer.SendIssueAssignedMail(issue, doer, ct, comment, []*user_model.User{reviewer}); err != nil { log.Error("Error in SendIssueAssignedMail for issue[%d] to reviewer[%d]: %v", issue.ID, reviewer.ID, err) diff --git a/modules/packages/container/metadata.go b/modules/packages/container/metadata.go index 087d38e5bd14..4222cdb30a78 100644 --- a/modules/packages/container/metadata.go +++ b/modules/packages/container/metadata.go @@ -16,6 +16,7 @@ import ( ) const ( + PropertyRepository = "container.repository" PropertyDigest = "container.digest" PropertyMediaType = "container.mediatype" PropertyManifestTagged = "container.manifest.tagged" diff --git a/modules/setting/database.go b/modules/setting/database.go index 8fdd5f2bcb2f..af4e780d76bd 100644 --- a/modules/setting/database.go +++ b/modules/setting/database.go @@ -39,6 +39,7 @@ var ( LogSQL bool Charset string Timeout int // seconds + SQLiteJournalMode string UseSQLite3 bool UseMySQL bool UseMSSQL bool @@ -91,6 +92,8 @@ func InitDBConfig() { Database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db")) Database.Timeout = sec.Key("SQLITE_TIMEOUT").MustInt(500) + Database.SQLiteJournalMode = sec.Key("SQLITE_JOURNAL_MODE").MustString("") + Database.MaxIdleConns = sec.Key("MAX_IDLE_CONNS").MustInt(2) if Database.UseMySQL { Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFETIME").MustDuration(3 * time.Second) @@ -136,7 +139,12 @@ func DBConnStr() (string, error) { if err := os.MkdirAll(path.Dir(Database.Path), os.ModePerm); err != nil { return "", fmt.Errorf("Failed to create directories: %v", err) } - connStr = fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d&_txlock=immediate", Database.Path, Database.Timeout) + journalMode := "" + if Database.SQLiteJournalMode != "" { + journalMode = "&_journal_mode=" + Database.SQLiteJournalMode + } + connStr = fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d&_txlock=immediate%s", + Database.Path, Database.Timeout, journalMode) default: return "", fmt.Errorf("Unknown database type: %s", Database.Type) } diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 733bc6d90e60..d0406dbf9028 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -48,6 +48,7 @@ var ( DefaultBranch string AllowAdoptionOfUnadoptedRepositories bool AllowDeleteOfUnadoptedRepositories bool + DisableDownloadSourceArchives bool // Repository editor settings Editor struct { diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go index 2affeb781a99..bffe29f4c385 100644 --- a/modules/ssh/ssh.go +++ b/modules/ssh/ssh.go @@ -11,6 +11,7 @@ import ( "crypto/rsa" "crypto/x509" "encoding/pem" + "errors" "fmt" "io" "net" @@ -142,10 +143,14 @@ func sessionHandler(session ssh.Session) { // Wait for the command to exit and log any errors we get err = cmd.Wait() if err != nil { - log.Error("SSH: Wait: %v", err) + // Cannot use errors.Is here because ExitError doesn't implement Is + // Thus errors.Is will do equality test NOT type comparison + if _, ok := err.(*exec.ExitError); !ok { + log.Error("SSH: Wait: %v", err) + } } - if err := session.Exit(getExitStatusFromError(err)); err != nil { + if err := session.Exit(getExitStatusFromError(err)); err != nil && !errors.Is(err, io.EOF) { log.Error("Session failed to exit. %s", err) } } diff --git a/modules/structs/mirror.go b/modules/structs/mirror.go new file mode 100644 index 000000000000..8e8a8a2705d2 --- /dev/null +++ b/modules/structs/mirror.go @@ -0,0 +1,25 @@ +// Copyright 2021 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 structs + +// CreatePushMirrorOption represents need information to create a push mirror of a repository. +type CreatePushMirrorOption struct { + RemoteAddress string `json:"remote_address"` + RemoteUsername string `json:"remote_username"` + RemotePassword string `json:"remote_password"` + Interval string `json:"interval"` +} + +// PushMirror represents information of a push mirror +// swagger:model +type PushMirror struct { + RepoName string `json:"repo_name"` + RemoteName string `json:"remote_name"` + RemoteAddress string `json:"remote_address"` + CreatedUnix string `json:"created"` + LastUpdateUnix string `json:"last_update"` + LastError string `json:"last_error"` + Interval string `json:"interval"` +} diff --git a/modules/structs/repo_file.go b/modules/structs/repo_file.go index 135e6484cd69..ce1a9fe4be36 100644 --- a/modules/structs/repo_file.go +++ b/modules/structs/repo_file.go @@ -87,9 +87,10 @@ type FileLinksResponse struct { // ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content type ContentsResponse struct { - Name string `json:"name"` - Path string `json:"path"` - SHA string `json:"sha"` + Name string `json:"name"` + Path string `json:"path"` + SHA string `json:"sha"` + LastCommitSHA string `json:"last_commit_sha"` // `type` will be `file`, `dir`, `symlink`, or `submodule` Type string `json:"type"` Size int64 `json:"size"` diff --git a/modules/templates/base.go b/modules/templates/base.go index 282019f826c1..9563650e127b 100644 --- a/modules/templates/base.go +++ b/modules/templates/base.go @@ -35,10 +35,11 @@ func BaseVars() Vars { "IsLandingPageExplore": setting.LandingPageURL == setting.LandingPageExplore, "IsLandingPageOrganizations": setting.LandingPageURL == setting.LandingPageOrganizations, - "ShowRegistrationButton": setting.Service.ShowRegistrationButton, - "ShowMilestonesDashboardPage": setting.Service.ShowMilestonesDashboardPage, - "ShowFooterBranding": setting.ShowFooterBranding, - "ShowFooterVersion": setting.ShowFooterVersion, + "ShowRegistrationButton": setting.Service.ShowRegistrationButton, + "ShowMilestonesDashboardPage": setting.Service.ShowMilestonesDashboardPage, + "ShowFooterBranding": setting.ShowFooterBranding, + "ShowFooterVersion": setting.ShowFooterVersion, + "DisableDownloadSourceArchives": setting.Repository.DisableDownloadSourceArchives, "EnableSwagger": setting.API.EnableSwagger, "EnableOpenIDSignIn": setting.Service.EnableOpenIDSignIn, diff --git a/modules/typesniffer/typesniffer.go b/modules/typesniffer/typesniffer.go index b6a6646d50ce..e50928e8c260 100644 --- a/modules/typesniffer/typesniffer.go +++ b/modules/typesniffer/typesniffer.go @@ -70,6 +70,16 @@ func (ct SniffedType) IsRepresentableAsText() bool { return ct.IsText() || ct.IsSvgImage() } +// IsBrowsableType returns whether a non-text type can be displayed in a browser +func (ct SniffedType) IsBrowsableBinaryType() bool { + return ct.IsImage() || ct.IsSvgImage() || ct.IsPDF() || ct.IsVideo() || ct.IsAudio() +} + +// GetMimeType returns the mime type +func (ct SniffedType) GetMimeType() string { + return strings.SplitN(ct.contentType, ";", 2)[0] +} + // DetectContentType extends http.DetectContentType with more content types. Defaults to text/unknown if input is empty. func DetectContentType(data []byte) SniffedType { if len(data) == 0 { diff --git a/options/license/MS-LPL b/options/license/MS-LPL new file mode 100644 index 000000000000..ea8bffcaae21 --- /dev/null +++ b/options/license/MS-LPL @@ -0,0 +1,24 @@ +Microsoft Limited Public License (Ms-LPL) + +This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. + +1. Definitions +The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to the software. A "contributor" is any person that distributes its contribution under this license. "Licensed patents" are a contributor's patent claims that read directly on its contribution. + +2. Grant of Rights + (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + + (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + +3. Conditions and Limitations + (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. + + (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + + (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. + + (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. + + (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees, or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. + + (F) Platform Limitation- The licenses granted in sections 2(A) & 2(B) extend only to the software or derivative works that you create that run on a Microsoft Windows operating system product. diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index cc449cae9fc4..2b7c18ca12bc 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -1691,10 +1691,6 @@ settings.mirror_settings.push_mirror.remote_url=URL vzdáleného Git repozitář settings.mirror_settings.push_mirror.add=Přidat zrcadlo pro nahrání settings.sync_mirror=Synchronizovat nyní settings.mirror_sync_in_progress=Právě probíhá synchronizace zrcadla. Zkuste to za chvíli. -settings.email_notifications.enable=Povolit e-mailová oznámení -settings.email_notifications.onmention=E-mail pouze při zmínce -settings.email_notifications.disable=Zakázat e-mailová oznámení -settings.email_notifications.submit=Nastavit předvolby e-mailu settings.site=Webová stránka settings.update_settings=Aktualizovat nastavení settings.branches.update_default_branch=Aktualizovat výchozí větev diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 796b4b835b93..b4e59e504a46 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -799,6 +799,7 @@ email_notifications.enable=E-Mail Benachrichtigungen aktivieren email_notifications.onmention=Nur E-Mail bei Erwähnung email_notifications.disable=E-Mail Benachrichtigungen deaktivieren email_notifications.submit=E-Mail-Einstellungen festlegen +email_notifications.andyourown=Und deine Eigenen Benachrichtigungen visibility=Nutzer Sichtbarkeit visibility.public=Öffentlich @@ -930,6 +931,7 @@ form.name_pattern_not_allowed='%s' ist nicht erlaubt für Repository-Namen. need_auth=Authentifizierung migrate_options=Migrationsoptionen migrate_service=Migrationsdienst +migrate_options_mirror_helper=Dieses Repository wird ein Spiegel sein migrate_options_lfs=LFS-Dateien migrieren migrate_options_lfs_endpoint.label=LFS-Endpunkt migrate_options_lfs_endpoint.description=Migration wird versuchen, über den entfernten Git-Server den LFS-Server zu bestimmen. Du kannst auch einen eigenen Endpunkt angeben, wenn die LFS-Dateien woanders gespeichert werden. @@ -1417,6 +1419,7 @@ issues.due_date_form_remove=Entfernen issues.due_date_not_writer=Du musst Schreibrechte in diesem Repository haben, um das Fälligkeitsdatum zu ändern. issues.due_date_not_set=Kein Fälligkeitsdatum gesetzt. issues.due_date_added=hat %[2]s das Fälligkeitsdatum %[1]s hinzugefügt +issues.due_date_modified=ändert das Abgabedatum von %[2]s auf %[1]s %[3]s s issues.due_date_remove=hat %[2]s das Fälligkeitsdatum %[1]s entfernt issues.due_date_overdue=Überfällig issues.due_date_invalid=Das Fälligkeitsdatum ist ungültig oder außerhalb des zulässigen Bereichs. Bitte verwende das Format „jjjj-mm-tt“. @@ -1528,6 +1531,7 @@ pulls.remove_prefix=%s Präfix entfernen pulls.data_broken=Dieser Pull-Requests ist kaputt, da Fork-Informationen gelöscht wurden. pulls.files_conflicted=Dieser Pull-Request hat Änderungen, die im Widerspruch zum Ziel-Branch stehen. pulls.is_checking=Die Konfliktprüfung läuft noch. Bitte aktualisiere die Seite in wenigen Augenblicken. +pulls.is_ancestor=Dieser Branch ist bereits im Zielbranch enthalten. Es gibt nichts zu mergen. pulls.required_status_check_failed=Einige erforderliche Prüfungen waren nicht erfolgreich. pulls.required_status_check_missing=Einige erforderliche Prüfungen fehlen. pulls.required_status_check_administrator=Als Administrator kannst du diesen Pull-Request weiterhin zusammenführen. @@ -1778,10 +1782,6 @@ settings.mirror_settings.push_mirror.remote_url=URL zum Git-Remote-Repository settings.mirror_settings.push_mirror.add=Push-Mirror hinzufügen settings.sync_mirror=Jetzt synchronisieren settings.mirror_sync_in_progress=Mirror-Synchronisierung wird zurzeit ausgeführt. Komm in ein paar Minuten zurück. -settings.email_notifications.enable=E-Mail Benachrichtigungen aktivieren -settings.email_notifications.onmention=E-Mail-Benachrichtigungen nur bei Erwähnung -settings.email_notifications.disable=E-Mail Benachrichtigungen deaktivieren -settings.email_notifications.submit=E-Mail-Einstellungen festlegen settings.site=Webseite settings.update_settings=Einstellungen speichern settings.branches.update_default_branch=Standardbranch aktualisieren diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 51bd62d91f32..c123bd6d2661 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -1784,10 +1784,6 @@ settings.mirror_settings.push_mirror.remote_url=URL Απομακρυσμένου settings.mirror_settings.push_mirror.add=Προσθήκη Είδωλου Push settings.sync_mirror=Συγχρονισμός Τώρα settings.mirror_sync_in_progress=Ο συγχρονισμός ειδώλου είναι σε εξέλιξη. Ελέγξτε ξανά σε λίγο. -settings.email_notifications.enable=Ενεργοποίηση Ειδοποιήσεων Email -settings.email_notifications.onmention=Email Μόνο σε Αναφορά -settings.email_notifications.disable=Απενεργοποίηση Ειδοποιήσεων Email -settings.email_notifications.submit=Ορισμός Προτίμησης Email settings.site=Ιστοσελίδα settings.update_settings=Ενημέρωση Ρυθμίσεων settings.branches.update_default_branch=Ενημέρωση Προεπιλεγμένου Κλάδου diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index a97e2e2b3b86..aad10ce87b1b 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -799,6 +799,7 @@ email_notifications.enable = Enable Email Notifications email_notifications.onmention = Only Email on Mention email_notifications.disable = Disable Email Notifications email_notifications.submit = Set Email Preference +email_notifications.andyourown = And Your Own Notifications visibility = User visibility visibility.public = Public @@ -1784,10 +1785,6 @@ settings.mirror_settings.push_mirror.remote_url = Git Remote Repository URL settings.mirror_settings.push_mirror.add = Add Push Mirror settings.sync_mirror = Synchronize Now settings.mirror_sync_in_progress = Mirror synchronization is in progress. Check back in a minute. -settings.email_notifications.enable = Enable Email Notifications -settings.email_notifications.onmention = Only Email on Mention -settings.email_notifications.disable = Disable Email Notifications -settings.email_notifications.submit = Set Email Preference settings.site = Website settings.update_settings = Update Settings settings.branches.update_default_branch = Update Default Branch diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index d2f5695ec2ee..4507bc553b6a 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -799,6 +799,7 @@ email_notifications.enable=Habilitar notificaciones por correo electrónico email_notifications.onmention=Enviar correo sólo al ser mencionado email_notifications.disable=Deshabilitar las notificaciones por correo electrónico email_notifications.submit=Establecer preferencias de correo electrónico +email_notifications.andyourown=Y sus propias notificaciones visibility=Visibilidad del usuario visibility.public=Público @@ -932,6 +933,7 @@ form.name_pattern_not_allowed=El patrón '%s' no está permitido en un nombre de need_auth=Autorización migrate_options=Opciones de migración migrate_service=Servicio de Migración +migrate_options_mirror_helper=Este repositorio será una réplica migrate_options_lfs=Migrar archivos LFS migrate_options_lfs_endpoint.label=Punto final de LFS migrate_options_lfs_endpoint.description=Migración intentará usar su mando Git para determinar el servidor LFS. También puede especificar un punto final personalizado si los datos LFS del repositorio se almacenan en otro lugar. @@ -1302,6 +1304,7 @@ issues.previous=Página Anterior issues.next=Página Siguiente issues.open_title=Abierta issues.closed_title=Cerrada +issues.draft_title=Borrador issues.num_comments=%d comentarios issues.commented_at=`comentado %s` issues.delete_comment_confirm=¿Seguro que deseas eliminar este comentario? @@ -1418,6 +1421,7 @@ issues.due_date_form_remove=Eliminar issues.due_date_not_writer=Necesita acceso de escritura al repositorio para actualizar la fecha de vencimiento de un issue. issues.due_date_not_set=Sin fecha de vencimiento. issues.due_date_added=añadió la fecha de vencimiento %s %s +issues.due_date_modified=modificó la fecha de vencimiento de %[2]s a %[1]s %[3]s issues.due_date_remove=eliminó la fecha de vencimiento %s %s issues.due_date_overdue=Vencido issues.due_date_invalid=La fecha de vencimiento es inválida o está fuera de rango. Por favor utilice el formato 'aaaa-mm-dd'. @@ -1529,6 +1533,8 @@ pulls.remove_prefix=Eliminar prefijo %s pulls.data_broken=Este pull request está rota debido a que falta información del fork. pulls.files_conflicted=Este pull request tiene cambios en conflicto con la rama de destino. pulls.is_checking=La comprobación de conflicto de fusión está en progreso. Inténtalo de nuevo en unos momentos. +pulls.is_ancestor=Esta rama ya está incluida en la rama de destino. No hay nada que fusionar. +pulls.is_empty=Los cambios en esta rama ya están en la rama de destino. Esto será un commit vacío. pulls.required_status_check_failed=Algunos controles requeridos no han tenido éxito. pulls.required_status_check_missing=Faltan algunos controles necesarios. pulls.required_status_check_administrator=Como administrador, aún puede fusionar este Pull Request. @@ -1779,10 +1785,6 @@ settings.mirror_settings.push_mirror.remote_url=URL del repositorio remoto de Gi settings.mirror_settings.push_mirror.add=Añadir Réplica de Push settings.sync_mirror=Sincronizar ahora settings.mirror_sync_in_progress=La sincronización del repositorio replicado está en curso. Vuelva a intentarlo más tarde. -settings.email_notifications.enable=Habilitar las notificaciones por correo electrónico -settings.email_notifications.onmention=Enviar correo sólo al mencionar -settings.email_notifications.disable=Deshabilitar las notificaciones por correo electrónico -settings.email_notifications.submit=Establecer Preferencia de correo electrónico settings.site=Sitio web settings.update_settings=Actualizar configuración settings.branches.update_default_branch=Actualizar rama por defecto @@ -2535,6 +2537,8 @@ users.delete_account=Eliminar Cuenta de Usuario users.cannot_delete_self=No puedes eliminarte a ti mismo users.still_own_repo=Este usuario todavía posee uno o más depósitos. Eliminar o transferir estos repositorios primero. users.still_has_org=Este usuario es un miembro de una organización. Primero retire el usuario de cualquier organización. +users.purge=Borrar usuario +users.purge_help=Borrar forzosamente el usuario y cualquier repositorio, organización y paquete propiedad del usuario. Todos los comentarios también serán borrados. users.still_own_packages=Este usuario todavía posee uno o más paquetes. Elimine estos paquetes primero. users.deletion_success=La cuenta de usuario ha sido eliminada. users.reset_2fa=Reiniciar 2FA @@ -3037,6 +3041,7 @@ title=Paquetes desc=Administrar paquetes del repositorio. empty=Todavía no hay paquetes. empty.documentation=Para más información sobre el registro de paquetes, consulte la documentación. +empty.repo=¿Has subido un paquete, pero no se muestra aquí? Ve a la configuración del paquete y añade el link a este repositorio. filter.type=Tipo filter.type.all=Todo filter.no_result=El filtro no produjo ningún resultado. diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index d97d3eead122..9999425f157f 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -1642,10 +1642,6 @@ settings.mirror_settings.push_mirror.remote_url=Git Remote Repository URL settings.mirror_settings.push_mirror.add=اضافه کردن Push Mirror settings.sync_mirror=همگام سازی کن settings.mirror_sync_in_progress=همگام سازی قرینه در حالت پردازش است. یک دقیقه دیگر مجددا بررسی کنید. -settings.email_notifications.enable=فعال‌سازی اعلان‌های ایمیل -settings.email_notifications.onmention=تنها یادوآری از طریق ایمیل -settings.email_notifications.disable=غیرفعال‌ کردن اعلان‌های ایمیل -settings.email_notifications.submit=ثبت اولویت ایمیل settings.site=تارنما settings.update_settings=به‌ روزرسانی تنظیمات settings.branches.update_default_branch=بروزرسانی شاخه پیش فرض diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index c1f7f6e0b295..acd17f88c595 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -2,6 +2,7 @@ home=Accueil dashboard=Tableau de bord explore=Explorateur help=Aide +logo=Logo sign_in=Connexion sign_in_with=Se connecter avec sign_out=Déconnexion @@ -35,6 +36,13 @@ twofa_scratch=Code de secours pour l'authentification à deux facteurs passcode=Code d'accès webauthn_insert_key=Insérez votre clé de sécurité +webauthn_sign_in=Appuyez sur le bouton de votre clé de sécurité. Si votre clé de sécurité n'a pas de bouton, réinsérez-la. +webauthn_press_button=Veuillez appuyer sur le bouton de votre clé de sécurité… +webauthn_use_twofa=Utilisez l'authentification à deux facteurs avec votre téléphone +webauthn_error=Impossible de lire votre clé de sécurité. +webauthn_unsupported_browser=Votre navigateur ne prend actuellement pas en charge WebAuthn. +webauthn_error_unknown=Une erreur indéterminée s'est produite. Veuillez réessayer. +webauthn_error_insecure=WebAuthn ne prend en charge que les connexions sécurisées. Pour les tests via HTTP, vous pouvez utiliser l'origine "localhost" ou "127.0.0.1" repository=Dépôt organization=Organisation @@ -156,7 +164,7 @@ http_port=Port d'écoute HTTP de Gitea http_port_helper=Port sur lequel le serveur web Gitea attendra des requêtes. app_url=URL de base de Gitea app_url_helper=Adresse HTTP(S) de base pour les clones git et les notifications par e-mail. -log_root_path=Chemin des fichiers log +log_root_path=Chemin des journaux log_root_path_helper=Les fichiers de journalisation seront écrits dans ce répertoire. optional_title=Paramètres facultatifs @@ -183,7 +191,7 @@ openid_signin_popup=Activer l'authentification via OpenID. openid_signup=Activer l'inscription OpenID openid_signup_popup=Activer l'inscription avec OpenID. enable_captcha=Activer le CAPTCHA d'inscription -enable_captcha_popup=Demander un Captcha à l'inscription. +enable_captcha_popup=Demander un CAPTCHA à l'inscription. require_sign_in_view=Exiger la connexion à un compte pour afficher les pages require_sign_in_view_popup=Limiter l'accès aux pages aux utilisateurs connectés. Les visiteurs ne verront que les pages de connexion et d'inscription. admin_setting_desc=La création d'un compte administrateur est facultative. Le premier utilisateur enregistré deviendra automatiquement un administrateur le cas échéant. @@ -608,6 +616,7 @@ ssh_key_name_used=Une clé SSH avec le même nom existe déjà sur votre compte. ssh_principal_been_used=Ce principal a déjà été ajouté au serveur. gpg_key_id_used=Une clef GPG publique avec le même identifiant existe déjà. gpg_key_verified=Clé vérifiée +gpg_key_verify=Vérifier gpg_invalid_token_signature=La clé GPG fournie, la signature et le jeton ne correspondent pas ou le jeton n'est pas à jour. gpg_token_required=Vous devez fournir une signature pour le jeton ci-dessous gpg_token=Jeton @@ -619,6 +628,7 @@ ssh_token_required=Vous devez fournir une signature pour le jeton ci-dessous ssh_token=Jeton ssh_token_help=Vous pouvez générer une signature en utilisant : ssh_token_signature=Signature SSH renforcée +verify_ssh_key_success=La clef SSH '%s' a été vérifiée. subkeys=Sous-clés key_id=Clé ID key_name=Nom de la Clé @@ -666,6 +676,8 @@ generate_token_success=Votre nouveau jeton a été généré. Copiez-le maintena generate_token_name_duplicate=%s a déjà été utilisé comme nom d'application. Veuillez en utiliser un nouveau. delete_token=Supprimer access_token_deletion=Suppression de jetons d'accès +access_token_deletion_cancel_action=Annuler +access_token_deletion_confirm_action=Supprimer delete_token_success=Ce jeton a été supprimé. Les applications l'utilisant n'ont plus accès à votre compte. manage_oauth2_applications=Gérer les applications OAuth2 @@ -1033,6 +1045,7 @@ editor.cannot_commit_to_protected_branch=Impossible de créer une révision sur editor.no_commit_to_branch=Impossible d'enregistrer la révisions directement sur la branche parce que : editor.user_no_push_to_branch=L'utilisateur ne peut pas pousser vers la branche editor.require_signed_commit=Cette branche nécessite une révision signée +editor.revert=Rétablir %s sur: commits.desc=Naviguer dans l'historique des modifications. commits.commits=Révisions @@ -1119,12 +1132,12 @@ issues.new.assignees=Affecté à issues.new.add_assignees_title=Assigner des utilisateurs issues.new.clear_assignees=Supprimer les affectations issues.new.no_assignees=Pas d'assignataires -issues.new.no_reviewers=Pas de relecteur +issues.new.no_reviewers=Aucune évaluation issues.new.add_reviewer_title=Demander une revue issues.choose.get_started=Démarrons issues.choose.blank=Par défaut issues.choose.blank_about=Créer un ticket à partir du modèle par défaut. -issues.no_ref=Aucune branche/tag spécifiés +issues.no_ref=Aucune branche/étiquette spécifiées issues.create=Créer un ticket issues.new_label=Nouvelle étiquette issues.new_label_placeholder=Nom de l'étiquette @@ -1625,10 +1638,6 @@ settings.mirror_settings.last_update=Dernière mise à jour settings.mirror_settings.push_mirror.remote_url=URL du dépôt distant Git settings.sync_mirror=Synchroniser maintenant settings.mirror_sync_in_progress=La synchronisation est en cours. Revenez dans une minute. -settings.email_notifications.enable=Activer les notifications par e-mail -settings.email_notifications.onmention=N'envoyer un e-mail que si vous êtes mentionné -settings.email_notifications.disable=Désactiver les notifications par e-mail -settings.email_notifications.submit=Définir la préférence e-mail settings.site=Site Web settings.update_settings=Valider settings.branches.update_default_branch=Changer la Branche par Défaut @@ -2695,4 +2704,5 @@ error.no_unit_allowed_repo=Vous n'êtes pas autorisé à accéder à n'importe q error.unit_not_allowed=Vous n'êtes pas autorisé à accéder à cette section du dépôt. [packages] +empty.repo=Avez-vous téléchargé un paquet, mais il n'est pas affiché ici? Allez dans les paramètres du paquet et liez le à ce dépôt. diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index 43bae7e10c02..a98bbedf3e84 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -1104,10 +1104,6 @@ settings.basic_settings=Alap beállítások settings.mirror_settings=Tükrözési beállítások settings.sync_mirror=Szinkronizálás most settings.mirror_sync_in_progress=Tükör szinkronizálása folyamatban. Kérem várjon. -settings.email_notifications.enable=Email értesítések engedélyezése -settings.email_notifications.onmention=Email küldése csak megjelölés esetén -settings.email_notifications.disable=Email értesítés kikapcsolása -settings.email_notifications.submit=E-mail beállítások megadása settings.site=Weboldal settings.update_settings=Beállítások frissítése settings.advanced_settings=Haladó beállítások diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini index c56b14f90774..9503c3c2778c 100644 --- a/options/locale/locale_is-IS.ini +++ b/options/locale/locale_is-IS.ini @@ -1026,10 +1026,6 @@ settings.mirror_settings.direction.pull=Pull settings.mirror_settings.direction.push=Push settings.mirror_settings.last_update=Síðasta uppfærsla settings.mirror_settings.push_mirror.remote_url=Vefslóð Git Fjarhugbúnaðarsafns -settings.email_notifications.enable=Virkja Tölvupósttilkynningar -settings.email_notifications.onmention=Aðeins Tölvupóst Þegar Minnst Er á Mig -settings.email_notifications.disable=Óvirkja Tölvupósttilkynningar -settings.email_notifications.submit=Stilla Val á Tölvupósti settings.site=Vefsíða settings.update_settings=Uppfæra Stillingar settings.branches.update_default_branch=Uppfæra Sjálfgefna Grein diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 5b397773f9de..7fd829873706 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -2,6 +2,7 @@ home=Home dashboard=Pannello di controllo explore=Esplora help=Aiuto +logo=Logo sign_in=Accedi sign_in_with=Accedi con sign_out=Esci @@ -34,6 +35,19 @@ twofa=Verifica in due passaggi twofa_scratch=Codice di recupero per la verifica in due passaggi passcode=Codice di sicurezza +webauthn_insert_key=Inserisci la tua chiave di sicurezza +webauthn_sign_in=Premere il pulsante sul tasto di sicurezza. Se il tasto di sicurezza non ha pulsante, reinseriscilo. +webauthn_press_button=Si prega di premere il pulsante sul tasto di sicurezza… +webauthn_use_twofa=Usa un codice a due fattori dal tuo telefono +webauthn_error=Impossibile leggere la tua chiave di sicurezza. +webauthn_unsupported_browser=Il tuo browser al momento non supporta WebAuthn. +webauthn_error_unknown=Si è verificato un errore sconosciuto. Riprova. +webauthn_error_insecure=WebAuthn supporta solo connessioni sicure. Per il test su HTTP, è possibile utilizzare l'origine "localhost" o "127.0.0.1" +webauthn_error_unable_to_process=Il server non può elaborare la richiesta. +webauthn_error_duplicated=La chiave di sicurezza non è consentita per questa richiesta. Assicurati che la chiave non sia già registrata. +webauthn_error_empty=Devi impostare un nome per questa chiave. +webauthn_error_timeout=Timeout raggiunto prima che la tua chiave possa essere letta. Ricarica la pagina e riprova. +webauthn_reload=Ricarica repository=Repository organization=Organizzazione @@ -71,7 +85,13 @@ add=Aggiungi add_all=Aggiungi tutti remove=Rimuovi remove_all=Rimuovi tutti +edit=Modifica +copy=Copia +copy_url=Copia URL +copy_branch=Copia nome del ramo +copy_success=Copiato! +copy_error=Copia fallita write=Scrivi preview=Anteprima @@ -80,11 +100,20 @@ loading=Caricamento… step1=Passo 1: step2=Passo 2: +error=Errore error404=La pagina che stai cercando di raggiungere non esiste oppure non sei autorizzato a visualizzarla. +never=Mai +rss_feed=Feed RSS [error] +occurred=Si è verificato un errore +report_message=Se sei sicuro che questo sia un bug Gitea, cerca i problemi su GitHub o apri un nuovo problema se necessario. +missing_csrf=Richiesta errata: nessun token CSRF presente +invalid_csrf=Richiesta errata: token CSRF non valido +not_found=Il bersaglio non è stato trovato. +network_error=Errore di rete [startpage] app_desc=Un servizio auto-ospitato per Git pronto all'uso @@ -101,6 +130,7 @@ license_desc=Ottieni la documentazione prima di cambiare qualsiasi impostazione. +require_db_desc=Gitea requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol). db_title=Impostazioni Database db_type=Tipo di database host=Host @@ -114,6 +144,11 @@ ssl_mode=SSL charset=Charset path=Percorso sqlite_helper=Percorso file del database SQLite3.
Inserisci un percorso assoluto se stai usando Gitea come servizio. +reinstall_error=Stai cercando di installare in un database Gitea esistente +reinstall_confirm_message=La reinstallazione con un database Gitea esistente può causare problemi multipli. Nella maggior parte dei casi, dovresti usare il tuo "app.ini" esistente per eseguire Gitea. Se sai cosa stai facendo, confermi quanto segue: +reinstall_confirm_check_1=I dati crittografati da SECRET_KEY nell'app. ni potrebbe essere perso: gli utenti potrebbero non essere in grado di accedere con 2FA/OTP & mirror potrebbe non funzionare correttamente. Selezionando questa casella confermi che il file attuale app.ini contiene il corretto SECRET_KEY. +reinstall_confirm_check_2=I repository e le impostazioni potrebbero avere bisogno di essere ri-sincronizzati. Selezionando questa casella confermi che potrai risincronizzare manualmente gli hook per i repository e il file authorized_keys. Confermi che assicurerai che le impostazioni del repository e del mirror siano corrette. +reinstall_confirm_check_3=Confermi di essere assolutamente sicuro che questo Gitea è in esecuzione con l'app corretta. ni posizione e che sei sicuro di dover reinstallare. Confermi di aver riconosciuto i rischi di cui sopra. err_empty_db_path=Il percorso del database SQLite3 non può essere vuoto. no_admin_and_disable_registration=Non puoi disabilitare l'auto-registrazione degli utenti senza creare un account amministratore. err_empty_admin_password=La password dell'amministratore non può essere vuota. @@ -131,6 +166,8 @@ lfs_path=Percorso radice di Git LFS lfs_path_helper=I file trovati da Git LFS saranno salvati in questa directory. Lasciare vuoto per disattivare. run_user=Esegui come Nome utente run_user_helper=Inserisci il nome utente del sistema operativo su cui Gitea viene eseguito. Nota che l'utente deve avere accesso al percorso radice dei repository. +domain=Dominio Server +domain_helper=Dominio o indirizzo host per il server. ssh_port=Porta Server SSH ssh_port_helper=Numero di porta in ascolto sul server SSH. Lasciare vuoto per disattivare. http_port=Porta in ascolto HTTP Gitea @@ -177,8 +214,12 @@ install_btn_confirm=Installare Gitea test_git_failed=Fallito il test del comando git: %v sqlite3_not_available=Questa versione di Gitea non supporta SQLite3. Si prega di scaricare la versione binaria ufficiale da %s (not the 'gobuild' version). invalid_db_setting=Le impostazioni del database sono invalide: %v +invalid_db_table=La tabella del database '%s' non è valida: %v invalid_repo_path=Il percorso radice del Repository è invalido: %v +invalid_app_data_path=Il percorso dati dell'app non è valido: %v run_user_not_match=Il nome utente 'esegui come' non è il nome utente attuale: %s -> %s +internal_token_failed=Generazione del token interno non riuscita: %v +secret_key_failed=Generazione della chiave segreta non riuscita: %v save_config_failed=Salvataggio della configurazione non riuscito: %v invalid_admin_setting=Le impostazioni dell'account amministratore sono invalide: %v install_success=Benvenuto! Grazie per aver scelto Gitea. Attenzione e buon divertimento! @@ -207,6 +248,7 @@ view_home=Vedi %s search_repos=Trova un repository… filter=Altro filtri filter_by_team_repositories=Filtra per repository del team +feed_of=Feed di "%s" show_archived=Archiviato show_both_archived_unarchived=Mostra sia gli archiviati che i non archiviati @@ -228,6 +270,7 @@ search=Cerca code=Codice search.fuzzy=Fuzzy search.match=Corrispondenze +code_search_unavailable=Attualmente la ricerca di codice non è disponibile. Contatta l'amministratore del sito. repo_no_results=Nessuna repository corrispondente. user_no_results=Nessun utente corrispondente. org_no_results=Nessun'organizzazione corrispondente trovata. @@ -241,6 +284,7 @@ register_helper_msg=Hai già un account? Accedi ora! social_register_helper_msg=Hai già un account? Accedi ora! disable_register_prompt=La registrazione è disabilitata. Si prega di contattare l'amministratore del sito. disable_register_mail=Email di conferma per la registrazione disabilitata. +manual_activation_only=Contatta l'amministratore del sito per completare l'attivazione. remember_me=Ricorda questo dispositivo forgot_password_title=Password Dimenticata forgot_password=Password dimenticata? @@ -279,12 +323,17 @@ oauth_signup_submit=Completa l'Account oauth_signin_tab=Collegamento ad un Account Esistente oauth_signin_title=Accedi per autorizzare l' Account collegato oauth_signin_submit=Collega Account +oauth.signin.error=Si è verificato un errore nell'elaborazione della richiesta di autorizzazione. Se questo errore persiste, si prega di contattare l'amministratore del sito. +oauth.signin.error.access_denied=La richiesta di autorizzazione è stata negata. +oauth.signin.error.temporarily_unavailable=Autorizzazione non riuscita perché il server di autenticazione non è temporaneamente disponibile. Riprova più tardi. openid_connect_submit=Connetti openid_connect_title=Connetti a una conta esistente openid_connect_desc=L'URI OpenID scelto è sconosciuto. Qui puoi associarlo a un nuovo account. openid_register_title=Crea Nuovo Account openid_register_desc=L'URI OpenID scelto è sconosciuto. Qui puoi associarlo a un nuovo account. openid_signin_desc=Inserisci il tuo URI OpenID. Ad esempio: https://anne.me, bob.openid.org.cn o gnusocial.net/carry. +disable_forgot_password_mail=Il recupero dell'account è disabilitato perché non è stata impostata alcuna email. Contatta l'amministratore del sito. +disable_forgot_password_mail_admin=Il recupero dell'account è disponibile solo quando l'email è impostata. Si prega di impostare un'email per abilitare il recupero dell'account. email_domain_blacklisted=Non è possibile registrarsi con il proprio indirizzo email. authorize_application=Autorizza applicazione authorize_redirect_notice=Verrai reindirizzato a %s se autorizzi questa applicazione. @@ -298,21 +347,64 @@ password_pwned=La password che hai scelto è in una lista %s, activate_account=Per favore attiva il tuo account +activate_account.title=%s, si prega di attivare il tuo account +activate_account.text_1=Ciao %[1]s, grazie per essersi registrato al %[2]s! +activate_account.text_2=Clicca sul seguente link per attivare il tuo account entro %s: activate_email=Verifica il tuo indirizzo e-mail +activate_email.title=%s, verifica il tuo indirizzo e-mail +activate_email.text=Clicca sul seguente link per verificare il tuo indirizzo email entro %s: register_notify=Benvenuto su Gitea +register_notify.title=%[1]s, benvenuto in %[2]s +register_notify.text_1=questa è la tua email di conferma di registrazione per %s! +register_notify.text_2=Ora è possibile accedere tramite nome utente: %s. +register_notify.text_3=Se questo account è stato creato per te, per favore imposta prima la tua password. reset_password=Recupera il tuo account +reset_password.title=%s, hai richiesto di recuperare il tuo account +reset_password.text=Clicca sul seguente link per recuperare il tuo account entro %s: register_success=Registrazione completata con successo - - - - +issue_assigned.pull=@%[1]s ti ha assegnato il Problema %[2]s nel repository %[3]s. +issue_assigned.issue=@%[1]s ti ha assegnato il Problema %[2]s nel repository %[3]s. + +issue.x_mentioned_you=@%s ti ha menzionato: +issue.action.force_push=%[1]s force-pushed il %[2]s da %[3]s a %[4]s. +issue.action.push_1=@%[1]s ha spinto %[3]d commit a %[2]s +issue.action.push_n=@%[1]s ha spinto %[3]d commit a %[2]s +issue.action.close=@%[1]s chiuso #%[2]d. +issue.action.reopen=@%[1]s riaperto #%[2]d. +issue.action.merge=@%[1]s unito #%[2]d in %[3]s. +issue.action.approve=@%[1]s ha approvato questa pull request. +issue.action.reject=@%[1]s ha richiesto modifiche su questa pull request. +issue.action.review=@%[1]s ha commentato questa pull request. +issue.action.review_dismissed=@%[1]s ha respinto l'ultima recensione da %[2]s per questa pull request. +issue.action.ready_for_review=@%[1]s ha contrassegnato questa pull request pronta per la revisione. +issue.action.new=@%[1]s creato #%[2]d. +issue.in_tree_path=In %s: + +release.new.subject=%s in %s rilasciato +release.new.text=@%[1]s rilasciato %[2]s in %[3]s +release.title=Titolo: %s +release.note=Nota: +release.downloads=Scaricamenti: +release.download.zip=Codice Sorgente (Zip) +release.download.targz=Codice Sorgente (Tar.Gz) + +repo.transfer.subject_to=%s vorrebbe trasferire "%s" a %s +repo.transfer.subject_to_you=%s vorrebbe trasferire "%s" a te +repo.transfer.to_you=tu +repo.transfer.body=Per accettare o respingerla visita %s o semplicemente ignorarla. + +repo.collaborator.added.subject=%s ti ha aggiunto a %s +repo.collaborator.added.text=Sei stato aggiunto come collaboratore del repository: [modal] yes=Sì @@ -350,8 +442,10 @@ size_error='deve essere %s.' min_size_error=` deve contenere almeno %s caratteri.` max_size_error=` deve contenere massimo %s caratteri.` email_error=` non è un indirizzo e-mail valido.` +url_error=%s" non è un URL valido. include_error=` deve contenere la stringa '%s'.` glob_pattern_error=` il pattern glob non è valido: %s.` +regex_pattern_error=` modello regex non valido: %s.` unknown_error=Errore sconosciuto: captcha_incorrect=Il codice CAPTCHA non è corretto. password_not_match=Le password non corrispondono. @@ -360,6 +454,7 @@ lang_select_error=Selezionare una lingua dall'elenco. username_been_taken=Il Nome utente esiste già. username_change_not_local_user=Gli utenti non locali non sono autorizzati a modificare il proprio nome utente. repo_name_been_taken=Il nome del repository esiste già. +repository_force_private=Force Private è abilitato: i repository privati non possono essere resi pubblici. repository_files_already_exist=File già esistenti per questo repository. Contatta l'amministratore di sistema. repository_files_already_exist.adopt=I file esistono già per questo repository e possono essere solo Adottati. repository_files_already_exist.delete=I file esistono già per questo repository. È necessario eliminarli. @@ -389,12 +484,15 @@ cannot_add_org_to_team=Un'organizzazione non può essere aggiunto come membro de invalid_ssh_key=Impossibile verificare la tua chiave SSH: %s invalid_gpg_key=Impossibile verificare la tua chiave GPG: %s +invalid_ssh_principal=Principal non valido: %s unable_verify_ssh_key=Impossibile verificare la tua chiave SSH; si prega di ricontrollarla per verificare eventuali errori. auth_failed=Autenticazione non riuscita: %v still_own_repo=Il tuo account possiede una o più repositories; rimuovile o trasferiscile per proseguire. still_has_org=Il tuo account è un membro di una o più organizzazioni; abbandonali prima di proseguire. +still_own_packages=Il tuo account possiede uno o più pacchetti; eliminali prima. org_still_own_repo=Questa organizzazione possiede ancora una o più repositories, rimuoverle o trasferirle per continuare. +org_still_own_packages=Questa organizzazione possiede ancora uno o più pacchetti; eliminarli prima. target_branch_not_exist=Il ramo (branch) di destinazione non esiste. @@ -405,6 +503,7 @@ repositories=Repository activity=Attività pubblica followers=Seguaci starred=Repositories votate +watched=Repository Osservate projects=Progetti following=Seguiti follow=Segui @@ -420,6 +519,7 @@ form.name_chars_not_allowed=Il nome utente '%s' contiene caratteri non validi. [settings] profile=Profilo account=Account +appearance=Aspetto password=Password security=Sicurezza avatar=Avatar @@ -433,6 +533,7 @@ twofa=Verifica in due passaggi account_link=Account collegati organization=Organizzazioni uid=Uid +webauthn=Chiavi Di Sicurezza public_profile=Profilo pubblico biography_placeholder=Raccontaci un po' di te @@ -443,7 +544,9 @@ website=Sito web location=Posizione update_theme=Aggiorna tema update_profile=Aggiorna Profilo +update_language=Aggiorna Lingua update_language_not_found=La lingua '%s' non è disponibile. +update_language_success=La lingua è stata aggiornata. update_profile_success=Il tuo profilo è stato aggiornato. change_username=Il tuo nome utente è stato modificato. change_username_prompt=Nota: i cambiamenti al nome utente vanno a modificare anche l'URL del tuo account. @@ -452,6 +555,22 @@ continue=Continua cancel=Annulla language=Lingua ui=Tema +hidden_comment_types=Tipi di commenti nascosti +comment_type_group_reference=Riferimento +comment_type_group_label=Etichetta +comment_type_group_milestone=Traguardo +comment_type_group_assignee=Assegnatario +comment_type_group_title=Titolo +comment_type_group_branch=Ramo +comment_type_group_time_tracking=Cronografo +comment_type_group_deadline=Scadenza +comment_type_group_dependency=Dipendenza +comment_type_group_lock=Stato Blocco +comment_type_group_review_request=Richiesta di revisione +comment_type_group_pull_request_push=Aggiunti commit +comment_type_group_project=Progetto +comment_type_group_issue_ref=Riferimento del problema +saved_successfully=Le impostazioni sono state salvate correttamente. privacy=Privacy keep_activity_private=Nascondi l'attività dal profilo keep_activity_private_popup=Rendi l'attività visibile solo da te e dagli amministratori @@ -465,6 +584,7 @@ delete_current_avatar=Elimina Avatar attuale uploaded_avatar_not_a_image=Il file caricato non è un'immagine. uploaded_avatar_is_too_big=Il file inviato eccede le dimensioni massime. update_avatar_success=Il tuo avatar è stato aggiornato. +update_user_avatar_success=L'avatar dell'utente è stato aggiornato. change_password=Aggiorna Password old_password=Password attuale @@ -508,6 +628,7 @@ keep_email_private_popup=Il tuo indirizzo email sarà nascosto agli altri utenti openid_desc=OpenID consente di delegare l'autenticazione ad un provider esterno. manage_ssh_keys=Gestisci chiavi SSH +manage_ssh_principals=Gestisci i Certificati SSH manage_gpg_keys=Gestisci Chiavi GPG add_key=Aggiungi Chiave ssh_desc=Queste chiavi SSH pubbliche sono associate con il tuo account. Le corrispondenti chiavi private consentono l'accesso completo alle tue repositories. @@ -519,10 +640,35 @@ add_new_key=Aggiungi Chiave SSH add_new_gpg_key=Aggiungi Chiave GPG key_content_ssh_placeholder=Inizia con 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', o 'sk-ssh-ed25519@openssh.com' key_content_gpg_placeholder=Comincia con '-----BEGIN PGP PUBLIC KEY BLOCK-----' +add_new_principal=Aggiungi Principal ssh_key_been_used=Questa chiave SSH è già stata aggiunta al server. ssh_key_name_used=Una chiave SSH con lo stesso nome esiste già sul tuo account. ssh_principal_been_used=Questa chiave SSH è già stata aggiunta al server. gpg_key_id_used=Esiste già una chiave GPG pubblica con lo stesso ID. +gpg_no_key_email_found=Questa chiave GPG non corrisponde a nessun indirizzo email attivato associato al tuo account. Potrebbe essere ancora aggiunto se firmi il token fornito. +gpg_key_matched_identities=Identità Corrispondenti: +gpg_key_matched_identities_long=Le identità incorporate in questa chiave corrispondono ai seguenti indirizzi email attivati per questo utente. I commit che corrispondono a questi indirizzi email possono essere verificati con questa chiave. +gpg_key_verified=Chiave Verificata +gpg_key_verified_long=La chiave è stata verificata con un token e può essere utilizzata per verificare che i commit corrispondano a tutti gli indirizzi email attivati per questo utente oltre a qualsiasi identità corrispondente per questa chiave. +gpg_key_verify=Verifica +gpg_invalid_token_signature=La chiave GPG fornita, la firma e il token non corrispondono o il token è obsoleto. +gpg_token_required=Devi fornire una firma per il token sottostante +gpg_token=Token +gpg_token_help=È possibile generare una firma utilizzando: +gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig +gpg_token_signature=Firma GPG corazzata +key_signature_gpg_placeholder=Comincia con '-----BEGIN PGP SIGNATURE-----' +verify_gpg_key_success=La chiave GPG '%s' è stata verificata. +ssh_key_verified=Chiave Verificata +ssh_key_verified_long=La chiave è stata verificata con un token e può essere utilizzata per verificare che i commit corrispondano a tutti gli indirizzi email attivati per questo utente. +ssh_key_verify=Verifica +ssh_invalid_token_signature=La chiave SSH fornita, la firma o il token non corrispondono o il token è obsoleto. +ssh_token_required=Devi fornire una firma per il token sottostante +ssh_token=Token +ssh_token_help=È possibile generare una firma utilizzando: +ssh_token_signature=Firma SSH corazzata +key_signature_ssh_placeholder=Comincia con '-----BEGIN SSH SIGNATURE-----' +verify_ssh_key_success=La chiave SSH '%s' è stata verificata. subkeys=Sottochiavi key_id=ID chiave key_name=Nome della Chiave @@ -537,8 +683,10 @@ gpg_key_deletion=Rimuovi chiave GPG ssh_principal_deletion=Rimuovi certificato SSH principale ssh_key_deletion_desc=Rimuovere una chiave SSH ne revoca l'accesso al tuo account. Continuare? gpg_key_deletion_desc=La rimozione di una chiave GPG invalida i commits firmati da essa. Continuare? +ssh_principal_deletion_desc=Rimuovere un Certificato Utente SSH ne revoca l'accesso al tuo account. Continuare? ssh_key_deletion_success=La chiave SSH è stata rimossa. gpg_key_deletion_success=La chiave GPG è stata rimossa. +ssh_principal_deletion_success=Il principale è stato rimosso. add_on=Aggiunto il valid_until=Valido fino al valid_forever=Valido per sempre @@ -548,6 +696,7 @@ can_read_info=Letto can_write_info=Scrivere key_state_desc=Questa chiave è stata utilizzata negli ultimi 7 giorni token_state_desc=Questo token è stato utilizzato negli ultimi 7 giorni +principal_state_desc=Questo principal è stato utilizzato negli ultimi 7 giorni show_openid=Mostra nel profilo hide_openid=Nascondi dal profilo ssh_disabled=SSH disabilitato @@ -567,6 +716,9 @@ generate_token_success=Il nuovo token è stato generato. Copia ora in quanto non generate_token_name_duplicate=%s è già stato utilizzato come nome dell'applicazione. Si prega di usarne uno nuovo. delete_token=Elimina access_token_deletion=Elimina token di accesso +access_token_deletion_cancel_action=Annulla +access_token_deletion_confirm_action=Elimina +access_token_deletion_desc=L'eliminazione di un token annullerà l'accesso al tuo account per le applicazioni che lo utilizzano. Questo non può essere annullato. Continuare? delete_token_success=Il token è stato eliminato. Le applicazioni che lo utilizzavano non hanno più accesso al tuo account. manage_oauth2_applications=Gestisci applicazioni OAuth2 @@ -619,10 +771,16 @@ passcode_invalid=Il codice di accesso non è corretto. Riprova. twofa_enrolled=Il tuo account è stato registrato alla verifica in due passaggi. Conserva il token di sicurezza (%s) in un luogo sicuro in quanto viene visualizzato sono una volta! twofa_failed_get_secret=Impossibile ottenere il segreto. +webauthn_desc=Le chiavi di sicurezza sono dispositivi hardware contenenti chiavi crittografiche. Possono essere utilizzate per l'autenticazione a due fattori. Le chiavi di sicurezza devono supportare lo standard WebAuthenticator di WebAuthn. +webauthn_register_key=Aggiungi Chiave Di Sicurezza +webauthn_nickname=Soprannome +webauthn_delete_key=Rimuovi Chiave Di Sicurezza +webauthn_delete_key_desc=Se si rimuove una chiave di sicurezza non è più possibile accedere con esso. Continuare? manage_account_links=Gestisci gli account collegati manage_account_links_desc=Questi account esterni sono collegati al tuo account Gitea. account_links_not_available=Attualmente non è collegato alcun account esterno al tuo account Gitea. +link_account=Collega Account remove_account_link=Rimuovi account collegato remove_account_link_desc=Rimuovere un account collegato ne revoca l'accesso al tuo account Gitea. Continuare? remove_account_link_success=L'account collegato è stato rimosso. @@ -632,6 +790,7 @@ repos_none=Non possiedi alcun repository delete_account=Elimina Account delete_prompt=Questa operazione eliminerà permanentemente il tuo account utente. NON PUÒ essere annullata. +delete_with_all_comments=Il tuo account è più recente di %s giorni. Per evitare commenti fantasma, tutti i commenti relativi a issue/PR verranno eliminati con esso. confirm_delete_account=Conferma Eliminazione delete_account_title=Elimina account utente delete_account_desc=Sei sicuro di voler rimuovere questo account utente permanentemente? @@ -640,10 +799,20 @@ email_notifications.enable=Abilita Notifiche Email email_notifications.onmention=Solo email su Menzione email_notifications.disable=Disabilita notifiche email email_notifications.submit=Imposta Preferenze Email +email_notifications.andyourown=E Le Tue Notifiche +visibility=Visibilità utente +visibility.public=Pubblico +visibility.public_tooltip=Visibile a tutti gli utenti +visibility.limited=Limitato +visibility.limited_tooltip=Visibile solo agli utenti registrati +visibility.private=Privato +visibility.private_tooltip=Visibile solo ai membri dell'organizzazione [repo] +new_repo_helper=Un repository contiene tutti i file del progetto, inclusa la cronologia delle revisioni. Lo hai già altrove? Migrare il repository. owner=Proprietario +owner_helper=Alcune organizzazioni potrebbero non essere visualizzate nel menu a discesa a causa di un limite massimo al numero di repository. repo_name=Nome Repository repo_name_helper=Un buon nome per un repository è costituito da parole chiave corte, facili da ricordare e uniche. repo_size=Dimensione repository @@ -659,33 +828,55 @@ visibility_fork_helper=(Questa modifica avrà effetto su tutti i fork) clone_helper=Hai bisogno di aiuto per la clonazione? Visita Help. fork_repo=Forka Repository fork_from=Forka da +already_forked=Hai già fatto il fork di %s +fork_to_different_account=Fai Fork a un account diverso fork_visibility_helper=La visibilità di un repository forkato non può essere modificata. use_template=Usa questo modello +clone_in_vsc=Clona nel codice VS +download_zip=Scarica ZIP +download_tar=Scarica TAR.GZ +download_bundle=Scarica BUNDLE generate_repo=Genera repository generate_from=Genera da repo_desc=Descrizione repo_desc_helper=Inserisci una breve descrizione (opzionale) repo_lang=Lingua repo_gitignore_helper=Seleziona i template di .gitignore. +repo_gitignore_helper_desc=Scegli di quali file non tenere traccia da un elenco di modelli per le lingue comuni. Gli artefatti tipici generati dagli strumenti di build di ogni lingua sono inclusi su .gitignore per impostazione predefinita. issue_labels=Etichette Issue issue_labels_helper=Seleziona un set di etichette per problemi. license=Licenza license_helper=Seleziona un file di licenza. +license_helper_desc=Una licenza governa ciò che gli altri possono e non possono fare con il tuo codice. Non sei sicuro di chi è giusto per il tuo progetto? Vedi Scegli una licenza. readme=LEGGIMI readme_helper=Seleziona un template per il file LEGGIMI. readme_helper_desc=Qui puoi scrivere una descrizione completa del progetto. auto_init=Inizializza Repository (Aggiungi .gitignore, Licenza e LEGGIMI) trust_model_helper=Seleziona il modello di fiducia per la verifica della firma. Le opzioni possibili sono: trust_model_helper_collaborator=Collaboratore: Fidati delle firme da parte dei collaboratori +trust_model_helper_committer=Committer: Fidati delle Firme che corrispondono ai committenti +trust_model_helper_collaborator_committer=Collaboratore+Committer: Fidati delle firme da parte dei collaboratori che corrispondono al committer +trust_model_helper_default=Predefinito: utilizzare il modello di trust predefinito per questa installazione create_repo=Crea Repository default_branch=Ramo (Branch) predefinito +default_branch_helper=Il ramo predefinito è il ramo base per le richieste di pull e i commit di codice. mirror_prune=Rimuovi mirror_prune_desc=Rimuovi i riferimenti di puntamento-remoto obsoleti +mirror_interval=Intervallo di specchio (le unità di tempo valide sono 'h', 'm', 's'). 0 per disabilitare la sincronizzazione periodica. (Intervallo minimo: %s) mirror_interval_invalid=L'intervallo di aggiornamento dei mirror non è valido. +mirror_sync_on_commit=Sincronizzazione quando i commit vengono premuti mirror_address=Clona da URL +mirror_address_desc=Metti tutte le credenziali richieste nella sezione Autorizzazione. mirror_address_url_invalid=L'url fornito non è valido. Devi effettuare l'escape completo tutti i componenti dell'Url. mirror_address_protocol_invalid=L'url fornito non è valido. Solo dai link http(s):// o git:// possono essere replicate. +mirror_lfs=Large File Storage (LFS) +mirror_lfs_desc=Attiva il mirroring dei dati LFS. +mirror_lfs_endpoint=Punto d'accesso LFS +mirror_lfs_endpoint_desc=La sincronizzazione tenterà di utilizzare l'url clone per determinare il server LFS. È inoltre possibile specificare un endpoint personalizzato se il repository dati LFS è memorizzato da qualche altra parte. mirror_last_synced=Ultima sincronizzazione +mirror_password_placeholder=(Inmodificato) +mirror_password_blank_placeholder=(Disattivato) +mirror_password_help=Cambia il nome utente per cancellare una password memorizzata. watchers=Osservatori stargazers=Fan forks=Fork @@ -702,12 +893,14 @@ delete_preexisting_label=Elimina delete_preexisting=Elimina file preesistenti delete_preexisting_content=Elimina file in %s delete_preexisting_success=Eliminato file non adottati in %s +blame_prior=Visualizza la colpa prima di questa modifica transfer.accept=Accetta trasferimento transfer.accept_desc=Trasferisci a "%s" transfer.reject=Rifiuta trasferimento transfer.reject_desc=Annulla il trasferimento a "%s" transfer.no_permission_to_accept=Non hai i permessi per accettare +transfer.no_permission_to_reject=Non hai i permessi per rifiutare desc.private=Privato desc.public=Pubblico @@ -720,6 +913,7 @@ desc.archived=Archiviato template.items=Elementi del modello template.git_content=Contenuto di Git (Ramo predefinito) template.git_hooks=Git Hooks +template.git_hooks_tooltip=Al momento non sei in grado di modificare o rimuovere Git Hooks una volta aggiunto. Selezionare questa opzione solo se ti fidi del template repository. template.webhooks=Webhooks template.topics=Argomenti template.avatar=Avatar @@ -731,11 +925,20 @@ archive.title=Questo repository è archiviato. Puoi vedere i file e clonarli, ma archive.issue.nocomment=Questo repository è archiviato. Non puoi commentare i problemi. archive.pull.nocomment=Questo repository è archiviato. Non puoi commentare le richieste di pull. +form.reach_limit_of_creation_1=Hai già raggiunto il tuo limite di %d repository. +form.reach_limit_of_creation_n=Hai già raggiunto il tuo limite di %d repository. form.name_reserved=Il nome repository '%s' è riservato. form.name_pattern_not_allowed=Il modello '%s' non è consentito come nome di un repository. +need_auth=Autorizzazione migrate_options=Opzioni di migrazione migrate_service=Servizio migrazione +migrate_options_mirror_helper=Questo repository sarà un mirror +migrate_options_lfs=Migra file LFS +migrate_options_lfs_endpoint.label=Punto d'accesso LFS +migrate_options_lfs_endpoint.description=La migrazione tenterà di utilizzare il tuo Git remote per determinare il server LFS. È inoltre possibile specificare un endpoint personalizzato se il repository dati LFS è memorizzato da qualche altra parte. +migrate_options_lfs_endpoint.description.local=È supportato anche un percorso server locale. +migrate_options_lfs_endpoint.placeholder=Lascia vuoto per derivare dall'URL della clonazione migrate_items=Elementi di migrazione migrate_items_wiki=Wiki migrate_items_milestones=Milestone @@ -747,9 +950,12 @@ migrate_items_releases=Rilasci migrate_repo=Migra Repository migrate.clone_address=Migra / Clona da URL migrate.clone_address_desc=URL HTTP (S) o Git 'clone' di un repository esistente +migrate.github_token_desc=È possibile mettere uno o più token con virgola separati qui per rendere la migrazione più veloce a causa del limite di velocità API GitHub. ATTENZIONE: L'abuso di questa funzione potrebbe violare la politica del fornitore di servizi e portare al blocco dell'account. migrate.clone_local_path=o un percorso del server locale migrate.permission_denied=Non è consentito importare repository locali. +migrate.permission_denied_blocked=Non è possibile importare da host non consentiti, si prega di chiedere all'amministratore di controllare ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS impostazioni. migrate.invalid_local_path=Percorso locale non valido, non esiste o non è una cartella. +migrate.invalid_lfs_endpoint=Il punto d'accesso LFS non è valido. migrate.failed=Migrazione non riuscita: %v migrate.migrate_items_options=Il Token di accesso è richiesto per migrare elementi aggiuntivi migrated_from=Migrato da %[2]s @@ -757,6 +963,23 @@ migrated_from_fake=Migrato da %[1]s migrate.migrate=Migra da %s migrate.migrating=Migrazione da %s... migrate.migrating_failed=Migrazione da %s fallita. +migrate.migrating_failed.error=Errore: %s +migrate.migrating_failed_no_addr=Migrazione non riuscita. +migrate.github.description=Migrare i dati da github.com o da altre istanze di GitHub. +migrate.git.description=Migra un repository solo da qualsiasi servizio Git. +migrate.gitlab.description=Migrare i dati da gitlab.com o da altre istanze di GitLab. +migrate.gitea.description=Migrare i dati da gitea.com o altre istanze di Gitea. +migrate.gogs.description=Migrare i dati da notabug.org o da altre istanze Gogs. +migrate.onedev.description=Migrare i dati da code.onedev.io o da altre istanze OneDev. +migrate.codebase.description=Migrare i dati da codebasehq.com. +migrate.gitbucket.description=Migra i dati dalle istanze di GitBucket. +migrate.migrating_git=Migrazione dei Dati Git +migrate.migrating_topics=Migrazione dei topic +migrate.migrating_milestones=Migrazione dei traguardi +migrate.migrating_labels=Migrazione delle etichette +migrate.migrating_releases=Migrazione delle uscite +migrate.migrating_issues=Migrazione dei problemi +migrate.migrating_pulls=Migrazione delle Pull Request mirror_from=mirror da forked_from=forkato da @@ -778,6 +1001,7 @@ clone_this_repo=Clona questo repository create_new_repo_command=Creazione di un nuovo repository da riga di comando push_exist_repo=Push di un repository esistente da riga di comando empty_message=Questo repository non contiene alcun contenuto. +broken_message=I dati Git sottostanti a questo repository non possono essere letti. Contattare l'amministratore di questa istanza o eliminare questo repository. code=Codice code.desc=Accedi al codice sorgente, file, commits e branches. @@ -785,11 +1009,13 @@ branch=Ramo (Branch) tree=Albero (Tree) clear_ref=`Cancella il riferimento corrente` filter_branch_and_tag=Filtra per branch o tag +find_tag=Trova etichetta branches=Rami (Branch) tags=Tag issues=Problemi pulls=Pull Requests project_board=Progetti +packages=Pacchetti labels=Etichette org_labels_desc=Etichette a livello di organizzazione che possono essere utilizzate con tutti i repository sotto questa organizzazione org_labels_desc_manage=gestisci @@ -800,21 +1026,38 @@ commit=Commit release=Rilascio releases=Rilasci tag=Etichetta +released_this=ha rilasciato questo +file.title=%s a %s file_raw=Originale file_history=Cronologia file_view_source=Visualizza sorgente +file_view_rendered=Visualizza Renderizzato file_view_raw=Vedi originale file_permalink=Permalink file_too_large=Il file è troppo grande per essere visualizzato. - +bidi_bad_header=`Questo file contiene caratteri Unicode Bidirezionali inattesi!` +bidi_bad_description=`Questo file contiene caratteri inaspettati Bidirezionali Unicode che possono essere elaborati in modo diverso da quello che appare di seguito. Se il tuo caso di utilizzo è intenzionale e legittimo, puoi tranquillamente ignorare questo avviso. Usa il pulsante Escape per rivelare caratteri nascosti.` +bidi_bad_description_escaped=`Questo file contiene caratteri Unicode bidirezionali inattesi. I caratteri unicode nascosti sono sfuggiti qui sotto. Usa il pulsante Unescape per mostrare come render.` +unicode_header=`Questo file contiene caratteri Unicode nascosti!` +unicode_description=`Questo file contiene caratteri Unicode nascosti che possono essere elaborati in modo diverso da quello che appare di seguito. Se il tuo caso di utilizzo è intenzionale e legittimo, puoi tranquillamente ignorare questo avviso. Usa il pulsante Escape per rivelare caratteri nascosti.` +unicode_description_escaped=`Questo file contiene caratteri Unicode nascosti. I caratteri unicode nascosti sono fuggiti qui sotto. Usa il pulsante Unescape per mostrare come render.` +line_unicode=`Questa riga ha caratteri unicode nascosti` + +escape_control_characters=Fuga +unescape_control_characters=Unescape +file_copy_permalink=Copia Permalink +view_git_blame=Visualizza Git Blame video_not_supported_in_browser=Il tuo browser non supporta i tag "video" di HTML5. audio_not_supported_in_browser=Il tuo browser non supporta il tag "video" di HTML5. stored_lfs=Memorizzati con Git LFS symbolic_link=Link Simbolico commit_graph=Grafico dei commit commit_graph.select=Seleziona rami +commit_graph.hide_pr_refs=Nascondi Pull Requests commit_graph.monochrome=Mono +commit_graph.color=Colore blame=Blame +download_file=Scarica file normal_view=Vista normale line=riga lines=righe @@ -842,7 +1085,12 @@ editor.add_tmpl=Aggiungi '' editor.add=Aggiungi '%s' editor.update=Aggiornare '%s' editor.delete=Eliminare '%s' +editor.patch=Applica Patch +editor.patching=Patching: +editor.fail_to_apply_patch=Impossibile applicare la patch '%s' +editor.new_patch=Nuova Patch editor.commit_message_desc=Aggiungi una descrizione estesa facoltativa… +editor.signoff_desc=Aggiungi "firmato da" dal committer alla fine del messaggio di log di commit. editor.commit_directly_to_this_branch=Impegnarsi direttamente con il %s branch. editor.create_new_branch=Creare un nuovo branch per questo commit e inizia una pull request. editor.create_new_branch_np=Crea un nuovo ramo per questo commit. @@ -863,7 +1111,11 @@ editor.file_already_exists=Un file di nome '%s' esiste già in questo repository editor.commit_empty_file_header=Commit di un file vuoto editor.commit_empty_file_text=Il file che stai per effettuare il commit è vuoto. Procedere? editor.no_changes_to_show=Non ci sono cambiamenti da mostrare. +editor.fail_to_update_file=Impossibile aggiornare/creare il file '%s'. editor.fail_to_update_file_summary=Messaggio d'errore: +editor.push_rejected_no_message=La modifica è stata rifiutata dal server senza un messaggio. Controlla Git Hooks. +editor.push_rejected=La modifica è stata rifiutata dal server. Controlla Git Hooks. +editor.push_rejected_summary=Messaggio Di Rifiuto Completo: editor.add_subdir=Aggiungi una directory… editor.unable_to_upload_files=Impossibile caricare i file su '%s' con errore:%v editor.upload_file_is_locked=Il file '%s' è bloccato da %s. @@ -872,10 +1124,13 @@ editor.cannot_commit_to_protected_branch=Impossibile eseguire un commit sul bran editor.no_commit_to_branch=Impossibile effettuare il commit direttamente sul branch perché: editor.user_no_push_to_branch=L'utente non può effettuare il push sul branch editor.require_signed_commit=Il branch richiede un commit firmato +editor.cherry_pick=Cherry-pick %s suto: +editor.revert=Ripristina %s su: commits.desc=Sfoglia la cronologia di modifiche del codice rogente. commits.commits=Commit commits.no_commits=Nessun commit in comune. '%s' e '%s' hanno storie completamente diverse. +commits.nothing_to_compare=Questi rami sono uguali. commits.search=Ricerca commits… commits.search.tooltip=Puoi anteporre le parole chiave con "author:", "committer:", "after:", o "before:", o "revert author:Alice before:2019-04-01". commits.find=Cerca @@ -889,11 +1144,21 @@ commits.signed_by=Firmato da commits.signed_by_untrusted_user=Firmato da un utente non attendibile commits.signed_by_untrusted_user_unmatched=Firmato da un utente non attendibile che non corrisponde al committer commits.gpg_key_id=ID Chiave GPG +commits.ssh_key_fingerprint=Impronta Digitale Chiave SSH +commit.actions=Azioni +commit.revert=Ripristina +commit.revert-header=Ripristina: %s +commit.revert-content=Selezionare il ramo su cui ripristinare: +commit.cherry-pick=Cherry-pick +commit.cherry-pick-header=Cherry-pick: %s +commit.cherry-pick-content=Seleziona il ramo su cui scegliere: +ext_issues=Accesso ai Problemi Esterni ext_issues.desc=Collegamento al puntatore di una issue esterna. projects=Progetti +projects.desc=Gestisci problemi e pull nelle schede di progetto. projects.description=Descrizione (opzionale) projects.description_placeholder=Descrizione projects.create=Crea un progetto @@ -920,10 +1185,13 @@ projects.board.new_title=Nuovo Nome Della Scheda projects.board.new_submit=Invia projects.board.new=Nuova Scheda projects.board.set_default=Imposta come predefinito +projects.board.set_default_desc=Imposta questa scheda come predefinita per problemi non categorizzati e pull projects.board.delete=Elimina Scheda projects.board.deletion_desc=L'eliminazione di una scheda di progetto sposta tutti i problemi correlati a 'Uncategorized'. Continuare? +projects.board.color=Colore projects.open=Apri projects.close=Chiudi +projects.board.assigned_to=Assegnato a issues.desc=Organizza le segnalazioni di bug, attività e pietre miliari. issues.filter_assignees=Filtra assegnatario @@ -970,6 +1238,11 @@ issues.label_templates.info=Non esistono etichette. Crea una etichetta con 'Nuov issues.label_templates.helper=Scegli un set di etichette issues.label_templates.use=Usa Set Etichette issues.label_templates.fail_to_load_file=Impossibile caricare il file template di etichetta '%s': %v +issues.add_label=ha aggiunto l'etichetta %s %s +issues.add_labels=ha aggiunto le %s etichette %s +issues.remove_label=rimosso l'etichetta %s %s +issues.remove_labels=rimosso le %s etichette %s +issues.add_remove_labels=aggiunto %s e rimosso %s etichette %s issues.add_milestone_at=`aggiunta alle pietre miliari %s %s` issues.add_project_at=`aggiunto questo al progetto %s %s` issues.change_milestone_at=`pietra miliare modificata da %s a %s %s` @@ -983,6 +1256,9 @@ issues.add_assignee_at=`è stato assegnato da %s %s` issues.remove_assignee_at=`è stato rimosso da %s %s` issues.remove_self_assignment=`Rimosso il loro incarico %s` issues.change_title_at=`Titolo modificato da %s a %s %s` +issues.change_ref_at=`ha cambiato il riferimento da %s a %s %s` +issues.remove_ref_at=`riferimento rimosso %s %s` +issues.add_ref_at=`aggiunto riferimento %s %s` issues.delete_branch_at=`branch %s eliminato %s` issues.filter_label=Etichetta issues.filter_label_exclude=`Usa alt + click/enter per escludere le etichette` @@ -1010,6 +1286,7 @@ issues.filter_sort.moststars=Più favoriti issues.filter_sort.feweststars=Meno favoriti issues.filter_sort.mostforks=Maggior numero di fork issues.filter_sort.fewestforks=Minor numero di fork +issues.keyword_search_unavailable=Attualmente la ricerca per parola chiave non è disponibile. Contatta l'amministratore del sito. issues.action_open=Aperto issues.action_close=Chiuso issues.action_label=Etichetta @@ -1018,19 +1295,28 @@ issues.action_milestone_no_select=Nessuna pietra miliare issues.action_assignee=Assegnatario issues.action_assignee_no_select=Nessun assegnatario issues.opened_by=aperto %[1]s da %[3]s +pulls.merged_by=di %[3]s è stato fuso %[1]s +pulls.merged_by_fake=di %[2]s è stato fuso %[1]s +issues.closed_by=di %[3]s è stato chiuso %[1]s +issues.opened_by_fake=aperto %[1]s da %[2]s +issues.closed_by_fake=di %[2]s è stato chiuso %[1]s issues.previous=Pagina precedente issues.next=Pagina successiva issues.open_title=Aperto issues.closed_title=Chiuso +issues.draft_title=Bozza issues.num_comments=%d commenti issues.commented_at=`%s ha commentato` issues.delete_comment_confirm=Sei sicuro/a di voler eliminare questo commento? issues.context.copy_link=Copia link issues.context.quote_reply=Quota risposta +issues.context.reference_issue=Fai riferimento in un nuovo problema issues.context.edit=Modifica issues.context.delete=Elimina issues.no_content=Non ci sono ancora contenuti. issues.close_issue=Chiudi +issues.pull_merged_at=`merged commit %[2]s in %[3]s %[4]s` +issues.manually_pull_merged_at=`merged commit %[2]s in %[3]s manualmente %[4]s` issues.close_comment_issue=Commenta e Chiudi issues.reopen_issue=Riapri issues.reopen_comment_issue=Commenta e Riapri @@ -1052,6 +1338,8 @@ issues.re_request_review=Revisione ri-richiesta issues.is_stale=Ci sono stati cambiamenti a questa PR da questa revisione issues.remove_request_review=Elimina richiesta revisione issues.remove_request_review_block=Impossibile rimuovere la richiesta di revisione +issues.dismiss_review=Respingi Recensione +issues.dismiss_review_warning=Sei sicuro di voler respingere questa recensione? issues.sign_in_require_desc=Effettua l'accesso per partecipare alla conversazione. issues.edit=Modifica issues.cancel=Annulla @@ -1095,13 +1383,21 @@ issues.lock.reason=Motivo per il blocco issues.lock.title=Blocca la conversazione su questa issue. issues.unlock.title=Sblocca la conversazione su questa issue. issues.comment_on_locked=Non puoi commentare un problema bloccato. +issues.delete=Elimina +issues.delete.title=Eliminare questo problema? +issues.delete.text=Vuoi davvero eliminare questo problema? (Questo rimuoverà permanentemente tutti i contenuti. Considera invece di chiuderlo, se vuoi tenerlo archiviato) issues.tracker=Cronografo +issues.start_tracking_short=Avvia timer issues.start_tracking=Avvia cronografo issues.start_tracking_history='ha iniziato a lavorare %s` issues.tracker_auto_close=Il timer verrà interrotto automaticamente una volta che il problema verrá chiuso +issues.tracking_already_started=`Hai già avviato il monitoraggio del tempo su un altro problema!` +issues.stop_tracking=Ferma timer issues.stop_tracking_history=`ha smesso di funzionare %s` +issues.cancel_tracking=Scarta issues.cancel_tracking_history=`ha cancellato il cronografo %s` issues.add_time=Aggiungi Tempo manualmente +issues.del_time=Elimina questo registro di tempo issues.add_time_short=Aggiungi tempo issues.add_time_cancel=Annulla issues.add_time_history=`aggiunto tempo trascorso %s` @@ -1117,6 +1413,7 @@ issues.error_modifying_due_date=Impossibile modificare la data di scadenza. issues.error_removing_due_date=Impossibile rimuovere la data di scadenza. issues.push_commit_1=aggiunto %d commit %s issues.push_commits_n=aggiunto %d commit %s +issues.force_push_codes=`force-pushed %[1]s from %[2]s to %[4]s %[6]s` issues.due_date_form=yyyy-mm-dd issues.due_date_form_add=Aggiungi data di scadenza issues.due_date_form_edit=Modifica @@ -1124,16 +1421,21 @@ issues.due_date_form_remove=Rimuovi issues.due_date_not_writer=E' necessario l'accesso di scrittura del repository per aggiornare la data di una sua issue. issues.due_date_not_set=Nessuna data di scadenza impostata. issues.due_date_added=la data di scadenza %s è stata aggiunta %s +issues.due_date_modified=ha modificato la data di scadenza da %[2]s a %[1]s %[3]s s issues.due_date_remove=rimossa la data di scadenza %s %s issues.due_date_overdue=Scaduto issues.due_date_invalid=La data di scadenza non è valida o fuori intervallo. Si prega di utilizzare il formato 'aaaa-mm-dd'. issues.dependency.title=Dipendenze +issues.dependency.issue_no_dependencies=Nessuna dipendenza impostata. +issues.dependency.pr_no_dependencies=Nessuna dipendenza impostata. issues.dependency.add=Aggiungi dipendenza… issues.dependency.cancel=Annulla issues.dependency.remove=Rimuovi issues.dependency.remove_info=Rimuovi questa dipendenza issues.dependency.added_dependency=`ha aggiunto una nuova dipendenza %s` issues.dependency.removed_dependency=`ha rimosso una dipendenza %s` +issues.dependency.pr_closing_blockedby=La chiusura di questa pull request è bloccata dai seguenti problemi +issues.dependency.issue_closing_blockedby=La chiusura di questo problema è bloccata dai seguenti problemi issues.dependency.issue_close_blocks=Questo problema impedisce la chiusura dei seguenti problemi issues.dependency.pr_close_blocks=Questa richiesta di pull impedisce la chiusura dei seguenti problemi issues.dependency.issue_close_blocked=Devi chiudere tutte le anomalie che bloiccano questo problema prima di chiudelo. @@ -1154,6 +1456,8 @@ issues.review.self.approval=Non puoi approvare la tua pull request. issues.review.self.rejection=Non puoi richiedere modifiche sulla tua pull request. issues.review.approve=hanno approvato queste modifiche %s issues.review.comment=revisionato %s +issues.review.dismissed=recensione %s di %s respinta +issues.review.dismissed_label=Respinta issues.review.left_comment=lascia un commento issues.review.content.empty=Devi lasciare un commento che indichi la modifica richiesta. issues.review.reject=richieste modifiche %s @@ -1162,8 +1466,10 @@ issues.review.add_review_request=recensione richiesta da %s %s issues.review.remove_review_request=ha rimosso la richiesta di revisione per %s %s issues.review.remove_review_request_self=ha rifiutato di rivedere %s issues.review.pending=In sospeso +issues.review.pending.tooltip=Questo commento non è attualmente visibile ad altri utenti. Per inviare i tuoi commenti in sospeso, seleziona '%s' -> '%s/%s/%s' nella parte superiore della pagina. issues.review.review=Revisiona issues.review.reviewers=Revisori +issues.review.outdated=Scaduto issues.review.show_outdated=Visualizza obsoleti issues.review.hide_outdated=Nascondere obsoleti issues.review.show_resolved=Mostra risolti @@ -1172,17 +1478,38 @@ issues.review.resolve_conversation=Risolvi la conversazione issues.review.un_resolve_conversation=Segnala la conversazione come non risolta issues.review.resolved_by=ha contrassegnato questa conversazione come risolta issues.assignee.error=Non tutte le assegnazioni sono state aggiunte a causa di un errore imprevisto. - +issues.reference_issue.body=Corpo +issues.content_history.deleted=eliminato +issues.content_history.edited=modificato +issues.content_history.created=creato +issues.content_history.delete_from_history=Elimina dalla cronologia +issues.content_history.delete_from_history_confirm=Eliminare dalla cronologia? +issues.content_history.options=Opzioni +issues.reference_link=Riferimento: %s + +compare.compare_base=base +compare.compare_head=confronta pulls.desc=Attiva pull request e revisioni di codice. pulls.new=Nuova Pull Request +pulls.view=Visualizza Pull Request pulls.compare_changes=Nuova Pull Request +pulls.allow_edits_from_maintainers=Consenti modifiche dai manutentori +pulls.allow_edits_from_maintainers_desc=Gli utenti con accesso in scrittura al ramo base possono anche inviare a questo ramo +pulls.allow_edits_from_maintainers_err=Aggiornamento non riuscito pulls.compare_changes_desc=Selezione il branch su cui eseguire il merge e il branch da cui eseguire il pull. +pulls.has_viewed_file=Visualizzato +pulls.has_changed_since_last_review=Modificato dalla tua ultima recensione +pulls.viewed_files_label=%[1]d / %[2]d file visti pulls.compare_base=unisci a pulls.compare_compare=esegui un pull da +pulls.switch_comparison_type=Cambia tipo di confronto +pulls.switch_head_and_base=Testa e base di commutazione pulls.filter_branch=Filtra branch pulls.no_results=Nessun risultato trovato. pulls.nothing_to_compare=Questi rami sono uguali. Non c'è alcuna necessità di creare una pull request. +pulls.nothing_to_compare_and_allow_empty_pr=Questi rami sono uguali. Questa PR sarà vuota. +pulls.has_pull_request=`Una pull request tra questi rami esiste già: %[2]s#%[3]d` pulls.create=Crea Pull Request pulls.title_desc=vorrebbe unire %[1]d commit da %[2]s a %[3]s pulls.merged_title_desc=ha unito %[1]d commit da %[2]s a %[3]s %[4]s @@ -1195,18 +1522,28 @@ pulls.cant_reopen_deleted_branch=Questa pull request non può essere riaperta pe pulls.merged=Unito pulls.merged_as=La pull request è stata unita come %[2]s. pulls.manually_merged=Unito manualmente +pulls.manually_merged_as=La pull request è stata unita manualmente come %[2]s. pulls.is_closed=La pull request è stata chiusa. pulls.has_merged=La pull request è stata unita. pulls.title_wip_desc=`Inizia il titolo con %s per evitare che la pull request venga unita accidentalmente.` +pulls.cannot_merge_work_in_progress=Questa pull request è contrassegnata come un lavoro in corso. +pulls.still_in_progress=Ancora in corso? +pulls.add_prefix=Aggiungi prefisso %s +pulls.remove_prefix=Rimuovi il prefisso %s pulls.data_broken=Questa pull request è rovinata a causa di informazioni mancanti del fork. pulls.files_conflicted=Questa pull request ha modifiche in conflitto con il branch di destinazione. pulls.is_checking=Verifica dei conflitti di merge in corso. Riprova tra qualche istante. +pulls.is_ancestor=Questo ramo è già incluso nel ramo di destinazione. Non c'è nulla da unire. +pulls.is_empty=Le modifiche di questo ramo sono già nel ramo di destinazione. Questo sarà un commit vuoto. pulls.required_status_check_failed=Alcuni controlli richiesti non hanno avuto successo. pulls.required_status_check_missing=Mancano alcuni controlli richiesti. pulls.required_status_check_administrator=Come amministratore, puoi ancora unire questa pull request. pulls.blocked_by_approvals=La richiesta Pull non ha abbastanza approvazioni. %d di %d approvazioni concesse. pulls.blocked_by_rejection=Questa Pull Request ha delle modifiche richieste da un revisore. +pulls.blocked_by_official_review_requests=Questa richiesta Pull ha richieste di recensione ufficiale. pulls.blocked_by_outdated_branch=Questa Pull Request è bloccata perché obsoleta. +pulls.blocked_by_changed_protected_files_1=Questa richiesta Pull è bloccata perché modifica un file protetto: +pulls.blocked_by_changed_protected_files_n=Questa richiesta Pull è bloccata perché modifica file protetti: pulls.can_auto_merge_desc=La pull request può essere unita automaticamente. pulls.cannot_auto_merge_desc=Questa pull request non può essere unita automaticamente a causa di conflitti. pulls.cannot_auto_merge_helper=Unire manualmente per risolvere i conflitti. @@ -1218,19 +1555,33 @@ pulls.reject_count_1=%d richiesta di cambiamento pulls.reject_count_n=%d richieste di cambiamento pulls.waiting_count_1=%d in attesa di revisione pulls.waiting_count_n=%d in attesa di revisione +pulls.wrong_commit_id=l'id del commit deve essere un id del commit nel branch di destinazione pulls.no_merge_desc=Questa pull request non può essere unita perché tutte le opzioni di merge del repository sono disattivate. pulls.no_merge_helper=Attiva le opzioni di merge nelle impostazioni del repository o unisci la pull request manualmente. pulls.no_merge_wip=Questa pull request non può essere unita perché è contrassegnata come un lavoro in corso. pulls.no_merge_not_ready=Questa pull request non è pronta per il merge, controlla lo stato della revisione e i controlli di stato. pulls.no_merge_access=Non sei autorizzato ad effettuare il merge su questa pull request. +pulls.merge_pull_request=Crea commit unito +pulls.rebase_merge_pull_request=Ricostruisci poi manda avanti +pulls.rebase_merge_commit_pull_request=Ricostruisci quindi crea commit unito +pulls.squash_merge_pull_request=Crea commit mescolato +pulls.merge_manually=Unito manualmente +pulls.merge_commit_id=L'ID del commit di merge pulls.require_signed_wont_sign=Il branch richiede commit firmati ma questo merge non verrà firmato pulls.invalid_merge_option=Non puoi utilizzare questa opzione di merge per questa pull request. +pulls.merge_conflict=Unione non riuscita: C'è stato un conflitto durante l'operazione. Suggerimento: Prova una strategia diversa pulls.merge_conflict_summary=Messaggio d'errore +pulls.rebase_conflict=Merge non riuscito: c'è stato un conflitto durante il rebase dell'commit: %[1]s. Suggerimento: Prova una strategia diversa +pulls.rebase_conflict_summary=Messaggio d'Errore ; %[2]s
%[3]s
pulls.unrelated_histories=Unione fallita: gli Head del ramo da unire e la base non condividono una storia cronologica in comune. Suggerimento: prova una strategia diversa pulls.merge_out_of_date=Unione fallita: Durante la generazione del merge, la base è stata aggiornata. Suggerimento: Riprova. +pulls.head_out_of_date=Unione non riuscita: durante la generazione della fusione, la testa è stata aggiornata. Suggerimento: Riprova. +pulls.push_rejected=Unisci non riuscito: il push è stato rifiutato. Rivedi gli Hooks Git per questo repository. +pulls.push_rejected_summary=Messaggio Di Rifiuto Completo +pulls.push_rejected_no_message=Unione non riuscita: il push è stato rifiutato ma non c'è stato un messaggio remoto.
Controlla gli Hooks di Git per questo repository pulls.open_unmerged_pull_exists=`Non è possibile riaprire questa pull request perché ne esiste un'altra (#%d) con proprietà identiche.` pulls.status_checking=Alcuni controlli sono in sospeso pulls.status_checks_success=Tutti i controlli sono stati effettuati con successo @@ -1239,13 +1590,31 @@ pulls.status_checks_failure=Alcuni controlli sono falliti pulls.status_checks_error=Alcuni controlli hanno segnalato errori pulls.status_checks_requested=Richiesto pulls.status_checks_details=Dettagli +pulls.update_branch=Aggiorna il ramo tramite merge +pulls.update_branch_rebase=Aggiorna il ramo per cambio base pulls.update_branch_success=Brench aggiornato con successo pulls.update_not_allowed=Non sei abilitato ad aggiornare il branch pulls.outdated_with_base_branch=Questo brench non è aggiornato con il branch di base +pulls.closed_at=`chiusa questa pull request %[2]s` +pulls.reopened_at=`riaperta questa pull request %[2]s` +pulls.merge_instruction_hint=`Puoi anche visualizzare le istruzioni da riga di comando.` +pulls.merge_instruction_step1_desc=Dal repository del tuo progetto, fai il check out di un nuovo branch e verifica le modifiche. +pulls.merge_instruction_step2_desc=Fai il merge delle modifiche e aggiorna su Gitea. +pulls.auto_merge_button_when_succeed=(Quando i controlli sono superati) +pulls.auto_merge_when_succeed=Unione automatica quando tutti i controlli sono superati +pulls.auto_merge_newly_scheduled=La pull request era programmata per unire quando tutti i controlli sono superati. +pulls.auto_merge_has_pending_schedule=%[1]s ha programmato questa pull request per unire automaticamente quando tutti i controlli hanno successo %[2]s. +pulls.auto_merge_cancel_schedule=Annulla fusione automatica +pulls.auto_merge_not_scheduled=Questa pull request non è programmata per la fusione automarica. +pulls.auto_merge_canceled_schedule=L'unione automatica è stata annullata per questa richiesta di pull. +pulls.auto_merge_newly_scheduled_comment=`ha programmato questa pull request per unire automaticamente quando tutti i controlli sono superati %[1]s` +pulls.auto_merge_canceled_schedule_comment=`cancella l'auto-merging di questa pull request quando tutti i testi sono superati %[1]s` +pulls.delete.title=Eliminare questa pull request? +pulls.delete.text=Vuoi davvero eliminare questo problema? (Questo rimuoverà permanentemente tutti i contenuti. Considera invece di chiuderlo, se vuoi tenerlo archiviato) milestones.new=Nuova Milestone milestones.closed=Chiuso %s @@ -1291,6 +1660,7 @@ signing.wont_sign.commitssigned=Questo merge non sarà firmato poiché i commit signing.wont_sign.approved=Il merge non sarà firmato poiché il PR non è approvato signing.wont_sign.not_signed_in=Non hai effettuato l'accesso +ext_wiki=Accesso al Wiki esterno ext_wiki.desc=Collegamento a una wiki esterna. wiki=Wiki @@ -1315,6 +1685,7 @@ wiki.page_already_exists=Esiste già una pagina Wiki con questo stesso nome. wiki.reserved_page=Il nome della pagina wiki '%s' è riservato. wiki.pages=Pagine wiki.last_updated=Ultimo aggiornamento: %s +wiki.page_name_desc=Inserisci un nome per questa pagina Wiki. Alcuni nomi speciali sono: 'Home', '_Sidebar' e '_Footer'. activity=Attività activity.period.filter_label=Periodo: @@ -1385,7 +1756,10 @@ activity.git_stats_deletion_n=%d cancellazioni search=Ricerca search.search_repo=Ricerca repository search.fuzzy=Fuzzy +search.match=Corrispondenze search.results=Risultati della ricerca per "%s" in %s +search.code_no_results=Nessun codice sorgente corrispondente al termine di ricerca trovato. +search.code_search_unavailable=Attualmente la ricerca di codice non è disponibile. Contatta l'amministratore del sito. settings=Impostazioni settings.desc=Impostazioni ti permette di gestire le impostazioni del repository @@ -1400,14 +1774,20 @@ settings.hooks=Webhooks settings.githooks=Git Hooks settings.basic_settings=Impostazioni di Base settings.mirror_settings=Impostazioni di mirror +settings.mirror_settings.docs=Configura il tuo progetto per inviare e/o ritirare automaticamente le modifiche a/da un altro repository. I rami, i tag e i commit verranno sincronizzati automaticamente. Come faccio i repository mirror? +settings.mirror_settings.mirrored_repository=Repository replicata +settings.mirror_settings.direction=Direzione +settings.mirror_settings.direction.pull=Tira +settings.mirror_settings.direction.push=Push +settings.mirror_settings.last_update=Ultimo aggiornamento +settings.mirror_settings.push_mirror.none=Nessun mirror push configurato +settings.mirror_settings.push_mirror.remote_url=Url Del Repository Remoto Git +settings.mirror_settings.push_mirror.add=Aggiungi Push Mirror settings.sync_mirror=Sincronizza ora settings.mirror_sync_in_progress=Sincronizzazione del mirror in corso. Torna tra qualche minuto. -settings.email_notifications.enable=Abilita Notifiche Email -settings.email_notifications.onmention=Solo email su Menzione -settings.email_notifications.disable=Disabilita notifiche email -settings.email_notifications.submit=Imposta Preferenze Email settings.site=Sito web settings.update_settings=Aggiorna Impostazioni +settings.branches.update_default_branch=Aggiorna Ramo Predefinito settings.advanced_settings=Opzioni avanzate settings.wiki_desc=Abilita Wiki Repository settings.use_internal_wiki=Utilizza la wiki incorporata @@ -1426,6 +1806,9 @@ settings.tracker_url_format_error=L'URL del tracker di problemi esterno non è u settings.tracker_issue_style=Formato numerico del tracciatore di issue esterno settings.tracker_issue_style.numeric=Numerico settings.tracker_issue_style.alphanumeric=Alfanumerico +settings.tracker_issue_style.regexp=Espressione Regolare +settings.tracker_issue_style.regexp_pattern=Motivo Espressione Regolare +settings.tracker_issue_style.regexp_pattern_desc=Il primo gruppo catturato verrà utilizzato al posto di {index}. settings.tracker_url_format_desc=Usa i segnaposto {user}, {repo} e {index} per il nome utente, il nome del repository e l'indice delle issue. settings.enable_timetracker=Abilita il cronografo settings.allow_only_contributors_to_track_time=Consenti soltanto ai contributori di utilizzare il cronografo @@ -1435,8 +1818,20 @@ settings.pulls.allow_merge_commits=Abilita il merging dei commit settings.pulls.allow_rebase_merge=Abilita l'unione dei commit mediante riassegnazione settings.pulls.allow_rebase_merge_commit=Abilita il rebase con commit ad unione esplicita (--no-ff) settings.pulls.allow_squash_commits=Abilita lo Squashing per unire i commits via merge +settings.pulls.allow_manual_merge=Abilita Mark PR come unito manualmente +settings.pulls.enable_autodetect_manual_merge=Abilita il rilevamento automatico della fusione manuale (Nota: in alcuni casi speciali possono verificarsi errori) +settings.pulls.allow_rebase_update=Abilita l'aggiornamento del ramo pull request per rebase +settings.pulls.default_delete_branch_after_merge=Elimina il ramo pull request dopo la fusione per impostazione predefinita +settings.packages_desc=Abilita Il Registro Dei Pacchetti Repository +settings.projects_desc=Abilita Progetti Repository settings.admin_settings=Impostazioni amministratore settings.admin_enable_health_check=Abilita verifica dell'integrità del repository (git fsck) +settings.admin_code_indexer=Indicizzatore del codice +settings.admin_stats_indexer=Indicizzatore di statistiche del codice +settings.admin_indexer_commit_sha=Hash SHA dell'ultimo commit indicizzato +settings.admin_indexer_unindexed=Non indicizzato +settings.reindex_button=Aggiungi alla coda di re-indicizzazione +settings.reindex_requested=Re-indicizzazione richiesta settings.admin_enable_close_issues_via_commit_in_any_branch=Chiudi un issue tramite un commit eseguito in un branch non predefinito settings.danger_zone=Zona Pericolosa settings.new_owner_has_same_repo=Il nuovo proprietario ha già un repository con lo stesso nome. Per favore scegli un altro nome. @@ -1451,11 +1846,20 @@ settings.convert_fork_notices_1=Questa operazione convertirà il fork in un norm settings.convert_fork_confirm=Converti Repository settings.convert_fork_succeed=Il fork è stato convertito in un repository regolare. settings.transfer=Trasferisci proprietà +settings.transfer.rejected=Il trasferimento del repository è stato rifiutato. +settings.transfer.success=Il trasferimento del repository è andato a buon fine. +settings.transfer_abort=Annulla trasferimento +settings.transfer_abort_invalid=Non è possibile annullare un trasferimento di repository non esistente. +settings.transfer_abort_success=Il trasferimento del repository a %s è stato annullato con successo. settings.transfer_desc=Trasferisci questo repository a un altro utente o a un'organizzazione nella quale hai diritti d'amministratore. settings.transfer_form_title=Inserisci il nome del repository come conferma: +settings.transfer_in_progress=Al momento c'è un trasferimento in corso. Si prega di annullarlo se si desidera trasferire questo repository a un altro utente. settings.transfer_notices_1=-Si perderà l'accesso al repository se lo si trasferisce ad un utente singolo. settings.transfer_notices_2=-Si manterrà l'accesso al repository se si trasferisce in un'organizzazione che possiedi (o condividi con qualcun'altro). +settings.transfer_notices_3=- Se il repository è privato e viene trasferito a un singolo utente, questa azione si assicura che l'utente abbia almeno i permessi di lettura (e le modifiche se necessario). settings.transfer_owner=Nuovo Proprietario +settings.transfer_perform=Esegui trasferimento +settings.transfer_started=Questo repository è stato contrassegnato per il trasferimento e attende conferma da "%s" settings.transfer_succeed=Il repository è stato trasferito. settings.signing_settings=Impostazioni Verifica Firma settings.trust_model=Modello di Fiducia per la Firma @@ -1465,6 +1869,11 @@ settings.trust_model.collaborator=Collaboratore settings.trust_model.collaborator.long=Collaboratore: Firme di fiducia da parte dei collaboratori settings.trust_model.collaborator.desc=Le firme valide da parte dei collaboratori di questo repository saranno contrassegnate con "trusted" (sia che corrispondano al committer o meno). Altrimenti, le firme valide saranno contrassegnate con "untrusted" se la firma corrisponde al committer e "unmatched" se non. settings.trust_model.committer=Committer +settings.trust_model.committer.long=Committer: firme affidabili che corrispondono ai committer (questo corrisponde a GitHub e costringerà i commit firmati di Gitea ad avere Gitea come committer) +settings.trust_model.committer.desc=Le firme valide saranno contrassegnate come "fidate" se corrispondono al committente, altrimenti saranno contrassegnate come "non corrispondono". Questo costringerà Gitea ad essere il committer dei commit firmati con l'effettivo committer contrassegnato come Co-Authored-By: e Co-Committed-By: nel commit. La chiave Gitea predefinita deve corrispondere a un utente nel database. +settings.trust_model.collaboratorcommitter=Collaboratore+Committer +settings.trust_model.collaboratorcommitter.long=Collaboratore+Committer: Firme di fiducia da parte dei collaboratori che corrispondono al committer +settings.trust_model.collaboratorcommitter.desc=Le firme valide da parte dei collaboratori di questa repository saranno contrassegnate "fidate" se corrispondono al committer. Altrimenti le firme saranno contrassegnate con "untrusted" se la firma corrisponde al committer non corrisponde. Questo costringerà Gitea a essere contrassegnato come committer su impegni firmati con l'effettivo committer contrassegnato come Co-Authored-By: e Co-Committed-By: nel commit. La chiave Gitea predefinita deve corrispondere a un utente nel database. settings.wiki_delete=Elimina dati Wiki settings.wiki_delete_desc=L'eliminazione dei dati della wiki del repository è permanente e non può essere annullata. settings.wiki_delete_notices_1=-Questa operazione eliminerà permanentemente e disabiliterà la wiki repository per %s. @@ -1495,6 +1904,8 @@ settings.add_team=Aggiungi Squadra settings.add_team_duplicate=Il team ha già il repository settings.add_team_success=Il team ha ora accesso al repository. settings.search_team=Cerca Squadra… +settings.change_team_permission_tip=Il permesso del team è impostato sulla pagina delle impostazioni del team e non può essere modificato per repository +settings.delete_team_tip=Questo team ha accesso a tutte le repository e non può essere rimosso settings.remove_team_success=L'accesso del team al repository è stato rimosso. settings.add_webhook=Aggiungi Webhook settings.add_webhook.invalid_channel_name=Il canale Webhook non può essere vuoto e contenere solo un # carattere. @@ -1509,6 +1920,9 @@ settings.webhook.response=Risposta settings.webhook.headers=Intestazioni settings.webhook.payload=Contenuto settings.webhook.body=Corpo +settings.webhook.replay.description=Riproduci questo webhook. +settings.webhook.delivery.success=Un evento è stato aggiunto alla coda di consegna. Potrebbe volerci qualche secondo prima che venga visualizzato nella cronologia delle consegne. +settings.githooks_desc=Git Hooks è alimentato da Git stesso. È possibile modificare i file hook qui sotto per impostare operazioni personalizzate. settings.githook_edit_desc=Se l'hook è inattivo, sarà presentato un contenuto esempio. Lasciando il contenuto vuoto disattiverai questo hook. settings.githook_name=Nome hook settings.githook_content=Contenuto hook @@ -1520,6 +1934,7 @@ settings.content_type=Tipo di contenuto POST settings.secret=Segreto settings.slack_username=Nome utente settings.slack_icon_url=URL icona +settings.slack_color=Colore settings.discord_username=Nome utente settings.discord_icon_url=URL icona settings.event_desc=Attivato su: @@ -1539,11 +1954,15 @@ settings.event_push=Push settings.event_push_desc=Git push in un repository. settings.event_repository=Repository settings.event_repository_desc=Repository creato o eliminato. +settings.event_header_issue=Eventi dei Problemi settings.event_issues=Issues settings.event_issues_desc=Issue aperto, chiuso, riaperto o modificato. settings.event_issue_assign=Issue Assegnato settings.event_issue_assign_desc=Issue assegnata o non assegnata. settings.event_issue_label=Issue etichettato +settings.event_issue_label_desc=Etichette dei Problemi aggiornate o cancellate. +settings.event_issue_milestone=Obiettivo Raggiunto +settings.event_issue_milestone_desc=Obiettivo raggiunto o abbandonato. settings.event_issue_comment=Commento Issue settings.event_issue_comment_desc=Commento issue creato, modificato o rimosso. settings.event_header_pull_request=Eventi di Pull Request @@ -1553,8 +1972,18 @@ settings.event_pull_request_assign=Pull Request assegnata settings.event_pull_request_assign_desc=Pull request assegnata o non assegnata. settings.event_pull_request_label=Pull Request etichettata settings.event_pull_request_label_desc=Etichette Pull request aggiornate o cancellate. +settings.event_pull_request_milestone=Pull Request raggiunta +settings.event_pull_request_milestone_desc=Pull request raggiunto o abbandonato. +settings.event_pull_request_comment=Commento su questa richiesta di pull +settings.event_pull_request_comment_desc=Commento della Pull request creato, modificato o cancellato. +settings.event_pull_request_review=Pull Request Revisionata +settings.event_pull_request_review_desc=Pull request approvata, respinta o recensione commento. +settings.event_pull_request_sync=Richiesta Pull Sincronizzata settings.event_pull_request_sync_desc=Pull request sincronizzata. +settings.event_package=Pacchetto +settings.event_package_desc=Pacchetto creato o eliminato in un repository. settings.branch_filter=Filtro branch +settings.branch_filter_desc=Whitelist dei rami per gli eventi di spinta, creazione dei rami e cancellazione dei rami, specificati come modello globo. Se vuoto o *, gli eventi per tutti i rami sono segnalati. Vedi la documentazione github.com/gobwas/glob per la sintassi. Esempi: master, {master,release*}. settings.active=Attivo settings.active_helper=Le informazioni sugli eventi innescati saranno inviate a questo URL del webhook. settings.add_hook_success=Il webhook è stato aggiunto. @@ -1566,6 +1995,23 @@ settings.hook_type=Tipo di Hook settings.slack_token=Gettone settings.slack_domain=Dominio settings.slack_channel=Canale +settings.add_web_hook_desc=Integra %s nel tuo repository. +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_telegram=Telegram +settings.web_hook_name_matrix=Matrix +settings.web_hook_name_msteams=Microsoft Teams +settings.web_hook_name_feishu_or_larksuite=Feishu / Lark Suite +settings.web_hook_name_feishu=Feishu +settings.web_hook_name_larksuite=Lark Suite +settings.web_hook_name_wechatwork=WeCom (Wechat Work) +settings.web_hook_name_packagist=Packagist +settings.packagist_username=Nome utente Packagist +settings.packagist_api_token=API token +settings.packagist_package_url=Url pacchetto pacchetti settings.deploy_keys=Dispiega Chiavi settings.add_deploy_key=Aggiungi Deploy Key settings.deploy_key_desc=Le deploy key possiedono l'accesso solamente alla lettura di un repository. @@ -1604,6 +2050,7 @@ settings.protect_merge_whitelist_committers_desc=Consentire soltanto agli utenti settings.protect_merge_whitelist_users=Utenti nella whitelist per il merging: settings.protect_merge_whitelist_teams=Team nella whitelist per il merging: settings.protect_check_status_contexts=Abilita Controllo Stato +settings.protect_check_status_contexts_desc=Richiedi il superamento di controlli di stato prima dell'unione di due rami. Scegliere quali controlli di stato devono passare prima che i rami possano essere uniti in un ramo che corrisponde a questa regola. Se abilitato, i commit devono prima essere inviati a un altro ramo, quindi uniti o pushati direttamente a un ramo che corrisponde a questa regola dopo aver superato i controlli di stato. Se non viene selezionato alcuna regola, l'ultimo commit avrá successo indipendentemente dal contesto. settings.protect_check_status_contexts_list=Controlli di stato trovati nell'ultima settimana per questo repository settings.protect_required_approvals=Approvazioni richieste: settings.protect_required_approvals_desc=Permetti solo di unire la richiesta pull con abbastanza recensioni positive. @@ -1614,6 +2061,11 @@ settings.protect_approvals_whitelist_teams=Team nella whitelist per le revisioni settings.dismiss_stale_approvals=Ignora impostazione vecchie settings.dismiss_stale_approvals_desc=Quando i nuovi commit che cambiano il contenuto della pull request vengono pushati nel branch, le vecchie approvazioni verranno eliminate. settings.require_signed_commits=Richiede commit firmati +settings.require_signed_commits_desc=Rifiuta i push a questo ramo se non sono firmati o verificabili. +settings.protect_protected_file_patterns=Modelli di file protetti (separati da punto e virgola '\;'): +settings.protect_protected_file_patterns_desc=File protetti che non possono essere modificati direttamente anche se l'utente ha i diritti di aggiungere, modificare o eliminare file in questo ramo. I modelli multipli possono essere separati usando il punto e virgola ('\;'). Vedi la documentazione github.com/gobwas/glob per la sintassi del modello. Esempi: .drone.yml, /docs/**/*.txt. +settings.protect_unprotected_file_patterns=Modelli di file non protetti (separati da punto e virgola '\;'): +settings.protect_unprotected_file_patterns_desc=File non protetti che possono essere modificati direttamente se l'utente ha accesso in scrittura, bypassando la restrizione push. Più modelli possono essere separati usando il punto e virgola ('\;'). Vedi la documentazione github.com/gobwas/glob per la sintassi del modello. Esempi: .drone.yml, /docs/**/*.txt. settings.add_protected_branch=Attiva protezione settings.delete_protected_branch=Disattiva protezione settings.update_protect_branch_success=La protezione branch per il branch '%s' è stata aggiornata. @@ -1622,11 +2074,26 @@ settings.protected_branch_deletion=Disattiva protezione branch settings.protected_branch_deletion_desc=Disattivare la protezione branch permette agli utenti con permesso di scrittura di pushare sul branch. Continuare? settings.block_rejected_reviews=Blocca il merge di revisioni rifiutate settings.block_rejected_reviews_desc=Il merge non sarà possibile quando sono richiesti cambiamenti da revisori, anche se ci sono sufficienti approvazioni. +settings.block_on_official_review_requests=Blocca il merge sulle richieste ufficiali di revisione +settings.block_on_official_review_requests_desc=Il merge non sarà possibile quando sono richiesti cambiamenti da revisori, anche se ci sono sufficienti approvazioni. +settings.block_outdated_branch=Blocca il merge se la pull request è obsoleta +settings.block_outdated_branch_desc=Il merging non sarà possibile quando il ramo testa è dietro il ramo base. settings.default_branch_desc=Seleziona un branch del repository predefinito per le pull request ed i commit di codice: +settings.default_merge_style_desc=Modalità di merge predefinita per le richieste di pull: settings.choose_branch=Scegli un branch… settings.no_protected_branch=Non ci sono branch protetti. settings.edit_protected_branch=Modifica settings.protected_branch_required_approvals_min=Le autorizzazioni richieste non possono essere negative. +settings.tags=Etichette +settings.tags.protection=Protezione Etichetta +settings.tags.protection.pattern=Sequenza Etichetta +settings.tags.protection.allowed=Consentito +settings.tags.protection.allowed.users=Utenti ammessi +settings.tags.protection.allowed.teams=Squadre ammesse +settings.tags.protection.allowed.noone=Nessuno +settings.tags.protection.create=Proteggi Etichetta +settings.tags.protection.none=Non ci sono etichette protette. +settings.tags.protection.pattern.description=È possibile utilizzare un singolo nome o un modello globo o un'espressione regolare per abbinare più tag. Leggi di più nella guida per i tag protetti. settings.bot_token=Token del Bot settings.chat_id=ID chat settings.matrix.homeserver_url=URL Homeserver @@ -1640,6 +2107,7 @@ settings.archive.success=Il repo è stato archiviato con successo. settings.archive.error=Si è verificato un errore durante il tentativo di archiviare il repo. Vedi il log per maggiori dettagli. settings.archive.error_ismirror=Non puoi archiviare un mirror repo. settings.archive.branchsettings_unavailable=Le impostazioni dei branch non sono disponibili se il repo è archiviato. +settings.archive.tagsettings_unavailable=Le impostazioni delle etichette non sono disponibili se il repo è archiviato. settings.unarchive.button=Non archiviare Repo settings.unarchive.header=Non archiviare questo Repo settings.unarchive.text=Dis-Archiviare la repository ripristinerà la sua capacità di ricevere commit e push, così come la creazione di nuovi problemi e richieste di pull. @@ -1671,6 +2139,12 @@ settings.lfs_pointers.inRepo=Nel repo settings.lfs_pointers.exists=Esiste nel negozio settings.lfs_pointers.accessible=Accessibile all'utente settings.lfs_pointers.associateAccessible=Associa %d OID accessibili +settings.rename_branch_failed_exist=Impossibile rinominare il ramo perché il ramo di destinazione %s esiste. +settings.rename_branch_failed_not_exist=Impossibile rinominare il ramo %s perché non esiste. +settings.rename_branch_success=Il ramo %s è stato rinominato con successo in %s. +settings.rename_branch_from=vecchio nome del ramo +settings.rename_branch_to=nuovo nome del ramo +settings.rename_branch=Rinomina ramo diff.browse_source=Sfoglia il codice sorgente diff.parent=parent @@ -1699,6 +2173,12 @@ diff.file_image_width=Larghezza diff.file_image_height=Altezza diff.file_byte_size=Dimensione diff.file_suppressed=File diff soppresso perché troppo grande +diff.file_suppressed_line_too_long=File diff soppresso perché una o più righe sono troppo lunghe +diff.too_many_files=Alcuni file non sono stati mostrati perché troppi file sono cambiati in questo diff +diff.show_more=Mostra Altro +diff.load=Carica Diff +diff.generated=generato +diff.vendored=esterno diff.comment.placeholder=Lascia un commento diff.comment.markdown_info=Lo stile con markdown è supportato. diff.comment.add_single_comment=Aggiungi un commento singolo @@ -1713,16 +2193,23 @@ diff.review.approve=Approva diff.review.reject=Richiedi cambiamenti diff.committed_by=committato da diff.protected=Protetto +diff.image.side_by_side=A fianco +diff.image.swipe=Scorri +diff.image.overlay=Sovrapposta +diff.has_escaped=Questa riga ha caratteri Unicode nascosti releases.desc=Tenere traccia di versioni e download del progetto. release.releases=Rilasci release.detail=Dettagli rilascio +release.tags=Etichette release.new_release=Nuovo Rilascio release.draft=Bozza release.prerelease=Pre-Rilascio release.stable=Stabile release.compare=Confronta release.edit=modifica +release.ahead.commits=%d commit +release.ahead.target=a %s da questa uscita release.source_code=Codice Sorgente release.new_subheader=Le release organizzano le versioni del progetto. release.edit_subheader=Le release organizzano le versioni del progetto. @@ -1738,13 +2225,20 @@ release.publish=Pubblica Rilascio release.save_draft=Salva Bozza release.edit_release=Aggiorna release release.delete_release=Elimina release +release.delete_tag=Elimina Etichetta release.deletion=Elimina release +release.deletion_desc=L'eliminazione di una release lo rimuove solo da Gitea. Il tag Git, i contenuti del repository e la cronologia rimangono invariati. Continuare? release.deletion_success=La release è stata eliminata. +release.deletion_tag_desc=Eliminerà questo tag dal repository. I contenuti del repository e la cronologia rimangono invariati. Continuare? release.deletion_tag_success=L'etichetta è stata eliminata. release.tag_name_already_exist=Una release con questo nome tag esiste già. release.tag_name_invalid=Il nome tag non è valido. +release.tag_name_protected=Il nome dell'etichetta è protetto. +release.tag_already_exist=Questo nome tag esiste già. release.downloads=Download release.download_count=Scarica: %s +release.add_tag_msg=Utilizzare il titolo e il contenuto del rilascio come messaggio di tag. +release.add_tag=Crea Solo Branch branch.name=Nome branch branch.search=Cerca branch @@ -1766,19 +2260,36 @@ branch.deleted_by=Eliminato da %s branch.restore_success=Il branch '%s' è stato ripristinato. branch.restore_failed=Impossibile ripristinare il branch '%s '. branch.protected_deletion_failed=Il branch '%s' è protetto. Non può essere eliminato. +branch.default_deletion_failed=Il branch '%s' è protetto. Non può essere eliminato. branch.restore=Ripristina Branch '%s' branch.download=Scarica Branch '%s' branch.included_desc=Questo ramo fa parte del ramo predefinito branch.included=Incluso +branch.create_new_branch=Crea un ramo dal ramo: +branch.confirm_create_branch=Crea ramo +branch.create_branch_operation=Crea ramo +branch.new_branch=Crea nuovo ramo +branch.new_branch_from=Crea un nuovo ramo da '%s' +branch.renamed=Il ramo %s è stato rinominato in %s. +tag.create_tag=Crea branch %s +tag.create_tag_operation=Crea etichetta +tag.confirm_create_tag=Crea etichetta +tag.create_tag_from=Crea un nuovo tag da '%s' +tag.create_success=Il branch '%s' è stato creato. topic.manage_topics=Gestisci argomenti topic.done=Fatto topic.count_prompt=Non puoi selezionare più di 25 argomenti topic.format_prompt=Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri. +find_file.go_to_file=Vai al file +find_file.no_matching=Nessun file corrispondente trovato +error.csv.too_large=Impossibile visualizzare questo file perché è troppo grande. +error.csv.unexpected=Impossibile visualizzare questo file perché contiene un carattere inatteso alla riga %d e alla colonna %d. +error.csv.invalid_field_count=Impossibile visualizzare questo file perché ha un numero errato di campi alla riga %d. [org] org_name_holder=Nome dell'Organizzazione @@ -1823,6 +2334,7 @@ settings.visibility.private_shortname=Privato settings.update_settings=Aggiorna Impostazioni settings.update_setting_success=Le impostazioni dell'organizzazione sono state aggiornate. settings.change_orgname_prompt=Nota: cambiare il nome dell'organizzazione cambia anche il relativo URL. +settings.change_orgname_redirect_prompt=Il vecchio nome reindirizzerà fino a quando non sarà richiesto. settings.update_avatar_success=L'avatar dell'organizzazione è stato aggiornato. settings.delete=Elimina organizzazione settings.delete_account=Elimina questa organizzazione @@ -1832,6 +2344,7 @@ settings.delete_org_title=Elimina organizzazione settings.delete_org_desc=Questa organizzazione verrà eliminata definitivamente. Continuare? settings.hooks_desc=Aggiungi i webhooks che verranno attivati per tutti i repository sotto questa organizzazione. +settings.labels_desc=Aggiungi i webhooks che verranno attivati per tutti i repository sotto questa organizzazione. members.membership_visibility=Visibilità appartenenza: members.public=Visibile @@ -1842,15 +2355,24 @@ members.member_role=Ruolo del membro: members.owner=Proprietario members.member=Membro members.remove=Rimuovi +members.remove.detail=Rimuovere %[1]s dalla %[2]s? members.leave=Abbandona +members.leave.detail=Lasciare %s? members.invite_desc=Aggiungi un nuovo membro a %s: members.invite_now=Invita ora teams.join=Iscriviti teams.leave=Abbandona +teams.leave.detail=Lasciare %s? teams.can_create_org_repo=Crea repository teams.can_create_org_repo_helper=I membri possono creare nuovi repository nell'organizzazione. Il creatore otterrà l'accesso di amministratore alla nuova repository. +teams.none_access=Nessun Accesso +teams.none_access_helper=I membri non possono visualizzare o fare altre azioni su questa unità. +teams.general_access=Accesso Generale +teams.general_access_helper=I permessi dei membri saranno decisi dalla seguente tabella dei permessi. +teams.read_access=Lettura teams.read_access_helper=I membri possono visualizzare e clonare i repository del team. +teams.write_access=Scrittura teams.write_access_helper=I membri possono leggere e pushare sui repository del team. teams.admin_access=Accesso amministratore teams.admin_access_helper=I membri possono pullare e pushare sulle repository del team e anche aggiungere collaboratori. @@ -1891,6 +2413,7 @@ dashboard=Pannello di Controllo users=Account utenti organizations=Organizzazioni repositories=Repository +hooks=Webhooks authentication=Fonti di autenticazione emails=Email Utente config=Configurazione @@ -1900,9 +2423,11 @@ first_page=Prima last_page=Ultima total=Totale: %d +dashboard.new_version_hint=Gitea %s è ora disponibile, stai eseguendo %s. Controlla il blog per maggiori dettagli. dashboard.statistic=Riepilogo dashboard.operations=Operazioni di manutenzione dashboard.system_status=Stato del sistema +dashboard.statistic_info=Il database Gitea contiene %d utenti, %d organizzazioni, %d chiavi pubbliche, %d depositi, %d orologi, %d stelle, ~%d azioni, %d accessi, %d problemi, %d commenti, %d conti sociali, %d seguono, %d specchi, %d rilasci, %d fonti di autenticazione, %d webhooks, %d pietre miliari, %d etichette, %d attività di aggancio, %d squadre, %d attività di aggiornamento, %d allegati. dashboard.operation_name=Nome Operazione dashboard.operation_switch=Cambia dashboard.operation_run=Esegui @@ -1914,24 +2439,34 @@ dashboard.task.cancelled=Compito: %[1]s annullato: %[3]s dashboard.task.error=Errore in Attività: %[1]s: %[3]s dashboard.task.finished=Compito: %[1]s iniziato da %[2]s ha finito dashboard.task.unknown=Attività sconosciuta: %[1]s +dashboard.cron.started=Cron Avviato: %[1]s dashboard.cron.process=Cron: %[1]s dashboard.cron.cancelled=Cron: %s cancellato: %[3]s dashboard.cron.error=Errore in Cron: %s: %[3]s dashboard.cron.finished=Cron: %[1]s ha finito dashboard.delete_inactive_accounts=Elimina tutti gli account non attivati dashboard.delete_inactive_accounts.started=Attività di eliminazione degli account non attivati iniziata. +dashboard.delete_repo_archives=Elimina tutti gli archivi dei repository (ZIP, TAR.GZ, etc..) dashboard.delete_repo_archives.started=Attività di eliminazione degli archivi del repository iniziata. dashboard.delete_missing_repos=Elimina tutti i repository mancanti dei loro file Git dashboard.delete_missing_repos.started=Elimina tutti i repository mancanti dei loro file Git. dashboard.delete_generated_repository_avatars=Elimina gli avatar generati nelle repository dashboard.update_mirrors=Aggiorna Mirror dashboard.repo_health_check=Controlla integrità di tutti i repository +dashboard.check_repo_stats=Controlla tutte le statistiche del repository dashboard.archive_cleanup=Elimina vecchi archivi del repository dashboard.deleted_branches_cleanup=Pulisci branch eliminati +dashboard.update_migration_poster_id=Aggiorna gli ID del poster di migrazione dashboard.git_gc_repos=Esegui la garbage collection su tutti i repository +dashboard.resync_all_sshkeys=Aggiornare il file '.ssh/authorized_keys' con le chiavi SSH Gitea. +dashboard.resync_all_sshkeys.desc=(Non necessario per il server SSH integrato.) +dashboard.resync_all_sshprincipals=Aggiornare il file '.ssh/authorized_keys' con le chiavi SSH Gitea. +dashboard.resync_all_sshprincipals.desc=(Non necessario per il server SSH integrato.) dashboard.resync_all_hooks=Sincronizza nuovamente gli hook di pre-ricezione, di aggiornamento e di post-ricezione di tutti i repository. dashboard.reinit_missing_repos=Reinizializza tutti i repository Git mancanti per i quali esistono cambiamenti registrati esistenti dashboard.sync_external_users=Sincronizza dati utente esterno +dashboard.cleanup_hook_task_table=Pulisci tabella hook_task +dashboard.cleanup_packages=Pulizia pacchetti scaduti dashboard.server_uptime=Tempo in Attività del Server dashboard.current_goroutine=Goroutine Correnti dashboard.current_memory_usage=Utilizzo di Memoria Corrente @@ -1961,6 +2496,10 @@ dashboard.total_gc_time=Pausa Totale della GC dashboard.total_gc_pause=Pausa Totale della GC dashboard.last_gc_pause=Ultima pausa della GC dashboard.gc_times=Esecuzioni GC +dashboard.delete_old_actions=Elimina tutte le vecchie azioni dal database +dashboard.delete_old_actions.started=Elimina tutte le vecchie azioni dal database iniziate. +dashboard.update_checker=Controllore dell'aggiornamento +dashboard.delete_old_system_notices=Elimina tutte le vecchie notifiche di sistema dal database users.user_manage_panel=Gestione account utente users.new_account=Crea account utente @@ -1995,9 +2534,26 @@ users.allow_import_local=Può importare repository locali users.allow_create_organization=Può creare organizzazioni users.update_profile=Aggiorna account utente users.delete_account=Elimina account utente +users.cannot_delete_self=Non puoi eliminare te stesso users.still_own_repo=Questo utente possiede ancora una o più repository. Eliminare o trasferire questi repository prima di continuare. users.still_has_org=Questo utente è membro di un'organizzazione. Rimuovi l'utente da tutte le organizzazioni prima di proseguire. +users.purge=Elimina Utente +users.purge_help=Eliminare forzatamente l'utente e tutti i depositi, le organizzazioni e i pacchetti di proprietà dell'utente. Tutti i commenti verranno eliminati troppo. +users.still_own_packages=Questo utente possiede ancora uno o più pacchetti. Elimina prima questi pacchetti. users.deletion_success=L'account utente è stato eliminato. +users.reset_2fa=Resetta 2FA +users.list_status_filter.menu_text=Filtro +users.list_status_filter.reset=Ripristina +users.list_status_filter.is_active=Attivo +users.list_status_filter.not_active=Inattivo +users.list_status_filter.is_admin=Amministratore +users.list_status_filter.not_admin=Non Amministratore +users.list_status_filter.is_restricted=Limitato +users.list_status_filter.not_restricted=Non Limitato +users.list_status_filter.is_prohibit_login=Divieto Di Login +users.list_status_filter.not_prohibit_login=Consenti Login +users.list_status_filter.is_2fa_enabled=2FA Abilitato +users.list_status_filter.not_2fa_enabled=2FA Disabilitato emails.email_manage_panel=Gestione delle Email Utente emails.primary=Primario @@ -2019,6 +2575,8 @@ orgs.members=Membri orgs.new_orga=Nuova Organizzazione repos.repo_manage_panel=Gestione Repository +repos.unadopted=Depositi Non Adottati +repos.unadopted.no_more=Nessun repository non adottato trovato repos.owner=Proprietario repos.name=Nome repos.private=Privati @@ -2028,9 +2586,24 @@ repos.forks=Fork repos.issues=Problemi repos.size=Dimensione - +packages.package_manage_panel=Gestione Pacchetti +packages.total_size=Dimensione totale: %s +packages.owner=Proprietario +packages.creator=Creatore +packages.name=Nome +packages.version=Versione +packages.type=Tipo +packages.repository=Repository +packages.size=Dimensione +packages.published=Pubblicata + +defaulthooks=Webhook predefiniti +defaulthooks.desc=I Webhooks effettuano automaticamente richieste HTTP POST ad un server quando si verificano determinati eventi Gitea. I Webhooks definiti qui sono predefiniti e verranno copiati in tutti i nuovi repository. Per saperne di più leggi la guida ai webhooks. +defaulthooks.add_webhook=Aggiungi Webhook predefinito +defaulthooks.update_webhook=Aggiorna Webhook predefinito systemhooks=Webhooks di Sistema +systemhooks.desc=I Webhooks effettuano automaticamente richieste HTTP POST ad un server quando si verificano determinati eventi Gitea. I Webhooks definiti qui agiranno su tutti i repository del sistema, quindi considera le eventuali implicazioni sulle performance che potrebbero avere. Per saperne di più leggi la guida ai webhooks. systemhooks.add_webhook=Aggiungi Webhook di Sistema systemhooks.update_webhook=Aggiorna Webhook di Sistema @@ -2057,6 +2630,7 @@ auths.attribute_name=Attributo nome auths.attribute_surname=Attributo cognome auths.attribute_mail=Attributo email auths.attribute_ssh_public_key=Attributo chiave SSH pubblica +auths.attribute_avatar=Attributo Avatar auths.attributes_in_bind=Estrai Attributi dal Contesto Bind DN auths.allow_deactivate_all=Consenti un risultato di ricerca vuoto per disattivare tutti gli utenti auths.use_paged_search=Utilizza ricerca per pagina @@ -2065,9 +2639,13 @@ auths.filter=Fitro utente auths.admin_filter=Filtro Amministratore auths.restricted_filter=Filtro riservato auths.restricted_filter_helper=Lasciare vuoto per non impostare alcun utente come limitato. Utilizzare un asterisco ('*') per impostare tutti gli utenti che non corrispondono al filtro amministratore. +auths.verify_group_membership=Verifica l'appartenenza al gruppo in LDAP (lascia vuoto il filtro per saltare) auths.group_search_base=Ricerca Gruppo Base DN auths.group_attribute_list_users=Gruppo Attributo Contenente Elenco Utenti auths.user_attribute_in_group=Attributo Utente Elencato nel Gruppo +auths.map_group_to_team=Mappa i gruppi LDAP alle squadre dell'organizzazione (lasciare vuoto il campo per saltare) +auths.map_group_to_team_removal=Rimuovi gli utenti dai team sincronizzati se l'utente non appartiene al gruppo LDAP corrispondente +auths.enable_ldap_groups=Abilita gruppi LDAP auths.ms_ad_sa=Attributi di ricerca AD MS auths.smtp_auth=Tipo di autenticazione SMTP auths.smtphost=Host SMTP @@ -2075,7 +2653,13 @@ auths.smtpport=Porta SMTP auths.allowed_domains=Domini consentiti auths.allowed_domains_helper=Lasciare vuoto per ammettere tutti i domini. Separare più domini con una virgola (','). auths.skip_tls_verify=Salta verifica TLS +auths.force_smtps=Forza SMTPS +auths.force_smtps_helper=SMTPS è sempre utilizzato sulla porta 465. Impostalo per forzare SMTPS su altre porte. (Otherwise STARTTLS sarà utilizzato su altre porte se è supportato dall'host.) +auths.helo_hostname=HELO nome dell'host +auths.helo_hostname_helper=Nome host inviato con HELO. Lasciare vuoto per inviare il nome host corrente. +auths.disable_helo=Disattiva HELO auths.pam_service_name=Nome del Servizio PAM +auths.pam_email_domain=Dominio Email PAM (opzionale) auths.oauth2_provider=OAuth2 Provider auths.oauth2_icon_url=URL icona auths.oauth2_clientID=ID Client (Chiave) @@ -2086,6 +2670,17 @@ auths.oauth2_tokenURL=URL token auths.oauth2_authURL=Autorizza URL auths.oauth2_profileURL=URL profilo auths.oauth2_emailURL=URL email +auths.skip_local_two_fa=Salta 2FA locale +auths.skip_local_two_fa_helper=Lasciare l'azzeramento significa che gli utenti locali con il set 2FA dovranno ancora passare 2FA per accedere +auths.oauth2_tenant=Comproprietà +auths.oauth2_scopes=Ambiti Aggiuntivi +auths.oauth2_required_claim_name=Nome Richiesto +auths.oauth2_required_claim_name_helper=Imposta questo nome per limitare il login da questa fonte agli utenti con un reclamo con questo nome +auths.oauth2_required_claim_value=Valore Richiesto +auths.oauth2_required_claim_value_helper=Imposta questo valore per limitare il login da questa fonte agli utenti con un reclamo con questo nome e valore +auths.oauth2_group_claim_name=Riscatta nome che fornisce nomi di gruppo per questa fonte (facoltativo) +auths.oauth2_admin_group=Valore del reclamo di gruppo per gli utenti amministratori. (Opzionale - richiede il nome della richiesta sopra) +auths.oauth2_restricted_group=Valore di reclamo di gruppo per utenti ristretti. (Facoltativo - richiede il nome di reclamo sopra) auths.enable_auto_register=Abilitare Registrazione Automatica auths.sspi_auto_create_users=Crea automaticamente gli utenti auths.sspi_auto_create_users_helper=Permetti al metodo di autenticazione SSPI di creare automaticamente nuovi account per gli utenti che accedono per la prima volta @@ -2102,6 +2697,7 @@ auths.tips.oauth2.general=Autenticazione OAuth2 auths.tips.oauth2.general.tip="Quando si registra una nuova autenticazione OAuth2, l'URL di callback/reindirizzamento deve essere:/user/oauth2//callback auths.tip.oauth2_provider=OAuth2 Provider auths.tip.bitbucket=Registra un nuovo cliente OAuth su https://bitbucket.org/account/user//oauth-consumers/new e aggiungi il permesso 'Account' - 'Read' +auths.tip.nextcloud=Registra un nuovo OAuth sulla tua istanza utilizzando il seguente menu "Impostazioni -> Sicurezza -> OAuth 2.0 client" auths.tip.dropbox=Crea una nuova applicazione su https://www.dropbox.com/developers/apps auths.tip.facebook=Registra una nuova applicazione su https://developers.facebook.com/apps e aggiungi il prodotto "Facebook Login" auths.tip.github=Registra una nuova applicazione OAuth su https://github.com/settings/applications/new @@ -2111,6 +2707,8 @@ auths.tip.openid_connect=Utilizza l'OpenID Connect Discovery URL (/.well auths.tip.twitter=Vai su https://dev.twitter.com/apps, crea una applicazione e assicurati che l'opzione "Allow this application to be used to Sign In with Twitter" sia abilitata auths.tip.discord=Registra una nuova applicazione su https://discordapp.com/developers/applications/me auths.tip.gitea=Registra una nuova applicazione OAuth2. La guida può essere trovata a https://docs.gitea.io/en-us/oauth2-provider/ +auths.tip.yandex=Crea una nuova applicazione su https://oauth.yandex.com/client/new. Seleziona i seguenti permessi da "Yandex. assport API": "Access to email address", "Access to user avatar" e "Access to username, name and surname, gender" +auths.tip.mastodon=Inserisci un URL di istanza personalizzato per l'istanza mastodon con cui vuoi autenticarti (o usa quella predefinita) auths.edit=Modifica fonte di autenticazione auths.activated=Questa fonte di autenticazione è attiva auths.new_success=L'autenticazione '%s' è stata aggiunta. @@ -2130,6 +2728,7 @@ config.app_ver=Versione Gitea config.app_url=URL di base di Gitea config.custom_conf=Percorso file di configurazione config.custom_file_root_path=Percorso Root File Personalizzato +config.domain=Dominio Server config.offline_mode=Modalità locale config.disable_router_log=Disattivare Log del Router config.run_user=Esegui come Nome utente @@ -2145,6 +2744,7 @@ config.reverse_auth_user=Autenticazione Utente Inversa config.ssh_config=Configurazione SSH config.ssh_enabled=Attivo config.ssh_start_builtin_server=Usa il server integrato +config.ssh_domain=Dominio Server Ssh config.ssh_port=Porta config.ssh_listen_port=Porta in ascolto config.ssh_root_path=Percorso Root @@ -2170,6 +2770,7 @@ config.db_path=Percorso config.service_config=Configurazione Servizio config.register_email_confirm=Richiedere la conferma Email per registrarsi config.disable_register=Disattiva Self-Registration +config.allow_only_internal_registration=Consenti la registrazione solo tramite Gitea stessa config.allow_only_external_registration=Attiva la registrazione solo tramite servizi esterni config.enable_openid_signup=Attiva OpenID Self-Registration config.enable_openid_signin=Attiva l'accesso tramite OpenID @@ -2203,6 +2804,7 @@ config.mailer_user=Utente config.mailer_use_sendmail=Utilizza Sendmail config.mailer_sendmail_path=Percorso Sendmail config.mailer_sendmail_args=Argomenti aggiuntivi per Sendmail +config.mailer_sendmail_timeout=Timeout Sendmail config.test_email_placeholder=Email (es. test@example.com) config.send_test_mail=Invia email di prova config.test_mail_failed=Impossibile inviare mail di prova a '%s': %v @@ -2262,12 +2864,16 @@ monitor.next=La Prossima Volta monitor.previous=La Scorsa Volta monitor.execute_times=Esecuzioni monitor.process=Processi in Esecuzione +monitor.stacktrace=Stacktraces +monitor.goroutines=%d Goroutines monitor.desc=Descrizione monitor.start=Orario Avvio monitor.execute_time=Tempo di Esecuzione +monitor.last_execution_result=Risultato monitor.process.cancel=Annulla processo monitor.process.cancel_desc=L'annullamento di un processo potrebbe causare la perdita di dati monitor.process.cancel_notices=Annulla: %s? +monitor.process.children=Figli monitor.queues=Code monitor.queue=Coda: %s monitor.queue.name=Nome @@ -2275,11 +2881,14 @@ monitor.queue.type=Tipo monitor.queue.exemplar=Tipo di esemplare monitor.queue.numberworkers=Numero di workers monitor.queue.maxnumberworkers=Massimo numero di Workers +monitor.queue.numberinqueue=Numero in coda monitor.queue.review=Rivedi configurazione monitor.queue.review_add=Rivedi/aggiungi Workers monitor.queue.configuration=Configurazione iniziale monitor.queue.nopool.title=Nessun pool di Workers monitor.queue.nopool.desc=Questa coda racchiude altre code al suo interno e non ha un proprio pool. +monitor.queue.wrapped.desc=Una coda a capo avvolge una coda iniziale lenta, le richieste in coda di buffering in un canale. Non ha un pool di lavoratori stesso. +monitor.queue.persistable-channel.desc=Un canale persistibile avvolge due code, una coda di canale che ha un proprio pool di operatori e una coda di livello per le richieste persistenti dagli arresti precedenti. Non ha un pool di operai. monitor.queue.pool.timeout=Timeout monitor.queue.pool.addworkers.title=Aggiungi Workers monitor.queue.pool.addworkers.submit=Aggiungi Workers @@ -2289,7 +2898,15 @@ monitor.queue.pool.addworkers.timeout.placeholder=Imposta 0 per non avere timeou monitor.queue.pool.addworkers.mustnumbergreaterzero=Il numero di Workers da aggiungere deve essere maggiore di zero monitor.queue.pool.addworkers.musttimeoutduration=Il timeout deve essere una durata golang, per esempio 5m o 0 monitor.queue.pool.flush.title=Pulisci Coda +monitor.queue.pool.flush.desc=Flush aggiungerà un worker che terminerà una volta che la coda sarà vuota, o il tempo sarà esaurito. monitor.queue.pool.flush.submit=Aggiungi un Flush Worker +monitor.queue.pool.flush.added=Flush Worker aggiunto per %[1]s +monitor.queue.pool.pause.title=Coda Di Pausa +monitor.queue.pool.pause.desc=La Pausa di una Coda impedirà all'elaborazione dei dati +monitor.queue.pool.pause.submit=Coda Di Pausa +monitor.queue.pool.resume.title=Riprendi Coda +monitor.queue.pool.resume.desc=Imposta questa coda per riprendere il lavoro +monitor.queue.pool.resume.submit=Riprendi Coda monitor.queue.settings.title=Impostazioni pool monitor.queue.settings.desc=I gruppi crescono dinamicamente con un boost in risposta al loro blocco delle code dei worker. Queste modifiche non influenzeranno i gruppi di worker attuali. @@ -2335,14 +2952,34 @@ notices.delete_success=Gli avvisi di sistema sono stati eliminati. [action] create_repo=ha creato il repository %s rename_repo=repository rinominato da %[1]s a [3]s +commit_repo=a inviato a %[3]s di %[4]s +create_issue=`ha aperto il problema %[3]s#%[2]s` +close_issue=`ha chiuso il problema %[3]s#%[2]s` +reopen_issue=`ha riaperto il problema %[3]s#%[2]s` +create_pull_request=`ha creato la pull request %[3]s#%[2]s` +close_pull_request=`ha chiuso la pull request %[3]s#%[2]s` +reopen_pull_request=`ha riaperto la pull request %[3]s#%[2]s` +comment_issue=`ha commentato sul problema %[3]s#%[2]s` +comment_pull=`ha commentato su pull request %[3]s#%[2]s` +merge_pull_request=`ha unito il pull request %[3]s#%[2]s` transfer_repo=repository %s trasferito in %s +push_tag=ha inviato il tag %[3]s su %[4]s delete_tag=tag eliminato %[2]s da %[3]s delete_branch=branch eliminato %[2]s da %[3]s compare_branch=Confronta compare_commits=Confronta %d commits compare_commits_general=Confronta commit +mirror_sync_push=ha sincronizzato i commit a %[3]s di %[4]s dal mirror +mirror_sync_create=ha sincronizzato un nuovo riferimento %[3]s su %[4]s dal mirror mirror_sync_delete=riferimento sincronizzato ed eliminato %[2]s a %[3]s dal mirror +approve_pull_request=`ha approvato %[3]s#%[2]s` +reject_pull_request=`ha suggerito modifiche per %[3]s#%[2]s` +publish_release=`ha rilasciato "%[4]s" su %[3]s` +review_dismissed=`respinta la recensione da %[4]s per %[3]s#%[2]s` review_dismissed_reason=Motivo: +create_branch=ha creato il ramo %[3]s in %[4]s +starred_repo=ha salvato come preferito %[2]s +watched_repo=ha iniziato a guardare %[2]s [tool] ago=%s fa @@ -2395,8 +3032,100 @@ error.probable_bad_signature=ATTENZIONE! Anche se esiste una chiave con questo I error.probable_bad_default_signature=ATTENZIONE! Anche se la chiave predefinita ha questo ID essa non verifica questo commit! Questo commit è SOSPETTO. [units] +unit=Unità error.no_unit_allowed_repo=Non possiedi il permesso di accedere ad alcuna sezione di questo repository. error.unit_not_allowed=Non possiedi il permesso di accedere a questa sezione di repository. [packages] +title=Pacchetti +desc=Gestisci pacchetti repository. +empty=Non ci sono ancora pacchetti. +empty.documentation=Per ulteriori informazioni sul registro dei pacchetti, consultare la documentazione. +empty.repo=Hai caricato un pacchetto, ma non è mostrato qui? Vai alle impostazioni del pacchetto e collegalo a questo repo. +filter.type=Tipo +filter.type.all=Tutti +filter.no_result=Il filtro non ha prodotto risultati. +filter.container.tagged=Etichettato +filter.container.untagged=Nont etichettato +published_by=Pubblicato %[1]s di %[3]s +published_by_in=Pubblicato %[1]s di %[3]s in %[5]s +installation=Installazione +about=Informazioni su questo pacchetto +requirements=Requisiti +dependencies=Dipendenze +keywords=Parole Chiave +details=Dettagli +details.author=Autore +details.project_site=Sito Del Progetto +details.license=Licenza +assets=Asset +versions=Versioni +versions.on=su +versions.view_all=Vedi tutti +dependency.id=ID +dependency.version=Versione +composer.registry=Imposta questo registro nel tuo file ~/.composer/config.json: +composer.install=Per installare il pacchetto utilizzando Composer, eseguire il seguente comando: +composer.documentation=Per ulteriori informazioni sul registro dei compositori, consultare la documentazione. +composer.dependencies=Dipendenze +composer.dependencies.development=Dipendenze Di Sviluppo +conan.details.repository=Repository +conan.registry=Configura questo registro dalla riga di comando: +conan.install=Per installare il pacchetto usando Conan, eseguire il seguente comando: +conan.documentation=Per ulteriori informazioni sul registro di Conan, consultare la documentazione. +container.details.type=Tipo Immagine +container.details.platform=Piattaforma +container.details.repository_site=Sito Repository +container.details.documentation_site=Sito Documentazione +container.pull=Tirare l'immagine dalla riga di comando: +container.documentation=Per ulteriori informazioni sul registro Container, vedere la documentazione. +container.multi_arch=OS / Arch +container.layers=Livelli Immagine +container.labels=Etichette +container.labels.key=Chiave +container.labels.value=Valore +generic.download=Scarica il pacchetto dalla riga di comando: +generic.documentation=Per ulteriori informazioni sul registro generico, consultare la documentazione. +helm.registry=Configura questo registro dalla riga di comando: +helm.install=Per installare il pacchetto, eseguire il seguente comando: +helm.documentation=Per ulteriori informazioni sul registro Helm, consultare la documentazione. +maven.registry=Configura questo registro nel file pom.xml del tuo progetto: +maven.install=Per utilizzare il pacchetto includere i seguenti nel blocco dipendenze nel file pom.xml: +maven.install2=Esegui tramite riga di comando: +maven.download=Per scaricare la dipendenza, eseguire tramite riga di comando: +maven.documentation=Per ulteriori informazioni sul registro Maven, consultare la documentazione. +nuget.registry=Configura questo registro dalla riga di comando: +nuget.install=Per installare il pacchetto utilizzando NuGet, eseguire il seguente comando: +nuget.documentation=Per ulteriori informazioni sul registro di NuGest, consultare la documentazione. +nuget.dependency.framework=Target Framework +npm.registry=Impostare questo registro nel file del progetto .npmrc: +npm.install=Per installare il pacchetto usando npm, eseguire il seguente comando: +npm.install2=o aggiungerlo al file package.json: +npm.documentation=Per ulteriori informazioni sul registro npm, vedere la documentazione. +npm.dependencies=Dipendenze +npm.dependencies.development=Dipendenze Di Sviluppo +npm.dependencies.peer=Dipendenze Peer +npm.dependencies.optional=Dipendenze Opzionali +npm.details.tag=Tag +pypi.requires=Richiede Python +pypi.install=Per installare il pacchetto usando pip, eseguire il seguente comando: +pypi.documentation=Per ulteriori informazioni sul registro PyPI, consultare la documentazione. +rubygems.install=Per installare il pacchetto usando gem, eseguire il seguente comando: +rubygems.install2=o aggiungerlo al file Gem: +rubygems.dependencies.runtime=Dipendenze Runtime +rubygems.dependencies.development=Dipendenze Di Sviluppo +rubygems.required.ruby=Richiede la versione di Ruby +rubygems.required.rubygems=Richiede la versione RubyGem +rubygems.documentation=Per ulteriori informazioni sul registro di RubyGems, vedere la documentazione. +settings.link=Collega questo pacchetto a un repository +settings.link.description=Se si collega un pacchetto a un repository, il pacchetto è elencato nell'elenco dei pacchetti del repository. +settings.link.select=Seleziona Repository +settings.link.button=Aggiorna Collegamento Repository +settings.link.success=Il link del repository è stato aggiornato correttamente. +settings.link.error=Impossibile aggiornare il link del repository. +settings.delete=Elimina pacchetto +settings.delete.description=L'eliminazione di un pacchetto è permanente e non può essere annullata. +settings.delete.notice=Stai per eliminare %s (%s). Questa operazione è irreversibile, sei sicuro? +settings.delete.success=Il pacchetto è stato eliminato. +settings.delete.error=Impossibile eliminare il pacchetto. diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 416f6add0293..b91f3c87e73b 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -799,6 +799,7 @@ email_notifications.enable=メール通知有効 email_notifications.onmention=メンションのみメール通知 email_notifications.disable=メール通知無効 email_notifications.submit=メール設定を保存 +email_notifications.andyourown=自分に関する通知も含める visibility=ユーザーの公開範囲 visibility.public=パブリック @@ -1784,10 +1785,6 @@ settings.mirror_settings.push_mirror.remote_url=リモートGitリポジトリ settings.mirror_settings.push_mirror.add=プッシュミラーを追加 settings.sync_mirror=今すぐ同期 settings.mirror_sync_in_progress=ミラー同期を実行しています。 しばらくあとでまた確認してください。 -settings.email_notifications.enable=メール通知有効 -settings.email_notifications.onmention=メンションのみメール通知 -settings.email_notifications.disable=メール通知無効 -settings.email_notifications.submit=メール設定を保存 settings.site=Webサイト settings.update_settings=設定を更新 settings.branches.update_default_branch=デフォルトブランチを更新 @@ -3044,6 +3041,7 @@ title=パッケージ desc=リポジトリ パッケージを管理します。 empty=パッケージはまだありません。 empty.documentation=パッケージレジストリの詳細については、 ドキュメント を参照してください。 +empty.repo=パッケージはアップロードしたけども、ここに表示されない? パッケージ設定を開いて、パッケージをこのリポジトリにリンクしてください。 filter.type=タイプ filter.type.all=すべて filter.no_result=フィルタの結果、空になりました。 diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index 6049c44313f7..ab8908e531f3 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -970,8 +970,6 @@ settings.basic_settings=기본 설정 settings.mirror_settings=미러 설정 settings.sync_mirror=지금 동기화 settings.mirror_sync_in_progress=미러 동기화 진행중입니다. 잠시 후 다시 확인해주십시오. -settings.email_notifications.enable=이메일 알림 켜기 -settings.email_notifications.disable=이메일 알림 끄기 settings.site=웹 사이트 settings.update_settings=설정 저장 settings.advanced_settings=고급 설정 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 5a43101b1ee2..5cecb321dc4c 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1775,10 +1775,6 @@ settings.mirror_settings.push_mirror.remote_url=Git attālinātā repozitorija U settings.mirror_settings.push_mirror.add=Pievienot iesūtīšanas spoguli settings.sync_mirror=Sinhronizēt tagad settings.mirror_sync_in_progress=Notiek spoguļa sinhronizācija. Atjaunojiet lapu, lai pārbaudītu atkārtoti, pēc brīža. -settings.email_notifications.enable=Iespējot e-pasta paziņojumus -settings.email_notifications.onmention=Tikai, ja esmu pieminēts -settings.email_notifications.disable=Nesūtīt paziņojumus -settings.email_notifications.submit=Saglabāt sūtīšanas iestatījumus settings.site=Mājas lapa settings.update_settings=Mainīt iestatījumus settings.branches.update_default_branch=Atjaunot noklusēto atzaru diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 3e54ef3fd787..fcc048b3c1c5 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -2,6 +2,7 @@ home=Beginscherm dashboard=Overzicht explore=Verkennen help=Help +logo=Logo sign_in=Inloggen sign_in_with=Inloggen met sign_out=Uitloggen @@ -25,7 +26,7 @@ licenses=Licenties return_to_gitea=Terug naar Gitea username=Gebruikersnaam -email=E-mail adres +email=E-mailadres password=Wachtwoord access_token=Toegangstoken re_type=Typ uw wachtwoord opnieuw in @@ -34,6 +35,19 @@ twofa=Twee factor authenticatie twofa_scratch=Eenmalige twee factor authenticatie code passcode=PIN +webauthn_insert_key=Voer uw beveiligingssleutel in +webauthn_sign_in=Druk op de knop van uw beveiligingssleutel. Als uw beveiligingssleutel geen knop heeft, voeg deze dan opnieuw in. +webauthn_press_button=Druk alstublieft op de knop van uw beveiligingssleutel… +webauthn_use_twofa=Gebruik een twee-factor code van uw telefoon +webauthn_error=Kon uw beveiligingssleutel niet lezen. +webauthn_unsupported_browser=Uw browser ondersteunt momenteel geen WebAuthn. +webauthn_error_unknown=Er is een onbekende fout opgetreden. Probeer het opnieuw. +webauthn_error_insecure=WebAuthn ondersteunt alleen beveiligde verbindingen. Om te testen via HTTP, kan je de oorsprong "localhost" of "127.0.0.1" gebruiken +webauthn_error_unable_to_process=De server kon uw verzoek niet verwerken. +webauthn_error_duplicated=De beveiligingssleutel is niet toegestaan voor dit verzoek. Zorg er alstublieft voor dat de sleutel niet al geregistreerd is. +webauthn_error_empty=U moet een naam voor deze sleutel instellen. +webauthn_error_timeout=Time-out bereikt voordat uw sleutel kon worden gelezen. Laad deze pagina opnieuw en probeer het opnieuw. +webauthn_reload=Vernieuwen repository=Repository organization=Organisatie @@ -55,7 +69,7 @@ your_settings=Instellingen all=Alles sources=Bronnen -mirrors=Kopieën +mirrors=Spiegels collaborative=Samenwerkend forks=Forks @@ -74,6 +88,7 @@ remove_all=Alles verwijderen edit=Bewerk copy=Kopieer +copy_url=Kopieer URL copy_branch=Kopieer branchnaam copy_success=Gekopieerd! copy_error=Kopiëren mislukt @@ -90,9 +105,15 @@ error404=De pagina die u probeert te bereiken bestaat niet of < never=Nooit +rss_feed=RSS Feed [error] +occurred=Er is een fout opgetreden +report_message=Als je zeker weet dat dit een Gitea bug is, zoek dan naar problemen op GitHub of open een nieuw probleem indien nodig. missing_csrf=Foutief verzoek: geen CSRF-token aanwezig +invalid_csrf=Verkeerd verzoek: ongeldig CSRF-token +not_found=Het doel kon niet worden gevonden. +network_error=Netwerk fout [startpage] app_desc=Een eenvoudige, self-hosted Git service @@ -109,6 +130,7 @@ license_desc=Alles staat op documentatie voordat je een instelling aanpast. +require_db_desc=Gitea vereist MySQL, PostgreSQL, MSSQL, SQLite3 of TiDB (MySQL protocol). db_title=Database-instellingen db_type=Database-type host=Server @@ -122,10 +144,15 @@ ssl_mode=SSL charset=Karakterset path=Pad sqlite_helper=Bestandspad voor de SQLite3-database.
Vul een volledig pad in als je GItea als een service uitvoert. +reinstall_error=U probeert te installeren in een bestaande Gitea database +reinstall_confirm_message=Herinstalleren met een bestaande Gitea-database kan meerdere problemen veroorzaken. In de meeste gevallen kun je het bestaande "app.ini" gebruiken om Gitea te laten draaien. Als je weet wat je aan het doen bent, bevestig dan het volgende: +reinstall_confirm_check_1=De gegevens versleuteld door de SECRET_KEY in de app.ini kan verloren gaan: gebruikers kunnen mogelijk niet meer inloggen met 2FA/OTP & spiegels werken mogelijk niet meer. Door dit vakje aan te vinken bevestigt u dat het huidige app.ini bestand de juiste SECRET_KEY bevat. +reinstall_confirm_check_2=De repositories en instellingen moeten mogelijk opnieuw worden gesynchroniseerd. Door dit vakje aan te vinken, bevestigt u dat u de hooks voor de repositories en authorized_keys bestand handmatig zult hersynchroniseren. U bevestigt dat u ervoor zult zorgen dat de instellingen van de repository en mirror correct zijn. +reinstall_confirm_check_3=Je bevestigt dat je er absoluut zeker van bent dat deze Gitea draait met de juiste app. Geen locatie en dat je zeker weet dat je opnieuw moet installeren. Je bevestigt dat je de hierbovenstaande risico's erkent. err_empty_db_path=SQLite3 database pad mag niet leeg zijn. no_admin_and_disable_registration=U kunt zelf-registratie van de gebruiker niet uitschakelen zonder het maken van een administrator-account. err_empty_admin_password=Het administrator-wachtwoord mag niet leeg zijn. -err_empty_admin_email=De e-mail van de beheerder mag niet leeg zijn. +err_empty_admin_email=Het e-mailadres van Het beheerder mag niet leeg zijn. err_admin_name_is_reserved=Gebruikersnaam van beheerder is ongeldig, gebruikersnaam is gereserveerd err_admin_name_pattern_not_allowed=Gebruikersnaam van beheerder is ongeldig, de gebruikersnaam is gereserveerd err_admin_name_is_invalid=Gebruikersnaam van beheerder is ongeldig @@ -182,13 +209,17 @@ admin_title=Instellingen beheerdersaccount admin_name=Admin gebruikersnaam admin_password=Wachtwoord confirm_password=Verifieer wachtwoord -admin_email=E-mail adres +admin_email=E-mailadres install_btn_confirm=Installeer Gitea test_git_failed=Git test niet gelukt: 'git' commando %v sqlite3_not_available=Deze Gitea-versie biedt geen ondersteuning voor SQLite3. Download de officiële build van %s (niet de versie van de 'gobuild'). invalid_db_setting=De database instelling zijn niet correct: %v +invalid_db_table=De database tabel '%s' is ongeldig: %v invalid_repo_path=Het pad van de hoofdmap van de repository is ongeldig: %v +invalid_app_data_path=Ongeldig app-gegevenspad: %v run_user_not_match=De 'uitvoeren als' gebruikersnaam is niet de huidige gebruikersnaam: %s -> %s +internal_token_failed=Interne token genereren mislukt: %v +secret_key_failed=Geheime sleutel genereren mislukt: %v save_config_failed=Kan de configuratie niet opslaan: %v invalid_admin_setting=Instelling van de administrator-account is ongeldig: %v install_success=Welkom! Bedankt dat u voor Gitea heeft gekozen. Veel plezier en succes ermee! @@ -202,6 +233,7 @@ default_enable_timetracking_popup=Tijdsregistratie voor nieuwe repositories stan no_reply_address=Verborgen e-maildomein no_reply_address_helper=Domeinnaam voor gebruikers met een verborgen e-mailadres. Bijvoorbeeld zal de gebruikersnaam 'joe' in Git worden geregistreerd als 'joe@noreply.example.org' als het verborgen email domein is ingesteld op 'noreply.example.org'. password_algorithm=Wachtwoord Hash Algoritme +password_algorithm_helper=Stel het wachtwoord hashing-algoritme in. Algoritmen hebben verschillende vereisten en sterkte. `argon2` heeft goede kenmerken, maar gebruikt veel geheugen en kan ongepast zijn voor kleinere systemen. [home] uname_holder=Gebruikersnaam of e-mailadres @@ -211,7 +243,7 @@ my_repos=Repositories show_more_repos=Toon meer repositories… collaborative_repos=Gedeelde repositories my_orgs=Mijn organisaties -my_mirrors=Mijn kopieën +my_mirrors=Mijn spiegels view_home=Bekijk %s search_repos=Zoek een repository… filter=Andere filters @@ -236,6 +268,9 @@ users=Gebruikers organizations=Organisaties search=Zoeken code=Code +search.fuzzy=Vergelijkbaar +search.match=Overeenkomst +code_search_unavailable=Er is momenteel geen code zoekfunctie beschikbaar. Neem contact op met uw sitebeheerder. repo_no_results=Er zijn geen overeenkomende repositories gevonden. user_no_results=Er zijn geen overeenkomende gebruikers gevonden. org_no_results=Er zijn geen overeenkomende organisaties gevonden. @@ -249,6 +284,8 @@ register_helper_msg=Heeft u al een account? Klik hier om in te loggen social_register_helper_msg=Heeft u al een account? Koppel deze nu! disable_register_prompt=Registratie is uitgeschakeld. Neem alstublieft contact op met de pagina beheerder. disable_register_mail=E-mailbevestiging voor registratie is uitgeschakeld. +manual_activation_only=Neem contact op met uw sitebeheerder om de activering te voltooien. +remember_me=Onthoud dit apparaat forgot_password_title=Wachtwoord vergeten forgot_password=Wachtwoord vergeten? sign_up_now=Een account nodig? Meld u nu aan. @@ -281,16 +318,22 @@ twofa_scratch_token_incorrect=Je eenmalige code is onjuist. login_userpass=Inloggen login_openid=OpenID oauth_signup_tab=Registreer nieuw account +oauth_signup_title=Voltooi nieuw account oauth_signup_submit=Account voltooien oauth_signin_tab=Bestaand account koppelen oauth_signin_title=Inloggen om het gekoppelde account te machtigen oauth_signin_submit=Account koppelen +oauth.signin.error=Er is een fout opgetreden bij het verwerken van het autorisatieverzoek. Als deze fout zich blijft voordoen, neem dan contact op met de sitebeheerder. +oauth.signin.error.access_denied=Het autorisatieverzoek is geweigerd. +oauth.signin.error.temporarily_unavailable=Autorisatie mislukt omdat de verificatieserver tijdelijk niet beschikbaar is. Probeer het later opnieuw. openid_connect_submit=Verbinden openid_connect_title=Verbind met een bestaand account openid_connect_desc=De gekozen OpenID-URI is onbekend. Koppel het aan een nieuw account hier. openid_register_title=Nieuw account aanmaken openid_register_desc=De gekozen OpenID-URI is onbekend. Koppel het aan een nieuw account hier. openid_signin_desc=Geef uw OpenID-URI. Bijvoorbeeld: https://anne.me, bob.openid.org.cn of gnusocial.net/carry. +disable_forgot_password_mail=Accountherstel is uitgeschakeld omdat er geen e-mailadres is ingesteld. Neem aub contact op met uw administrator. +disable_forgot_password_mail_admin=Accountherstel is alleen beschikbaar wanneer een e-mailadres is ingesteld. Stel e-mailadres in om accountherstel te activeren. email_domain_blacklisted=Je kan je niet registreren met dit e-mailadres. authorize_application=Autoriseer applicatie authorize_redirect_notice=U wordt doorgestuurd naar %s als u deze toepassing toestaat. @@ -304,19 +347,47 @@ password_pwned=Het gekozen wachtwoord staat op een uw wachtwoord instellen. reset_password=Account herstellen +reset_password.title=%s, u heeft verzocht om uw account te herstellen +reset_password.text=Klik op de volgende link om je account te herstellen binnen %s: register_success=Registratie succesvol - +issue_assigned.pull=@%[1]s heeft u toegewezen aan de pull request %[2]s in repository %[3]s. +issue_assigned.issue=@%[1]heeft u toegewezen aan issue %[2]s in repository %[3]s. + +issue.x_mentioned_you=@%s heeft u vermeld: +issue.action.force_push=%[1]s heeft een force-push uitgevoerd %[2]s van %[3]s naar %[4]s. +issue.action.push_1=@%[1]s heeft %[3]d commits gepusht naar %[2]s +issue.action.push_n=@%[1]s heeft %[3]d commits gepusht naar %[2]s +issue.action.close=@%[1]s sloot #%[2]d. +issue.action.reopen=@%[1]s heropend #%[2]d. +issue.action.merge=@%[1] heeft een merge uitgevoerd van #%[2]d naar %[3]s. +issue.action.approve=@%[1]s heeft deze pull request goedgekeurd. +issue.action.reject=@%[1]s vraagt om wijzigingen op deze pull request. +issue.action.review=@%[1]s heeft gereageerd op deze pull request. +issue.action.review_dismissed=@%[1]s wees de laatste review af van %[2]s voor deze pull request. +issue.action.ready_for_review=@%[1]s markeerde deze pull request klaar voor beoordeling. +issue.action.new=@%[1]s heeft #%[2]d aangemaakt. issue.in_tree_path=In %s: release.new.subject=%s in %s vrijgegeven @@ -330,8 +401,10 @@ release.download.targz=Broncode (TAR.GZ) repo.transfer.subject_to=%s zou "%s" willen overdragen aan %s repo.transfer.subject_to_you=%s wil "%s" aan jou overdragen repo.transfer.to_you=jij +repo.transfer.body=Om het te accepteren of afwijzen, bezoek %s of negeer het gewoon. repo.collaborator.added.subject=%s heeft jou toegevoegd aan %s +repo.collaborator.added.text=U bent toegevoegd als een medewerker van de repository: [modal] yes=Ja @@ -369,8 +442,10 @@ size_error=moet groter zijn dan %s min_size_error=moet minimaal %s karakters bevatten. max_size_error=mag maximaal %s karakters bevatten. email_error=is niet een valide e-mail adres. +url_error=`'%s' is niet een geldige URL.` include_error=` moet substring '%s' bevatten.` glob_pattern_error=` globpatroon is ongeldig: %s.` +regex_pattern_error=` regex patroon is ongeldig: %s.` unknown_error=Onbekende fout: captcha_incorrect=De CAPTCHA-code is onjuist. password_not_match=De wachtwoorden komen niet overeen. @@ -379,6 +454,7 @@ lang_select_error=Selecteer een taal uit de lijst. username_been_taken=Deze naam is al in gebruik. username_change_not_local_user=Niet-lokale gebruikers mogen hun gebruikersnaam niet wijzigen. repo_name_been_taken=De repository-naam wordt al gebruikt. +repository_force_private=Forceer privé is ingeschakeld: privé repositories kunnen niet openbaar worden gemaakt. repository_files_already_exist=Er bestaan al bestanden voor deze repository. Neem contact op met de systeembeheerder. repository_files_already_exist.adopt=Bestanden bestaan al voor deze repository en kunnen alleen worden geadopteerd. repository_files_already_exist.delete=Er bestaan al bestanden voor deze repository. U moet deze verwijderen. @@ -389,6 +465,7 @@ org_name_been_taken=Naam van de organisatie wordt al gebruikt. team_name_been_taken=De teamnaam is al in gebruik. team_no_units_error=Toegang verlenen tot ten minste één repository sectie. email_been_used=Het emailadres is al in gebruik. +email_invalid=Het e-mailadres is ongeldig. openid_been_used=OpenID adres '%s' reeds gebruikt. username_password_incorrect=Gebruikersnaam of wachtwoord is onjuist. password_complexity=Wachtwoord voldoet niet aan complexiteit eisen: @@ -397,6 +474,7 @@ password_uppercase_one=Minstens één hoofdletter password_digit_one=Minstens één cijfer password_special_one=Minstens één speciaal teken (interpunctie, haakjes, aanhalingstekens, etc.) enterred_invalid_repo_name=De repository-naam die u hebt ingevoerd is niet correct. +enterred_invalid_org_name=De organizatienaam die u hebt ingevoerd is niet correct. enterred_invalid_owner_name=De nieuwe eigenaarnaam is niet geldig. enterred_invalid_password=Het ingevoerde wachtwoord is onjuist. user_not_exist=De gebruiker bestaat niet. @@ -412,7 +490,9 @@ auth_failed=Verificatie mislukt: %v still_own_repo=Je account is nog eigenaar van één of meerdere repositories. Deze moeten eerst verwijderd of overgedragen worden. still_has_org=Je account is lid van één of meerdere organisaties. Verlaat deze eerst. +still_own_packages=Uw account bezit één of meer pakketten; verwijder deze eerst. org_still_own_repo=Deze organisatie bezit minstens één repositories. Verwijder deze of draag deze eerst over. +org_still_own_packages=Deze organisatie is nog eigenaar van één of meer pakketten; verwijder deze eerst. target_branch_not_exist=Doel branch bestaat niet @@ -423,6 +503,7 @@ repositories=repositories activity=Openbare activiteit followers=Volgers starred=Repositories met ster +watched=Gevolgde repositories projects=Projecten following=Volgt follow=Volg @@ -438,6 +519,7 @@ form.name_chars_not_allowed=Gebruikersnaam '%s' bevat ongeldige tekens. [settings] profile=Profiel account=Account +appearance=Vormgeving password=Wachtwoord security=Beveiliging avatar=Profielfoto @@ -451,6 +533,7 @@ twofa=Twee factor authenticatie account_link=Gekoppelde Accounts organization=Organisaties uid=uid +webauthn=Beveiligingssleutels public_profile=Openbaar profiel biography_placeholder=Vertel ons iets over jezelf @@ -461,14 +544,33 @@ website=Website location=Locatie update_theme=Thema bijwerken update_profile=Profiel bijwerken +update_language=Taal wijzigen update_language_not_found=De taal '%s' is niet beschikbaar. +update_language_success=Taal is bijgewerkt. update_profile_success=Je profiel is bijgewerkt. change_username=Je gebruikersnaam is gewijzigd. change_username_prompt=Let op: Als je je gebruikersnaam aanpast, verandert je account-URL ook. +change_username_redirect_prompt=De oude gebruikersnaam wordt doorgestuurd tot deze wordt opgeëist. continue=Doorgaan cancel=Annuleren language=Taal ui=Thema +hidden_comment_types=Verborgen commentaartypes +comment_type_group_reference=Referentie +comment_type_group_label=Label +comment_type_group_milestone=Mijlpaal +comment_type_group_assignee=Aangewezene +comment_type_group_title=Titel +comment_type_group_branch=Branch +comment_type_group_time_tracking=Tijdregistratie +comment_type_group_deadline=Deadline +comment_type_group_dependency=Afhankelijkheid +comment_type_group_lock=Vergrendel Status +comment_type_group_review_request=Review aanvragen +comment_type_group_pull_request_push=Commits toegevoegd +comment_type_group_project=Project +comment_type_group_issue_ref=Referentie issue +saved_successfully=Uw instellingen zijn succesvol opgeslagen. privacy=Privacy keep_activity_private=De activiteit van de profielpagina verbergen keep_activity_private_popup=Maakt de activiteit alleen zichtbaar voor jou en de admins @@ -482,6 +584,7 @@ delete_current_avatar=Verwijder huidige avatar uploaded_avatar_not_a_image=Het geüploade bestand is geen afbeelding. uploaded_avatar_is_too_big=Het geüploade bestand heeft de maximale grootte overschreden. update_avatar_success=Je avatar is bijgewerkt. +update_user_avatar_success=De avatar van de gebruiker is bijgewerkt. change_password=Wachtwoord bijwerken old_password=Huidige wachtwoord @@ -535,13 +638,37 @@ ssh_helper=Weet u niet hoe? Lees dan onze handleiding voor het gpg_helper=Hulp nodig? Neem een kijkje op de GitHub handleiding over GPG. add_new_key=SSH sleutel toevoegen add_new_gpg_key=GPG sleutel toevoegen +key_content_ssh_placeholder=Begint met 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', of 'sk-ssh-ed25519@openssh.com' key_content_gpg_placeholder=Begint met '-----BEGIN PGP PUBLIC KEY BLOCK-----' add_new_principal=Verantwoordelijke toevoegen ssh_key_been_used=Deze SSH-sleutel is al toegevoegd aan de server. ssh_key_name_used=Er bestaat al een SSH sleutel met dezelfde naam in uw account. ssh_principal_been_used=Deze verantwoordelijke is al toegevoegd aan de server. gpg_key_id_used=Een publieke GPG-sleutel met dit ID bestaat al. +gpg_no_key_email_found=Deze GPG-sleutel komt met geen enkele geactiveerd e-mailadres dat aan uw account is gekoppeld overeen. Het kan nog steeds worden toegevoegd als u de opgegeven token tekent. +gpg_key_matched_identities=Overeenkomende identiteiten: +gpg_key_matched_identities_long=De ingesloten identiteiten in deze sleutel komen overeen met de geactiveerde e-mailadressen voor deze gebruiker. Commits die overeenkomen met deze e-mailadressen kunnen worden geverifieerd met deze sleutel. +gpg_key_verified=Geverifieerde sleutel +gpg_key_verified_long=Sleutel is geverifieerd met een token en kan worden gebruikt om commits te verifiëren die overeenkomen met alle geactiveerde e-mailadressen voor deze gebruiker naast de bijbehorende identiteiten voor deze sleutel. +gpg_key_verify=Verifiëren +gpg_invalid_token_signature=De opgegeven GPG-sleutel, handtekening en token komen niet overeen of de token is verouderd. +gpg_token_required=U moet een handtekening opgeven voor de onderstaande token gpg_token=Token +gpg_token_help=U kunt een handtekening genereren met: +gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig +gpg_token_signature=Gepantserde GPG-handtekening +key_signature_gpg_placeholder=Begint met '-----BEGIN PGP SIGNATURE-----' +verify_gpg_key_success=GPG-sleutel '%s' is geverifieerd. +ssh_key_verified=Geverifieerde sleutel +ssh_key_verified_long=Sleutel is geverifieerd met een token en kan worden gebruikt om commits te verifiëren die overeenkomen met alle geactiveerde e-mailadressen voor deze gebruiker. +ssh_key_verify=Verifiëren +ssh_invalid_token_signature=De verstrekte SSH-sleutel, handtekening of token komen niet overeen of de token is verouderd. +ssh_token_required=U moet een handtekening opgeven voor het onderstaande token +ssh_token=Token +ssh_token_help=U kunt een handtekening genereren door het volgende: +ssh_token_signature=Gepantserde SSH handtekening +key_signature_ssh_placeholder=Begint met '-----BEGIN SSH SIGNATURE-----' +verify_ssh_key_success=SSH sleutel '%s' is geverifieerd. subkeys=Subkeys key_id=Key-ID key_name=Sleutel naam @@ -589,6 +716,9 @@ generate_token_success=Je nieuwe token is gegenereerd. Kopieer hem nu, want hij generate_token_name_duplicate=%s is al gebruikt als een applicatienaam. Gebruik een nieuwe. delete_token=Verwijderen access_token_deletion=Verwijder toegangstoken +access_token_deletion_cancel_action=Annuleren +access_token_deletion_confirm_action=Verwijderen +access_token_deletion_desc=Als je een token verwijdert, heeft de applicatie die het gebruikt geen toegang meer tot je account. Doorgaan? delete_token_success=De token is verwijderd. Applicaties die hem gebruiken, verliezen toegang tot je account. manage_oauth2_applications=Beheer OAuth2-applicaties @@ -639,11 +769,18 @@ or_enter_secret=Of voer deze geheime code in: %s then_enter_passcode=En vul de toegangscode, die in de applicatie weergegeven wordt, in: passcode_invalid=De code is niet correct. Probeer het nogmaals. twofa_enrolled=Tweefactorsauthenticatie is geactiveerd voor dit account. Bewaar je token (%s) op een veilige plek, omdat hij maar één keer wordt weergegeven! +twofa_failed_get_secret=Kon geheim niet ophalen. +webauthn_desc=Beveiligingssleutels zijn hardware apparaten die cryptografische sleutels bevatten. Ze kunnen worden gebruikt voor tweestapsverificatie. Beveiligingssleutels moeten de WebAuthn Authenticator standaard ondersteunen. +webauthn_register_key=Voeg beveiligingssleutel toe +webauthn_nickname=Bijnaam +webauthn_delete_key=Verwijder beveiligingssleutel +webauthn_delete_key_desc=Als u een beveiligingssleutel verwijdert, kunt u er niet meer mee inloggen. Doorgaan? manage_account_links=Gekoppelde accounts beheren manage_account_links_desc=Deze externe accounts zijn gekoppeld aan je Gitea-account. account_links_not_available=Er zijn momenteel geen externe accounts aan je Gitea-account gelinkt. +link_account=Account koppelen remove_account_link=Gekoppeld account verwijderen remove_account_link_desc=Als je een gekoppeld account verwijdert, verliest dit account toegang tot je Gitea-account. Doorgaan? remove_account_link_success=Het gekoppelde account is verwijderd. @@ -653,6 +790,7 @@ repos_none=U bezit geen repositories delete_account=Verwijder uw account delete_prompt=Als je doorgaat, wordt je gebruikersaccount permanent verwijderd. Dit KAN NIET ongedaan gemaakt worden. +delete_with_all_comments=Uw account is jonger dan %s. Om spook opmerkingen te vermijden, worden alle issue/PR reacties er samen mee verwijderd. confirm_delete_account=Bevestig verwijdering delete_account_title=Verwijder gebruikers account delete_account_desc=Weet je zeker dat je dit gebruikersaccount permanent wil verwijderen? @@ -661,12 +799,18 @@ email_notifications.enable=E-mailnotificaties inschakelen email_notifications.onmention=Alleen e-mail op vermelding email_notifications.disable=E-mailnotificaties uitschakelen email_notifications.submit=E-mailvoorkeur instellen +email_notifications.andyourown=En je eigen notificaties +visibility=Gebruiker zichtbaarheid visibility.public=Openbaar +visibility.public_tooltip=Zichtbaar voor alle gebruikers visibility.limited=Beperkt +visibility.limited_tooltip=Alleen zichtbaar voor ingelogde gebruikers visibility.private=Privé +visibility.private_tooltip=Enkel zichtbaar voor organisatieleden [repo] +new_repo_helper=Een repository bevat alle projectbestanden, inclusief de revisiegeschiedenis. Heeft u het ergens anders al? Migreer repository. owner=Eigenaar owner_helper=Sommige organisaties kunnen niet worden weergegeven in de dropdown vanwege een limiet op het maximale aantal repositories. repo_name=Naam van repository @@ -684,31 +828,55 @@ visibility_fork_helper=(Verandering van deze waarde zal van invloed zijn op alle clone_helper=Heb je hulp nodig om te clonen? Bekijk dan de handleiding. fork_repo=Repository forken fork_from=Afsplitsing van +already_forked=Je hebt %s al geforked +fork_to_different_account=Fork naar een ander account fork_visibility_helper=De zichtbaarheid van een geforkte repository kan niet worden veranderd. use_template=Gebruik dit sjabloon +clone_in_vsc=Kloon in VS Code +download_zip=ZIP downloaden +download_tar=TAR.GZ downloaden +download_bundle=BUNDLE downloaden generate_repo=Repository genereren generate_from=Genereer van repo_desc=Omschrijving repo_desc_helper=Voer korte beschrijving in (optioneel) repo_lang=Taal repo_gitignore_helper=Selecteer .gitignore templates. +repo_gitignore_helper_desc=Kies welke bestanden niet bij te houden vanuit een lijst met sjablonen voor alledaagse talen. Gebruikelijke artefacten gegenereerd door de build tools van elke taal zijn standaard inbegrepen met .gitignore. issue_labels=Issuelabels issue_labels_helper=Selecteer een issuelabelset. license=Licentie license_helper=Selecteer een licentie bestand. +license_helper_desc=Een licentie bepaalt wat anderen wel en niet met je code kunnen doen. Niet zeker welke juist is voor jouw project? Zie Kies een licentie. readme=README readme_helper=Selecteer een README-bestandssjabloon. +readme_helper_desc=Dit is de plek waar je een volledige beschrijving van je project kunt schrijven. auto_init=Initialiseer repository (voegt .gitignore, License en README toe) +trust_model_helper=Selecteer het vertrouwensmodel voor handtekeningverificatie. Mogelijke opties zijn: +trust_model_helper_collaborator=Medewerker: Vertrouw handtekeningen door medewerkers +trust_model_helper_committer=Committer: Vertrouw handtekeningen die overeenkomen met de committers +trust_model_helper_collaborator_committer=Medewerker+Committer: Vertrouw handtekeningen door medewerkers die overeenkomen met de committer +trust_model_helper_default=Standaard: Gebruik het standaard vertrouwemsmodel voor deze installatie create_repo=Nieuwe repository default_branch=Standaard branch +default_branch_helper=De standaard branch is de basis branch voor pull requests en code commits. mirror_prune=Opschonen mirror_prune_desc=Verwijder verouderde remote-tracking-referenties +mirror_interval=Spiegel Interval (geldige tijdseenheden zijn 'h', 'm', 's'). 0 om automatische synchronisatie uit te schakelen (Minimum interval: %s) mirror_interval_invalid=Kloon-interval is niet geldig. +mirror_sync_on_commit=Synchroniseer wanneer commits gepusht worden mirror_address=Klonen van URL +mirror_address_desc=Voeg alle vereiste inloggegevens toe in de autorisatie sectie. mirror_address_url_invalid=De opgegeven url is ongeldig. U dient alle componenten van de url correct te escapen. mirror_address_protocol_invalid=De opgegeven url is ongeldig. Alleen http(s):// of git:// locaties kunnen worden gemirrord. +mirror_lfs=Grote bestandsopslag (LFS) +mirror_lfs_desc=Activeer spiegelen van LFS-gegevens. +mirror_lfs_endpoint=LFS Eindpunt +mirror_lfs_endpoint_desc=Synchronisatie zal proberen de kloon-url te gebruiken om de LFS-serverte bepalen. Je kan ook een aangepast eindpunt opgeven als de LFS-gegevens ergens anders zijn opgeslagen. mirror_last_synced=Laatst gesynchroniseerd +mirror_password_placeholder=(Ongewijzigd) mirror_password_blank_placeholder=(Niet ingesteld) +mirror_password_help=Wijzig de gebruikersnaam om een opgeslagen wachtwoord te wissen. watchers=Volgers stargazers=Stargazers forks=Forks @@ -725,7 +893,14 @@ delete_preexisting_label=Verwijderen delete_preexisting=Verwijder reeds bestaande bestanden delete_preexisting_content=Verwijder bestanden in %s delete_preexisting_success=Niet-geadopteerde bestanden verwijderd in %s +blame_prior=Bekijk de schuld voorafgaand aan deze verandering +transfer.accept=Accepteer overdracht +transfer.accept_desc=Overmaken naar "%s" +transfer.reject=Overdracht afwijzen +transfer.reject_desc=Annuleer overdracht naar "%s" +transfer.no_permission_to_accept=Je hebt geen toestemming om te accepteren +transfer.no_permission_to_reject=Je hebt geen toestemming om te weigeren desc.private=Privé desc.public=Openbaar @@ -738,6 +913,7 @@ desc.archived=Gearchiveerd template.items=Sjabloon items template.git_content=Git inhoud (standaard Branch) template.git_hooks=Git Hooks +template.git_hooks_tooltip=Je bent momenteel niet in staat om Git Hooks één keer te wijzigen of te verwijderen. Selecteer deze optie alleen als je de sjabloonrepository vertrouwt. template.webhooks=Webhooks template.topics=Onderwerpen template.avatar=Profielfoto @@ -749,11 +925,20 @@ archive.title=Deze repo is gearchiveerd. U kunt bestanden bekijken en het klonen archive.issue.nocomment=Deze repo is gearchiveerd. U kunt niet reageren op problemen. archive.pull.nocomment=Deze repo is gearchiveerd. U kunt niet reageren op pull requests. +form.reach_limit_of_creation_1=U heeft al uw limiet van %d repository bereikt. +form.reach_limit_of_creation_n=U heeft al uw limiet van %d repositories bereikt. form.name_reserved=Repositorienaam '%s' is gereserveerd. form.name_pattern_not_allowed=Het patroon '%s' is niet toegestaan in de naam van een repository. +need_auth=Autorisatie migrate_options=Migratie opties migrate_service=Migratie Service +migrate_options_mirror_helper=Deze repositorie zal een spiegel zijn +migrate_options_lfs=Migreer LFS bestanden +migrate_options_lfs_endpoint.label=LFS Eindpunt +migrate_options_lfs_endpoint.description=Migratie zal proberen om je Git remote te gebruiken om de LFS-server te bepalen. Je kan ook een aangepast eindpunt opgeven als de LFS-gegevens ergens anders zijn opgeslagen. +migrate_options_lfs_endpoint.description.local=Een lokaal serverpad wordt ook ondersteund. +migrate_options_lfs_endpoint.placeholder=Laat dit leeg om af te leiden uit de kloon-url migrate_items=Migratie Items migrate_items_wiki=Wiki migrate_items_milestones=Mijlpalen @@ -765,9 +950,12 @@ migrate_items_releases=Releases migrate_repo=Migreer repository migrate.clone_address=Migreer / kloon van URL migrate.clone_address_desc=De HTTP(s)- of 'git clone'-URL van een bestaande repository +migrate.github_token_desc=Je kunt hier een of meerdere tokens met komma gescheiden plaatsen om sneller te migreren door de GitHub API limiet te beperken. WAARSCHUWING: Het misbruik van deze functie kan in strijd zijn met het beleid van de serviceprovider en leiden tot het blokkeren van rekeningen. migrate.clone_local_path=of een lokaal pad migrate.permission_denied=U bent niet gemachtigd om deze lokale repositories te importeren. +migrate.permission_denied_blocked=Je kunt niet importeren uit niet-toegestane hosts, vraag de beheerder om de instellingen ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS te controleren. migrate.invalid_local_path=Het lokale pad is ongeldig, bestaat niet of is geen map. +migrate.invalid_lfs_endpoint=Het LFS-eindpunt is niet geldig. migrate.failed=Migratie is mislukt: %v migrate.migrate_items_options=Toegangstoken is vereist om extra items te migreren migrated_from=Gemigreerd van %[2]s @@ -776,6 +964,22 @@ migrate.migrate=Migreer van %s migrate.migrating=Migreren van %s... migrate.migrating_failed=Migreren van %s is mislukt. migrate.migrating_failed.error=Foutmelding: %s +migrate.migrating_failed_no_addr=Migratie is mislukt. +migrate.github.description=Gegevens overzetten van github.com of andere GitHub instanties. +migrate.git.description=Migreer een repositorie van elke Git service. +migrate.gitlab.description=Gegevens migreren van gitlab.com of andere GitLab-instanties. +migrate.gitea.description=Gegevens overzetten van gitea.com of andere Gitea instanties. +migrate.gogs.description=Gegevens overzetten van notabug.org of andere Gogs instanties. +migrate.onedev.description=Gegevens overzetten van code.onedev.io of andere OneDev instanties. +migrate.codebase.description=Gegevens migreren van codebasehq.com. +migrate.gitbucket.description=Gegevens migreren van GitBucket instanties. +migrate.migrating_git=Git gegevens migreren +migrate.migrating_topics=Onderwerpen migreren +migrate.migrating_milestones=Mijlpalen migreren +migrate.migrating_labels=Labels migreren +migrate.migrating_releases=Releases migreren +migrate.migrating_issues=Issues migreren +migrate.migrating_pulls=Pull-verzoeken migreren mirror_from=kopie van forked_from=geforked van @@ -797,6 +1001,7 @@ clone_this_repo=Kloon deze repository create_new_repo_command=Maak een nieuwe repository aan vanaf de console push_exist_repo=Push een bestaande repositorie vanaf de console empty_message=Deze repository bevat geen inhoud. +broken_message=De Git gegevens die ten grondslag liggen aan deze repository kunnen niet worden gelezen. Neem contact op met de beheerder van deze instantie of verwijder deze repository. code=Code code.desc=Toegang tot broncode, bestanden, commits en branches. @@ -804,11 +1009,13 @@ branch=Branch tree=Tree clear_ref=`Huidige referentie wissen` filter_branch_and_tag=Filter op branch of tag +find_tag=Label zoeken branches=Branches tags=Labels issues=Kwesties pulls=Pull-aanvragen project_board=Projecten +packages=Paketten labels=Labels org_labels_desc=Organisatielabel dat gebruikt kan worden met alle repositories onder deze organisatie org_labels_desc_manage=beheren @@ -816,23 +1023,41 @@ org_labels_desc_manage=beheren milestones=Mijlpalen commits=Commits commit=Commit +release=Release releases=Publicaties tag=Label +released_this=heeft dit gepubliceerd file.title=%s op %s file_raw=Ruw file_history=Geschiedenis +file_view_source=Bron weergeven +file_view_rendered=Weergave weergeven file_view_raw=Weergave ruw bestand file_permalink=Permalink file_too_large=Dit bestand is te groot om te tonen. - +bidi_bad_header=`Dit bestand bevat onverwachte Bidirectionele Unicode karakters!` +bidi_bad_description=`Dit bestand bevat onverwachte Bidirectionele Unicode-tekens die anders verwerkt kunnen worden dan hieronder het geval is. Als uw gebruik ervan opzettelijk en legitiem is, kunt u deze waarschuwing veilig negeren. Gebruik de Escape knop om verborgen karakters te onthullen.` +bidi_bad_description_escaped=`Dit bestand bevat onverwachte Bidirectionele Unicode-tekens. Verborgen unicode tekens worden hieronder ge-escaped. Gebruik de knop Onescape om te laten zien hoe ze renderen.` +unicode_header=`Dit bestand bevat verborgen Unicode karakters!` +unicode_description=`Dit bestand bevat verborgen Unicode-tekens die anders verwerkt kunnen worden dan hieronder het geval is. Als uw gebruik ervan opzettelijk en legitiem is, kunt u deze waarschuwing veilig negeren. Gebruik de Escape knop om verborgen karakters te onthullen.` +unicode_description_escaped=`Dit bestand bevat verborgen Unicode-tekens. Verborgen unicode tekens zijn hieronder ge-escaped. Gebruik de knop Onescape om te laten zien hoe ze renderen.` +line_unicode=`Deze regel bevat verborgen Unicode karakters` + +escape_control_characters=Escape +unescape_control_characters=Onescape +file_copy_permalink=Permalink kopiëren +view_git_blame=Bekijk Git Blame video_not_supported_in_browser=Je browser ondersteunt de HTML5 'video'-tag niet. audio_not_supported_in_browser=Je browser ondersteunt de HTML5 'audio'-tag niet. stored_lfs=Opgeslagen met Git LFS symbolic_link=Symbolic link commit_graph=Commit grafiek +commit_graph.select=Selecteer branches +commit_graph.hide_pr_refs=Verberg pull verzoeken commit_graph.monochrome=Monochroom commit_graph.color=Kleur blame=Blame +download_file=Download het bestand normal_view=Normale weergave line=regel lines=regels @@ -860,7 +1085,12 @@ editor.add_tmpl='' toevoegen editor.add='%s' toevoegen editor.update='%s' updaten editor.delete='%s' verwijderen +editor.patch=Patch toepassen +editor.patching=Patchen: +editor.fail_to_apply_patch=Kan patch '%s' niet toepassen +editor.new_patch=Nieuwe Patch editor.commit_message_desc=Voeg een optionele uitgebreide omschrijving toe… +editor.signoff_desc=Voeg een Signed-off-by toe aan het einde van het commit logbericht. editor.commit_directly_to_this_branch=Commit direct naar de branch '%s'. editor.create_new_branch=Maak een nieuwe branch voor deze commit en start van een pull-aanvraag. editor.create_new_branch_np=Maak een nieuwe branch voor deze commit. @@ -883,6 +1113,8 @@ editor.commit_empty_file_text=Het bestand dat u wilt committen is leeg. Doorgaan editor.no_changes_to_show=Er zijn geen wijzigingen om weer te geven. editor.fail_to_update_file=Bijwerken/aanmaken van bestand '%s ' mislukt. editor.fail_to_update_file_summary=Foutmelding: +editor.push_rejected_no_message=De wijziging is afgewezen door de server zonder bericht. Controleer de Git Hooks alsjeblieft. +editor.push_rejected=De wijziging is afgewezen door de server. Controleer Controleer de Git Hooks alsjeblieft. editor.push_rejected_summary=Volledig afwijzingsbericht: editor.add_subdir=Een map toevoegen… editor.unable_to_upload_files=Uploaden van bestand '%s' is mislukt: %v @@ -892,10 +1124,13 @@ editor.cannot_commit_to_protected_branch=Kan niet committen naar de beveiligde b editor.no_commit_to_branch=Kan niet rechtstreeks naar branch committen omdat: editor.user_no_push_to_branch=Gebruiker kan niet pushen naar branch editor.require_signed_commit=Branch vereist een ondertekende commit +editor.cherry_pick=Cherry-pick %s op: +editor.revert=%s ongedaan maken op: commits.desc=Bekijk de broncode-wijzigingsgeschiedenis. commits.commits=Commits commits.no_commits=Geen overeenkomstige commits. '%s' en '%s' hebben totaal verschillende histories. +commits.nothing_to_compare=Deze branches zijn gelijk. commits.search=Zoek commits… commits.search.tooltip=U kunt trefwoorden prefixen met "auteur:", "committer:", "na:" of "voor:", bv. "revert auteur:Alice voor:2019-0401". commits.find=Zoek @@ -909,11 +1144,23 @@ commits.signed_by=Getekend door commits.signed_by_untrusted_user=Ondertekend door niet-vertrouwde gebruiker commits.signed_by_untrusted_user_unmatched=Ondertekend door niet-vertrouwde gebruiker die niet overeenkomt met de committer commits.gpg_key_id=GPG sleutel-ID +commits.ssh_key_fingerprint=SSH sleutel vingerafdruk +commit.actions=Acties +commit.revert=Ongedaan maken +commit.revert-header=Maak %s ongedaan +commit.revert-content=Selecteer een branch om terug te zetten: +commit.cherry-pick=Cherry-pick +commit.cherry-pick-header=Cherry-pick: %s +commit.cherry-pick-content=Selecteer een branch om te cherry-pick op: +ext_issues=Toegang tot Externe Issues ext_issues.desc=Koppelen aan een externe kwestie-tracker. projects=Projecten +projects.desc=Beheer issues en pulls in projectborden. +projects.description=Omschrijving (optioneel) +projects.description_placeholder=Omschrijving projects.create=Project aanmaken projects.title=Titel projects.new=Nieuw project @@ -937,11 +1184,14 @@ projects.board.edit_title=Nieuwe boardnaam projects.board.new_title=Nieuwe boardnaam projects.board.new_submit=Versturen projects.board.new=Nieuw bord +projects.board.set_default=Instellen als standaard +projects.board.set_default_desc=Stel dit board in als standaard voor niet gecategoriseerde issues en pulls projects.board.delete=Verwijder bord projects.board.deletion_desc=Als een projectbord wordt verwijdert, worden alle gerelateerde kwesties naar 'Ongecategoriseerd' verplaatst. Doorgaan? projects.board.color=Kleur projects.open=Open projects.close=Sluiten +projects.board.assigned_to=Toegewezen aan issues.desc=Organiseer bugrapporten, taken en mijlpalen. issues.filter_assignees=Filter verantwoordelijke @@ -988,6 +1238,10 @@ issues.label_templates.info=Er bestaan nog geen labels. Maak een nieuw label met issues.label_templates.helper=Selecteer een labelset issues.label_templates.use=Label Set gebruiken issues.label_templates.fail_to_load_file=Kan het labelsjabloonbestand '%s' niet openen: %v +issues.add_label=voegde het %s label %s toe +issues.add_labels=voegde de %s labels %s toe +issues.remove_label=verwijderde het %s label %s +issues.remove_labels=verwijderde de %s labels %s issues.add_milestone_at=`heeft dit %[2]s aan de mijlpaal %[1]s toegevoegd` issues.add_project_at=`heeft dit toegevoegd aan het %s project %s` issues.change_milestone_at='mijlpaal bewerkt van %s %s %s' @@ -1151,6 +1405,7 @@ issues.dependency.remove=Verwijder issues.dependency.remove_info=Verwijder afhankelijkheid issues.dependency.added_dependency=`voegde een nieuwe afhankelijkheid %s toe ` issues.dependency.removed_dependency=`verwijderde een afhankelijkheid %s` +issues.dependency.issue_closing_blockedby=Het sluiten van dit issue is geblokkeerd door de volgende problemen issues.dependency.issue_close_blocks=Deze kwestie blokkeert het sluiten van de volgende kwesties issues.dependency.pr_close_blocks=Deze pull-aanvraag blokkeert het sluiten van de volgende kwesties issues.dependency.issue_close_blocked=Je moet alle kwesties die deze kwestie blokkeren sluiten voordat je deze kan sluiten. @@ -1171,6 +1426,8 @@ issues.review.self.approval=Je kan je eigen pull-aanvraag niet goedkeuren. issues.review.self.rejection=Je kan geen wijzigingen aanvragen op je eigen pull-aanvraag. issues.review.approve=heeft deze veranderingen %s goedgekeurd issues.review.comment=beoordeeld %s +issues.review.dismissed=%s's beoordeling afgewezen %s +issues.review.dismissed_label=Afgewezen issues.review.left_comment=heeft een reactie achtergelaten issues.review.content.empty=Je moet een reactie achterlaten die de gewenste verandering(en) beschrijft. issues.review.reject=aangevraagde wijzigingen %s @@ -1179,6 +1436,7 @@ issues.review.add_review_request=heeft een review aangevraagd van %s %s issues.review.remove_review_request=beoordelingsaanvraag voor %s %s verwijderd issues.review.remove_review_request_self=beoordeling geweigerd %s issues.review.pending=In behandeling +issues.review.pending.tooltip=Deze reactie is momenteel niet zichtbaar voor andere gebruikers. Selecteer '%s' -> '%s/%s/%s' ' boven aan de pagina. issues.review.review=Review issues.review.reviewers=Reviewers issues.review.outdated=Verouderd @@ -1190,21 +1448,38 @@ issues.review.resolve_conversation=Gesprek oplossen issues.review.un_resolve_conversation=Gesprek niet oplossen issues.review.resolved_by=markeerde dit gesprek als opgelost issues.assignee.error=Niet alle aangewezen personen zijn toegevoegd vanwege een onverwachte fout. +issues.reference_issue.body=Inhoud issues.content_history.deleted=verwijderd issues.content_history.edited=bewerkt +issues.content_history.created=gecreëerd +issues.content_history.delete_from_history=Uit geschiedenis verwijderen +issues.content_history.delete_from_history_confirm=Uit geschiedenis verwijderen? +issues.content_history.options=Opties +issues.reference_link=Referentie: %s compare.compare_base=basis compare.compare_head=vergelijk pulls.desc=Schakel pull-aanvragen en code-beoordelingen in. pulls.new=Nieuwe Pull aanvraag +pulls.view=Pull verzoek bekijken pulls.compare_changes=Nieuwe pull-aanvraag +pulls.allow_edits_from_maintainers=Bewerkingen toestaan van maintainers +pulls.allow_edits_from_maintainers_desc=Gebruikers met schrijftoegang tot de basis branch kunnen ook pushen naar deze branch +pulls.allow_edits_from_maintainers_err=Updaten mislukt pulls.compare_changes_desc=Selecteer de samen te voegen doel- en bron-branch. +pulls.has_viewed_file=Gezien +pulls.has_changed_since_last_review=Veranderd sinds de laatste beoordeling +pulls.viewed_files_label=%[1]d / %[2]d bestanden bekeken pulls.compare_base=samenvoegen met pulls.compare_compare=trekken van +pulls.switch_comparison_type=Wissel vergelijking type +pulls.switch_head_and_base=Verwissel hoofd en basis pulls.filter_branch=Filter branch pulls.no_results=Geen resultaten gevonden. pulls.nothing_to_compare=Deze branches zijn gelijk. Er is geen pull-aanvraag nodig. +pulls.nothing_to_compare_and_allow_empty_pr=Deze branches zijn gelijk. Deze pull verzoek zal leeg zijn. +pulls.has_pull_request=`Een pull-verzoek tussen deze branches bestaat al: %[2]s#%[3]d` pulls.create=Pull verzoek aanmaken pulls.title_desc=wil %[1]d commits van %[2]s samenvoegen met %[3]s pulls.merged_title_desc=heeft %[1]d commits samengevoegd van %[2]s naar %[3]s %[4]s @@ -1216,17 +1491,26 @@ pulls.reopen_to_merge=Heropen dit pull request aub om een een merge actie uit te pulls.cant_reopen_deleted_branch=Deze pull-aanvraag kan niet opnieuw worden geopend omdat de branch is verwijderd. pulls.merged=Samengevoegd pulls.merged_as=De pull request is samengevoegd als %[2]s. +pulls.manually_merged=Handmatig samengevoegd +pulls.manually_merged_as=Het pull-verzoek is handmatig samengevoegd als %[2]s. pulls.is_closed=Deze pull-aanvraag is gesloten. pulls.has_merged=Deze pull-aanvraag is al samengevoegd. pulls.title_wip_desc=`Start de titel met %s om te voorkomen dat deze pull-aanvraag per ongeluk wordt samengevoegd.` +pulls.cannot_merge_work_in_progress=Dit pull request is gemarkeerd als werk in uitvoering. +pulls.still_in_progress=Nog steeds bezig? +pulls.add_prefix=Voeg %s prefix toe +pulls.remove_prefix=Verwijder %s prefix pulls.data_broken=Deze pull-aanvraag is ongeldig wegens missende fork-informatie. pulls.files_conflicted=Dit pull request heeft wijzigingen die strijdig zijn met de doel branch. pulls.is_checking=Controle op samenvoegingsconflicten is nog bezig. Probeer later nog een keer. +pulls.is_ancestor=Deze branch is al opgenomen in de toegewezen branch. Er is niets om samen te voegen. +pulls.is_empty=De wijzigingen in deze branch bevinden zich al in de toegewezen branch. Dit zal een lege commit zijn. pulls.required_status_check_failed=Sommige vereiste controles waren niet succesvol. pulls.required_status_check_missing=Er ontbreken enkele vereiste controles. pulls.required_status_check_administrator=Als een beheerder kunt u deze pull-aanvraag nog samenvoegen. pulls.blocked_by_approvals=Deze pull-aanvraag heeft nog niet genoeg goedkeuringen. %d van de %d goedkeuringen zijn gegeven. pulls.blocked_by_rejection=Deze pull-aanvraag heeft wijzigingen aangevraagd door een officiële beoordelaar. +pulls.blocked_by_official_review_requests=Dit pull-verzoek heeft officiële beoordelingsverzoeken. pulls.blocked_by_outdated_branch=Deze pull-aanvraag is geblokkeerd omdat het verouderd is. pulls.blocked_by_changed_protected_files_1=Deze pull-aanvraag is geblokkeerd omdat het een beschermd bestand veranderd: pulls.blocked_by_changed_protected_files_n=Deze pull-aanvraag is geblokkeerd omdat het beschermde bestanden veranderd: @@ -1241,12 +1525,19 @@ pulls.reject_count_1=%d wijzigingsverzoek pulls.reject_count_n=%d wijzigingsverzoeken pulls.waiting_count_1=%d wachtende beoordeling pulls.waiting_count_n=%d wachtende beoordelingen +pulls.wrong_commit_id=commit id moet een commit id zijn op de doelbranch pulls.no_merge_desc=Deze pull-aanvraag kan niet worden samengevoegd, omdat alle samenvoegingsopties zijn uitgeschakeld. pulls.no_merge_helper=Schakel samenvoegingsopties in in de repositoryinstellingen of voeg de pull-aanvraag handmatig samen. pulls.no_merge_wip=Deze pull-aanvraag kan niet worden samengevoegd omdat hij als "work in progress" is gemarkeerd. pulls.no_merge_not_ready=Deze pull-aanvraag is niet klaar om samen te voegen, controleer de status en status controles. pulls.no_merge_access=Je bent niet gemachtigd om deze pull-aanvraag samen te voegen. +pulls.merge_pull_request=Maak samenvoeg-commit +pulls.rebase_merge_pull_request=Herbaseren dan snel-voorwaarts +pulls.rebase_merge_commit_pull_request=Herbaseren dan samenvoeg-commit maken +pulls.squash_merge_pull_request=Maak samenvoeg-commit +pulls.merge_manually=Handmatig samengevoegd +pulls.merge_commit_id=De merge commit ID pulls.require_signed_wont_sign=De branch heeft ondertekende commits nodig, maar deze merge zal niet worden ondertekend pulls.invalid_merge_option=Je kan de samenvoegingsoptie niet gebruiken voor deze pull-aanvraag. @@ -1257,7 +1548,10 @@ pulls.rebase_conflict_summary=Foutmelding ; %[2]s
%[3]s
pulls.unrelated_histories=Samenvoegen mislukt: de HEAD en base delen geen gemeenschappelijke geschiedenis. Tip: Probeer een andere strategie pulls.merge_out_of_date=Samenvoegen mislukt: Tijdens het samenvoegen is de basis bijgewerkt. Tip: Probeer het opnieuw. +pulls.head_out_of_date=Samenvoegen mislukt: tijdens het genereren van de samenvoeging is de kop bijgewerkt. Tip: Probeer het opnieuw. +pulls.push_rejected=Samenvoegen mislukt: De push is geweigerd. Controleer de Git Hooks voor deze repository. pulls.push_rejected_summary=Volledig afwijzingsbericht +pulls.push_rejected_no_message=Samenvoegen mislukt: De push is afgewezen, maar er was geen extern bericht.
Controleer de Git Hooks voor deze repository pulls.open_unmerged_pull_exists=`Je kan deze pull-aanvraag niet opnieuw openen omdat er een andere (#%d) met identieke eigenschappen open staat.` pulls.status_checking=Sommige controles zijn in behandeling pulls.status_checks_success=Alle checks waren succesvol @@ -1266,13 +1560,23 @@ pulls.status_checks_failure=Sommige controles zijn mislukt pulls.status_checks_error=Sommige controles hebben foutmeldingen gerapporteerd pulls.status_checks_requested=Vereist pulls.status_checks_details=Details +pulls.update_branch=Update branch via samenvoegen +pulls.update_branch_rebase=Update branch via herbaseren pulls.update_branch_success=Branch update is geslaagd pulls.update_not_allowed=Je hebt geen toestemming om branch bij te werken pulls.outdated_with_base_branch=Deze branch is verouderd met de basis branch pulls.closed_at=`heeft deze pull request gesloten %[2]s` pulls.reopened_at=`heropende deze pull request %[2]s` +pulls.merge_instruction_hint=`Je kunt ook command line instructies bekijken.` +pulls.merge_instruction_step1_desc=Vanuit het project, check een branch uit en test de veranderingen. +pulls.merge_instruction_step2_desc=Voeg de wijzigingen samen en update ze op Gitea. +pulls.auto_merge_button_when_succeed=(Bij geslaagde controles) +pulls.auto_merge_when_succeed=Automatisch samenvoegen wanneer alle controles gelukt zijn +pulls.auto_merge_newly_scheduled=De pull-verzoek was gepland om samen te voegen wanneer alle controles geslaagd zijn. +pulls.auto_merge_has_pending_schedule=%[1]s heeft deze pull-verzoek automatisch samengevoegd wanneer alle checks succesvol zijn geweest %[2]s. +pulls.auto_merge_cancel_schedule=Automatisch samenvoegen annuleren @@ -1430,12 +1734,9 @@ settings.mirror_settings=Kopie Settings settings.mirror_settings.direction=Richting settings.mirror_settings.direction.pull=Pull settings.mirror_settings.direction.push=Push +settings.mirror_settings.push_mirror.none=Geen spiegels geconfigureerd settings.sync_mirror=Synchroniseer settings.mirror_sync_in_progress=Mirror-synchronisatie is momenteel bezig - kom later terug. -settings.email_notifications.enable=E-mailnotificaties inschakelen -settings.email_notifications.onmention=Alleen e-mail op vermelding -settings.email_notifications.disable=E-mailnotificaties uitschakelen -settings.email_notifications.submit=E-mailvoorkeur instellen settings.site=Website settings.update_settings=Instellingen bewerken settings.advanced_settings=Geavanceerde opties @@ -1615,6 +1916,22 @@ settings.hook_type=Type hook settings.slack_token=Slack token settings.slack_domain=Slack domein settings.slack_channel=Slack kanaal +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_telegram=Telegram +settings.web_hook_name_matrix=Matrix +settings.web_hook_name_msteams=Microsoft Teams +settings.web_hook_name_feishu_or_larksuite=Feishu / Lark Suite +settings.web_hook_name_feishu=Feishu +settings.web_hook_name_larksuite=Lark Suite +settings.web_hook_name_wechatwork=WeCom (Wechat Work) +settings.web_hook_name_packagist=Packagist +settings.packagist_username=Packagist gebruikersnaam +settings.packagist_api_token=API token +settings.packagist_package_url=Packagist pakket URL settings.deploy_keys=Installeer sleutels settings.add_deploy_key=Toevoegen deploy sleutel settings.deploy_key_desc=Deploy keys hebben alleen-lezen pull-toegang tot de repository. @@ -1653,6 +1970,7 @@ settings.protect_merge_whitelist_committers_desc=Sta alleen gebruikers of teams settings.protect_merge_whitelist_users=Toegestane gebruikers voor samenvoegen: settings.protect_merge_whitelist_teams=Toegestane teams voor samenvoegen: settings.protect_check_status_contexts=Status controle inschakelen +settings.protect_check_status_contexts_desc=Statuscontroles zijn vereist om te kunnen samenvoegen. Kies welke statuscontroles moeten slagen voordat branches kunnen worden samengevoegd tot een branch die aan deze regel voldoet. Wanneer ingeschakeld, moeten commits eerst naar een andere branch worden gepusht, vervolgens samengevoegd of gepusht worden naar een branch die overeenkomt met deze regel nadat de statuscontroles zijn uitgevoerd. Als er geen contexten worden geselecteerd, moet de laatste commit succesvol zijn, ongeacht de context. settings.protect_check_status_contexts_list=Status controles gevonden in de afgelopen week voor deze repository settings.protect_required_approvals=Vereiste goedkeuringen: settings.protect_required_approvals_desc=Sta alleen toe om pull request samen te voegen met voldoende positieve beoordelingen. @@ -1665,6 +1983,9 @@ settings.dismiss_stale_approvals_desc=Wanneer nieuwe commits die de inhoud van h settings.require_signed_commits=Ondertekende Commits vereisen settings.require_signed_commits_desc=Weiger pushes naar deze branch als deze niet ondertekend of niet verifieerbaar is. settings.protect_protected_file_patterns=Beschermde bestandspatronen (gescheiden door een puntkomma '\;'): +settings.protect_protected_file_patterns_desc=Beschermde bestanden die niet direct gewijzigd mogen worden, zelfs als de gebruiker het recht heeft om bestanden in deze branch toe te voegen, te bewerken of te verwijderen. Meerdere patronen kunnen worden gescheiden met een puntkomma ('\;'). Zie github.com/gobwas/glob documentatie voor patroon syntax. Voorbeelden: .drone.yml, /docs/**/*.txt. +settings.protect_unprotected_file_patterns=Onbeschermde bestandspatronen (gescheiden met een puntkomma '\;'): +settings.protect_unprotected_file_patterns_desc=Onbeschermde bestanden die direct mogen worden gewijzigd als gebruiker schrijfrechten heeft, waardoor push-beperking wordt omzeild. Meerdere patronen kunnen worden gescheiden met behulp van een puntkomma ('\;'). Zie github.com/gobwas/glob documentatie voor patroon syntax. Voorbeelden: .drone.yml, /docs/**/*.txt. settings.add_protected_branch=Bescherming aanzetten settings.delete_protected_branch=Bescherming uitzetten settings.update_protect_branch_success=Branch bescherming voor branch '%s' is bijgewerkt. @@ -1673,16 +1994,26 @@ settings.protected_branch_deletion=Branch bescherming uitschakelen settings.protected_branch_deletion_desc=Branch bescherming uitschakelen zorgt ervoor dat gebruikers met schrijfrechten naar de branch kunnen pushen. Doorgaan? settings.block_rejected_reviews=Samenvoegen van afgewezen beoordelingen blokkeren settings.block_rejected_reviews_desc=Samenvoegen zal niet mogelijk zijn wanneer er wijzigingen worden aangevraagd door officiële beoordelaars, zelfs niet als er genoeg goedkeuringen zijn. +settings.block_on_official_review_requests=Blokkeer de samenvoeging van officiële beoordelingsverzoeken +settings.block_on_official_review_requests_desc=Samenvoegen is niet mogelijk wanneer het officiële herzieningsverzoeken heeft, ook al zijn er genoeg goedkeuringen. settings.block_outdated_branch=Samenvoegen blokkeren als pull request verouderd is settings.block_outdated_branch_desc=Samenvoegen is niet mogelijk als de hoofd branch achter loop op de basis branch. settings.default_branch_desc=Selecteer een standaard repository branch voor pull requests en code commits: +settings.default_merge_style_desc=Standaard samenvoegstijl voor pull verzoeken: settings.choose_branch=Kies een branch… settings.no_protected_branch=Er zijn geen beschermde branches. settings.edit_protected_branch=Bewerken settings.protected_branch_required_approvals_min=Vereiste goedkeuringen kunnen niet negatief zijn. settings.tags=Labels +settings.tags.protection=Label Bescherming +settings.tags.protection.pattern=Label Patroon settings.tags.protection.allowed=Toegestaan +settings.tags.protection.allowed.users=Toegestane gebruikers +settings.tags.protection.allowed.teams=Toegestane teams settings.tags.protection.allowed.noone=Niemand +settings.tags.protection.create=Beveilig Label +settings.tags.protection.none=Er zijn geen beveiligde labels. +settings.tags.protection.pattern.description=U kunt een enkele naam gebruiken of een glob patroon of reguliere expressie om meerdere labels te matchen. Lees meer in de beschermde labels gids. settings.bot_token=Bot Token settings.chat_id=Chat-ID settings.matrix.homeserver_url=Homeserver URL @@ -1696,6 +2027,7 @@ settings.archive.success=De repo is succesvol gearchiveerd. settings.archive.error=Er is een fout opgetreden tijdens het archiveren van de repo. Zie het logboek voor meer informatie. settings.archive.error_ismirror=U kunt geen gespiegelde repo archiveren. settings.archive.branchsettings_unavailable=Branch instellingen zijn niet beschikbaar als de repo is gearchiveerd. +settings.archive.tagsettings_unavailable=Labelinstellingen zijn niet beschikbaar als de repo is gearchiveerd. settings.unarchive.button=Repo De-Archiveren settings.unarchive.header=Deze Repo de-archiveren settings.unarchive.text=De-Archiveren van de repo herstelt zijn vermogen om commits en pushes te ontvangen, evenals nieuwe problemen en pull-requests. @@ -1727,6 +2059,12 @@ settings.lfs_pointers.inRepo=In Repo settings.lfs_pointers.exists=Bestaat in opslag settings.lfs_pointers.accessible=Toegankelijk voor gebruiker settings.lfs_pointers.associateAccessible=Koppel toegankelijke %d OIDs +settings.rename_branch_failed_exist=Kan branch niet hernoemen omdat doel branch %s bestaat. +settings.rename_branch_failed_not_exist=Kan branch %s niet hernoemen omdat deze niet bestaat. +settings.rename_branch_success=Branch %s is succesvol hernoemd naar %s. +settings.rename_branch_from=oude branch naam +settings.rename_branch_to=nieuwe branch naam +settings.rename_branch=Hernoem branch diff.browse_source=Bladeren bron diff.parent=bovenliggende @@ -1745,7 +2083,9 @@ diff.whitespace_ignore_all_whitespace=Witruimte negeren bij het vergelijken van diff.whitespace_ignore_amount_changes=Negeer veranderingen in de hoeveelheid witruimte diff.whitespace_ignore_at_eol=Negeren van wijzigingen in witruimte op EOL diff.stats_desc=%d gewijzigde bestanden met toevoegingen van %d en %d verwijderingen +diff.stats_desc_file=%d wijzigingen: %d toevoegingen en %d verwijderingen diff.bin=BIN +diff.bin_not_shown=Binair bestand niet weergegeven. diff.view_file=Bestand weergeven diff.file_before=Voor diff.file_after=Na @@ -1753,6 +2093,12 @@ diff.file_image_width=Breedte diff.file_image_height=Hoogte diff.file_byte_size=Grootte diff.file_suppressed=Diff onderdrukt omdat het te groot bestand +diff.file_suppressed_line_too_long=Bestand-diff onderdrukt omdat een of meer regels te lang zijn +diff.too_many_files=Sommige bestanden werden niet getoond omdat er teveel bestanden zijn veranderd in deze diff +diff.show_more=Meer weergeven +diff.load=Laad Diff +diff.generated=gegenereerd +diff.vendored=vendored diff.comment.placeholder=Opmerking toevoegen diff.comment.markdown_info=Styling met markdown wordt ondersteund. diff.comment.add_single_comment=Één reactie toevoegen @@ -1767,13 +2113,20 @@ diff.review.approve=Goedkeuren diff.review.reject=Wijzigingen aanvragen diff.committed_by=gecommit door diff.protected=Beveiligd +diff.image.side_by_side=Zij aan zij +diff.image.swipe=Vegen +diff.image.overlay=Overlay +diff.has_escaped=Deze regel heeft verborgen Unicode-tekens releases.desc=Volg de projectversies en downloads. release.releases=Publicaties +release.detail=Release details +release.tags=Labels release.new_release=Nieuwe release release.draft=Concept release.prerelease=Voorlopige versie release.stable=Stabiel +release.compare=Vergelijk release.edit=bewerken release.ahead.commits=%d commits release.ahead.target=aan %s sinds deze release @@ -1954,6 +2307,7 @@ total=Totaal: %d dashboard.statistic=Overzicht dashboard.operations=Onderhoudswerkzaamheden dashboard.system_status=Systeemtatus +dashboard.statistic_info=De Gitea database heeft %d gebruikers, %d organisaties, %d openbare sleutels, %d repositories, %d volgers, %d sterren, %d acties, %d participanten, %d issues, %d reacties, %d sociale accounten, %d volgers, %d spiegels, %d publicaties, %d authenticatiebronnen, %d webhooks, %d mijlpalen, %d labels, %d hook taken, %d teams, %d bijgewerkte taken, %d bijlagen. dashboard.operation_name=Bewerking naam dashboard.operation_switch=Omschakelen dashboard.operation_run=Uitvoeren @@ -2019,6 +2373,7 @@ dashboard.total_gc_time=Totaal GC verwerkingstijd dashboard.total_gc_pause=Totaal GC verwerkingstijd dashboard.last_gc_pause=Laatste GC verwerkingstijd dashboard.gc_times=GC verwerkingen +dashboard.delete_old_system_notices=Verwijder alle oude systeemmededelingen uit de database users.user_manage_panel=Gebruikersaccount beheer users.new_account=Nieuw account aanmaken @@ -2068,7 +2423,7 @@ emails.filter_sort.email=E-mail emails.filter_sort.email_reverse=E-mail (omgekeerd) emails.filter_sort.name=Gebruikersnaam emails.filter_sort.name_reverse=Gebruikersnaam (omgekeerd) -emails.updated=E-mail bijgewerkt +emails.updated=E-mailadres bijgewerkt emails.not_updated=Bijwerken van het gevraagde e-mailadres is mislukt: %v emails.duplicate_active=Dit e-mailadres is al actief voor een andere gebruiker. emails.change_email_header=Update E-mail Eigenschappen @@ -2258,7 +2613,7 @@ config.mailer_use_sendmail=Gebruik Sendmail config.mailer_sendmail_path=Sendmail pad config.mailer_sendmail_args=Extra argumenten voor Sendmail config.mailer_sendmail_timeout=Sendmail time-out -config.test_email_placeholder=E-mail (bijv. test@example.com) +config.test_email_placeholder=E-mailadres (bijv. test@example.com) config.send_test_mail=Test e-mail verzenden config.test_mail_failed=Verzenden van een testmail naar '%s' is mislukt: %v config.test_mail_sent=Test-email is verstuurd naar '%s'. @@ -2319,6 +2674,7 @@ monitor.process=Draaiende processen monitor.desc=Omschrijving monitor.start=Starttijd monitor.execute_time=Uitvoertijd +monitor.last_execution_result=Resultaat monitor.process.cancel=Annuleer proces monitor.process.cancel_desc=Annuleren van een proces kan gegevensverlies veroorzaken monitor.process.cancel_notices=Annuleer: %s? @@ -2447,4 +2803,8 @@ error.no_unit_allowed_repo=U heeft geen toegang tot een enkele sectie van deze r error.unit_not_allowed=U heeft geen toegang tot deze sectie van de repository. [packages] +assets=Assets +rubygems.required.ruby=Vereist Ruby versie +rubygems.required.rubygems=Vereist RubyGem versie +settings.link.button=Repository link bijwerken diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 5e6c078b2d02..75c3d6287c3d 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -1610,10 +1610,6 @@ settings.mirror_settings.push_mirror.none=Brak skonfigurowanych kopii zapasowych settings.mirror_settings.push_mirror.remote_url=Adres URL zdalnego repozytorium Git settings.sync_mirror=Synchronizuj teraz settings.mirror_sync_in_progress=Synchronizacja kopii lustrzanych jest w toku. Sprawdź ponownie za minutę. -settings.email_notifications.enable=Włącz powiadomienia e-mail -settings.email_notifications.onmention=Wyślij wiadomość e-mail wyłącznie przy wzmiankach -settings.email_notifications.disable=Wyłącz powiadomienia e-mail -settings.email_notifications.submit=Ustaw preferencje wiadomości e-mail settings.site=Strona settings.update_settings=Aktualizuj ustawienia settings.branches.update_default_branch=Aktualizuj domyślną gałąź diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 72262c608655..99d8fb2db18a 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -861,6 +861,7 @@ default_branch=Branch Padrão default_branch_helper=O branch padrão é o branch base para pull requests e commits de código. mirror_prune=Varrer mirror_prune_desc=Remover referências obsoletas de controle remoto +mirror_interval=Intervalo de espelhamento (unidades válidas são 'h', 'm', ou 's'). O desabilita a sincronização automática. (Intervalo mínimo: %s) mirror_interval_invalid=O intervalo do espelhamento não é válido. mirror_sync_on_commit=Sincronizar quando commits forem enviados mirror_address=Clonar de URL @@ -1419,6 +1420,7 @@ issues.due_date_form_remove=Remover issues.due_date_not_writer=Você deve ter permissão de escrita no repositório para atualizar a data limite de uma issue. issues.due_date_not_set=Data limite não informada. issues.due_date_added=adicionou a data limite %s %s +issues.due_date_modified=modificou a data limite de %[2]para %[1]s %[3]s issues.due_date_remove=removeu a data limite %s %s issues.due_date_overdue=Em atraso issues.due_date_invalid=A data limite é inválida ou está fora do intervalo. Por favor, use o formato 'dd/mm/aaaa'. @@ -1530,6 +1532,7 @@ pulls.remove_prefix=Remover o prefixo %s pulls.data_broken=Este pull request está quebrado devido a falta de informação do fork. pulls.files_conflicted=Este pull request tem alterações conflitantes com o branch de destino. pulls.is_checking=Verificação de conflitos do merge está em andamento. Tente novamente em alguns momentos. +pulls.is_empty=As alterações neste branch já estão na branch de destino. Este será um commit vazio. pulls.required_status_check_failed=Algumas verificações necessárias não foram bem sucedidas. pulls.required_status_check_missing=Estão faltando algumas verificações necessárias. pulls.required_status_check_administrator=Como administrador, você ainda pode aplicar o merge deste pull request. @@ -1597,10 +1600,14 @@ pulls.merge_instruction_step1_desc=No repositório do seu projeto, crie um novo pulls.merge_instruction_step2_desc=Faça merge das alterações e atualize no Gitea. pulls.auto_merge_button_when_succeed=(Quando a verificação for bem-sucedida) +pulls.auto_merge_when_succeed=Mesclar automaticamente quando todas as verificações forem bem sucedidas pulls.auto_merge_newly_scheduled=O merge do pull request foi agendado para quando todas as verificações forem bem-sucedidas. pulls.auto_merge_cancel_schedule=Cancelar merge automático +pulls.auto_merge_not_scheduled=Este pull request não está programado para ser automaticamente mesclado. +pulls.auto_merge_canceled_schedule=O merge automático foi cancelado para este pull request. +pulls.auto_merge_canceled_schedule_comment=`cancelou o merge automático deste pull request quando todos as verificações tiverem sucesso %[1]s` pulls.delete.title=Excluir este pull request? pulls.delete.text=Você realmente deseja excluir este pull request? (Isto irá remover permanentemente todo o conteúdo. Considere fechá-la em vez disso, se você pretende mantê-la arquivado) @@ -1774,10 +1781,6 @@ settings.mirror_settings.push_mirror.remote_url=URL do repositório do Git remot settings.mirror_settings.push_mirror.add=Adicionar Espelho de Push settings.sync_mirror=Sincronizar agora settings.mirror_sync_in_progress=Sincronização do espelhamento está em andamento. Verifique novamente em um minuto. -settings.email_notifications.enable=Habilitar notificações de e-mail -settings.email_notifications.onmention=Somente e-mail com menção -settings.email_notifications.disable=Desabilitar notificações de e-mail -settings.email_notifications.submit=Atualizar preferências de e-mail settings.site=Site settings.update_settings=Atualizar configurações settings.branches.update_default_branch=Atualizar Branch Padrão @@ -2526,6 +2529,8 @@ users.delete_account=Excluir conta de usuário users.cannot_delete_self=Você não pode excluir você mesmo 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.purge=Eliminar usuário +users.purge_help=Exclua forçosamente o usuário e quaisquer repositórios, organizações e pacotes pertencentes ao usuário. Todos os comentários também serão excluídos. 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 diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index b18bb0cbb820..57254925c488 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -799,6 +799,7 @@ email_notifications.enable=Habilitar notificações por email email_notifications.onmention=Enviar email somente quando mencionado(a) email_notifications.disable=Desabilitar notificações por email email_notifications.submit=Definir preferência do email +email_notifications.andyourown=e as suas próprias notificações visibility=Visibilidade do utilizador visibility.public=Pública @@ -1784,10 +1785,6 @@ settings.mirror_settings.push_mirror.remote_url=URL do repositório remoto Git settings.mirror_settings.push_mirror.add=Adicionar réplica de envio settings.sync_mirror=Sincronizar agora settings.mirror_sync_in_progress=A sincronização da réplica está em andamento. Volte a verificar daqui a um minuto. -settings.email_notifications.enable=Habilitar notificações por email -settings.email_notifications.onmention=Enviar email somente quando mencionado(a) -settings.email_notifications.disable=Desabilitar notificações por email -settings.email_notifications.submit=Definir preferência do email settings.site=Sítio web settings.update_settings=Modificar configurações settings.branches.update_default_branch=Definir o ramo principal @@ -3044,6 +3041,7 @@ title=Pacotes desc=Gerir pacotes do repositório. empty=Ainda não há pacotes. empty.documentation=Para obter mais informação sobre o registo de pacotes, veja a documentação. +empty.repo=Carregou um pacote mas este não é apresentado aqui? Vá às configurações do pacote e ligue-o a este repositório. filter.type=Tipo filter.type.all=Todos filter.no_result=O seu filtro não produziu quaisquer resultados. diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index e95ceb24531d..ad8ee86d02e1 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -2,6 +2,7 @@ home=Главная dashboard=Панель управления explore=Обзор help=Помощь +logo=Логотип sign_in=Вход sign_in_with=Войдите с помощью sign_out=Выход @@ -1260,6 +1261,7 @@ issues.previous=Предыдущая issues.next=Следующая issues.open_title=Открыто issues.closed_title=Закрыто +issues.draft_title=Черновик issues.num_comments=комментариев: %d issues.commented_at=`прокомментировал(а) %s` issues.delete_comment_confirm=Вы уверены, что хотите удалить этот комментарий? @@ -1281,7 +1283,7 @@ issues.reopened_at=`переоткрыл(а) эту проблему %[2]s` issues.ref_issue_from=`ссылка на эту проблему %[4]s %[2]s` issues.ref_pull_from=`ссылается на этот запрос на слияние %[4]s %[2]s` -issues.ref_closing_from=`ссылается на запрос на слияние %[4], который закроет эту задачу %[2]s` +issues.ref_closing_from=`ссылается на запрос на слияние %[4]s, который закроет эту задачу %[2]s` issues.ref_reopening_from=`ссылается на запрос на слияние %[4]s, который вновь откроет эту задачу %[2]s` issues.ref_closed_from=`закрыл этот запрос %[4]s %[2]s` issues.ref_reopened_from=`переоткрыл эту задачу %[4]s %[2]s` @@ -1716,10 +1718,6 @@ settings.mirror_settings.push_mirror.remote_url=URL удалённого хра settings.mirror_settings.push_mirror.add=Добавить Push-зеркало settings.sync_mirror=Синхронизировать settings.mirror_sync_in_progress=Синхронизируются репозитории-зеркала. Подождите минуту и обновите страницу. -settings.email_notifications.enable=Включить почтовые уведомления -settings.email_notifications.onmention=Посылать письмо на эл. почту только при упоминании -settings.email_notifications.disable=Отключить почтовые уведомления -settings.email_notifications.submit=Установить настройки электронной почты settings.site=Сайт settings.update_settings=Обновить настройки settings.branches.update_default_branch=Обновить ветку по умолчанию @@ -2932,6 +2930,8 @@ installation=Установка about=Об этом пакете requirements=Требования dependencies=Зависимости +composer.dependencies=Зависимости +conan.details.repository=Репозиторий container.multi_arch=ОС / архитектура container.labels.key=Ключ container.labels.value=Значение diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index a07ff0378800..069a871045c2 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -1585,10 +1585,6 @@ settings.mirror_settings.push_mirror.remote_url=GIT දුරස්ථ ගබඩ settings.mirror_settings.push_mirror.add=Push මිරර් එකතු කරන්න settings.sync_mirror=සමමුහූර්ත කරන්න settings.mirror_sync_in_progress=මිරර් සමමුහුර්තකරණය ක්රියාත්මක වෙමින් පවතී. විනාඩියකින් නැවත පරීක්ෂා කරන්න. -settings.email_notifications.enable=වි-තැපැල් දැනුම්දීම් සබල කරන්න -settings.email_notifications.onmention=සැඳහුම් සඳහා තැපැල් කරන්න -settings.email_notifications.disable=වි-තැපැල් දැනුම්දීම් අබල කරන්න -settings.email_notifications.submit=ඊ-තැපැල් මනාප සකසන්න settings.site=වියමන අඩවිය settings.update_settings=යාවත්කාල සැකසුම් settings.branches.update_default_branch=පෙරනිමි ශාඛාව යාවත්කාල කරන්න diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index c2ea1b327b94..717481ec5401 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -1299,10 +1299,6 @@ settings.basic_settings=Basinställningar settings.mirror_settings=Inställningar för spegling settings.sync_mirror=Synkronisera nu settings.mirror_sync_in_progress=Synkronisering utav speglingar pågår. Kontrollera igen om en minut. -settings.email_notifications.enable=Aktivera notiser via mejl -settings.email_notifications.onmention=Endast e-post vid omnämnande -settings.email_notifications.disable=Inaktivera notiser via mejl -settings.email_notifications.submit=Ställ in e-postinställningar settings.site=Webbplats settings.update_settings=Uppdatera inställningar settings.advanced_settings=Advancerade Inställningar diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index d2a19bc8f5cd..b17272725b40 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -2,6 +2,7 @@ home=Ana Sayfa dashboard=Pano explore=Keşfet help=Yardım +logo=Logo sign_in=Giriş Yap sign_in_with=Şununla giriş yap sign_out=Çıkış Yap @@ -34,6 +35,14 @@ twofa=İki Aşamalı Doğrulama twofa_scratch=İki aşamalı kazınmış kod passcode=Şifre +webauthn_insert_key=Güvenlik anahtarınızı ekleyin +webauthn_sign_in=Güvenlik anahtarınızdaki düğmeye basın. Eğer düğme yoksa güvenlik anahtarınızı tekrar ekleyin. +webauthn_press_button=Lütfen güvenlik anahtarınızdaki düğmeye basın… +webauthn_use_twofa=Telefonunuzdan iki aşamalı doğrulama kodu kullanın +webauthn_error=Güvenlik anahtarınız okunamıyor. +webauthn_error_unknown=Bilinmeyen bir hata oluştu. Lütfen tekrar deneyin. +webauthn_error_unable_to_process=Sunucu isteğinizi işleyemedi. +webauthn_error_duplicated=Güvenlik anahtarının bu istek için izni yok. Anahtarın halihazırda kayıtlı olmadığından emin olun. repository=Depo organization=Organizasyon @@ -91,9 +100,12 @@ error404=Ulaşmaya çalıştığınız sayfa mevcut değil veya never=Asla +rss_feed=RSS Beslemesi [error] +occurred=Bir hata oluştu missing_csrf=Hatalı İstek: CSRF anahtarı yok +network_error=Ağ hatası [startpage] app_desc=Zahmetsiz, kendi sunucunuzda barındırabileceğiniz Git servisi @@ -196,7 +208,10 @@ sqlite3_not_available=Bu Gieta sürümü SQLite3 desteklemiyor. Lütfen %s adres invalid_db_setting=Veritabanı ayarları geçersiz: %v invalid_db_table='%s' veritabanı tablosu geçersiz: %v invalid_repo_path=Depo kök dizini geçersiz: %v +invalid_app_data_path=Uygulama veri yolu geçersiz: %v run_user_not_match='Birlikte çalıştır' kullanıcı adı şimdiki kullanıcı adından farklıdır: %s -> %s +internal_token_failed=Dahili belirteç oluşturulamadı: %v +secret_key_failed=Gizli anahtar oluşturulamadı: %v save_config_failed=%v Yapılandırması kaydedilirken hata oluştu invalid_admin_setting=Yönetici hesap ayarları geçersiz: %v install_success=Hoşgeldiniz! Gitea'yı seçtiğiniz için teşekkür ederiz. Eğlenin ve kendinize iyi bakın! @@ -225,6 +240,7 @@ view_home=%s Görüntüle search_repos=Depo bul… filter=Diğer Süzgeçler filter_by_team_repositories=Takım depolarına göre süz +feed_of="%s" beslemesi show_archived=Arşivlenmiş show_both_archived_unarchived=Arşivlenenler ve arşivlenmeyenlerin hepsi gösteriliyor @@ -246,6 +262,7 @@ search=Ara code=Kod search.fuzzy=Belirsiz search.match=Eşleştir +code_search_unavailable=Kod arama şu an mevcut değil. Lütfen site yöneticinizle bağlantıya geçin. repo_no_results=Eşleşen bir depo bulunamadı. user_no_results=Eşleşen kullanıcı bulunamadı. org_no_results=Eşleşen organizasyon bulunamadı. @@ -259,6 +276,7 @@ register_helper_msg=Bir hesabınız var mı? Şimdi giriş yapın! social_register_helper_msg=Hesabınız var mı? Hemen bağlayın! disable_register_prompt=Kayıt işlemi devre dışıdır. Lütfen site yöneticinizle iletişim kurun. disable_register_mail=Kayıt için e-posta doğrulama devre dışıdır. +manual_activation_only=Etkinleştirmeyi tamamlamak için site yöneticinizle bağlantıya geçin. remember_me=Bu Aygıtı hatırla forgot_password_title=Şifremi unuttum forgot_password=Şifrenizi mi unuttunuz? @@ -297,6 +315,9 @@ oauth_signup_submit=Hesabı Tamamla oauth_signin_tab=Mevcut Hesaba Bağla oauth_signin_title=Bağlantılı Hesabı Yetkilendirmek için Giriş Yapın oauth_signin_submit=Hesabı Bağla +oauth.signin.error=Yetkilendirme isteğini işlerken bir hata oluştu. Eğer hata devam ederse lütfen site yöneticisiyle bağlantıya geçin. +oauth.signin.error.access_denied=Yetkilendirme isteği reddedildi. +oauth.signin.error.temporarily_unavailable=Yetkilendirme sunucusu geçici olarak erişilemez olduğu için yetkilendirme başarısız oldu. Lütfen daha sonra tekrar deneyin. openid_connect_submit=Bağlan openid_connect_title=Mevcut olan bir hesaba bağlan openid_connect_desc=Seçilen OpenID URI'si bilinmiyor. Burada yeni bir hesapla ilişkilendir. @@ -413,6 +434,7 @@ size_error=` uzunluk en fazla %s olmalıdır.` min_size_error=` en az %s karakter içermelidir.` max_size_error=` en fazla %s karakter içermelidir.` email_error=' geçerli bir e-posta adresi değil.' +url_error=`'%s' geçerli bir bağlantı değil.` include_error=` '%s' içermelidir.` glob_pattern_error=` glob deseni geçersiz: %s.` regex_pattern_error=` regex dizisi geçersiz: %s.` @@ -424,6 +446,7 @@ lang_select_error=Listeden bir dil seçin. username_been_taken=Bu kullanıcı adı daha önce alınmış. username_change_not_local_user=Yerel olmayan kullanıcılar kendi kullanıcı adlarını değiştiremezler. repo_name_been_taken=Depo adı zaten kullanılıyor. +repository_force_private=Gizliyi Zorla devrede: gizli depolar herkese açık yapılamaz. repository_files_already_exist=Bu depo için dosyalar zaten var. Sistem yöneticisine başvurun. repository_files_already_exist.adopt=Bu depo için dosyalar zaten var ve yalnızca Kabul Edilebilir. repository_files_already_exist.delete=Bu depo için dosyalar zaten var. Onları silmelisiniz. @@ -459,7 +482,9 @@ auth_failed=Kimlik doğrulaması başarısız oldu: %v still_own_repo=Hesabınız bir veya daha fazla depoya sahip; önce onları silin veya transfer edin. still_has_org=Hesabınız bir veya daha fazla organizasyonun üyesi; öncelikle onlardan ayrılın. +still_own_packages=Hesabınız bir veya daha fazla pakete sahip; önce onları silin. org_still_own_repo=Bu organizasyon hala bir veya daha fazla depoya sahip; önce onları silin veya transfer edin. +org_still_own_packages=Bu organizasyon hala bir veya daha fazla pakete sahip; önce onları silin. target_branch_not_exist=Hedef dal mevcut değil. @@ -486,6 +511,7 @@ form.name_chars_not_allowed='%s' kullanıcı adı geçersiz karakterler içeriyo [settings] profile=Profil account=Hesap +appearance=Görünüm password=Parola security=Güvenlik avatar=Avatar @@ -499,6 +525,7 @@ twofa=İki Aşamalı Doğrulama account_link=Bağlı Hesaplar organization=Organizasyonlar uid=Tekil ID +webauthn=Güvenlik Anahtarları public_profile=Herkese Açık Profil biography_placeholder=Bize biraz kendinizden bahsedin @@ -509,7 +536,9 @@ website=Web Sitesi location=Konum update_theme=Temayı Güncelle update_profile=Profili Güncelle +update_language=Dili Güncelle update_language_not_found=‘%s‘ dili mevcut değil. +update_language_success=Dil güncellendi. update_profile_success=Profil resminiz güncellendi. change_username=Kullanıcı adınız değiştirildi. change_username_prompt=Not: Kullanıcı adı değişiklikleri hesap URL'nizi de değiştirir. @@ -518,6 +547,20 @@ continue=Devam Et cancel=İptal language=Dil ui=Tema +hidden_comment_types=Gizli yorum türleri +comment_type_group_label=Etiket +comment_type_group_milestone=Dönüm noktası +comment_type_group_assignee=Atanan +comment_type_group_title=Başlık +comment_type_group_branch=Dal +comment_type_group_time_tracking=Zaman İzleme +comment_type_group_deadline=Son Tarih +comment_type_group_dependency=Bağımlılık +comment_type_group_lock=Kilit Durumu +comment_type_group_review_request=İnceleme isteği +comment_type_group_pull_request_push=Eklenen işlemeler +comment_type_group_project=Proje +saved_successfully=Ayarlarınız başarılı bir şekilde kaydedildi. privacy=Gizlilik keep_activity_private=Etkinliği profil sayfasından gizle keep_activity_private_popup=Etkinliği yalnızca siz ve yöneticiler için görünür hale getirir @@ -531,6 +574,7 @@ delete_current_avatar=Güncel Avatarı Sil uploaded_avatar_not_a_image=Yüklenen dosya bir resim dosyası değil. uploaded_avatar_is_too_big=Yüklenen dosya maksimum boyutu aştı. update_avatar_success=Profil resminiz değiştirildi. +update_user_avatar_success=Kullanıcının avatarı güncellendi. change_password=Parolayı Güncelle old_password=Mevcut Parola @@ -600,7 +644,13 @@ gpg_key_verify=Doğrula gpg_invalid_token_signature=Verilen GPG anahtarı, imza ve anahtar uyuşmuyor veya anahtar çok eski. gpg_token_required=Aşağıdaki anahtar için bir imza sağlamalısınız gpg_token=Anahtar +gpg_token_help=Şunu kullanarak bir imza oluşturabilirsiniz: +gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig +gpg_token_signature=Korumalı GPG imzası key_signature_gpg_placeholder='-----PGP İMZA BAŞLAT -----' ile başlar +verify_gpg_key_success=GPG anahtarı '%s' doğrulandı. +ssh_key_verified=Doğrulanmış Anahtar +ssh_key_verified_long=Bu anahtar bir belirteç ile doğrulandı ve bu kullanıcı için etkinleştirilmiş herhangi bir e-posta adresi ile uyuşan işlemeleri doğrulamak için kullanılabilir. subkeys=Alt anahtarlar key_id=Anahtar Kimliği key_name=Anahtar İsmi @@ -1183,7 +1233,7 @@ issues.reopened_at=`%[2]s konusunu yeniden açt issues.commit_ref_at=`%[2]s işlemesinde bu konuyu işaret etti` issues.ref_issue_from=`bu konuya referansta bulundu %[4]s %[2]s` issues.ref_pull_from=`bu değişiklik isteğine referansta bulundu %[4]s %[2]s` -issues.ref_closing_from=`bir değişiklik isteğine referansta bulundu %[4] bu konu kapatılacak %[2]s` +issues.ref_closing_from=`bir değişiklik isteğine referansta bulundu %[4]s bu konu kapatılacak %[2]s` issues.ref_reopening_from=`bir değişiklik isteğine referansta bulundu %[4]s bu konu yeniden açılacak %[2]s` issues.ref_closed_from=`bu konuyu kapat%[4]s %[2]s` issues.ref_reopened_from=`konuyu yeniden aç%[4]s %[2]s` @@ -1600,10 +1650,6 @@ settings.mirror_settings.push_mirror.remote_url=Git Uzak Depo URL'si settings.mirror_settings.push_mirror.add=Yansı Gönderimi Ekle settings.sync_mirror=Şimdi Eşitle settings.mirror_sync_in_progress=Yansı senkronizasyonu devam ediyor. Bir dakika sonra tekrar kontrol edin. -settings.email_notifications.enable=E-posta Bildirimlerini Etkinleştir -settings.email_notifications.onmention=Sadece Bahsedilen E-posta -settings.email_notifications.disable=E-posta Bildirimlerini Devre Dışı Bırak -settings.email_notifications.submit=E-posta Tercihlerini Ayarla settings.site=Web Sitesi settings.update_settings=Ayarları Güncelle settings.branches.update_default_branch=Varsayılan Dalı Değiştir diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 7fbb0b62d1dd..49be51a4de7d 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -1650,10 +1650,6 @@ settings.mirror_settings.push_mirror.remote_url=URL віддаленого ре settings.mirror_settings.push_mirror.add=Додати Push дзеркало settings.sync_mirror=Синхронізувати зараз settings.mirror_sync_in_progress=Синхронізуються репозиторії-дзеркала. Зачекайте хвилину і обновіть сторінку. -settings.email_notifications.enable=Увімкнути сповіщення email -settings.email_notifications.onmention=Повідомнення email тільки при згадуванні -settings.email_notifications.disable=Вимкнути email сповіщення -settings.email_notifications.submit=Налаштувати параметри email settings.site=Веб-сайт settings.update_settings=Оновити налаштування settings.branches.update_default_branch=Оновити гілку за замовчуванням diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 2823b9a2eb19..fb7c0e505586 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -1680,7 +1680,7 @@ wiki.page_already_exists=相同名称的 Wiki 页面已经存在。 wiki.reserved_page=维基名称 '%s' 是被保留的。 wiki.pages=所有页面 wiki.last_updated=最后更新于 %s -wiki.page_name_desc=输入此 Wiki 页面的名称。特殊名称有:'Home', '_Sidebar ' 和 '_Footer'。 +wiki.page_name_desc=输入此 Wiki 页面的名称。特殊名称有:'Home', '_Sidebar' 和 '_Footer'。 activity=动态 activity.period.filter_label=周期: @@ -1780,10 +1780,6 @@ settings.mirror_settings.push_mirror.remote_url=Git 远程仓库链接 settings.mirror_settings.push_mirror.add=添加推送镜像 settings.sync_mirror=同步 settings.mirror_sync_in_progress=镜像同步正在进行中,请稍后再试。 -settings.email_notifications.enable=启用邮件通知 -settings.email_notifications.onmention=只在被提到时邮件通知 -settings.email_notifications.disable=停用邮件通知 -settings.email_notifications.submit=邮件通知设置 settings.site=网站 settings.update_settings=更新仓库设置 settings.branches.update_default_branch=更新默认分支 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index f795879e115e..8d89c51a1d20 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -758,7 +758,7 @@ twofa_is_enrolled=您的帳戶已經啟用兩步驟驗證。 twofa_not_enrolled=您的帳戶目前尚未啟用兩步驟驗證。 twofa_disable=停用兩步驟驗證 twofa_scratch_token_regenerate=重新產生備用驗證碼 -twofa_scratch_token_regenerated=您的備用驗證碼是 %s。請將它保存到一個安全的地方。 +twofa_scratch_token_regenerated=您的備用驗證碼是 %s。請將它保存到安全的地方。 twofa_enroll=啟用兩步驟驗證 twofa_disable_note=如有需要,您可以停用兩步驟驗證。 twofa_disable_desc=關閉兩步驟驗證會使您的帳戶安全性降低,是否繼續? @@ -768,7 +768,7 @@ scan_this_image=使用您的授權應用程式來掃瞄圖片: or_enter_secret=或者輸入密碼: %s then_enter_passcode=然後輸入應用程式中顯示的驗證碼: passcode_invalid=無效的驗證碼,請重試。 -twofa_enrolled=您的帳戶已經啟用了兩步驟驗證。請將備用驗證碼 (%s) 保存到一個安全的地方,它只會顯示這麼一次! +twofa_enrolled=您的帳戶已經啟用了兩步驟驗證。請將備用驗證碼 (%s) 保存到安全的地方,它只會顯示這麼一次! twofa_failed_get_secret=取得密鑰 (Secret) 失敗。 webauthn_desc=安全金鑰是包含加密密鑰的硬體設備,它們可以用於兩步驟驗證。安全金鑰必須支援 WebAuthn Authenticator 標準。 @@ -843,7 +843,7 @@ repo_lang=儲存庫語言 repo_gitignore_helper=選擇 .gitignore 範本 repo_gitignore_helper_desc=從常見語言範本清單中挑選忽略追蹤的檔案。預設情況下各種語言建置工具產生的特殊檔案都包含在 .gitignore 中。 issue_labels=問題標籤 -issue_labels_helper=選擇一個問題標籤集 +issue_labels_helper=選擇問題標籤集 license=授權條款 license_helper=請選擇授權條款檔案 license_helper_desc=授權條款定義了他人使用您原始碼的允許和禁止事項。不確定哪個適用於您的專案?查看選擇授權條款。 @@ -1091,7 +1091,7 @@ editor.commit_message_desc=(選用) 加入詳細說明... editor.signoff_desc=在提交訊息底部加入提交者的「Signed-off-by」資訊。 editor.commit_directly_to_this_branch=直接提交到 %s 分支。 editor.create_new_branch=為此提交建立新分支並提出合併請求。 -editor.create_new_branch_np=為本次提交建立一個 新分支。 +editor.create_new_branch_np=為本次提交建立新分支。 editor.propose_file_change=提出檔案變更 editor.new_branch_name_desc=新的分支名稱... editor.cancel=取消 @@ -1175,7 +1175,7 @@ projects.type.none=無 projects.type.basic_kanban=基本看板 projects.type.bug_triage=Bug 檢傷分類 projects.template.desc=專案範本 -projects.template.desc_helper=選擇一個專案範本以開始 +projects.template.desc_helper=選擇專案範本以開始 projects.type.uncategorized=未分類 projects.board.edit=編輯看板 projects.board.edit_title=新看板名稱 @@ -1780,10 +1780,6 @@ settings.mirror_settings.push_mirror.remote_url=Git 遠端儲存庫 URL settings.mirror_settings.push_mirror.add=新增推送鏡像 settings.sync_mirror=立即同步 settings.mirror_sync_in_progress=鏡像同步正在進行中。 請稍後再回來看看。 -settings.email_notifications.enable=啟用郵件通知 -settings.email_notifications.onmention=只在被提到時傳送郵件通知 -settings.email_notifications.disable=關閉郵件通知 -settings.email_notifications.submit=套用郵件偏好設定 settings.site=網站 settings.update_settings=更新設定 settings.branches.update_default_branch=更新預設分支 @@ -2214,7 +2210,7 @@ release.new_subheader=發布、整理專案的版本。 release.edit_subheader=發布、整理專案的版本。 release.tag_name=標籤名稱 release.target=目標分支 -release.tag_helper=新增或選擇一個既有的標籤。 +release.tag_helper=新增或選擇既有的標籤。 release.title=標題 release.content=內容 release.prerelease_desc=標記為 Pre-Release @@ -2695,8 +2691,8 @@ auths.tips.oauth2.general.tip=註冊新的 OAuth2 認證時,callback/redirect auths.tip.oauth2_provider=OAuth2 提供者 auths.tip.bitbucket=註冊新的 OAuth 客戶端並加入權限「Account - Read」。網址:https://bitbucket.org/account/user//oauth-consumers/new auths.tip.nextcloud=在您的執行個體中,於選單「設定 -> 安全性 -> OAuth 2.0 客戶端」註冊新的 OAuth 客戶端 -auths.tip.dropbox=建立一個新的 App。網址:https://www.dropbox.com/developers/apps -auths.tip.facebook=註冊一個新的應用程式並新增產品「Facebook 登入」。網址:https://developers.facebook.com/apps +auths.tip.dropbox=建立新的 App。網址:https://www.dropbox.com/developers/apps +auths.tip.facebook=註冊新的應用程式並新增產品「Facebook 登入」。網址:https://developers.facebook.com/apps auths.tip.github=註冊新的 OAuth 應用程式。網址:https://github.com/settings/applications/new auths.tip.gitlab=註冊新的應用程式。網址:https://gitlab.com/profile/applications auths.tip.google_plus=從 Google API 控制台取得 OAuth2 用戶端憑證。網址:https://console.developers.google.com/ diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index b5fdc739d7c1..bb9a42e33dc9 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -257,6 +257,7 @@ func ContainerRoutes() *web.Route { r.Get("", container.ReqContainerAccess, container.DetermineSupport) r.Get("/token", container.Authenticate) + r.Get("/_catalog", container.ReqContainerAccess, container.GetRepositoryList) r.Group("/{username}", func() { r.Group("/{image}", func() { r.Group("/blobs/uploads", func() { diff --git a/routers/api/packages/composer/api.go b/routers/api/packages/composer/api.go index 5e1cc293da0f..45bb7eae1c19 100644 --- a/routers/api/packages/composer/api.go +++ b/routers/api/packages/composer/api.go @@ -88,7 +88,7 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac for _, pd := range pds { packageType := "" - for _, pvp := range pd.Properties { + for _, pvp := range pd.VersionProperties { if pvp.Name == composer_module.TypeProperty { packageType = pvp.Value break diff --git a/routers/api/packages/composer/composer.go b/routers/api/packages/composer/composer.go index b7c1f140dcf4..81cef39f1c49 100644 --- a/routers/api/packages/composer/composer.go +++ b/routers/api/packages/composer/composer.go @@ -227,7 +227,7 @@ func UploadPackage(ctx *context.Context) { SemverCompatible: true, Creator: ctx.Doer, Metadata: cp.Metadata, - Properties: map[string]string{ + VersionProperties: map[string]string{ composer_module.TypeProperty: cp.Type, }, }, diff --git a/routers/api/packages/container/blob.go b/routers/api/packages/container/blob.go index 8f6254f58321..8a9cbd4a15fb 100644 --- a/routers/api/packages/container/blob.go +++ b/routers/api/packages/container/blob.go @@ -29,6 +29,7 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic contentStore := packages_module.NewContentStore() err := db.WithTx(func(ctx context.Context) error { + created := true p := &packages_model.Package{ OwnerID: pi.Owner.ID, Type: packages_model.TypeContainer, @@ -37,12 +38,21 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic } var err error if p, err = packages_model.TryInsertPackage(ctx, p); err != nil { - if err != packages_model.ErrDuplicatePackage { + if err == packages_model.ErrDuplicatePackage { + created = false + } else { log.Error("Error inserting package: %v", err) return err } } + if created { + if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository, strings.ToLower(pi.Owner.LowerName+"/"+pi.Name)); err != nil { + log.Error("Error setting package property: %v", err) + return err + } + } + pv := &packages_model.PackageVersion{ PackageID: p.ID, CreatorID: pi.Owner.ID, diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go index 2a564b3446a1..b961cd4afb39 100644 --- a/routers/api/packages/container/container.go +++ b/routers/api/packages/container/container.go @@ -112,7 +112,7 @@ func apiErrorDefined(ctx *context.Context, err *namedError) { // ReqContainerAccess is a middleware which checks the current user valid (real user or ghost for anonymous access) func ReqContainerAccess(ctx *context.Context) { if ctx.Doer == nil { - ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+setting.AppURL+`v2/token"`) + ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+setting.AppURL+`v2/token",service="container_registry",scope="*"`) apiErrorDefined(ctx, errUnauthorized) } } @@ -151,6 +151,39 @@ func Authenticate(ctx *context.Context) { }) } +// https://docs.docker.com/registry/spec/api/#listing-repositories +func GetRepositoryList(ctx *context.Context) { + n := ctx.FormInt("n") + if n <= 0 || n > 100 { + n = 100 + } + last := ctx.FormTrim("last") + + repositories, err := container_model.GetRepositories(ctx, ctx.Doer, n, last) + if err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + + type RepositoryList struct { + Repositories []string `json:"repositories"` + } + + if len(repositories) == n { + v := url.Values{} + if n > 0 { + v.Add("n", strconv.Itoa(n)) + } + v.Add("last", repositories[len(repositories)-1]) + + ctx.Resp.Header().Set("Link", fmt.Sprintf(`; rel="next"`, v.Encode())) + } + + jsonResponse(ctx, http.StatusOK, RepositoryList{ + Repositories: repositories, + }) +} + // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#mounting-a-blob-from-another-repository // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#single-post // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-a-blob-in-chunks diff --git a/routers/api/packages/container/manifest.go b/routers/api/packages/container/manifest.go index d899ac8ee2f6..319c9bcabc11 100644 --- a/routers/api/packages/container/manifest.go +++ b/routers/api/packages/container/manifest.go @@ -267,6 +267,7 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H } func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, metadata *container_module.Metadata) (*packages_model.PackageVersion, error) { + created := true p := &packages_model.Package{ OwnerID: mci.Owner.ID, Type: packages_model.TypeContainer, @@ -275,12 +276,21 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met } var err error if p, err = packages_model.TryInsertPackage(ctx, p); err != nil { - if err != packages_model.ErrDuplicatePackage { + if err == packages_model.ErrDuplicatePackage { + created = false + } else { log.Error("Error inserting package: %v", err) return nil, err } } + if created { + if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository, strings.ToLower(mci.Owner.LowerName+"/"+mci.Image)); err != nil { + log.Error("Error setting package property: %v", err) + return nil, err + } + } + metadata.IsTagged = mci.IsTagged metadataJSON, err := json.Marshal(metadata) diff --git a/routers/api/packages/generic/generic.go b/routers/api/packages/generic/generic.go index d862f77259ac..9a3a185d9da5 100644 --- a/routers/api/packages/generic/generic.go +++ b/routers/api/packages/generic/generic.go @@ -8,6 +8,7 @@ import ( "errors" "net/http" "regexp" + "strings" packages_model "code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/modules/context" @@ -15,8 +16,6 @@ import ( packages_module "code.gitea.io/gitea/modules/packages" "code.gitea.io/gitea/routers/api/packages/helper" packages_service "code.gitea.io/gitea/services/packages" - - "github.com/hashicorp/go-version" ) var ( @@ -97,8 +96,7 @@ func UploadPackage(ctx *context.Context) { Name: packageName, Version: packageVersion, }, - SemverCompatible: true, - Creator: ctx.Doer, + Creator: ctx.Doer, }, &packages_service.PackageFileCreationInfo{ PackageFileInfo: packages_service.PackageFileInfo{ @@ -157,10 +155,10 @@ func sanitizeParameters(ctx *context.Context) (string, string, string, error) { return "", "", "", errors.New("Invalid package name or filename") } - v, err := version.NewSemver(ctx.Params("packageversion")) - if err != nil { - return "", "", "", err + packageVersion := strings.TrimSpace(ctx.Params("packageversion")) + if packageVersion == "" { + return "", "", "", errors.New("Invalid package version") } - return packageName, v.String(), filename, nil + return packageName, packageVersion, filename, nil } diff --git a/routers/api/packages/npm/api.go b/routers/api/packages/npm/api.go index 56c897704398..4b6b803971b7 100644 --- a/routers/api/packages/npm/api.go +++ b/routers/api/packages/npm/api.go @@ -25,7 +25,7 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac for _, pd := range pds { versions[pd.SemVer.String()] = createPackageMetadataVersion(registryURL, pd) - for _, pvp := range pd.Properties { + for _, pvp := range pd.VersionProperties { if pvp.Name == npm_module.TagProperty { distTags[pvp.Value] = pd.Version.Version } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 44e0c290a088..e1478fa2aa99 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -982,6 +982,15 @@ func Routes() *web.Route { }) }, reqRepoReader(unit.TypeReleases)) m.Post("/mirror-sync", reqToken(), reqRepoWriter(unit.TypeCode), repo.MirrorSync) + m.Post("/push_mirrors-sync", reqAdmin(), repo.PushMirrorSync) + m.Group("/push_mirrors", func() { + m.Combo("").Get(repo.ListPushMirrors). + Post(bind(api.CreatePushMirrorOption{}), repo.AddPushMirror) + m.Combo("/{name}"). + Delete(repo.DeletePushMirrorByRemoteName). + Get(repo.GetPushMirrorByName) + }, reqAdmin()) + m.Get("/editorconfig/{filename}", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetEditorconfig) m.Group("/pulls", func() { m.Combo("").Get(repo.ListPullRequests). diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go index 3d29383550c3..91e5e0c03161 100644 --- a/routers/api/v1/repo/mirror.go +++ b/routers/api/v1/repo/mirror.go @@ -6,13 +6,25 @@ package repo import ( "errors" + "fmt" "net/http" + "time" + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/convert" mirror_module "code.gitea.io/gitea/modules/mirror" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/v1/utils" + "code.gitea.io/gitea/services/forms" + "code.gitea.io/gitea/services/migrations" + mirror_service "code.gitea.io/gitea/services/mirror" ) // MirrorSync adds a mirrored repository to the sync queue @@ -63,3 +75,317 @@ func MirrorSync(ctx *context.APIContext) { ctx.Status(http.StatusOK) } + +// PushMirrorSync adds all push mirrored repositories to the sync queue +func PushMirrorSync(ctx *context.APIContext) { + // swagger:operation POST /repos/{owner}/{repo}/push_mirrors-sync repository repoPushMirrorSync + // --- + // summary: Sync all push mirrored repository + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo to sync + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo to sync + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/empty" + // "400": + // "$ref": "#/responses/error" + // "403": + // "$ref": "#/responses/forbidden" + + if !setting.Mirror.Enabled { + ctx.Error(http.StatusBadRequest, "PushMirrorSync", "Mirror feature is disabled") + return + } + // Get All push mirrors of a specific repo + pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, ctx.Repo.Repository.ID, db.ListOptions{}) + if err != nil { + ctx.Error(http.StatusNotFound, "PushMirrorSync", err) + return + } + for _, mirror := range pushMirrors { + ok := mirror_service.SyncPushMirror(ctx, mirror.ID) + if !ok { + ctx.Error(http.StatusInternalServerError, "PushMirrorSync", "error occurred when syncing push mirror "+mirror.RemoteName) + return + } + } + + ctx.Status(http.StatusOK) +} + +// ListPushMirrors get list of push mirrors of a repository +func ListPushMirrors(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/push_mirrors repository repoListPushMirrors + // --- + // summary: Get all push mirrors of the repository + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results + // type: integer + // responses: + // "200": + // "$ref": "#/responses/PushMirrorList" + // "400": + // "$ref": "#/responses/error" + // "403": + // "$ref": "#/responses/forbidden" + + if !setting.Mirror.Enabled { + ctx.Error(http.StatusBadRequest, "GetPushMirrorsByRepoID", "Mirror feature is disabled") + return + } + + repo := ctx.Repo.Repository + // Get all push mirrors for the specified repository. + pushMirrors, count, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, utils.GetListOptions(ctx)) + if err != nil { + ctx.Error(http.StatusNotFound, "GetPushMirrorsByRepoID", err) + return + } + + responsePushMirrors := make([]*api.PushMirror, 0, len(pushMirrors)) + for _, mirror := range pushMirrors { + m, err := convert.ToPushMirror(mirror) + if err == nil { + responsePushMirrors = append(responsePushMirrors, m) + } + + } + ctx.SetLinkHeader(len(responsePushMirrors), utils.GetListOptions(ctx).PageSize) + ctx.SetTotalCountHeader(count) + ctx.JSON(http.StatusOK, responsePushMirrors) +} + +// GetPushMirrorByName get push mirror of a repository by name +func GetPushMirrorByName(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/push_mirrors/{name} repository repoGetPushMirrorByRemoteName + // --- + // summary: Get push mirror of the repository by remoteName + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: name + // in: path + // description: remote name of push mirror + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/PushMirror" + // "400": + // "$ref": "#/responses/error" + // "403": + // "$ref": "#/responses/forbidden" + + if !setting.Mirror.Enabled { + ctx.Error(http.StatusBadRequest, "GetPushMirrorByRemoteName", "Mirror feature is disabled") + return + } + + mirrorName := ctx.Params(":name") + // Get push mirror of a specific repo by remoteName + pushMirror, err := repo_model.GetPushMirror(ctx, repo_model.PushMirrorOptions{RepoID: ctx.Repo.Repository.ID, RemoteName: mirrorName}) + if err != nil { + ctx.Error(http.StatusNotFound, "GetPushMirrors", err) + return + } + m, err := convert.ToPushMirror(pushMirror) + if err != nil { + ctx.ServerError("GetPushMirrorByRemoteName", err) + return + } + ctx.JSON(http.StatusOK, m) +} + +// AddPushMirror adds a push mirror to a repository +func AddPushMirror(ctx *context.APIContext) { + // swagger:operation POST /repos/{owner}/{repo}/push_mirrors repository repoAddPushMirror + // --- + // summary: add a push mirror to the repository + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/CreatePushMirrorOption" + // responses: + // "201": + // "$ref": "#/responses/PushMirror" + // "403": + // "$ref": "#/responses/forbidden" + // "400": + // "$ref": "#/responses/error" + + if !setting.Mirror.Enabled { + ctx.Error(http.StatusBadRequest, "AddPushMirror", "Mirror feature is disabled") + return + } + + pushMirror := web.GetForm(ctx).(*api.CreatePushMirrorOption) + CreatePushMirror(ctx, pushMirror) +} + +// DeletePushMirrorByRemoteName deletes a push mirror from a repository by remoteName +func DeletePushMirrorByRemoteName(ctx *context.APIContext) { + // swagger:operation DELETE /repos/{owner}/{repo}/push_mirrors/{name} repository repoDeletePushMirror + // --- + // summary: deletes a push mirror from a repository by remoteName + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: name + // in: path + // description: remote name of the pushMirror + // type: string + // required: true + // responses: + // "204": + // "$ref": "#/responses/empty" + // "404": + // "$ref": "#/responses/notFound" + // "400": + // "$ref": "#/responses/error" + + if !setting.Mirror.Enabled { + ctx.Error(http.StatusBadRequest, "DeletePushMirrorByName", "Mirror feature is disabled") + return + } + + remoteName := ctx.Params(":name") + // Delete push mirror on repo by name. + err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{RepoID: ctx.Repo.Repository.ID, RemoteName: remoteName}) + if err != nil { + ctx.Error(http.StatusNotFound, "DeletePushMirrors", err) + return + } + ctx.Status(http.StatusNoContent) +} + +func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirrorOption) { + repo := ctx.Repo.Repository + + interval, err := time.ParseDuration(mirrorOption.Interval) + if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) { + ctx.Error(http.StatusBadRequest, "CreatePushMirror", err) + return + } + + address, err := forms.ParseRemoteAddr(mirrorOption.RemoteAddress, mirrorOption.RemoteUsername, mirrorOption.RemotePassword) + if err == nil { + err = migrations.IsMigrateURLAllowed(address, ctx.ContextUser) + } + if err != nil { + HandleRemoteAddressError(ctx, err) + return + } + + remoteSuffix, err := util.CryptoRandomString(10) + if err != nil { + ctx.ServerError("CryptoRandomString", err) + return + } + + pushMirror := &repo_model.PushMirror{ + RepoID: repo.ID, + Repo: repo, + RemoteName: fmt.Sprintf("remote_mirror_%s", remoteSuffix), + Interval: interval, + } + + if err = repo_model.InsertPushMirror(ctx, pushMirror); err != nil { + ctx.ServerError("InsertPushMirror", err) + return + } + + // if the registration of the push mirrorOption fails remove it from the database + if err = mirror_service.AddPushMirrorRemote(ctx, pushMirror, address); err != nil { + if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: pushMirror.ID, RepoID: pushMirror.RepoID}); err != nil { + ctx.ServerError("DeletePushMirrors", err) + } + ctx.ServerError("AddPushMirrorRemote", err) + return + } + m, err := convert.ToPushMirror(pushMirror) + if err != nil { + ctx.ServerError("ToPushMirror", err) + return + } + ctx.JSON(http.StatusOK, m) +} + +func HandleRemoteAddressError(ctx *context.APIContext, err error) { + if models.IsErrInvalidCloneAddr(err) { + addrErr := err.(*models.ErrInvalidCloneAddr) + switch { + case addrErr.IsProtocolInvalid: + ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Invalid mirror protocol") + case addrErr.IsURLError: + ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Invalid Url ") + case addrErr.IsPermissionDenied: + ctx.Error(http.StatusUnauthorized, "CreatePushMirror", "Permission denied") + default: + ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Unknown error") + } + return + } +} diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index df3d01124697..e8cfc0706f5c 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -169,4 +169,7 @@ type swaggerParameterBodies struct { // in:body CreateWikiPageOptions api.CreateWikiPageOptions + + // in:body + CreatePushMirrorOption api.CreatePushMirrorOption } diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go index ab802db7812f..3522e242764e 100644 --- a/routers/api/v1/swagger/repo.go +++ b/routers/api/v1/swagger/repo.go @@ -345,6 +345,20 @@ type swaggerWikiCommitList struct { Body api.WikiCommitList `json:"body"` } +// PushMirror +// swagger:response PushMirror +type swaggerPushMirror struct { + // in:body + Body api.PushMirror `json:"body"` +} + +// PushMirrorList +// swagger:response PushMirrorList +type swaggerPushMirrorList struct { + // in:body + Body []api.PushMirror `json:"body"` +} + // RepoCollaboratorPermission // swagger:response RepoCollaboratorPermission type swaggerRepoCollaboratorPermission struct { diff --git a/routers/api/v1/utils/git.go b/routers/api/v1/utils/git.go index f18442d0468f..816f8b3595f7 100644 --- a/routers/api/v1/utils/git.go +++ b/routers/api/v1/utils/git.go @@ -8,7 +8,6 @@ import ( "fmt" "net/http" - "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" @@ -35,19 +34,11 @@ func ResolveRefOrSha(ctx *context.APIContext, ref string) string { } } - if ctx.Repo.GitRepo != nil && ctx.Repo.GitRepo.LastCommitCache == nil { - commitsCount, err := cache.GetInt64(ctx.Repo.Repository.GetCommitsCountCacheKey(ref, true), func() (int64, error) { - commit, err := ctx.Repo.GitRepo.GetCommit(sha) - if err != nil { - return 0, err - } - return commit.CommitsCount() - }) + if ctx.Repo.GitRepo != nil { + err := ctx.Repo.GitRepo.AddLastCommitCache(ctx.Repo.Repository.GetCommitsCountCacheKey(ref, ref != sha), ctx.Repo.Repository.FullName(), sha) if err != nil { log.Error("Unable to get commits count for %s in %s. Error: %v", sha, ctx.Repo.Repository.FullName(), err) - return sha } - ctx.Repo.GitRepo.LastCommitCache = git.NewLastCommitCache(commitsCount, ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, cache.GetCache()) } return sha diff --git a/routers/common/repo.go b/routers/common/repo.go index b3cd749115fb..a9e80fad48c8 100644 --- a/routers/common/repo.go +++ b/routers/common/repo.go @@ -7,12 +7,13 @@ package common import ( "fmt" "io" + "net/url" "path" "path/filepath" "strings" "time" - "code.gitea.io/gitea/modules/charset" + charsetModule "code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/httpcache" @@ -42,7 +43,7 @@ func ServeBlob(ctx *context.Context, blob *git.Blob, lastModified time.Time) err } // ServeData download file from io.Reader -func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) error { +func ServeData(ctx *context.Context, filePath string, size int64, reader io.Reader) error { buf := make([]byte, 1024) n, err := util.ReadAtMost(reader, buf) if err != nil { @@ -52,56 +53,73 @@ func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) buf = buf[:n] } - ctx.Resp.Header().Set("Cache-Control", "public,max-age=86400") + httpcache.AddCacheControlToHeader(ctx.Resp.Header(), 5*time.Minute) if size >= 0 { ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", size)) } else { - log.Error("ServeData called to serve data: %s with size < 0: %d", name, size) + log.Error("ServeData called to serve data: %s with size < 0: %d", filePath, size) } - name = path.Base(name) - // Google Chrome dislike commas in filenames, so let's change it to a space - name = strings.ReplaceAll(name, ",", " ") + fileName := path.Base(filePath) + sniffedType := typesniffer.DetectContentType(buf) + isPlain := sniffedType.IsText() || ctx.FormBool("render") + mimeType := "" + charset := "" - st := typesniffer.DetectContentType(buf) - - mappedMimeType := "" if setting.MimeTypeMap.Enabled { - fileExtension := strings.ToLower(filepath.Ext(name)) - mappedMimeType = setting.MimeTypeMap.Map[fileExtension] + fileExtension := strings.ToLower(filepath.Ext(fileName)) + mimeType = setting.MimeTypeMap.Map[fileExtension] } - if st.IsText() || ctx.FormBool("render") { - cs, err := charset.DetectEncoding(buf) - if err != nil { - log.Error("Detect raw file %s charset failed: %v, using by default utf-8", name, err) - cs = "utf-8" + + if mimeType == "" { + if sniffedType.IsBrowsableBinaryType() { + mimeType = sniffedType.GetMimeType() + } else if isPlain { + mimeType = "text/plain" + } else { + mimeType = typesniffer.ApplicationOctetStream } - if mappedMimeType == "" { - mappedMimeType = "text/plain" + } + + if isPlain { + charset, err = charsetModule.DetectEncoding(buf) + if err != nil { + log.Error("Detect raw file %s charset failed: %v, using by default utf-8", filePath, err) + charset = "utf-8" } - ctx.Resp.Header().Set("Content-Type", mappedMimeType+"; charset="+strings.ToLower(cs)) + } + + if charset != "" { + ctx.Resp.Header().Set("Content-Type", mimeType+"; charset="+strings.ToLower(charset)) } else { - ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") - if mappedMimeType != "" { - ctx.Resp.Header().Set("Content-Type", mappedMimeType) - } - if (st.IsImage() || st.IsPDF()) && (setting.UI.SVG.Enabled || !st.IsSvgImage()) { - ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name)) - if st.IsSvgImage() || st.IsPDF() { - ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox") - ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff") - if st.IsSvgImage() { - ctx.Resp.Header().Set("Content-Type", typesniffer.SvgMimeType) - } else { - ctx.Resp.Header().Set("Content-Type", typesniffer.ApplicationOctetStream) - } - } - } else { - ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name)) - } + ctx.Resp.Header().Set("Content-Type", mimeType) + } + ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff") + + isSVG := sniffedType.IsSvgImage() + + // serve types that can present a security risk with CSP + if isSVG { + ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox") + } else if sniffedType.IsPDF() { + // no sandbox attribute for pdf as it breaks rendering in at least safari. this + // should generally be safe as scripts inside PDF can not escape the PDF document + // see https://bugs.chromium.org/p/chromium/issues/detail?id=413851 for more discussion + ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'") } + disposition := "inline" + if isSVG && !setting.UI.SVG.Enabled { + disposition = "attachment" + } + + // encode filename per https://datatracker.ietf.org/doc/html/rfc5987 + encodedFileName := `filename*=UTF-8''` + url.PathEscape(fileName) + + ctx.Resp.Header().Set("Content-Disposition", disposition+"; "+encodedFileName) + ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") + _, err = ctx.Resp.Write(buf) if err != nil { return err diff --git a/routers/web/auth/webauthn.go b/routers/web/auth/webauthn.go index 4778c9a9a369..917cbdd57bf6 100644 --- a/routers/web/auth/webauthn.go +++ b/routers/web/auth/webauthn.go @@ -5,7 +5,6 @@ package auth import ( - "encoding/base32" "errors" "net/http" @@ -129,7 +128,7 @@ func WebAuthnLoginAssertionPost(ctx *context.Context) { } // Success! Get the credential and update the sign count with the new value we received. - dbCred, err := auth.GetWebAuthnCredentialByCredID(user.ID, base32.HexEncoding.EncodeToString(cred.ID)) + dbCred, err := auth.GetWebAuthnCredentialByCredID(user.ID, cred.ID) if err != nil { ctx.ServerError("GetWebAuthnCredentialByCredID", err) return diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index c22a124e7421..3f7bc59856f3 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -24,6 +24,7 @@ import ( user_setting "code.gitea.io/gitea/routers/web/user/setting" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/org" + container_service "code.gitea.io/gitea/services/packages/container" repo_service "code.gitea.io/gitea/services/repository" user_service "code.gitea.io/gitea/services/user" ) @@ -88,6 +89,12 @@ func SettingsPost(ctx *context.Context) { } return } + + if err := container_service.UpdateRepositoryNames(ctx, org.AsUser(), form.Name); err != nil { + ctx.ServerError("UpdateRepositoryNames", err) + return + } + // reset ctx.org.OrgLink with new name ctx.Org.OrgLink = setting.AppSubURL + "/org/" + url.PathEscape(form.Name) log.Trace("Organization name changed: %s -> %s", org.Name, form.Name) diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index 7f6b0feafbd0..a59824cecdb4 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -90,7 +90,7 @@ func SettingsCtxData(ctx *context.Context) { } ctx.Data["StatsIndexerStatus"] = status } - pushMirrors, err := repo_model.GetPushMirrorsByRepoID(ctx.Repo.Repository.ID) + pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, ctx.Repo.Repository.ID, db.ListOptions{}) if err != nil { ctx.ServerError("GetPushMirrorsByRepoID", err) return @@ -284,7 +284,7 @@ func SettingsPost(ctx *context.Context) { return } - m, err := selectPushMirrorByForm(form, repo) + m, err := selectPushMirrorByForm(ctx, form, repo) if err != nil { ctx.NotFound("", nil) return @@ -305,7 +305,7 @@ func SettingsPost(ctx *context.Context) { // as an error on the UI for this action ctx.Data["Err_RepoName"] = nil - m, err := selectPushMirrorByForm(form, repo) + m, err := selectPushMirrorByForm(ctx, form, repo) if err != nil { ctx.NotFound("", nil) return @@ -316,7 +316,7 @@ func SettingsPost(ctx *context.Context) { return } - if err = repo_model.DeletePushMirrorByID(m.ID); err != nil { + if err = repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: m.ID, RepoID: m.RepoID}); err != nil { ctx.ServerError("DeletePushMirrorByID", err) return } @@ -364,14 +364,14 @@ func SettingsPost(ctx *context.Context) { SyncOnCommit: form.PushMirrorSyncOnCommit, Interval: interval, } - if err := repo_model.InsertPushMirror(m); err != nil { + if err := repo_model.InsertPushMirror(ctx, m); err != nil { ctx.ServerError("InsertPushMirror", err) return } if err := mirror_service.AddPushMirrorRemote(ctx, m, address); err != nil { - if err := repo_model.DeletePushMirrorByID(m.ID); err != nil { - log.Error("DeletePushMirrorByID %v", err) + if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: m.ID, RepoID: m.RepoID}); err != nil { + log.Error("DeletePushMirrors %v", err) } ctx.ServerError("AddPushMirrorRemote", err) return @@ -1222,13 +1222,13 @@ func SettingsDeleteAvatar(ctx *context.Context) { ctx.Redirect(ctx.Repo.RepoLink + "/settings") } -func selectPushMirrorByForm(form *forms.RepoSettingForm, repo *repo_model.Repository) (*repo_model.PushMirror, error) { +func selectPushMirrorByForm(ctx *context.Context, form *forms.RepoSettingForm, repo *repo_model.Repository) (*repo_model.PushMirror, error) { id, err := strconv.ParseInt(form.PushMirrorID, 10, 64) if err != nil { return nil, err } - pushMirrors, err := repo_model.GetPushMirrorsByRepoID(repo.ID) + pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, db.ListOptions{}) if err != nil { return nil, err } diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 1a39b21c6ff8..6a9c6b9bbace 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -15,7 +15,6 @@ import ( "net/http" "net/url" "path" - "strconv" "strings" "time" @@ -57,17 +56,8 @@ type namedBlob struct { blob *git.Blob } -func linesBytesCount(s []byte) int { - nl := []byte{'\n'} - n := bytes.Count(s, nl) - if len(s) > 0 && !bytes.HasSuffix(s, nl) { - n++ - } - return n -} - // FIXME: There has to be a more efficient way of doing this -func getReadmeFileFromPath(commit *git.Commit, treePath string) (*namedBlob, error) { +func getReadmeFileFromPath(ctx *context.Context, commit *git.Commit, treePath string) (*namedBlob, error) { tree, err := commit.SubTree(treePath) if err != nil { return nil, err @@ -78,50 +68,33 @@ func getReadmeFileFromPath(commit *git.Commit, treePath string) (*namedBlob, err return nil, err } - var readmeFiles [4]*namedBlob - exts := []string{".md", ".txt", ""} // sorted by priority + // Create a list of extensions in priority order + // 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md + // 2. Txt files - e.g. README.txt + // 3. No extension - e.g. README + exts := append(localizedExtensions(".md", ctx.Language()), ".txt", "") // sorted by priority + extCount := len(exts) + readmeFiles := make([]*namedBlob, extCount+1) for _, entry := range entries { if entry.IsDir() { continue } - for i, ext := range exts { - if markup.IsReadmeFile(entry.Name(), ext) { - if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].name, entry.Blob().Name()) { - name := entry.Name() - isSymlink := entry.IsLink() - target := entry - if isSymlink { - target, err = entry.FollowLinks() - if err != nil && !git.IsErrBadLink(err) { - return nil, err - } - } - if target != nil && (target.IsExecutable() || target.IsRegular()) { - readmeFiles[i] = &namedBlob{ - name, - isSymlink, - target.Blob(), - } - } - } - } - } - - if markup.IsReadmeFile(entry.Name()) { - if readmeFiles[3] == nil || base.NaturalSortLess(readmeFiles[3].name, entry.Blob().Name()) { + if i, ok := markup.IsReadmeFileExtension(entry.Name(), exts...); ok { + if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].name, entry.Blob().Name()) { name := entry.Name() isSymlink := entry.IsLink() + target := entry if isSymlink { - entry, err = entry.FollowLinks() + target, err = entry.FollowLinks() if err != nil && !git.IsErrBadLink(err) { return nil, err } } - if entry != nil && (entry.IsExecutable() || entry.IsRegular()) { - readmeFiles[3] = &namedBlob{ + if target != nil && (target.IsExecutable() || target.IsRegular()) { + readmeFiles[i] = &namedBlob{ name, isSymlink, - entry.Blob(), + target.Blob(), } } } @@ -161,13 +134,38 @@ func renderDirectory(ctx *context.Context, treeLink string) { renderReadmeFile(ctx, readmeFile, readmeTreelink) } +// localizedExtensions prepends the provided language code with and without a +// regional identifier to the provided extenstion. +// Note: the language code will always be lower-cased, if a region is present it must be separated with a `-` +// Note: ext should be prefixed with a `.` +func localizedExtensions(ext, languageCode string) (localizedExts []string) { + if len(languageCode) < 1 { + return []string{ext} + } + + lowerLangCode := "." + strings.ToLower(languageCode) + + if strings.Contains(lowerLangCode, "-") { + underscoreLangCode := strings.ReplaceAll(lowerLangCode, "-", "_") + indexOfDash := strings.Index(lowerLangCode, "-") + // e.g. [.zh-cn.md, .zh_cn.md, .zh.md, .md] + return []string{lowerLangCode + ext, underscoreLangCode + ext, lowerLangCode[:indexOfDash] + ext, ext} + } + + // e.g. [.en.md, .md] + return []string{lowerLangCode + ext, ext} +} + 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 - var readmeFiles [4]*namedBlob - var docsEntries [3]*git.TreeEntry - exts := []string{".md", ".txt", ""} // sorted by priority + // Create a list of extensions in priority order + // 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md + // 2. Txt files - e.g. README.txt + // 3. No extension - e.g. README + exts := append(localizedExtensions(".md", ctx.Language()), ".txt", "") // sorted by priority + extCount := len(exts) + readmeFiles := make([]*namedBlob, extCount+1) + + docsEntries := make([]*git.TreeEntry, 3) // (one of docs/, .gitea/ or .github/) for _, entry := range entries { if entry.IsDir() { lowerName := strings.ToLower(entry.Name()) @@ -188,47 +186,24 @@ func findReadmeFile(ctx *context.Context, entries git.Entries, treeLink string) continue } - for i, ext := range exts { - if markup.IsReadmeFile(entry.Name(), ext) { - log.Debug("%s", entry.Name()) - name := entry.Name() - isSymlink := entry.IsLink() - target := entry - if isSymlink { - var err error - target, err = entry.FollowLinks() - if err != nil && !git.IsErrBadLink(err) { - ctx.ServerError("FollowLinks", err) - return nil, "" - } - } - log.Debug("%t", target == nil) - if target != nil && (target.IsExecutable() || target.IsRegular()) { - readmeFiles[i] = &namedBlob{ - name, - isSymlink, - target.Blob(), - } - } - } - } - - if markup.IsReadmeFile(entry.Name()) { + if i, ok := markup.IsReadmeFileExtension(entry.Name(), exts...); ok { + log.Debug("Potential readme file: %s", entry.Name()) name := entry.Name() isSymlink := entry.IsLink() + target := entry if isSymlink { var err error - entry, err = entry.FollowLinks() + target, err = entry.FollowLinks() if err != nil && !git.IsErrBadLink(err) { ctx.ServerError("FollowLinks", err) return nil, "" } } - if entry != nil && (entry.IsExecutable() || entry.IsRegular()) { - readmeFiles[3] = &namedBlob{ + if target != nil && (target.IsExecutable() || target.IsRegular()) { + readmeFiles[i] = &namedBlob{ name, isSymlink, - entry.Blob(), + target.Blob(), } } } @@ -249,7 +224,7 @@ func findReadmeFile(ctx *context.Context, entries git.Entries, treeLink string) continue } var err error - readmeFile, err = getReadmeFileFromPath(ctx.Repo.Commit, entry.GetSubJumpablePathName()) + readmeFile, err = getReadmeFileFromPath(ctx, ctx.Repo.Commit, entry.GetSubJumpablePathName()) if err != nil { ctx.ServerError("getReadmeFileFromPath", err) return nil, "" @@ -555,8 +530,14 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st ) } else { buf, _ := io.ReadAll(rd) - lineNums := linesBytesCount(buf) - ctx.Data["NumLines"] = strconv.Itoa(lineNums) + + // empty: 0 lines; "a": one line; "a\n": two lines; "a\nb": two lines; + // the NumLines is only used for the display on the UI: "xxx lines" + if len(buf) == 0 { + ctx.Data["NumLines"] = 0 + } else { + ctx.Data["NumLines"] = bytes.Count(buf, []byte{'\n'}) + 1 + } ctx.Data["NumLinesSet"] = true language := "" @@ -584,7 +565,11 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st language = "" } } - fileContent := highlight.File(lineNums, blob.Name(), language, buf) + fileContent, err := highlight.File(blob.Name(), language, buf) + if err != nil { + log.Error("highlight.File failed, fallback to plain text: %v", err) + fileContent = highlight.PlainText(buf) + } status, _ := charset.EscapeControlReader(bytes.NewReader(buf), io.Discard) ctx.Data["EscapeStatus"] = status statuses := make([]charset.EscapeStatus, len(fileContent)) diff --git a/routers/web/repo/view_test.go b/routers/web/repo/view_test.go new file mode 100644 index 000000000000..9d5a88fca412 --- /dev/null +++ b/routers/web/repo/view_test.go @@ -0,0 +1,63 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// Copyright 2014 The Gogs 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 repo + +import ( + "reflect" + "testing" +) + +func Test_localizedExtensions(t *testing.T) { + tests := []struct { + name string + ext string + languageCode string + wantLocalizedExts []string + }{ + { + name: "empty language", + ext: ".md", + wantLocalizedExts: []string{".md"}, + }, + { + name: "No region - lowercase", + languageCode: "en", + ext: ".csv", + wantLocalizedExts: []string{".en.csv", ".csv"}, + }, + { + name: "No region - uppercase", + languageCode: "FR", + ext: ".txt", + wantLocalizedExts: []string{".fr.txt", ".txt"}, + }, + { + name: "With region - lowercase", + languageCode: "en-us", + ext: ".md", + wantLocalizedExts: []string{".en-us.md", ".en_us.md", ".en.md", ".md"}, + }, + { + name: "With region - uppercase", + languageCode: "en-CA", + ext: ".MD", + wantLocalizedExts: []string{".en-ca.MD", ".en_ca.MD", ".en.MD", ".MD"}, + }, + { + name: "With region - all uppercase", + languageCode: "ZH-TW", + ext: ".md", + wantLocalizedExts: []string{".zh-tw.md", ".zh_tw.md", ".zh.md", ".md"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotLocalizedExts := localizedExtensions(tt.ext, tt.languageCode); !reflect.DeepEqual(gotLocalizedExts, tt.wantLocalizedExts) { + t.Errorf("localizedExtensions() = %v, want %v", gotLocalizedExts, tt.wantLocalizedExts) + } + }) + } +} diff --git a/routers/web/user/package.go b/routers/web/user/package.go index aa379152b30b..59aaf07ff2a8 100644 --- a/routers/web/user/package.go +++ b/routers/web/user/package.go @@ -8,6 +8,7 @@ import ( "net/http" "code.gitea.io/gitea/models/db" + org_model "code.gitea.io/gitea/models/organization" packages_model "code.gitea.io/gitea/models/packages" container_model "code.gitea.io/gitea/models/packages/container" "code.gitea.io/gitea/models/perm" @@ -93,6 +94,21 @@ func ListPackages(ctx *context.Context) { ctx.Data["Total"] = total ctx.Data["RepositoryAccessMap"] = repositoryAccessMap + // TODO: context/org -> HandleOrgAssignment() can not be used + if ctx.ContextUser.IsOrganization() { + org := org_model.OrgFromUser(ctx.ContextUser) + ctx.Data["Org"] = org + ctx.Data["OrgLink"] = ctx.ContextUser.OrganisationLink() + + if ctx.Doer != nil { + ctx.Data["IsOrganizationMember"], _ = org_model.IsOrganizationMember(ctx, org.ID, ctx.Doer.ID) + ctx.Data["IsOrganizationOwner"], _ = org_model.IsOrganizationOwner(ctx, org.ID, ctx.Doer.ID) + } else { + ctx.Data["IsOrganizationMember"] = false + ctx.Data["IsOrganizationOwner"] = false + } + } + pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) pager.AddParam(ctx, "q", "Query") pager.AddParam(ctx, "type", "PackageType") diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index cdb24c606674..8b95caf2fcb0 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -156,7 +156,8 @@ func EmailPost(ctx *context.Context) { preference := ctx.FormString("preference") if !(preference == user_model.EmailNotificationsEnabled || preference == user_model.EmailNotificationsOnMention || - preference == user_model.EmailNotificationsDisabled) { + preference == user_model.EmailNotificationsDisabled || + preference == user_model.EmailNotificationsAndYourOwn) { log.Error("Email notifications preference change returned unrecognized option %s: %s", preference, ctx.Doer.Name) ctx.ServerError("SetEmailPreference", errors.New("option unrecognized")) return diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go index b07813e7250e..c9a7afe982f7 100644 --- a/routers/web/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -30,6 +30,7 @@ import ( "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/services/agit" "code.gitea.io/gitea/services/forms" + container_service "code.gitea.io/gitea/services/packages/container" user_service "code.gitea.io/gitea/services/user" ) @@ -90,6 +91,11 @@ func HandleUsernameChange(ctx *context.Context, user *user_model.User, newName s return err } + if err := container_service.UpdateRepositoryNames(ctx, user, newName); err != nil { + ctx.ServerError("UpdateRepositoryNames", err) + return err + } + log.Trace("User name changed: %s -> %s", user.Name, newName) return nil } diff --git a/routers/web/web.go b/routers/web/web.go index b4e8666c44fd..a9f43fb2c4fe 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -290,6 +290,13 @@ func RegisterRoutes(m *web.Route) { } } + dlSourceEnabled := func(ctx *context.Context) { + if setting.Repository.DisableDownloadSourceArchives { + ctx.Error(http.StatusNotFound) + return + } + } + // FIXME: not all routes need go through same middleware. // Especially some AJAX requests, we can reduce middleware number to improve performance. // Routers. @@ -1106,7 +1113,7 @@ func RegisterRoutes(m *web.Route) { m.Group("/archive", func() { m.Get("/*", repo.Download) m.Post("/*", repo.InitiateDownload) - }, repo.MustBeNotEmpty, reqRepoCodeReader) + }, repo.MustBeNotEmpty, dlSourceEnabled, reqRepoCodeReader) m.Group("/branches", func() { m.Get("", repo.Branches) diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go index 5c330f6e00b8..b4827e83a757 100644 --- a/services/mailer/mail_issue.go +++ b/services/mailer/mail_issue.go @@ -91,7 +91,9 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo visited := make(map[int64]bool, len(unfiltered)+len(mentions)+1) // Avoid mailing the doer - visited[ctx.Doer.ID] = true + if ctx.Doer.EmailNotificationsPreference != user_model.EmailNotificationsAndYourOwn { + visited[ctx.Doer.ID] = true + } // =========== Mentions =========== if err = mailIssueCommentBatch(ctx, mentions, visited, true); err != nil { @@ -133,6 +135,7 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi // At this point we exclude: // user that don't have all mails enabled or users only get mail on mention and this is one ... if !(user.EmailNotificationsPreference == user_model.EmailNotificationsEnabled || + user.EmailNotificationsPreference == user_model.EmailNotificationsAndYourOwn || fromMention && user.EmailNotificationsPreference == user_model.EmailNotificationsOnMention) { continue } diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go index 8321829ad26b..3b4a8e5f8a6d 100644 --- a/services/mirror/mirror.go +++ b/services/mirror/mirror.go @@ -106,7 +106,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { pushMirrorsRequested := 0 if pushLimit != 0 { - if err := repo_model.PushMirrorsIterate(pushLimit, func(idx int, bean interface{}) error { + if err := repo_model.PushMirrorsIterate(ctx, pushLimit, func(idx int, bean interface{}) error { if err := handler(idx, bean); err != nil { return err } diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index 2927bed72b27..0c8960d78bf2 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -94,7 +94,7 @@ func SyncPushMirror(ctx context.Context, mirrorID int64) bool { log.Error("PANIC whilst syncPushMirror[%d] Panic: %v\nStacktrace: %s", mirrorID, err, log.Stack(2)) }() - m, err := repo_model.GetPushMirrorByID(mirrorID) + m, err := repo_model.GetPushMirror(ctx, repo_model.PushMirrorOptions{ID: mirrorID}) if err != nil { log.Error("GetPushMirrorByID [%d]: %v", mirrorID, err) return false @@ -116,7 +116,7 @@ func SyncPushMirror(ctx context.Context, mirrorID int64) bool { m.LastUpdateUnix = timeutil.TimeStampNow() - if err := repo_model.UpdatePushMirror(m); err != nil { + if err := repo_model.UpdatePushMirror(ctx, m); err != nil { log.Error("UpdatePushMirror [%d]: %v", m.ID, err) return false diff --git a/services/packages/container/cleanup.go b/services/packages/container/cleanup.go index 3e44f9aa1a0f..d23a481f279e 100644 --- a/services/packages/container/cleanup.go +++ b/services/packages/container/cleanup.go @@ -6,10 +6,13 @@ package container import ( "context" + "strings" "time" packages_model "code.gitea.io/gitea/models/packages" container_model "code.gitea.io/gitea/models/packages/container" + user_model "code.gitea.io/gitea/models/user" + container_module "code.gitea.io/gitea/modules/packages/container" "code.gitea.io/gitea/modules/util" ) @@ -78,3 +81,25 @@ func cleanupExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) e return nil } + +// UpdateRepositoryNames updates the repository name property for all packages of the specific owner +func UpdateRepositoryNames(ctx context.Context, owner *user_model.User, newOwnerName string) error { + ps, err := packages_model.GetPackagesByType(ctx, owner.ID, packages_model.TypeContainer) + if err != nil { + return err + } + + newOwnerName = strings.ToLower(newOwnerName) + + for _, p := range ps { + if err := packages_model.DeletePropertyByName(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository); err != nil { + return err + } + + if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository, newOwnerName+"/"+p.LowerName); err != nil { + return err + } + } + + return nil +} diff --git a/services/packages/packages.go b/services/packages/packages.go index aa1796e8b3f7..975c5ddd3589 100644 --- a/services/packages/packages.go +++ b/services/packages/packages.go @@ -34,10 +34,11 @@ type PackageInfo struct { // PackageCreationInfo describes a package to create type PackageCreationInfo struct { PackageInfo - SemverCompatible bool - Creator *user_model.User - Metadata interface{} - Properties map[string]string + SemverCompatible bool + Creator *user_model.User + Metadata interface{} + PackageProperties map[string]string + VersionProperties map[string]string } // PackageFileInfo describes a package file @@ -110,8 +111,9 @@ func createPackageAndAddFile(pvci *PackageCreationInfo, pfci *PackageFileCreatio } func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, allowDuplicate bool) (*packages_model.PackageVersion, bool, error) { - log.Trace("Creating package: %v, %v, %v, %s, %s, %+v, %v", pvci.Creator.ID, pvci.Owner.ID, pvci.PackageType, pvci.Name, pvci.Version, pvci.Properties, allowDuplicate) + log.Trace("Creating package: %v, %v, %v, %s, %s, %+v, %+v, %v", pvci.Creator.ID, pvci.Owner.ID, pvci.PackageType, pvci.Name, pvci.Version, pvci.PackageProperties, pvci.VersionProperties, allowDuplicate) + packageCreated := true p := &packages_model.Package{ OwnerID: pvci.Owner.ID, Type: pvci.PackageType, @@ -121,18 +123,29 @@ func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, all } var err error if p, err = packages_model.TryInsertPackage(ctx, p); err != nil { - if err != packages_model.ErrDuplicatePackage { + if err == packages_model.ErrDuplicatePackage { + packageCreated = false + } else { log.Error("Error inserting package: %v", err) return nil, false, err } } + if packageCreated { + for name, value := range pvci.PackageProperties { + if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, name, value); err != nil { + log.Error("Error setting package property: %v", err) + return nil, false, err + } + } + } + metadataJSON, err := json.Marshal(pvci.Metadata) if err != nil { return nil, false, err } - created := true + versionCreated := true pv := &packages_model.PackageVersion{ PackageID: p.ID, CreatorID: pvci.Creator.ID, @@ -142,7 +155,7 @@ func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, all } if pv, err = packages_model.GetOrInsertVersion(ctx, pv); err != nil { if err == packages_model.ErrDuplicatePackageVersion { - created = false + versionCreated = false } if err != packages_model.ErrDuplicatePackageVersion || !allowDuplicate { log.Error("Error inserting package: %v", err) @@ -150,8 +163,8 @@ func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, all } } - if created { - for name, value := range pvci.Properties { + if versionCreated { + for name, value := range pvci.VersionProperties { if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, name, value); err != nil { log.Error("Error setting package version property: %v", err) return nil, false, err @@ -159,7 +172,7 @@ func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, all } } - return pv, created, nil + return pv, versionCreated, nil } // AddFileToExistingPackage adds a file to an existing package. If the package does not exist, ErrPackageNotExist is returned @@ -350,9 +363,18 @@ func Cleanup(unused context.Context, olderThan time.Duration) error { return err } - if err := packages_model.DeletePackagesIfUnreferenced(ctx); err != nil { + ps, err := packages_model.FindUnreferencedPackages(ctx) + if err != nil { return err } + for _, p := range ps { + if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypePackage, p.ID); err != nil { + return err + } + if err := packages_model.DeletePackageByID(ctx, p.ID); err != nil { + return err + } + } pbs, err := packages_model.FindExpiredUnreferencedBlobs(ctx, olderThan) if err != nil { diff --git a/services/pull/patch.go b/services/pull/patch.go index bb09acc89f63..32895b2e784f 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -124,6 +124,7 @@ func (e *errMergeConflict) Error() string { } func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, gitRepo *git.Repository) error { + log.Trace("Attempt to merge:\n%v", file) switch { case file.stage1 != nil && (file.stage2 == nil || file.stage3 == nil): // 1. Deleted in one or both: @@ -295,7 +296,8 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo * var treeHash string treeHash, _, err = git.NewCommand(ctx, "write-tree").RunStdString(&git.RunOpts{Dir: tmpBasePath}) if err != nil { - return false, err + lsfiles, _, _ := git.NewCommand(ctx, "ls-files", "-u").RunStdString(&git.RunOpts{Dir: tmpBasePath}) + return false, fmt.Errorf("unable to write unconflicted tree: %w\n`git ls-files -u`:\n%s", err, lsfiles) } treeHash = strings.TrimSpace(treeHash) baseTree, err := gitRepo.GetTree("base") diff --git a/services/pull/patch_unmerged.go b/services/pull/patch_unmerged.go index 38394191429c..465465d0daed 100644 --- a/services/pull/patch_unmerged.go +++ b/services/pull/patch_unmerged.go @@ -42,6 +42,17 @@ func (line *lsFileLine) SameAs(other *lsFileLine) bool { line.path == other.path } +// String provides a string representation for logging +func (line *lsFileLine) String() string { + if line == nil { + return "" + } + if line.err != nil { + return fmt.Sprintf("%d %s %s %s %v", line.stage, line.mode, line.path, line.sha, line.err) + } + return fmt.Sprintf("%d %s %s %s", line.stage, line.mode, line.path, line.sha) +} + // readUnmergedLsFileLines calls git ls-files -u -z and parses the lines into mode-sha-stage-path quadruplets // it will push these to the provided channel closing it at the end func readUnmergedLsFileLines(ctx context.Context, tmpBasePath string, outputChan chan *lsFileLine) { @@ -118,6 +129,17 @@ type unmergedFile struct { err error } +// String provides a string representation of the an unmerged file for logging +func (u *unmergedFile) String() string { + if u == nil { + return "" + } + if u.err != nil { + return fmt.Sprintf("error: %v\n%v\n%v\n%v", u.err, u.stage1, u.stage2, u.stage3) + } + return fmt.Sprintf("%v\n%v\n%v", u.stage1, u.stage2, u.stage3) +} + // unmergedFiles will collate the output from readUnstagedLsFileLines in to file triplets and send them // to the provided channel, closing at the end. func unmergedFiles(ctx context.Context, tmpBasePath string, unmerged chan *unmergedFile) { @@ -138,6 +160,7 @@ func unmergedFiles(ctx context.Context, tmpBasePath string, unmerged chan *unmer next := &unmergedFile{} for line := range lsFileLineChan { + log.Trace("Got line: %v Current State:\n%v", line, next) if line.err != nil { log.Error("Unable to run ls-files -u -z! Error: %v", line.err) unmerged <- &unmergedFile{err: fmt.Errorf("unable to run ls-files -u -z! Error: %v", line.err)} @@ -149,7 +172,7 @@ func unmergedFiles(ctx context.Context, tmpBasePath string, unmerged chan *unmer case 0: // Should not happen as this represents successfully merged file - we will tolerate and ignore though case 1: - if next.stage1 != nil { + if next.stage1 != nil || next.stage2 != nil || next.stage3 != nil { // We need to handle the unstaged file stage1,stage2,stage3 unmerged <- next } diff --git a/services/repository/files/content.go b/services/repository/files/content.go index c2069092899e..34c8aeec25e4 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -165,13 +165,24 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref } selfURLString := selfURL.String() + err = gitRepo.AddLastCommitCache(repo.GetCommitsCountCacheKey(ref, refType != git.ObjectCommit), repo.FullName(), commitID) + if err != nil { + return nil, err + } + + lastCommit, err := commit.GetCommitByPath(treePath) + if err != nil { + return nil, err + } + // All content types have these fields in populated contentsResponse := &api.ContentsResponse{ - Name: entry.Name(), - Path: treePath, - SHA: entry.ID.String(), - Size: entry.Size(), - URL: &selfURLString, + Name: entry.Name(), + Path: treePath, + SHA: entry.ID.String(), + LastCommitSHA: lastCommit.ID.String(), + Size: entry.Size(), + URL: &selfURLString, Links: &api.FileLinksResponse{ Self: &selfURLString, }, diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go index 342ebae32916..24fcd6c4c5d5 100644 --- a/services/repository/files/content_test.go +++ b/services/repository/files/content_test.go @@ -33,17 +33,18 @@ func getExpectedReadmeContentsResponse() *api.ContentsResponse { gitURL := "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/" + sha downloadURL := "https://try.gitea.io/user2/repo1/raw/branch/master/" + treePath return &api.ContentsResponse{ - Name: treePath, - Path: treePath, - SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", - Type: "file", - Size: 30, - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: treePath, + Path: treePath, + SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", + LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", + Type: "file", + Size: 30, + Encoding: &encoding, + Content: &content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, diff --git a/services/repository/files/file_test.go b/services/repository/files/file_test.go index ee0582dfc217..e158c63de24a 100644 --- a/services/repository/files/file_test.go +++ b/services/repository/files/file_test.go @@ -43,17 +43,18 @@ func getExpectedFileResponse() *api.FileResponse { downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath return &api.FileResponse{ Content: &api.ContentsResponse{ - Name: treePath, - Path: treePath, - SHA: sha, - Type: "file", - Size: 30, - Encoding: &encoding, - Content: &content, - URL: &selfURL, - HTMLURL: &htmlURL, - GitURL: &gitURL, - DownloadURL: &downloadURL, + Name: treePath, + Path: treePath, + SHA: sha, + LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", + Type: "file", + Size: 30, + Encoding: &encoding, + Content: &content, + URL: &selfURL, + HTMLURL: &htmlURL, + GitURL: &gitURL, + DownloadURL: &downloadURL, Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, diff --git a/templates/mail/release.tmpl b/templates/mail/release.tmpl index a95647105ca5..931c0d1b541e 100644 --- a/templates/mail/release.tmpl +++ b/templates/mail/release.tmpl @@ -31,12 +31,14 @@
{{.locale.Tr "mail.release.downloads"}}
    + {{if not .DisableDownloadSourceArchives}}
  • {{.locale.Tr "mail.release.download.zip"}}
  • {{.locale.Tr "mail.release.download.targz"}}
  • + {{end}} {{if .Release.Attachments}} {{range .Release.Attachments}}
  • diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl index 82b88a00ba79..4a85692a8315 100644 --- a/templates/repo/branch/list.tmpl +++ b/templates/repo/branch/list.tmpl @@ -26,13 +26,15 @@ {{svg "octicon-git-branch"}} {{end}} - @@ -46,24 +46,11 @@ git push -u origin {{.Repository.DefaultBranch}}

    {{.locale.Tr "repo.push_exist_repo"}}

    -
    git remote add origin 
    +									
    git remote add origin 
     git push -u origin {{.Repository.DefaultBranch}}
    - - + {{template "repo/clone_script" .}} {{end}} {{else}}
    diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 5e1af447a49a..0734076eff49 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -124,6 +124,8 @@ {{if eq $n 0}}
    {{template "repo/clone_buttons" .}} + {{template "repo/clone_script" .}} + {{if not .DisableDownloadSourceArchives}} + {{end}}
    {{end}}
    diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index 04f7dcd6ae0d..2a53239f1c6f 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -28,6 +28,12 @@
    + {{if $.CanWriteIssuesOrPulls}} +
    + + +
    + {{end}} {{template "repo/issue/openclose" .}}
    diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index be2c8f0c0ef4..59e0962d1795 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -357,6 +357,7 @@ 'emptyCommit': {{.Issue.PullRequest.IsEmpty}}, 'pullHeadCommitID': {{.PullHeadCommitID}}, 'isPullBranchDeletable': {{.IsPullBranchDeletable}}, + 'defaultMergeStyle': {{.MergeStyle}}, 'defaultDeleteBranchAfterMerge': {{$prUnit.PullRequestsConfig.DefaultDeleteBranchAfterMerge}}, 'mergeMessageFieldPlaceHolder': {{$.locale.Tr "repo.editor.commit_message_desc"}}, diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl index 58869d470c09..4eb099129af1 100644 --- a/templates/repo/release/list.tmpl +++ b/templates/repo/release/list.tmpl @@ -37,8 +37,10 @@
    {{if $.Permission.CanRead $.UnitTypeCode}} {{svg "octicon-git-commit" 16 "mr-2"}}{{ShortSha .Sha1}} - {{svg "octicon-file-zip" 16 "mr-2"}}ZIP - {{svg "octicon-file-zip" 16 "mr-2"}}TAR.GZ + {{if not $.DisableDownloadSourceArchives}} + {{svg "octicon-file-zip" 16 "mr-2"}}ZIP + {{svg "octicon-file-zip" 16 "mr-2"}}TAR.GZ + {{end}} {{if (and $.CanCreateRelease $release.IsTag)}} {{svg "octicon-tag" 16 "mr-2"}}{{$.locale.Tr "repo.release.new_release"}} {{end}} @@ -104,8 +106,10 @@ {{else}} @@ -146,7 +150,7 @@ {{$.locale.Tr "repo.release.downloads"}}
      - {{if and (not .IsDraft) ($.Permission.CanRead $.UnitTypeCode)}} + {{if and (not $.DisableDownloadSourceArchives) (not .IsDraft) ($.Permission.CanRead $.UnitTypeCode)}}
    • {{svg "octicon-file-zip" 16 "mr-2"}}{{$.locale.Tr "repo.release.source_code"}} (ZIP)
    • diff --git a/templates/repo/wiki/revision.tmpl b/templates/repo/wiki/revision.tmpl index 3cc879dcf57e..1c3264624730 100644 --- a/templates/repo/wiki/revision.tmpl +++ b/templates/repo/wiki/revision.tmpl @@ -7,6 +7,7 @@
      diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl index a7b5abf516c9..d66fead8daa0 100644 --- a/templates/repo/wiki/view.tmpl +++ b/templates/repo/wiki/view.tmpl @@ -31,6 +31,7 @@
      diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index ff0b4c6468ae..daec38044f9f 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -8774,6 +8774,233 @@ } } }, + "/repos/{owner}/{repo}/push_mirrors": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Get all push mirrors of the repository", + "operationId": "repoListPushMirrors", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "$ref": "#/responses/PushMirrorList" + }, + "400": { + "$ref": "#/responses/error" + }, + "403": { + "$ref": "#/responses/forbidden" + } + } + }, + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "add a push mirror to the repository", + "operationId": "repoAddPushMirror", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/CreatePushMirrorOption" + } + } + ], + "responses": { + "201": { + "$ref": "#/responses/PushMirror" + }, + "400": { + "$ref": "#/responses/error" + }, + "403": { + "$ref": "#/responses/forbidden" + } + } + } + }, + "/repos/{owner}/{repo}/push_mirrors-sync": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Sync all push mirrored repository", + "operationId": "repoPushMirrorSync", + "parameters": [ + { + "type": "string", + "description": "owner of the repo to sync", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo to sync", + "name": "repo", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/empty" + }, + "400": { + "$ref": "#/responses/error" + }, + "403": { + "$ref": "#/responses/forbidden" + } + } + } + }, + "/repos/{owner}/{repo}/push_mirrors/{name}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Get push mirror of the repository by remoteName", + "operationId": "repoGetPushMirrorByRemoteName", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "remote name of push mirror", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/PushMirror" + }, + "400": { + "$ref": "#/responses/error" + }, + "403": { + "$ref": "#/responses/forbidden" + } + } + }, + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "deletes a push mirror from a repository by remoteName", + "operationId": "repoDeletePushMirror", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "remote name of the pushMirror", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/empty" + }, + "400": { + "$ref": "#/responses/error" + }, + "404": { + "$ref": "#/responses/notFound" + } + } + } + }, "/repos/{owner}/{repo}/raw/{filepath}": { "get": { "produces": [ @@ -13779,6 +14006,10 @@ "type": "string", "x-go-name": "HTMLURL" }, + "last_commit_sha": { + "type": "string", + "x-go-name": "LastCommitSHA" + }, "name": { "type": "string", "x-go-name": "Name" @@ -14437,6 +14668,29 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, + "CreatePushMirrorOption": { + "type": "object", + "title": "CreatePushMirrorOption represents need information to create a push mirror of a repository.", + "properties": { + "interval": { + "type": "string", + "x-go-name": "Interval" + }, + "remote_address": { + "type": "string", + "x-go-name": "RemoteAddress" + }, + "remote_password": { + "type": "string", + "x-go-name": "RemotePassword" + }, + "remote_username": { + "type": "string", + "x-go-name": "RemoteUsername" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "CreateReleaseOption": { "description": "CreateReleaseOption options when creating a release", "type": "object", @@ -17512,6 +17766,41 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, + "PushMirror": { + "description": "PushMirror represents information of a push mirror", + "type": "object", + "properties": { + "created": { + "type": "string", + "x-go-name": "CreatedUnix" + }, + "interval": { + "type": "string", + "x-go-name": "Interval" + }, + "last_error": { + "type": "string", + "x-go-name": "LastError" + }, + "last_update": { + "type": "string", + "x-go-name": "LastUpdateUnix" + }, + "remote_address": { + "type": "string", + "x-go-name": "RemoteAddress" + }, + "remote_name": { + "type": "string", + "x-go-name": "RemoteName" + }, + "repo_name": { + "type": "string", + "x-go-name": "RepoName" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "Reaction": { "description": "Reaction contain one reaction", "type": "object", @@ -19289,6 +19578,21 @@ } } }, + "PushMirror": { + "description": "PushMirror", + "schema": { + "$ref": "#/definitions/PushMirror" + } + }, + "PushMirrorList": { + "description": "PushMirrorList", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/PushMirror" + } + } + }, "Reaction": { "description": "Reaction", "schema": { @@ -19568,7 +19872,7 @@ "parameterBodies": { "description": "parameterBodies", "schema": { - "$ref": "#/definitions/CreateWikiPageOptions" + "$ref": "#/definitions/CreatePushMirrorOption" } }, "redirect": { diff --git a/templates/user/overview/header.tmpl b/templates/user/overview/header.tmpl index 8f9ad1d6ccfb..edd4375e08c3 100644 --- a/templates/user/overview/header.tmpl +++ b/templates/user/overview/header.tmpl @@ -1,14 +1,22 @@
      -
      -
      -
      -
      - {{avatar .ContextUser 32}} - {{.ContextUser.Name}} + + {{with .ContextUser}} +
      +
      +
      +
      + {{avatar . 100}} + {{.DisplayName}} + + {{if .Visibility.IsLimited}}
      {{$.locale.Tr "org.settings.visibility.limited_shortname"}}
      {{end}} + {{if .Visibility.IsPrivate}}
      {{$.locale.Tr "org.settings.visibility.private_shortname"}}
      {{end}} +
      +
      -
      + {{end}} +
      diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl index 326a8cb5122e..53fd25313a83 100644 --- a/templates/user/settings/account.tmpl +++ b/templates/user/settings/account.tmpl @@ -59,9 +59,10 @@