Compare commits

..

27 commits

Author SHA1 Message Date
d0e4cdc193 common-global.js: VSCode broke the lint (fixed) and my soul.
Some checks failed
Integration tests for the release process / release-simulation (push) Has been cancelled
2025-07-17 18:57:01 +02:00
95c10cb239 common-global.js: .append(input).append(inputPath) required changing in .append(input, inputPath) 2025-07-17 18:43:34 +02:00
David Rotermund
c66062eaef Merge branch 'forgejo' into upload_with_path_structure 2025-07-17 17:11:22 +02:00
forgejo-release-manager
5645456cac chore(release-notes): Forgejo v12.0.0 (#8540)
https://codeberg.org/forgejo/forgejo/milestone/12836
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8540
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: forgejo-release-manager <contact-forgejo-release-manager@forgejo.org>
Co-committed-by: forgejo-release-manager <contact-forgejo-release-manager@forgejo.org>
2025-07-17 16:25:09 +02:00
oliverpool
dd3f24deef fix: storage(minio): prevent io.Reader close (#8541)
Fixes #8529, reverts #8527.

I was able to reproduce the problem in a test:
- it triggered only when the reader was an io.Reader
- and the size was provided (-1 takes another code path in minio)

287b1f21e1 should fail when running:
```
docker run --rm -e MINIO_DOMAIN=minio -e MINIO_ROOT_USER=123456 -e MINIO_ROOT_PASSWORD=12345678 -p 9000:9000  data.forgejo.org/oci/bitnami/minio:2024.8.17
```

and

```
TEST_MINIO_ENDPOINT=localhost:9000  go test -v -run ^TestMinioStorageIterator$ ./modules/storage
```

### Tests

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [x] I do not want this change to show in the release notes.
- [ ] I want the title to show in the release notes with a link to this pull request.
- [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8541
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: oliverpool <git@olivier.pfad.fr>
Co-committed-by: oliverpool <git@olivier.pfad.fr>
2025-07-17 12:31:38 +02:00
0ko
501cc5c7c5 merge commit: i18n: update of translations from Codeberg Translate (#8490)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8490
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
2025-07-16 19:47:56 +02:00
Codeberg Translate
8efb6c09db
i18n: update of translations from Codeberg Translate
Co-authored-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: Benedikt Straub <benedikt-straub@web.de>
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-authored-by: Edgarsons <edgarsons@noreply.codeberg.org>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Fjuro <git@alius.cz>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-authored-by: Juno Takano <jutty@noreply.codeberg.org>
Co-authored-by: SomeTr <sometr@noreply.codeberg.org>
Co-authored-by: Vyxie <kitakita@disroot.org>
Co-authored-by: Wuzzy <wuzzy@disroot.org>
Co-authored-by: adf19 <adf19@noreply.codeberg.org>
Co-authored-by: amv-bamboo <amv-bamboo@noreply.codeberg.org>
Co-authored-by: justbispo <justbispo@noreply.codeberg.org>
Co-authored-by: oatbiscuits <oatbiscuits@noreply.codeberg.org>
Co-authored-by: pixelcode <pixelcode@noreply.codeberg.org>
Co-authored-by: xtex <xtexchooser@duck.com>
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ar/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/be/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/cs/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/de/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/fil/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/lv/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/nds/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/nl/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/pt_BR/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/pt_PT/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/ru/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/uk/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo-next/zh_Hans/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/ar/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/de/
Translate-URL: https://translate.codeberg.org/projects/forgejo/forgejo/uk/
Translation: Forgejo/forgejo
Translation: Forgejo/forgejo-next
2025-07-16 16:30:00 +00:00
Panagiotis "Ivory" Vasilopoulos
0e1bafdff3 feat(ui): make 'Reference' in the issue sidebar more uniform (#8506)
This breaks the reference's contents into a new line to make more space for the contents, and because using a single column to present information is something we do not do in the sidebar. Although further changes to other components may be required so that the sidebar looks uniform in its entirety, this small tweak is a small start.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8506
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Panagiotis "Ivory" Vasilopoulos <git@n0toose.net>
Co-committed-by: Panagiotis "Ivory" Vasilopoulos <git@n0toose.net>
2025-07-16 18:29:51 +02:00
Gusted
772bb20875 feat: improve checking if diffs differ (#8451)
This change is very similar to what was done in forgejo/forgejo#7727. When a PR is updated, `checkIfPRContentChanged` is called to check if the diff changed - this is done on a temporary repository. This change improves this checking by doing this operation on the bare repository. The change is split into several commits.

The following changes were made (in this exact order)

1. Update the `getTestPatchCtx` function that was introduced in forgejo/forgejo#7727 so it can be used outside the context of conflict checking. This is a simple change by making the caller determine if it can use a bare repository or not.

2. Do a small refactor of `ValidatePullRequest` to avoid indentation hell in this function, this is purely a refactor but necessary to not blow my brain while working on this function.

3. The first enhancement, introduce `testPatchCtx` in `ValidatePullRequest` to get diverging commits via the bare repository.

4. The main enhancement, do a refactor to move the function to be part of the repository struct and do a rename as this fits as a general purpose function. This refactoring includes it no longer being specific to a temporary repository and works on a bare repository.

5. Add extensive units tests, integration tests are added in forgejo/forgejo#8450 it checks that both calls to `CheckIfDiffDiffers` work. Because it also fixes a bug I sent it as a different PR.

6. Extend the integration test to check for diffs with commits from different repositories, to demonstrate that `getTestPatchCtx` works (specifically the `env` variable).

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8451
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-07-16 18:19:27 +02:00
Michael Kriese
09c9108a35 fix(packages): skip another stack frame from logging (#8530)
Log the right stack frame line. We currently always show the `apiError` method call.

related to #8529

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8530
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
Co-committed-by: Michael Kriese <michael.kriese@visualon.de>
2025-07-16 18:07:19 +02:00
Earl Warren
ec8c0a50c2 fix: ignore "Close" error when uploading container blob (#8527)
https://github.com/go-gitea/gitea/pull/34620/files

(cherry picked from commit 7a59f5a8253402d6f98234bf0047ec53156d3af9)

## Testing

Ask @viceice to run the reproducer in his environment once the v12 backport lands

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8527
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
2025-07-16 15:23:52 +02:00
viceice
d61bd928a4 chore(renovate): enable v12 branch (#8525)
We should enable renovate on v12 for easier backports.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8525
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: viceice <michael.kriese@gmx.de>
Co-committed-by: viceice <michael.kriese@gmx.de>
2025-07-16 14:21:42 +02:00
Michael Kriese
93f029cb75 chore: failed authentication attempts are not errors and are displayed at the log info level (#8524)
I think those are not errors but simple info messages for admins.

I saw them a lot in my logs when no auth was passed to the api calls.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8524
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
Co-committed-by: Michael Kriese <michael.kriese@visualon.de>
2025-07-16 14:17:06 +02:00
Renovate Bot
1695ff8125 Update module github.com/golang-jwt/jwt/v5 to v5.2.3 (forgejo) (#8521)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8521
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-07-16 12:39:48 +02:00
Mathieu Fenniak
7f6b9a8867 fix: expanding exactly 20 lines between diff sections leaves visual artifact (#8519)
Fixes #8488.

The issue was caused by having exactly 20 lines of text between two diff sections, which is `BlobExcerptChunkSize`.  The previous diff section ends at 530, the next diff section starts at 551, leaving 20 hidden lines.  This triggered an off-by-one error in determining whether a synthetic section should be added to the render to include a new expander; it was falsely being triggered when it shouldn't have been.

Mostly correct logic was already present in `GetExpandDirection`, so I changed the `ExcerptBlob` web rendering to use `GetExpandDirection` on whether to include the synthetic section.  Then I covered `GetExpandDirection` with unit tests covering all boundary conditions, which discovered one other minor bug -- the case where exactly `BlobExcerptChunkSize` is hidden between two diff sections should never have displayed an "Up" and "Down" expansion, but just a single expander (as below).

![Untitled-2025-07-04-1538(1)](/attachments/05573c5e-3cd4-46d5-8c1f-ecdb28302a19)

Note that *technically* the `ExcerptBlob` change is not covered by any new tests... but the relevant logic has been moved to somewhere more easily testable and fully covered.  If this isn't covered enough for tests, let me know.

## Checklist

The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).

### Tests

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I added test coverage for JavaScript changes...
  - [ ] in `web_src/js/*.test.js` if it can be unit tested.
  - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)).

### Documentation

- [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
- [x] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] I do not want this change to show in the release notes.
- [x] I want the title to show in the release notes with a link to this pull request.
- [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8519
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
2025-07-16 08:04:38 +02:00
Renovate Bot
ac43750ada Update dependency webpack to v5.100.2 (forgejo) (#8520)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8520
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-07-16 02:56:06 +02:00
Mathieu Fenniak
e47fa23729 fix: PR not blocked by review request for a whitelisted team (#8511)
Fixes #8491.

Previous behavior always updated the newly created review to set the `official` flag to false.  This logic is now removed.

The most likely reason that this logic existed was that team reviews were being marked as official based upon `doer`, rather than the target team, which seems undesirable.  The expected behavior was retained by removing the check for `IsOfficialReviewer(..., doer)`, ensuring that when making a review request for a team, it doesn't matter *who* makes the request.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8511
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
2025-07-15 23:21:42 +02:00
Gusted
e186b5c039 feat: reduce amount of morphing for milestone (#8350)
- Avoid morphing too much HTML, only morph the currently selected milestone text.
- Changes that the milestone dropdown is not morphed, which contains event listeners and newer versions of the morphing library seem does not preserve event listeners in most cases.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8350
Reviewed-by: Otto <otto@codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-07-15 02:22:49 +02:00
oliverpool
5158493ba6 git/commit: re-implement submodules file reader (#8438)
Reimplement the submodules parser to not depend on the go-git dependency.

See #8222 for the full refactor context.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8438
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: oliverpool <git@olivier.pfad.fr>
Co-committed-by: oliverpool <git@olivier.pfad.fr>
2025-07-15 00:20:00 +02:00
0ko
48cc6e684a fix(ui): improve branch filter help (#8509)
Followup to https://codeberg.org/forgejo/forgejo/pulls/7823.

* move `documentation` into link content
* fix software name

Preview:
	https://codeberg.org/attachments/b17b9e90-4f82-495e-914a-b481c6f82adc
	-> https://codeberg.org/attachments/bb139365-966b-4b0f-83dd-7b95e34cff32

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8509
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
2025-07-14 19:31:26 +02:00
Renovate Bot
f1fc008d02 Lock file maintenance (forgejo) (#8503)
This PR contains the following updates:

| Update | Change |
|---|---|
| lockFileMaintenance | All locks refreshed |

🔧 This Pull Request updates lock files to use the latest dependency versions.

---

### Configuration

📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM, only on Monday ( * 0-3 * * 1 ) (UTC), Automerge - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4yMy4yIiwidXBkYXRlZEluVmVyIjoiNDEuMjMuMiIsInRhcmdldEJyYW5jaCI6ImZvcmdlam8iLCJsYWJlbHMiOlsiZGVwZW5kZW5jeS11cGdyYWRlIiwidGVzdC9ub3QtbmVlZGVkIl19-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8503
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-07-14 16:25:59 +02:00
zokki
1937fcf476 fix(ui): multiple ComboMarkdownEditors on one page interfere (#8417)
When there are multiple combo-markdown-editors, then only the first will get changes from the toolbar buttons.
Fixes: #6742

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8417
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: zokki <zokki.softwareschmiede@gmail.com>
Co-committed-by: zokki <zokki.softwareschmiede@gmail.com>
2025-07-14 13:24:45 +02:00
Renovate Bot
81e59014da Update renovate to v41.32.0 (forgejo) (#8501)
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-07-14 07:00:31 +02:00
Renovate Bot
e4bf651aa7 Update module github.com/go-webauthn/webauthn to v0.13.3 (forgejo) (#8494)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8494
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-07-13 00:24:14 +02:00
Shiny Nematoda
d07821d275 fix(code-search): HighlightSearchResultCode should count the number of bytes and not the number of runes (#8492)
fixes incorrect handling of unicode in the matched line

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8492
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Shiny Nematoda <snematoda.751k2@aleeas.com>
Co-committed-by: Shiny Nematoda <snematoda.751k2@aleeas.com>
2025-07-12 18:03:37 +02:00
kochklops
9f5e1157f0 feat: introduce global merge message templates (#8347)
Allow for a directory `default_merge_message` in the `CustomPath` to be used as a fallback if no merge template is specified by the repository. Effectively being a global merge message template feature.

Resolves #6648

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8347
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: kochklops <kochklops@noreply.codeberg.org>
Co-committed-by: kochklops <kochklops@noreply.codeberg.org>
2025-07-12 16:00:42 +02:00
Renovate Bot
f666b882a8 Update dependency webpack to v5.100.1 (forgejo) (#8493)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8493
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-07-12 02:46:48 +02:00
97 changed files with 3044 additions and 979 deletions

View file

@ -28,7 +28,7 @@ jobs:
runs-on: docker
container:
image: data.forgejo.org/renovate/renovate:41.23.2
image: data.forgejo.org/renovate/renovate:41.32.1
steps:
- name: Load renovate repo cache

View file

@ -42,6 +42,8 @@ linters:
desc: do not use the ini package, use gitea's config system instead
- pkg: github.com/minio/sha256-simd
desc: use crypto/sha256 instead, see https://codeberg.org/forgejo/forgejo/pulls/1528
- pkg: github.com/go-git/go-git
desc: use forgejo.org/modules/git instead, see https://codeberg.org/forgejo/forgejo/pulls/4941
gocritic:
disabled-checks:
- ifElseChain

View file

@ -47,7 +47,7 @@ GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasour
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.34.0 # renovate: datasource=go
GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go
RENOVATE_NPM_PACKAGE ?= renovate@41.23.2 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
RENOVATE_NPM_PACKAGE ?= renovate@41.32.1 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
# https://github.com/disposable-email-domains/disposable-email-domains/commits/main/
DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ...

8
go.mod
View file

@ -45,15 +45,14 @@ require (
github.com/go-chi/cors v1.2.2
github.com/go-co-op/gocron v1.37.0
github.com/go-enry/go-enry/v2 v2.9.2
github.com/go-git/go-git/v5 v5.13.2
github.com/go-ldap/ldap/v3 v3.4.6
github.com/go-openapi/spec v0.21.0
github.com/go-sql-driver/mysql v1.9.3
github.com/go-webauthn/webauthn v0.13.1
github.com/go-webauthn/webauthn v0.13.3
github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/golang-jwt/jwt/v5 v5.2.3
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/google/go-github/v64 v64.0.0
github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5
@ -166,11 +165,12 @@ require (
github.com/go-fed/httpsig v1.1.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-git/go-git/v5 v5.13.2 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-openapi/jsonpointer v0.21.1 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/go-webauthn/x v0.1.22 // indirect
github.com/go-webauthn/x v0.1.23 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect

12
go.sum
View file

@ -250,10 +250,10 @@ github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI6
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-webauthn/webauthn v0.13.1 h1:Q3/GLXsckVJUPE+BGR6ex26yRIiZ/X2ITaMeSkOftuc=
github.com/go-webauthn/webauthn v0.13.1/go.mod h1:HeaBromTjgMg1sHZOzyjEiqcrk4Og7mxafDTWDepaXI=
github.com/go-webauthn/x v0.1.22 h1:rHilV/rYXawarI0uA3uZ5nhLb30Ex8RgbVAsOSt/57o=
github.com/go-webauthn/x v0.1.22/go.mod h1:+iV9BF4OsvLYzETdc0lmQO2webTos10oH6QydSoWxDM=
github.com/go-webauthn/webauthn v0.13.3 h1:rvX539Gy9U4xAuFQRFJtkgoH5E1GEUyIVbHUDC89Mo4=
github.com/go-webauthn/webauthn v0.13.3/go.mod h1:H9EdVnxXFMMJyx8Nd/OL3aFFEop3Rb+Af1naR0IbuUQ=
github.com/go-webauthn/x v0.1.23 h1:9lEO0s+g8iTyz5Vszlg/rXTGrx3CjcD0RZQ1GPZCaxI=
github.com/go-webauthn/x v0.1.23/go.mod h1:AJd3hI7NfEp/4fI6T4CHD753u91l510lglU7/NMN6+E=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
@ -268,8 +268,8 @@ github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=

View file

@ -0,0 +1,16 @@
-
id: 23
repo_id: 2
index: 3
poster_id: 2
original_author_id: 0
name: protected branch pull
content: pull request to a protected branch
milestone_id: 0
priority: 0
is_pull: true
is_closed: false
num_comments: 0
created_unix: 1707270422
updated_unix: 1707270422
is_locked: false

View file

@ -0,0 +1,28 @@
- id: 1
repo_id: 2
branch_name: protected-main
can_push: false
enable_whitelist: true
whitelist_user_i_ds: [1]
whitelist_team_i_ds: []
enable_merge_whitelist: true
whitelist_deploy_keys: false
merge_whitelist_user_i_ds: [1]
merge_whitelist_team_i_ds: []
enable_status_check: false
status_check_contexts: []
enable_approvals_whitelist: true
approvals_whitelist_user_i_ds: []
approvals_whitelist_team_i_ds: [1]
required_approvals: 1
block_on_rejected_reviews: true
block_on_official_review_requests: true
block_on_outdated_branch: true
dismiss_stale_approvals: true
ignore_stale_approvals: false
require_signed_commits: false
protected_file_patterns: ""
unprotected_file_patterns: ""
apply_to_admins: true
created_unix: 1752513073
updated_unix: 1752513073

View file

@ -0,0 +1,12 @@
-
id: 11
type: 0 # gitea pull request
status: 2 # mergeable
issue_id: 23
index: 3
head_repo_id: 2
base_repo_id: 2
head_branch: feature/protected-branch-pr
base_branch: protected-main
merge_base: 4a357436d925b5c974181ff12a994538ddc5a269
has_merged: false

View file

@ -781,10 +781,6 @@ func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organizat
official, err := IsOfficialReviewerTeam(ctx, issue, reviewer)
if err != nil {
return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err)
} else if !official {
if official, err = IsOfficialReviewer(ctx, issue, doer); err != nil {
return nil, fmt.Errorf("isOfficialReviewer(): %w", err)
}
}
if review, err = CreateReview(ctx, CreateReviewOptions{
@ -797,12 +793,6 @@ func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organizat
return nil, err
}
if official {
if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_team_id=?", false, issue.ID, reviewer.ID); err != nil {
return nil, err
}
}
comment, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeReviewRequest,
Doer: doer,

View file

@ -8,6 +8,7 @@ import (
"forgejo.org/models/db"
issues_model "forgejo.org/models/issues"
organization_model "forgejo.org/models/organization"
repo_model "forgejo.org/models/repo"
"forgejo.org/models/unittest"
user_model "forgejo.org/models/user"
@ -319,3 +320,80 @@ func TestAddReviewRequest(t *testing.T) {
require.Error(t, err)
assert.True(t, issues_model.IsErrReviewRequestOnClosedPR(err))
}
func TestAddTeamReviewRequest(t *testing.T) {
defer unittest.OverrideFixtures("models/fixtures/TestAddTeamReviewRequest")()
require.NoError(t, unittest.PrepareTestDatabase())
setupForProtectedBranch := func() (*issues_model.Issue, *user_model.User) {
// From override models/fixtures/TestAddTeamReviewRequest/issue.yml; issue #23 is a PR into a protected branch
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 23})
require.NoError(t, issue.LoadRepo(db.DefaultContext))
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
return issue, doer
}
t.Run("Protected branch, not official team", func(t *testing.T) {
issue, doer := setupForProtectedBranch()
// Team 2 is not part of the whitelist for this protected branch
team := unittest.AssertExistsAndLoadBean(t, &organization_model.Team{ID: 2})
comment, err := issues_model.AddTeamReviewRequest(db.DefaultContext, issue, team, doer)
require.NoError(t, err)
require.NotNil(t, comment)
review, err := issues_model.GetTeamReviewerByIssueIDAndTeamID(db.DefaultContext, issue.ID, team.ID)
require.NoError(t, err)
require.NotNil(t, review)
assert.Equal(t, issues_model.ReviewTypeRequest, review.Type)
assert.Equal(t, team.ID, review.ReviewerTeamID)
// This review request should not be marked official because it is not a request for a team in the branch
// protection rule's whitelist...
assert.False(t, review.Official)
})
t.Run("Protected branch, official team", func(t *testing.T) {
issue, doer := setupForProtectedBranch()
// Team 1 is part of the whitelist for this protected branch
team := unittest.AssertExistsAndLoadBean(t, &organization_model.Team{ID: 1})
comment, err := issues_model.AddTeamReviewRequest(db.DefaultContext, issue, team, doer)
require.NoError(t, err)
require.NotNil(t, comment)
review, err := issues_model.GetTeamReviewerByIssueIDAndTeamID(db.DefaultContext, issue.ID, team.ID)
require.NoError(t, err)
require.NotNil(t, review)
assert.Equal(t, issues_model.ReviewTypeRequest, review.Type)
assert.Equal(t, team.ID, review.ReviewerTeamID)
// Expected to be considered official because team 1 is in the review whitelist for this protected branch
assert.True(t, review.Official)
})
t.Run("Unprotected branch, official team", func(t *testing.T) {
// Working on a PR into a branch that is not protected, issue #2
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
require.NoError(t, issue.LoadRepo(db.DefaultContext))
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
// team is a team that has write perms against the repo
team := unittest.AssertExistsAndLoadBean(t, &organization_model.Team{ID: 1})
comment, err := issues_model.AddTeamReviewRequest(db.DefaultContext, issue, team, doer)
require.NoError(t, err)
require.NotNil(t, comment)
review, err := issues_model.GetTeamReviewerByIssueIDAndTeamID(db.DefaultContext, issue.ID, team.ID)
require.NoError(t, err)
require.NotNil(t, review)
assert.Equal(t, issues_model.ReviewTypeRequest, review.Type)
assert.Equal(t, team.ID, review.ReviewerTeamID)
// Will not be marked as official because PR #2 there's no branch protection rule that enables whitelist
// approvals (verifying logic in `IsOfficialReviewerTeam` indirectly)
assert.False(t, review.Official)
// Adding the same team review request again should be a noop
comment, err = issues_model.AddTeamReviewRequest(db.DefaultContext, issue, team, doer)
require.NoError(t, err)
require.Nil(t, comment)
})
}

View file

@ -29,6 +29,8 @@ const (
MergeStyleRebaseUpdate MergeStyle = "rebase-update-only"
)
var MergeStyles = []MergeStyle{MergeStyleMerge, MergeStyleRebase, MergeStyleRebaseMerge, MergeStyleSquash, MergeStyleFastForwardOnly, MergeStyleManuallyMerged, MergeStyleRebaseUpdate}
type UpdateStyle string
const (

View file

@ -114,7 +114,7 @@ func EntryIcon(entry *git.TreeEntry) string {
return "file-symlink-file"
case entry.IsDir():
return "file-directory-fill"
case entry.IsSubModule():
case entry.IsSubmodule():
return "file-submodule"
}

View file

@ -16,8 +16,6 @@ import (
"forgejo.org/modules/log"
"forgejo.org/modules/util"
"github.com/go-git/go-git/v5/config"
)
// Commit represents a git commit.
@ -29,8 +27,8 @@ type Commit struct {
CommitMessage string
Signature *ObjectSignature
Parents []ObjectID // ID strings
submoduleCache *ObjectCache
Parents []ObjectID // ID strings
submodules map[string]Submodule // submodule indexed by path
}
// Message returns the commit message. Same as retrieving CommitMessage directly.
@ -352,64 +350,6 @@ func (c *Commit) GetFileContent(filename string, limit int) (string, error) {
return string(bytes), nil
}
// GetSubModules get all the sub modules of current revision git tree
func (c *Commit) GetSubModules() (*ObjectCache, error) {
if c.submoduleCache != nil {
return c.submoduleCache, nil
}
entry, err := c.GetTreeEntryByPath(".gitmodules")
if err != nil {
if _, ok := err.(ErrNotExist); ok {
return nil, nil
}
return nil, err
}
content, err := entry.Blob().GetBlobContent(10 * 1024)
if err != nil {
return nil, err
}
c.submoduleCache, err = parseSubmoduleContent([]byte(content))
if err != nil {
return nil, err
}
return c.submoduleCache, nil
}
func parseSubmoduleContent(bs []byte) (*ObjectCache, error) {
cfg := config.NewModules()
if err := cfg.Unmarshal(bs); err != nil {
return nil, err
}
submoduleCache := newObjectCache()
if len(cfg.Submodules) == 0 {
return nil, errors.New("no submodules found")
}
for _, subModule := range cfg.Submodules {
submoduleCache.Set(subModule.Path, subModule.URL)
}
return submoduleCache, nil
}
// GetSubModule returns the URL to the submodule according entryname
func (c *Commit) GetSubModule(entryname string) (string, error) {
modules, err := c.GetSubModules()
if err != nil {
return "", err
}
if modules != nil {
module, has := modules.Get(entryname)
if has {
return module.(string), nil
}
}
return "", nil
}
// GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only')
func (c *Commit) GetBranchName() (string, error) {
cmd := NewCommand(c.repo.Ctx, "name-rev", "--exclude", "refs/tags/*", "--name-only", "--no-undefined").AddDynamicArguments(c.ID.String())

View file

@ -15,9 +15,9 @@ import (
// CommitInfo describes the first commit with the provided entry
type CommitInfo struct {
Entry *TreeEntry
Commit *Commit
SubModuleFile *SubModuleFile
Entry *TreeEntry
Commit *Commit
Submodule Submodule
}
// GetCommitsInfo gets information of all commits that are corresponding to these entries
@ -71,19 +71,18 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
}
// If the entry if a submodule add a submodule file for this
if entry.IsSubModule() {
if entry.IsSubmodule() {
var fullPath string
if len(treePath) > 0 {
fullPath = treePath + "/" + entry.Name()
} else {
fullPath = entry.Name()
}
subModuleURL, err := commit.GetSubModule(fullPath)
submodule, err := commit.GetSubmodule(fullPath, entry)
if err != nil {
return nil, nil, err
}
subModuleFile := NewSubModuleFile(commitsInfo[i].Commit, subModuleURL, entry.ID.String())
commitsInfo[i].SubModuleFile = subModuleFile
commitsInfo[i].Submodule = submodule
}
}

View file

@ -436,33 +436,3 @@ func TestGetAllBranches(t *testing.T) {
assert.Equal(t, []string{"branch1", "branch2", "master"}, branches)
}
func Test_parseSubmoduleContent(t *testing.T) {
submoduleFiles := []struct {
fileContent string
expectedPath string
expectedURL string
}{
{
fileContent: `[submodule "jakarta-servlet"]
url = ../../ALP-pool/jakarta-servlet
path = jakarta-servlet`,
expectedPath: "jakarta-servlet",
expectedURL: "../../ALP-pool/jakarta-servlet",
},
{
fileContent: `[submodule "jakarta-servlet"]
path = jakarta-servlet
url = ../../ALP-pool/jakarta-servlet`,
expectedPath: "jakarta-servlet",
expectedURL: "../../ALP-pool/jakarta-servlet",
},
}
for _, kase := range submoduleFiles {
submodule, err := parseSubmoduleContent([]byte(kase.fileContent))
require.NoError(t, err)
v, ok := submodule.Get(kase.expectedPath)
assert.True(t, ok)
assert.Equal(t, kase.expectedURL, v)
}
}

View file

@ -0,0 +1,56 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package git
import (
"bytes"
"context"
"fmt"
"os"
"forgejo.org/modules/log"
"forgejo.org/modules/util"
)
// CheckIfDiffDiffers returns if the diff of the newCommitID and
// oldCommitID with the merge base of the base branch has changed.
//
// Informally it checks if the following two diffs are exactly the same in their
// contents, thus ignoring different commit IDs, headers and messages:
// 1. git diff --merge-base baseReference newCommitID
// 2. git diff --merge-base baseReference oldCommitID
func (repo *Repository) CheckIfDiffDiffers(base, oldCommitID, newCommitID string, env []string) (hasChanged bool, err error) {
cmd := NewCommand(repo.Ctx, "diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base)
stdoutReader, stdoutWriter, err := os.Pipe()
if err != nil {
return false, fmt.Errorf("unable to open pipe for to run diff: %w", err)
}
stderr := new(bytes.Buffer)
if err := cmd.Run(&RunOpts{
Dir: repo.Path,
Stdout: stdoutWriter,
Stderr: stderr,
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close()
defer func() {
_ = stdoutReader.Close()
}()
return util.IsEmptyReader(stdoutReader)
},
}); err != nil {
if err == util.ErrNotEmpty {
return true, nil
}
err = ConcatenateError(err, stderr.String())
log.Error("Unable to run git diff on %s %s %s in %q: Error: %v",
newCommitID, oldCommitID, base,
repo.Path,
err)
return false, fmt.Errorf("Unable to run git diff --name-only -z %s %s %s: %w", newCommitID, oldCommitID, base, err)
}
return false, nil
}

View file

@ -0,0 +1,421 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package git
import (
"bytes"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCheckIfDiffDiffers(t *testing.T) {
tmpDir := t.TempDir()
err := InitRepository(t.Context(), tmpDir, false, Sha1ObjectFormat.Name())
require.NoError(t, err)
gitRepo, err := openRepositoryWithDefaultContext(tmpDir)
require.NoError(t, err)
defer gitRepo.Close()
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("aaa"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "initial commit").Run(&RunOpts{Dir: tmpDir}))
t.Run("Simple fast-forward", func(t *testing.T) {
// Check that A--B--C, where A is the base branch.
t.Run("Different diff", func(t *testing.T) {
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "a-1", "main").Run(&RunOpts{Dir: tmpDir}))
// B commit
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "a-2").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("ccc"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir}))
changed, err := gitRepo.CheckIfDiffDiffers("main", "a-1", "a-2", nil)
require.NoError(t, err)
assert.True(t, changed)
})
t.Run("Same diff", func(t *testing.T) {
// Because C is a empty commit, the diff does not differ relative to the
// base branch.
// B commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "a-3", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "a-4").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "--allow-empty", "-m", "No changes to the README").Run(&RunOpts{Dir: tmpDir}))
changed, err := gitRepo.CheckIfDiffDiffers("main", "a-3", "a-4", nil)
require.NoError(t, err)
assert.False(t, changed)
})
})
t.Run("Merge-base is base reference", func(t *testing.T) {
// B
// /
// A
// \
// C
t.Run("Different diff", func(t *testing.T) {
// B commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "b-1", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changed to the README").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "b-2", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("ccc"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changed to the README").Run(&RunOpts{Dir: tmpDir}))
changed, err := gitRepo.CheckIfDiffDiffers("main", "b-1", "b-2", nil)
require.NoError(t, err)
assert.True(t, changed)
})
t.Run("Same diff", func(t *testing.T) {
// B commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "b-3", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changed to the README").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "b-4", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changed to the README").Run(&RunOpts{Dir: tmpDir}))
changed, err := gitRepo.CheckIfDiffDiffers("main", "b-3", "b-4", nil)
require.NoError(t, err)
assert.False(t, changed)
})
})
t.Run("Merge-base is different", func(t *testing.T) {
// B
// /
// A--D
// \
// C
// Where D is the base reference.
// D commit, where A is `main`.
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "main-D", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "FUNFACT"), []byte("Smithy was the runner up to be Forgejo's name"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "FUNFACT").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Forgejo history").Run(&RunOpts{Dir: tmpDir}))
t.Run("Different diff", func(t *testing.T) {
// B commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "c-1", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "c-2", "main-D").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "FUNFACT"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "FUNFACT").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the funfact").Run(&RunOpts{Dir: tmpDir}))
changed, err := gitRepo.CheckIfDiffDiffers("main-D", "c-1", "c-2", nil)
require.NoError(t, err)
assert.True(t, changed)
})
t.Run("Same diff", func(t *testing.T) {
// B commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "c-3", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "c-4", "main-D").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir}))
changed, err := gitRepo.CheckIfDiffDiffers("main-D", "c-3", "c-4", nil)
require.NoError(t, err)
assert.False(t, changed)
})
})
t.Run("Merge commit", func(t *testing.T) {
// B
// /
// A - D
// \
// C
//
// From B, it merges D where E is the merge commit :
// B---E
// / /
// A---D
// \
// C
t.Run("Different diff", func(t *testing.T) {
// B commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "d-1", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir}))
// E commit
require.NoError(t, NewCommand(t.Context(), "merge", "--no-ff", "main-D").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "d-2", "main-D").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "FUNFACT"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "FUNFACT").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the funfact").Run(&RunOpts{Dir: tmpDir}))
changed, err := gitRepo.CheckIfDiffDiffers("main-D", "d-1", "d-2", nil)
require.NoError(t, err)
assert.True(t, changed)
})
t.Run("Same diff", func(t *testing.T) {
// B commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "d-3", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir}))
// Merges D.
require.NoError(t, NewCommand(t.Context(), "merge", "--no-ff", "main-D").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "d-4", "main-D").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir}))
changed, err := gitRepo.CheckIfDiffDiffers("main-D", "d-3", "d-4", nil)
require.NoError(t, err)
assert.False(t, changed)
})
})
t.Run("Non-typical rebase", func(t *testing.T) {
// B
// /
// A--D
// \
// C
// Where D is the base reference.
// B was rebased onto D, which produced C.
// B and D made the same change to same file.
// D commit.
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "main-D-2", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "FUNFACT"), []byte("Smithy was the runner up to be Forgejo's name"), 0o600))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("ccc"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "FUNFACT", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Forgejo history").Run(&RunOpts{Dir: tmpDir}))
// B commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "e-1", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "CONTACT"), []byte("@example.com"), 0o600))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("ccc"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README", "CONTACT").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the contact and README").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "e-2").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "rebase", "main-D-2").Run(&RunOpts{Dir: tmpDir}))
// The diff changed, because it no longers shows the change made to `README`.
changed, err := gitRepo.CheckIfDiffDiffers("main-D-2", "e-1", "e-2", nil)
require.NoError(t, err)
assert.False(t, changed) // This should be true.
})
t.Run("Directory", func(t *testing.T) {
// B
// /
// A
// \
// C
t.Run("Same directory", func(t *testing.T) {
t.Run("Different diff", func(t *testing.T) {
// B commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-1", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "docs"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "docs", "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "docs/README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changed to the README").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-2", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "docs"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "docs", "README"), []byte("ccc"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "docs/README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changed to the README").Run(&RunOpts{Dir: tmpDir}))
changed, err := gitRepo.CheckIfDiffDiffers("main", "f-1", "f-2", nil)
require.NoError(t, err)
assert.True(t, changed)
})
t.Run("Same diff", func(t *testing.T) {
// B commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-3", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "docs"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "docs", "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "docs/README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changed to the README").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-4", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "docs"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "docs", "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "docs/README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir}))
changed, err := gitRepo.CheckIfDiffDiffers("main", "f-3", "f-4", nil)
require.NoError(t, err)
assert.False(t, changed)
})
})
t.Run("Different directory", func(t *testing.T) {
// B commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-5", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "docs"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "docs", "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "docs/README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-6", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "documentation"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "documentation", "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "documentation/README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir}))
changed, err := gitRepo.CheckIfDiffDiffers("main", "f-5", "f-6", nil)
require.NoError(t, err)
assert.True(t, changed)
})
t.Run("Directory and file", func(t *testing.T) {
// B commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-7", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "docs"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "docs", "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "docs/README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "f-8", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README"), []byte("bbb"), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "README").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Changes to the README").Run(&RunOpts{Dir: tmpDir}))
changed, err := gitRepo.CheckIfDiffDiffers("main", "f-7", "f-8", nil)
require.NoError(t, err)
assert.True(t, changed)
})
})
t.Run("Rebase", func(t *testing.T) {
// B
// /
// A--D
// \
// C
// Where D is the base reference.
// B was rebased onto D, which produced C.
// B and D made different (non conflicting) changes to same file.
// A commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "main-3", "main").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "REBASE"), bytes.Repeat([]byte{'b', 'b', 'b', '\n'}, 100), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "REBASE").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Rebasing is fun").Run(&RunOpts{Dir: tmpDir}))
// B commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "g-1", "main-3").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "REBASE"), append(bytes.Repeat([]byte{'b', 'b', 'b', '\n'}, 100), 'a', 'a', 'a'), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "REBASE").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Rebasing is fun").Run(&RunOpts{Dir: tmpDir}))
// D commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "g-2", "main-3").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "REBASE"), append([]byte{'a', 'a', 'a'}, bytes.Repeat([]byte{'b', 'b', 'b', '\n'}, 99)...), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "REBASE").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Rebasing is fun").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "g-3", "g-1").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "rebase", "g-2").Run(&RunOpts{Dir: tmpDir}))
changed, err := gitRepo.CheckIfDiffDiffers("g-2", "g-1", "g-3", nil)
require.NoError(t, err)
assert.True(t, changed)
})
t.Run("Rebasing change not shown", func(t *testing.T) {
// B
// /
// A--D
// \
// C
// Where D is the base reference.
// B was rebased onto D, which produced C.
// B and D made different (non conflicting) changes to same file.
// A commit
require.NoError(t, NewCommand(t.Context(), "switch", "--orphan", "main-4").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.MkdirAll(filepath.Join(tmpDir, "A"), 0o700))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "A", "a"), bytes.Repeat([]byte{'A', 'A', 'A', '\n'}, 100), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "A/a").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Just wondering").Run(&RunOpts{Dir: tmpDir}))
// B commit
// Changes last line.
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "h-1").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "A", "a"), append(bytes.Repeat([]byte{'A', 'A', 'A', '\n'}, 99), 'B', 'B', 'B', '\n'), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "A/a").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Just wondering").Run(&RunOpts{Dir: tmpDir}))
// D commit
// Changes first line.
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "h-2", "main-4").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "A", "a"), append([]byte{'B', 'B', 'B', '\n'}, bytes.Repeat([]byte{'A', 'A', 'A', '\n'}, 99)...), 0o600))
require.NoError(t, NewCommand(t.Context(), "add", "A/a").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "commit", "-m", "Just wondering").Run(&RunOpts{Dir: tmpDir}))
// C commit
require.NoError(t, NewCommand(t.Context(), "switch", "-c", "h-3").Run(&RunOpts{Dir: tmpDir}))
require.NoError(t, NewCommand(t.Context(), "rebase", "h-2").Run(&RunOpts{Dir: tmpDir}))
changed, err := gitRepo.CheckIfDiffDiffers("h-2", "h-1", "h-3", nil)
require.NoError(t, err)
assert.False(t, changed)
})
}

View file

@ -6,38 +6,124 @@ package git
import (
"fmt"
"io"
"net"
"net/url"
"path"
"regexp"
"strings"
"forgejo.org/modules/setting"
"forgejo.org/modules/util"
"gopkg.in/ini.v1" //nolint:depguard // used to read .gitmodules
)
var scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+@)?([a-zA-Z0-9._-]+):(.*)$`)
// SubModule submodule is a reference on git repository
type SubModule struct {
Name string
URL string
}
// SubModuleFile represents a file with submodule type.
type SubModuleFile struct {
*Commit
refURL string
refID string
}
// NewSubModuleFile create a new submodule file
func NewSubModuleFile(c *Commit, refURL, refID string) *SubModuleFile {
return &SubModuleFile{
Commit: c,
refURL: refURL,
refID: refID,
// GetSubmodule returns the Submodule of a given path
func (c *Commit) GetSubmodule(path string, entry *TreeEntry) (Submodule, error) {
err := c.readSubmodules()
if err != nil {
// the .gitmodules file exists but could not be read or parsed
return Submodule{}, err
}
sm, ok := c.submodules[path]
if !ok {
// no info found in .gitmodules: fallback to what we can provide
return Submodule{
Path: path,
Commit: entry.ID,
}, nil
}
sm.Commit = entry.ID
return sm, nil
}
// readSubmodules populates the submodules field by reading the .gitmodules file
func (c *Commit) readSubmodules() error {
if c.submodules != nil {
return nil
}
entry, err := c.GetTreeEntryByPath(".gitmodules")
if err != nil {
if IsErrNotExist(err) {
c.submodules = make(map[string]Submodule)
return nil
}
return err
}
rc, _, err := entry.Blob().NewTruncatedReader(10 * 1024)
if err != nil {
return err
}
defer rc.Close()
c.submodules, err = parseSubmoduleContent(rc)
return err
}
func parseSubmoduleContent(r io.Reader) (map[string]Submodule, error) {
// https://git-scm.com/docs/gitmodules#_description
// The .gitmodules file, located in the top-level directory of a Git working tree
// is a text file with a syntax matching the requirements of git-config[1].
// https://git-scm.com/docs/git-config#_configuration_file
cfg := ini.Empty(ini.LoadOptions{
InsensitiveKeys: true, // "The variable names are case-insensitive", but "Subsection names are case sensitive"
})
err := cfg.Append(r)
if err != nil {
return nil, err
}
sections := cfg.Sections()
submodule := make(map[string]Submodule, len(sections))
for _, s := range sections {
sm := parseSubmoduleSection(s)
if sm.Path == "" || sm.URL == "" {
continue
}
submodule[sm.Path] = sm
}
return submodule, nil
}
func parseSubmoduleSection(s *ini.Section) Submodule {
section, name, _ := strings.Cut(s.Name(), " ")
if !util.ASCIIEqualFold("submodule", section) { // See https://codeberg.org/forgejo/forgejo/pulls/8438#issuecomment-5805251
return Submodule{}
}
_ = name
sm := Submodule{}
if key, _ := s.GetKey("path"); key != nil {
sm.Path = key.Value()
}
if key, _ := s.GetKey("url"); key != nil {
sm.URL = key.Value()
}
return sm
}
// Submodule represents a parsed git submodule reference.
type Submodule struct {
Path string // path property
URL string // upstream URL
Commit ObjectID // upstream Commit-ID
}
// ResolveUpstreamURL resolves the upstream URL relative to the repo URL.
func (sm Submodule) ResolveUpstreamURL(repoURL string) string {
repoFullName := strings.TrimPrefix(repoURL, setting.AppURL) // currently hacky, but can be dropped when refactoring getRefURL
return getRefURL(sm.URL, setting.AppURL, repoFullName, setting.SSH.Domain)
}
var scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+@)?([a-zA-Z0-9._-]+):(.*)$`)
func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string {
if refURL == "" {
return ""
@ -53,7 +139,7 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string {
urlPrefix = strings.TrimSuffix(urlPrefix, "/")
// FIXME: Need to consider branch - which will require changes in modules/git/commit.go:GetSubModules
// FIXME: Need to consider branch - which will require changes in parseSubmoduleSection
// Relative url prefix check (according to git submodule documentation)
if strings.HasPrefix(refURI, "./") || strings.HasPrefix(refURI, "../") {
return urlPrefix + path.Clean(path.Join("/", repoFullName, refURI))
@ -107,13 +193,3 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string {
return ""
}
// RefURL guesses and returns reference URL.
func (sf *SubModuleFile) RefURL(urlPrefix, repoFullName, sshDomain string) string {
return getRefURL(sf.refURL, urlPrefix, repoFullName, sshDomain)
}
// RefID returns reference ID.
func (sf *SubModuleFile) RefID() string {
return sf.refID
}

View file

@ -4,9 +4,11 @@
package git
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetRefURL(t *testing.T) {
@ -40,3 +42,74 @@ func TestGetRefURL(t *testing.T) {
assert.Equal(t, kase.expect, getRefURL(kase.refURL, kase.prefixURL, kase.parentPath, kase.SSHDomain))
}
}
func Test_parseSubmoduleContent(t *testing.T) {
submoduleFiles := []struct {
fileContent string
expectedPath string
expected Submodule
}{
{
fileContent: `[submodule "jakarta-servlet"]
url = ../../ALP-pool/jakarta-servlet
path = jakarta-servlet`,
expectedPath: "jakarta-servlet",
expected: Submodule{
Path: "jakarta-servlet",
URL: "../../ALP-pool/jakarta-servlet",
},
},
{
fileContent: `[submodule "jakarta-servlet"]
path = jakarta-servlet
url = ../../ALP-pool/jakarta-servlet`,
expectedPath: "jakarta-servlet",
expected: Submodule{
Path: "jakarta-servlet",
URL: "../../ALP-pool/jakarta-servlet",
},
},
{
fileContent: `[submodule "about/documents"]
path = about/documents
url = git@github.com:example/documents.git
branch = gh-pages
[submodule "custom-name"]
path = manifesto
url = https://github.com/example/manifesto.git
[submodule]
path = relative/url
url = ../such-relative.git
`,
expectedPath: "relative/url",
expected: Submodule{
Path: "relative/url",
URL: "../such-relative.git",
},
},
{
fileContent: `# .gitmodules
# Subsection names are case sensitive
[submodule "Seanpm2001/Degoogle-your-life"]
path = Its-time-to-cut-WideVine-DRM/DeGoogle-Your-Life/submodule.gitmodules
url = https://github.com/seanpm2001/Degoogle-your-life/
[submodule "seanpm2001/degoogle-your-life"]
url = https://github.com/seanpm2001/degoogle-your-life/
# This second section should not be merged with the first, because of casing
`,
expectedPath: "Its-time-to-cut-WideVine-DRM/DeGoogle-Your-Life/submodule.gitmodules",
expected: Submodule{
Path: "Its-time-to-cut-WideVine-DRM/DeGoogle-Your-Life/submodule.gitmodules",
URL: "https://github.com/seanpm2001/Degoogle-your-life/",
},
},
}
for _, kase := range submoduleFiles {
submodule, err := parseSubmoduleContent(strings.NewReader(kase.fileContent))
require.NoError(t, err)
v, ok := submodule[kase.expectedPath]
assert.True(t, ok)
assert.Equal(t, kase.expected, v)
}
}

View file

@ -0,0 +1 @@
Initial

View file

@ -0,0 +1 @@
ref: refs/heads/master

View file

@ -0,0 +1,5 @@
[core]
repositoryformatversion = 0
filemode = true
bare = true
logallrefupdates = true

View file

@ -0,0 +1 @@
Unnamed repository; edit this file 'description' to name the repository.

Binary file not shown.

View file

@ -0,0 +1,6 @@
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~

View file

@ -0,0 +1 @@
45697427ce0595075c5c8efa42567f050208510d refs/heads/master

View file

@ -0,0 +1,2 @@
P pack-abb44544ae19d590e95822e963f78d069d27ba9e.pack

View file

@ -0,0 +1,2 @@
# pack-refs with: peeled fully-peeled sorted
45697427ce0595075c5c8efa42567f050208510d refs/heads/master

View file

@ -17,7 +17,6 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) {
ptree: t,
ID: t.ID,
name: "",
fullName: "",
entryMode: EntryModeTree,
}, nil
}
@ -55,7 +54,7 @@ func (t *Tree) GetBlobByPath(relpath string) (*Blob, error) {
return nil, err
}
if !entry.IsDir() && !entry.IsSubModule() {
if !entry.IsDir() && !entry.IsSubmodule() {
return entry.Blob(), nil
}

View file

@ -21,16 +21,12 @@ type TreeEntry struct {
entryMode EntryMode
name string
size int64
sized bool
fullName string
size int64
sized bool
}
// Name returns the name of the entry
func (te *TreeEntry) Name() string {
if te.fullName != "" {
return te.fullName
}
return te.name
}
@ -68,8 +64,8 @@ func (te *TreeEntry) Size() int64 {
return te.size
}
// IsSubModule if the entry is a sub module
func (te *TreeEntry) IsSubModule() bool {
// IsSubmodule if the entry is a submodule
func (te *TreeEntry) IsSubmodule() bool {
return te.entryMode == EntryModeCommit
}
@ -214,7 +210,7 @@ func (te *TreeEntry) Tree() *Tree {
// GetSubJumpablePathName return the full path of subdirectory jumpable ( contains only one directory )
func (te *TreeEntry) GetSubJumpablePathName() string {
if te.IsSubModule() || !te.IsDir() {
if te.IsSubmodule() || !te.IsDir() {
return ""
}
tree, err := te.ptree.SubTree(te.Name())
@ -241,7 +237,7 @@ type customSortableEntries struct {
var sorter = []func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool{
func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool {
return (t1.IsDir() || t1.IsSubModule()) && !t2.IsDir() && !t2.IsSubModule()
return (t1.IsDir() || t1.IsSubmodule()) && !t2.IsDir() && !t2.IsSubmodule()
},
func(t1, t2 *TreeEntry, cmp func(s1, s2 string) bool) bool {
return cmp(t1.Name(), t2.Name())

View file

@ -97,7 +97,7 @@ func HighlightSearchResultCode(filename string, lineNums []int, highlightRanges
conv := hcd.ConvertToPlaceholders(string(hl))
convLines := strings.Split(conv, "\n")
// each highlightRange is of the form [line number, start pos, end pos]
// each highlightRange is of the form [line number, start byte offset, end byte offset]
for _, highlightRange := range highlightRanges {
ln, start, end := highlightRange[0], highlightRange[1], highlightRange[2]
line := convLines[ln]
@ -105,15 +105,18 @@ func HighlightSearchResultCode(filename string, lineNums []int, highlightRanges
continue
}
sr := strings.NewReader(line)
sb := strings.Builder{}
count := -1
isOpen := false
for _, r := range line {
for r, size, err := sr.ReadRune(); err == nil; r, size, err = sr.ReadRune() {
if token, ok := hcd.PlaceholderTokenMap[r];
// token was not found
!ok ||
// token was marked as used
token == "" ||
!ok {
count += size
} else if
// token was marked as used
token == "" ||
// the token is not an valid html tag emitted by chroma
!(len(token) > 6 && (token[0:5] == "<span" || token[0:6] == "</span")) {
count++
@ -132,15 +135,15 @@ func HighlightSearchResultCode(filename string, lineNums []int, highlightRanges
continue
}
switch count {
case end:
switch {
case count >= end:
// if tag is not open, no need to close
if !isOpen {
break
}
sb.WriteRune(endTag)
isOpen = false
case start:
case count >= start:
// if tag is open, do not open again
if isOpen {
break
@ -161,7 +164,7 @@ func HighlightSearchResultCode(filename string, lineNums []int, highlightRanges
highlightedLines := strings.Split(hcd.Recover(conv), "\n")
// The lineNums outputted by highlight.Code might not match the original lineNums, because "highlight" removes the last `\n`
lines := make([]ResultLine, min(len(highlightedLines), len(lineNums)))
for i := 0; i < len(lines); i++ {
for i := range len(lines) {
lines[i].Num = lineNums[i]
lines[i].FormattedContent = template.HTML(highlightedLines[i])
}

View file

@ -0,0 +1,122 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package code
import (
"html/template"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestHighlightSearchResultCode(t *testing.T) {
opts := []struct {
Title string
File string
Lines []int
Range [][3]int
Code string
Result []template.HTML
}{
{
Title: "One Match Text",
File: "test.txt",
Range: [][3]int{{1, 5, 9}},
Code: "First Line\nMark this only\nThe End",
Result: []template.HTML{
"First Line",
"Mark <span class=\"search-highlight\">this</span> only",
"The End",
},
},
{
Title: "Two Match Text",
File: "test.txt",
Range: [][3]int{
{1, 5, 9},
{2, 5, 9},
},
Code: "First Line\nMark this only\nMark this too\nThe End",
Result: []template.HTML{
"First Line",
"Mark <span class=\"search-highlight\">this</span> only",
"Mark <span class=\"search-highlight\">this</span> too",
"The End",
},
},
{
Title: "Unicode Before",
File: "test.txt",
Range: [][3]int{{1, 10, 14}},
Code: "First Line\nMark 👉 this only\nThe End",
Result: []template.HTML{
"First Line",
"Mark 👉 <span class=\"search-highlight\">this</span> only",
"The End",
},
},
{
Title: "Unicode Between",
File: "test.txt",
Range: [][3]int{{1, 5, 14}},
Code: "First Line\nMark this 😊 only\nThe End",
Result: []template.HTML{
"First Line",
"Mark <span class=\"search-highlight\">this 😊</span> only",
"The End",
},
},
{
Title: "Unicode Before And Between",
File: "test.txt",
Range: [][3]int{{1, 10, 19}},
Code: "First Line\nMark 👉 this 😊 only\nThe End",
Result: []template.HTML{
"First Line",
"Mark 👉 <span class=\"search-highlight\">this 😊</span> only",
"The End",
},
},
{
Title: "Golang",
File: "test.go",
Range: [][3]int{{1, 14, 23}},
Code: "func main() {\n\tfmt.Println(\"mark this\")\n}",
Result: []template.HTML{
"<span class=\"kd\">func</span> <span class=\"nf\">main</span><span class=\"p\">(</span><span class=\"p\">)</span> <span class=\"p\">{</span>",
"\t<span class=\"nx\">fmt</span><span class=\"p\">.</span><span class=\"nf\">Println</span><span class=\"p\">(</span><span class=\"s\">&#34;<span class=\"search-highlight\">mark this</span>&#34;</span><span class=\"p\">)</span>",
"<span class=\"p\">}</span>",
},
},
{
Title: "Golang Unicode",
File: "test.go",
Range: [][3]int{{1, 14, 28}},
Code: "func main() {\n\tfmt.Println(\"mark this 😊\")\n}",
Result: []template.HTML{
"<span class=\"kd\">func</span> <span class=\"nf\">main</span><span class=\"p\">(</span><span class=\"p\">)</span> <span class=\"p\">{</span>",
"\t<span class=\"nx\">fmt</span><span class=\"p\">.</span><span class=\"nf\">Println</span><span class=\"p\">(</span><span class=\"s\">&#34;<span class=\"search-highlight\">mark this 😊</span>&#34;</span><span class=\"p\">)</span>",
"<span class=\"p\">}</span>",
},
},
}
for _, o := range opts {
t.Run(o.Title, func(t *testing.T) {
lines := []int{}
for i := range strings.Count(strings.TrimSuffix(o.Code, "\n"), "\n") + 1 {
lines = append(lines, i+1)
}
res := HighlightSearchResultCode(o.File, lines, o.Range, o.Code)
assert.Len(t, res, len(o.Result))
assert.Len(t, res, len(lines))
for i, r := range res {
require.Equal(t, lines[i], r.Num)
require.Equal(t, o.Result[i], r.FormattedContent)
}
})
}
}

View file

@ -209,7 +209,7 @@ func (m *MinioStorage) Save(path string, r io.Reader, size int64) (int64, error)
m.ctx,
m.bucket,
m.buildMinioPath(path),
r,
io.NopCloser(r), // prevent minio from closing the reader
size,
minio.PutObjectOptions{
ContentType: "application/octet-stream",

View file

@ -5,6 +5,7 @@ package storage
import (
"bytes"
"io"
"testing"
"forgejo.org/modules/setting"
@ -13,22 +14,39 @@ import (
"github.com/stretchr/testify/require"
)
type spyCloser struct {
io.Reader
closed int
}
func (s *spyCloser) Close() error {
s.closed++
return nil
}
var _ io.ReadCloser = &spyCloser{}
func testStorageIterator(t *testing.T, typStr Type, cfg *setting.Storage) {
l, err := NewStorage(typStr, cfg)
require.NoError(t, err)
testFiles := [][]string{
{"a/1.txt", "a1"},
{"/a/1.txt", "aa1"}, // same as above, but with leading slash that will be trim
{"ab/1.txt", "ab1"},
{"b/1.txt", "b1"},
{"b/2.txt", "b2"},
{"b/3.txt", "b3"},
{"b/x 4.txt", "bx4"},
testFiles := []struct {
path, content string
size int64
}{
{"a/1.txt", "a1", -1},
{"/a/1.txt", "aa1", -1}, // same as above, but with leading slash that will be trim
{"ab/1.txt", "ab1", 3},
{"b/1.txt", "b1", 2}, // minio closes when the size is set
{"b/2.txt", "b2", -1},
{"b/3.txt", "b3", -1},
{"b/x 4.txt", "bx4", -1},
}
for _, f := range testFiles {
_, err = l.Save(f[0], bytes.NewBufferString(f[1]), -1)
sc := &spyCloser{bytes.NewBufferString(f.content), 0}
_, err = l.Save(f.path, sc, f.size)
require.NoError(t, err)
assert.Equal(t, 0, sc.closed)
}
expectedList := map[string][]string{

View file

@ -50,7 +50,7 @@ concept_user_organization = المنظمة
link_account = ربط الحساب
rerun_all = أعِد تشغيل جميع الوظائف
your_profile = الملف الشخصي
sign_out = سجل الخروج
sign_out = سجّل الخروج
settings = الإعدادات
locked = مقفول
error = خطأ
@ -87,7 +87,7 @@ add_all = أضف الكل
new_fork = اشتقاق جديد لمستودع
new_project_column = عمود جديد
add = أضف
active_stopwatch = تتبع وقت الإنجاز
active_stopwatch = متتبِّع وقت النشاط
organization = منظمة
new_migrate = ترحيل جديد
save = احفظ
@ -114,7 +114,7 @@ twofa_scratch = الرمز الاحتياطي للمصادقة بعاملين
home = الرئيسية
email = عنوان البريد الإلكتروني
issues = المسائل
error404 = الصفحة التي تحاول الوصول لها إما <strong>لا توجد</strong> أو <strong>أنت لست مأذون لك</strong> بعرضها.
error404 = الصفحة التي تحاول الوصول لها إما <strong>غير موجودو</strong> أو <strong>أنك غير مصرح لك</strong> بعرضها.
powered_by = مدعوم بواسطة %s
retry = أعد المحاولة
tracked_time_summary = ملخص للتتبع الزمني وفقًا لنتائج تصفية قائمة المسائل
@ -127,8 +127,8 @@ toggle_menu = تبديل القائمة
more_items = عناصر اضافية
copy_generic = نسخ إلى الحافظة
invalid_data = بيانات غير صالحة: %v
filter.clear = مسح المرشحات
filter = مرشح
filter.clear = مسح عوامل التصفية
filter = عامل تصفية
filter.is_archived = مؤرشف
filter.is_template = قوالب
filter.not_mirror = ليست مرايا
@ -137,13 +137,17 @@ filter.is_mirror = مرايا
filter.is_fork = الاشتقاقات
filter.not_fork = ليست اشتقاقات
filter.not_archived = ليس مؤرشف
filter.public = علني
filter.public = عام
filter.private = خاص
new_repo.title = مستودع جديد
new_migrate.title = انتقال جديد
new_org.title = منظمة جديدة
new_repo.link = مستودع جديد
new_migrate.link = انتقال جديد
copy_path = نسخ المسار
test = اختبار
new_org.link = منظمة جديدة
error413 = لقد استنفدت حصتك.
[install]
db_name = اسم قاعدة البيانات
@ -170,7 +174,7 @@ reinstall_confirm_check_2 = وقد يلزم إعادة تزامن المستود
run_user = شغّل عبر مستخدم
err_admin_name_is_invalid = اسم مستخدم المدير غير صالح
reinstall_confirm_check_3 = أنتِ تؤكد أنكِ متأكد تماماً من أن فورجيو يعمل مع مسار app.ini الصحيح وأنك متأكد من أنه يجب عليك إعادة تثبيته. أنت تُؤكّدُ بأنّك تُقرّ بالمخاطر السالفة الذكر.
repo_path = المسار الجذري للمستودع
repo_path = المسار الجذر للمستودع
err_empty_admin_email = عنوان بريد المدير لا يمكن أن يكون فارغ.
no_admin_and_disable_registration = لا يمكنك تعطيل التسجيل الذاتي للمستخدمين بدون إنشاء حساب إداري.
err_admin_name_pattern_not_allowed = اسم مستخدم المدير غير صالح، هذا الأسم يطابق نمطا محجوز
@ -179,10 +183,10 @@ repo_path_helper = ستُحفظ كلّ مستودعات جِت البعيدة ف
general_title = الإعدادات العامة
lfs_path_helper = الملفات التي تم تعقبها بواسطة Git LFS ستُخزن في هذا الدليل. اتركه فارغًا لتعطيله.
err_empty_db_path = طريق قاعدة بيانات SQLite3 لا يمكن أن يكون فارغا.
lfs_path = مسار جذر جِت LFS
app_name_helper = يمكنك إدخال اسم شركتك هنا.
lfs_path = مسار جذر Git LFS
app_name_helper = أدخل اسم المثيل هنا. سيظهر هذا الاسم في كل الصفحات.
err_admin_name_is_reserved = اسم مستخدم المدير غير صالح، هذا الأسم محجوز
app_name = عنوان الموقع
app_name = عنوان المثيل
log_root_path = مسار السجل
log_root_path_helper = ستُكتب ملفات السجل في هذا الدليل.
smtp_addr = مضيف SMTP
@ -190,7 +194,7 @@ smtp_port = منفذ SMTP
mailer_password = كلمة مرور SMTP
app_url_helper = العنوان الأساسي لاستنساخ عناوين URL HTTP(S) وإشعارات البريد الإلكتروني.
mailer_user = اسم مستخدم SMTP
disable_gravatar.description = عطل جرافاتار والجهات الخارجية للصور الرمزية. ستُستخدم صورة رمزية مبدئية حتى يرفع المستخدم صورة.
disable_gravatar.description = عطل Gravatar والجهات الخارجية للصور الرمزية. ستُستخدم صورة رمزية مبدئية حتى يرفع المستخدم صورة.
offline_mode.description = عطل خدمات توصيل المحتوى من الجهات الخارجية، واخدم كل المحتوى محلياً.
run_user_helper = اسم مستخدم نظام التشغيل الذي يشغل فورجيو. ملاحظة: هذا المستخدم يجب أن يكون له حق الوصول إلى المسار الجذري للمستودع.
domain = نطاق الخادم
@ -199,28 +203,28 @@ smtp_from = أرسل البريد الإلكتروني كـ
federated_avatar_lookup = تفعيل الصور الرمزية الاتحادية
optional_title = إعدادات اختيارية
domain_helper = نطاق أو عنوان المضيف لخادمك.
mail_notify = فعّل التنبيه عبر البريد الإلكتروني
app_url = الرابط الأساس لفورجيو
mail_notify = فعّل التنبيهات عبر البريد الإلكتروني
app_url = الرابط الأساس
smtp_from_helper = عنوان البريد الإلكتروني الذي سيستخدمه فورجيو. أدخل عنوان بريد إلكتروني عادي أو استخدم صيغة"Name" <email@example.com>.
ssh_port_helper = رقم المنفذ الذي يستمع له خادم SSH. اتركه فارغاً لتعطيله.
http_port_helper = المنفذ الذي سيستمع إليه خادم الويب لفورجيو.
http_port = منفذ استماع HTTP لفورجيو
http_port_helper = المنفذ الذي سيستمع إليه خادم ويب Forgejo.
http_port = منفذ استماع HTTP
ssh_port = منفذ خادم SSH
email_title = إعدادات البريد الإلكتروني
offline_mode = فعل الوضع المحلي
server_service_title = إعدادات الخادم وخدمات الجهات الخارجية
register_confirm = الزم تأكيد البريد الإلكتروني للتسجيل
allow_only_external_registration.description = لا يسمح بالتسجيل إلا من خلال الخدمات الخارجية
allow_only_external_registration.description = لن يتمكن المستخدمون من إنشاء حسابات جديدة إلا باستخدام خدمات خارجية مهيأة.
disable_registration = عطّل التسجيل الذاتي
federated_avatar_lookup.description = تفعيل الصور الرمزية الاتحادية باستخدام ليبرافاتار.
federated_avatar_lookup.description = تفعيل الصور الرمزية الاتحادية باستخدام Libravatar.
openid_signup = فعّل التسجيل الذاتي عبر OpenID
disable_registration.description = عطل التسجيل الذاتي. المديرون فقط سيكونون قادرين على إنشاء حسابات جديدة للمستخدمين.
disable_registration.description = سيتمكن مسؤولو المثيل فقط من إنشاء حسابات مستخدمين جديدة. يوصى بشدة بإبقاء التسجيل معطلاً إلا إذا كنت تنوي استضافة مثيل عام للجميع ومستعد للتعامل مع كميات كبيرة من الحسابات غير المرغوب بها.
openid_signin = فعّل تسجيل الدخول عبر OpenID
openid_signin.description = فعّل تسجيل دخول المستخدمين عبر OpenID.
enable_captcha = فعّل كابتشا التسجيل
enable_captcha.description = الزم وجود كابتشا للتسجيل الذاتي للمستخدمين.
enable_captcha.description = مطالبة المستخدمين باجتياز اختبار CAPTCHA من أجل إنشاء حسابات.
openid_signup.description = فعّل التسجيل الذاتي للمستخدمين عبر OpenID.
require_sign_in_view = الزم تسجيل الدخول لعرض الصفحات
require_sign_in_view = يتطلب تسجيل الدخول لعرض محتوى المثيل
require_sign_in_view.description = مكّن وصول الصفحات للمستخدمين فقط. لن يرى الزائرون سوى صفحات التسجيل والتسجيل.
admin_setting.description = إنشاء حساب إداري هو اختياري. أول مستخدم مُسجل سيصبح تلقائيا مديرا.
admin_password = كلمة المرور
@ -233,7 +237,7 @@ test_git_failed = يتعذر اختبار أمر جِت: %v
confirm_password = أكّد كلمة المرور
invalid_admin_setting = إعداد حساب المدير غير صالح: %v
invalid_log_root_path = مسار السجل غير صالح: %v
default_enable_timetracking = فعّل تتبع الوقت مبدئيا
default_enable_timetracking = فعّل التتبع الزمني افتراضيًا
env_config_keys_prompt = ستطبق المتغيرات البيئية التالية أيضاً على ملف الإعدادات:
admin_title = إعدادات حساب المدير
no_reply_address_helper = النطاق للمستخدمين بعنوان بريد إلكتروني مخفي. مثلاً، اسم المستخدم "sarah" سوف يسجل في جِت كـ"sarah@noreply.example.org" لو كان نطاق البريد الإلكتروني الخفي مدخل كـ"noreply.example.org".
@ -242,9 +246,9 @@ default_enable_timetracking.description = فعل تتبع الوقت للمست
run_user_not_match = مستخدم التشغيل غير مطابق لأسم المستخدم الحالي: %s -> %s
invalid_db_setting = إعدادات قاعدة البيانات غير صالحة: %v
invalid_db_table = جدول قاعدة البيانات "%s" غير صالح: %v
default_keep_email_private.description = أخفِ عناوين البريد الإلكتروني للحسابات الجديدة مبدئيا.
default_keep_email_private.description = قم بتمكين إخفاء عنوان البريد الإلكتروني للمستخدمين الجدد افتراضيًا حتى لا يتم تسريب هذه المعلومات فور التسجيل.
env_config_keys = إعدادات بيئية
default_allow_create_organization = اسمح بإنشاء المنظمات مبدئيا
default_allow_create_organization = اسمح بإنشاء المنظمات بشكل افتراضي
invalid_app_data_path = مسار بيانات التطبيق غير صالح: %v
enable_update_checker_helper = يفحص لإيجاد اصدارات جديدة عن طريق الإتصال بسيرفرات فورجيو.
invalid_repo_path = المسار الجزري للمستودع غير صالح: %v
@ -252,10 +256,15 @@ internal_token_failed = فشل توليد الرمز الداخلي: %v
no_reply_address = نطاقات البريد الإلكتروني المخفية
default_keep_email_private = أخفِ عناوين البريد الإلكتروني مبدئيا
admin_name = اسم مستخدم المدير
default_allow_create_organization.description = اسمح بحسابات المستخدمين الجديدة بإنشاء المنظمات مبدئيا.
default_allow_create_organization.description = السماح للمستخدمين الجدد بإنشاء منتديات المجموعة بشكل افتراضي. عند تعطيل هذا الخيار، سيتعين على المسؤول منح إذن لإنشاء منتديات المجموعة للمستخدمين الجدد.
password_algorithm = خوارزمية تجزئة كلمة المرور
invalid_password_algorithm = خوارزمية بصمة كلمة المرور غير صالحة
password_algorithm_helper = اختر خوارزمية بصمة كلمة المرور. تختلف الخوارزميات في متطلباتها وقوتها. خوارزمية argon2 آمنة لكن تتطلب الكثير من الذاكرة ولذلك قد تكون غير ملائمة للأنظمة الصغيرة.
app_slogan_helper = أدخل شعار المثيل الخاص بك هنا. اتركه فارغاً لتعطيله.
app_slogan = شعار المثيل
allow_only_external_registration = السماح بالتسجيل عبر الخدمات الخارجية فقط
config_location_hint = سيتم حفظ خيارات التهيئة هذه في:
smtp_from_invalid = عنوان "،بريد الإرسال كـ" غير صالح
[editor]
buttons.list.ordered.tooltip = أضف قائمة مرقمة
@ -272,10 +281,22 @@ buttons.mention.tooltip = اذكر مستخدمًا أو فريقًا
buttons.italic.tooltip = أضف نصًا مائلًا
buttons.link.tooltip = اضف رابط
buttons.disable_monospace_font = عطّل الخط الثابت العرض
buttons.unindent.tooltip = ‪عناصر غير متساوية من نفس المستوى
buttons.indent.tooltip = تداخل العناصر بنفس المستوى
table_modal.header = إضافة جدول
table_modal.placeholder.header = الترويسة
table_modal.placeholder.content = المحتوى
table_modal.label.rows = الصفوف
table_modal.label.columns = الأعمدة
link_modal.url = Url
link_modal.description = الوصف
buttons.new_table.tooltip = إضافة جدول
link_modal.header = إضافة رابط
link_modal.paste_reminder = تلميح: باستخدام عنوان URL في حافظتك، يمكنك اللصق مباشرةً في المحرر لإنشاء رابط.
[aria]
navbar = شريط التنقل
footer.software = عن البرمجية
footer.software = عن هذه البرمجية
footer.links = روابط
footer = الذيل
@ -1386,6 +1407,21 @@ repo.transfer.subject_to = %s يود نقل ملكية "%s" إلى %s
issue.action.ready_for_review = <b>@%[1]s</b> علّم هذا الطلب للسحب كجاهز للمراجعة.
issue_assigned.pull = @%[1]s عيّنك إلى طلب سحب %[2]s في مستودع %[3]s.
issue.action.review_dismissed = <b>@%[1]s</b> أستبعد آخر مراجعة من %[2]s لهذا الطلب للسحب.
password_change.subject = تم تغيير كلمة مرورك
totp_disabled.subject = تم تعطيل TOTP
totp_disabled.text_1 = تم تعطيل كلمة المرور لمرة واحدة المستندة إلى الوقت (TOTP) على حسابك للتو.
totp_enrolled.text_1.has_webauthn = لقد قمت للتو بتمكين TOTP لحسابك. هذا يعني أنه بالنسبة لجميع عمليات تسجيل الدخول المستقبلية إلى حسابك، يمكنك استخدام TOTP كطريقة للمصادقة الثنائية ، أو استخدام أي من مفاتيح الأمان الخاصة بك.
totp_enrolled.subject = لقد قمت بتشيط TOTP كطريقة 2FA
removed_security_key.subject = تمت إزالة مفتاح الأمان
removed_security_key.text_1 = تم إزالة مفتاح الأمان ”%[1] s“ للتو من حسابك.
account_security_caution.text_1 = إذا كان هذا أنت، فيمكنك تجاهل هذا البريد بأمان.
totp_disabled.no_2fa = لم تعد هناك طرق أُخرى للمصادقة الثنائية (2FA) قيد التهيئة عد الآن ، أي أنه لم يعد من الضروري تسجيل الدخول إلى حسابك باستخدام المصادقة الثنائية (2FA).
removed_security_key.no_2fa = لم تعد هناك طرق أخرى للمصادقة الثنائية (2FA) قيد التهيئة بعد الآن، أي لم يعد من الضروري تسجيل الدخول إلى حسابك باستخدام المصادقة الثنائية (2FA).
primary_mail_change.subject = تم تغيير البريد الأساسي الخاص بك
password_change.text_1 = تم تغيير كلمة مرور حسابك للتو.
primary_mail_change.text_1 = تم تغيير البريد الإلكتروني الأساسي لحسابك إلى %[1]s. هذا يعني أن عنوان البريد الإلكتروني هذا لن يتلقى إشعارات البريد لحسابك بعد الآن.
account_security_caution.text_2 = إذا لم تكن أنت، فهذا يعني أن حسابك مخترق. يرجى الاتصال بمسؤولي هذا الموقع.
totp_enrolled.text_1.no_webauthn = لقد قمت للتو بتمكين TOTP لحسابك. هذا يعني أنه بالنسبة لجميع عمليات تسجيل الدخول المستقبلية إلى حسابك، يجب عليك استخدام TOTP كطريقة للمصادقة الثنائية.
[error]
not_found = تعذر العثور على الهدف.
@ -1421,7 +1457,7 @@ joined_on = انضم في %s
user_bio = السيرة الذاتية
repositories = المستودعات
activity = النشاط العام
projects = مشاريع
projects = المشاريع
unfollow = إلغِ المتابعة
settings = إعدادات المستخدم
following_few = %d يتابع
@ -1429,7 +1465,7 @@ follow = تابع
followers_few = %d متابعين
form.name_reserved = اسم المستخدم "%s" محجوز.
email_visibility.limited = عنوان بريدك الإلكتروني ظاهر لكل المستخدمين المُستَوثَقين
code = البرمجية
code = الكود
overview = نظرة عامة
watched = المستودعات المشاهدة
disabled_public_activity = هذا المستخدم عطّل الظهور العام للنشاط.
@ -1438,6 +1474,17 @@ email_visibility.private = عنوان بريدك الإلكتروني ظاهر
starred = المستودعات المميّزة بنجمة
form.name_chars_not_allowed = اسم المستخدم "%s" يحتوي على رموز غير صالحة.
form.name_pattern_not_allowed = النمط "s%" غير مسموح به في إسم المستخدم.
followers.title.one = متابِع
public_activity.visibility_hint.admin_private = هذا النشاط مرئي لك لأنك مسؤول، ولكن المستخدم يريد أن يظل خاصاً.
public_activity.visibility_hint.self_private = نشاطك مرئي لك ولسُعاة المثيل فقط. <a href="%s">تعديل الإعدادات</a>.
followers_one = %d متابِع
following.title.one = متابعة
followers.title.few = متابعين
following_one = %d يُتابع
following.title.few = متابعة
public_activity.visibility_hint.self_public = نشاطك مرئي للجميع، باستثناء التفاعلات في المساحات الخاصة. <a href="%s">اضبط الإعدادات</a>.
public_activity.visibility_hint.admin_public = هذا النشاط مرئي للجميع، ولكن بصفتك مسؤولاً يمكنك أيضًا رؤية التفاعلات في المساحات الخاصة.
public_activity.visibility_hint.self_private_profile = نشاطك مرئي لك ولسُعاة المثيل فقط لأن ملفك الشخصي خاص. <a href="%s">تعديل الإعدادات</a>.
[auth]
change_unconfirmed_email_error = تعذر تغيير البريد الإلكتروني: %v
@ -1458,11 +1505,11 @@ active_your_account = فعّل حسابك
register_helper_msg = هل لديك حساب بالفعل؟ سجل الدخول!
manual_activation_only = تواصل مع مدير موقعك لإكمال التفعيل.
must_change_password = حدّث كلمة المرور الخاصة بك
send_reset_mail = أرسل رسالة استعادة حساب
send_reset_mail = أرسل بريد الاستعادة
resend_mail = اضغط هنا لإعادة إرسالة رسالة تفعيل حسابك
has_unconfirmed_mail = أهلا يا %s، لديك عنوان بريد إلكتروني غير مؤكَّد (<b>%s</b>). إن لم تستلم رسالة تأكيد أو تريد إرسال واحدة جديدة، فنرجو الضغط على الزر الذي بالأسفل.
email_not_associate = عنوان البريد هذا غير مرتبط بأي حساب.
reset_password = استعادة حساب
reset_password = استعادة الحساب
oauth_signin_tab = أربط بحساب موجود
invalid_password = كلمة المرور الخاصة بك لا تطابق كلمة المرور التي استخدمت لتسجيل الحساب.
oauth_signin_title = سجّل الدخول لتأذن للحساب المربوط
@ -1483,13 +1530,13 @@ reset_password_wrong_user = أنت مُسجل كـ %s، لكن رابط أعاد
openid_connect_title = اتصل بحساب موجود
confirmation_mail_sent_prompt = تم إرسال بريد تأكيد جديد إلى <b>%s</b>. يرجى التأكد من صندوق بريدك في خلال %s حتى تكتمل عملية التسجيل. إذا كان عنوان البريد خاطئ، يمكنك تسجيل الدخول وطلب بريد تأكيد جديد يُرسل إلى عنوان آخر.
scratch_code = رمز الخدش
invalid_code_forgot_password = رمز تأكيدك غير صحيح أو انتهى اضغط <a href="%s">هنا</a> للإعادة.
invalid_code_forgot_password = رمز تأكيدك غير صحيح أو انتهت صلاحيته. اضغط <a href="%s">هنا</a> للإعادة.
openid_register_title = أنشئ حسابًا جديدًا
verify = تحقق
twofa_scratch_used = لقد استخدمت رمز الخدش الخاص بك. لقد تم إعادة توجيهك إلى إعدادات المصادقة الثنائية حتى يمكنك إزالة تسجيل جهازك أو توليد رمز خدش جديد.
oauth_signup_submit = أكمل الحساب
oauth.signin.error = كان هناك خطأ في تجهيز طلب الإذن إذا استمر هذا الخطأ، يرجى الاتصال بالمدير.
invalid_code = رمز تأكيدك غير صحيح أو انتهى.
invalid_code = رمز تأكيدك غير صحيح أو انتهت صلاحيته.
oauth_signup_title = أكمل حساب جديد
resent_limit_prompt = لقد طلبت بالفعل بريداً إلكترونياً للتفعيل مؤخراً من فضلك انتظر 3 دقائق وحاول مرة أخرى.
reset_password_mail_sent_prompt = تم إرسال بريد تأكيد جديد إلى <b>%s</b>. يرجى التأكد من صندوق بريدك في خلال %s حتى تكتمل عملية استعادة الحساب.
@ -1513,6 +1560,13 @@ openid_register_desc = مسار الـOpenID المختار مجهول. اربط
remember_me = تذكر هذا الجهاز
remember_me.compromised = رمز الاحتفاظ بتسجيل الدخول لم يعد صالحا، مما قد يعني اختراق الحساب. نرجو مراجعة حسابك لرؤية أي نشاط غير مألوف.
authorization_failed_desc = فشل التفويض لأننا اكتشفنا طلبًا غير صالح. يرجى الاتصال بمشرف التطبيق الذي حاولت ترخيصه.
sign_in_openid = المتابعة باستخدام OpenID
hint_login = لديك حساب بالفعل؟ <a href="%s">سجّل الدخول الآن!</a>
hint_register = يلزمك حساب ؟ <a href="%s">سجِّل الآن.</a>
sign_up_button = سجِّل الآن.
back_to_sign_in = العودة إلى تسجيل الدخول
use_onetime_code = استخدم رمزًا لمرة واحدة
unauthorized_credentials = بيانات الاعتماد غير صحيحة أو انتهت صلاحيتها. أعد محاولة تنفيذ الأمر أو راجع %s لمزيد من المعلومات
[packages]
rpm.repository.multiple_groups = هذه الحزمة متوفرة في مجموعات متعددة.
@ -1556,6 +1610,9 @@ less = أقل
number_of_contributions_in_the_last_12_months = %s مساهم في آخر 12 شهر
contributions_zero = بلا مساهمات
more = أكثر
contributions_format = {contributions} مساهمة في {day} {month} {year}
contributions_one = المساهمة
contributions_few = المساهمات
[admin]
self_check.database_fix_mysql = لمستخدمين ميسكول/ماريا دي بي، يمكنك استخدام أمر "forgejo doctor convert" لإصلاح مشاكل التجمّع، أو يمكنك أيضاً إصلاح المشكلة عن طريق تعديل السيكول يدوياً.
@ -1707,7 +1764,7 @@ enterred_invalid_org_name = اسم المنظمة التي أدخلته خطأ.
lang_select_error = اختر لغة من القائمة.
alpha_dash_error = ` لا يجب أن يحتوي إلا على الحروف الإنجليزية والأرقام والشرطة ("-") والشرطة السفلية ("_").`
alpha_dash_dot_error = ` لا يجب أن يحتوي إلا على الحروف الإنجليزية والأرقام والشرطة ("-") والشرطة السفلية ("_") والنقطة (".").`
repo_name_been_taken = اسم المستودع مستعمل بالفعل.
repo_name_been_taken = اسم المستودع مستخدم بالفعل.
Email = البريد الإلكتروني
auth_failed = فشل الاستيثاق: %v
email_error = ` ليس عنوان بريد إلكتروني صالح.`
@ -1727,10 +1784,10 @@ still_has_org = "حسابك عضو في منظمة أو أكثر؛ غادرهم
repository_files_already_exist.adopt_or_delete = الملفات موجودة بالفعل لهذا المستودع. إما اعتمادها أو حذفها.
repository_files_already_exist.delete = الملفات موجودة بالفعل لهذا المستودع. يجب عليك حذفها.
repository_files_already_exist.adopt = الملفات موجودة بالفعل لهذا المستودع ويمكن اعتمادها فقط.
repository_files_already_exist = الملفات موجودة بالفعل لهذا المستودع. تواصل مع مدير النظام.
repository_files_already_exist = الملفات موجودة بالفعل لهذا المستودع. اتصل بمدير النظام.
TeamName = اسم الفريق
username_has_not_been_changed = لم يتم تغيير اسم المستخدم
username_change_not_local_user = المستخدمين غير المحليين غير مسموح لهم بتغيير أسماؤهم.
username_change_not_local_user = المستخدمين غير المحليين غير مسموح لهم بتغيير أسمائهم.
captcha_incorrect = الكابتشا خاطئة.
AdminEmail = عنوان البريد الإلكتروني للمدير
team_no_units_error = اسمح بالوصول إلى قسم واحد على الأقل في المستودعات.
@ -1757,6 +1814,23 @@ glob_pattern_error = `النمط الشامل غير صالح: %s.`
CommitChoice = إختيار الإداع
regex_pattern_error = ` نمط التعبير النمطي غير صالح: %s.`
username_error = ` يُمكنه أن يحتوي على حروف إنجليزية وأرقام وشرطة ("-") وشرطة سفلية ("_") و نقطة (".") فقط. ويمكنه ان يبدأ وينتهي بحرف او برقم.`
Biography = النبذة
Website = موقع الويب
To = اسم الفرع
AccessToken = رمز الوصول
repository_force_private = وضع الخاص الإجباري مفعّل: لا يمكن تحويل المستودعات الخاصة إلى عامة.
FullName = الاسم الكامل
Description = الوصف
Pronouns = الضمائر
username_claiming_cooldown = لا يمكن المطالبة باسم المستخدم، لأن فترة تباطؤه لم تنتهِ بعد. يمكن المطالبة به عند %[1]s.
Location = الموقع
invalid_group_team_map_error = ` التعيين غير صالح: %s `
visit_rate_limit = تناولت الزيارة عن بُعد الحد من معدلها.
email_domain_is_not_allowed = نطاق البريد الإلكتروني للمستخدم <b>%s</b> يتعارض مع قائمة النطاقات المسموحة ، أو الممنوعة. يرجى التأكد من إدخال عنوان البريد الإلكتروني بشكل صحيح.
unset_password = المستخدم المسجل لم يقم بتعيين كلمة مرور.
unsupported_login_type = نوع تسجيل الدخول غير مدعوم لحذف الحساب.
invalid_ssh_principal = أصل غير صالح: %s
required_prefix = المُدخل يجب أن يبدأ مع "%s"
[home]
filter = تصفيات أخرى
@ -1803,6 +1877,10 @@ code_no_results = لم يتم العثور على برمجية تطابق الب
relevant_repositories_tooltip = تم أخفاء المستودعات التي هي مشتقات وأيضاً التي ليس لها موضوع، ولا أيقونة، ولا يوجد وصف.
relevant_repositories = يتم اظهار المستودعات المتعلقة فقط. <a href="%s">أظهر النتائج غير المصفاة</a>.
code_last_indexed_at = فُهرس آخر مرة %s
stars_few = %d نجوم
forks_one = %d نسخة
forks_few = %d نُسَخ
stars_one = %d نجمة
[actions]
variables.none = لا توجد متغيرات بعد.
@ -1976,7 +2054,7 @@ component_failed_to_load = حدث خطأ غير متوقع.
[search]
org_kind = بحث في المنظمات…
code_search_unavailable = البحث في الكود غير متوفر حاليًا. يرجى الاتصال بمدير الموقع.
search = ابحث...
search = البحث…
type_tooltip = نوع البحث
fuzzy = أجعد
fuzzy_tooltip = قم بتضمين النتائج التي تتطابق أيضًا مع مصطلح البحث بشكل وثيق
@ -1985,10 +2063,19 @@ match_tooltip = قم بتضمين النتائج التي تطابق مصطلح
repo_kind = بحث في المستودعات…
user_kind = بحث عن المستخدمين…
team_kind = بحث عن الفرق…
code_kind = بحث في الكود…
code_kind = بحث ضمن الكود…
project_kind = البحث ضمن المشاريع…
branch_kind = البحث ضمن الفروع…
no_results = لا توجد نتائج مطابقة.
issue_kind = البحث ضمن الأعطال…
pull_kind = البحث ضمن طلبات السحب…
keyword_search_unavailable = البحث من خلال الكلمات المفتاحية ليس متوفر حالياً. رجاءاً تواصل مع مشرف الموقع.
package_kind = البحث ضمن الحزم…
regexp_tooltip = تعامل مع عبارة البحث على أنها تعبير نمطي
commit_kind = البحث ضمن الإيداعات…
union = مطابقة عامة
runner_kind = البحث ضمن المشغِّلات…
exact = مطابق
exact_tooltip = عرض النتائج التي تطابق مصطلح البحث بالضبط فقط
regexp = RegExp
union_tooltip = عرض النتائج التي تطابق أي من الكلمات المفتاحية المفصولة بمسافات

View file

@ -251,12 +251,12 @@ db_schema_helper=Leer lassen, um den Datenbank-Standardwert („public“) zu ve
ssl_mode=SSL
path=Pfad
sqlite_helper=Dateipfad zur SQLite3-Datenbank.<br>Gib einen absoluten Pfad an, wenn Forgejo als Service gestartet wird.
reinstall_error=Du versuchst, in eine bereits existierende Forgejo Datenbank zu installieren
reinstall_error=Du versuchst, in eine bereits existierende Forgejo-Datenbank zu installieren
reinstall_confirm_message=Eine Neuinstallation mit einer bestehenden Forgejo-Datenbank kann mehrere Probleme verursachen. In den meisten Fällen solltest du deine vorhandene „app.ini“ verwenden, um Forgejo auszuführen. Wenn du weißt, was du tust, bestätige die folgenden Angaben:
reinstall_confirm_check_1=Die von der SECRET_KEY in app.ini verschlüsselten Daten können verloren gehen: Benutzer können sich unter Umständen nicht mit 2FA/OTP einloggen und Spiegel könnten nicht mehr richtig funktionieren. Mit der Ankreuzung dieses Kästchens bestätigst du, dass die aktuelle app.ini-Datei den korrekten SECRET_KEY enthält.
reinstall_confirm_check_2=Die Repositorys und Einstellungen müssen eventuell neu synchronisiert werden. Durch das Ankreuzen dieses Kästchens bestätigst du, dass du die Hooks für die Repositorys und die authorized_keys-Datei manuell neu synchronisierst. Du bestätigst, dass du sicherstellst, dass die Repository- und Spiegeleinstellungen korrekt sind.
reinstall_confirm_check_3=Du bestätigst, dass du absolut sicher bist, dass diese Forgejo mit der richtigen app.ini läuft, und du sicher bist, dass du neu installieren musst. Du bestätigst, dass du die oben genannten Risiken anerkennst.
err_empty_db_path=Der SQLite3 Datenbankpfad darf nicht leer sein.
err_empty_db_path=Der SQLite3-Datenbankpfad darf nicht leer sein.
no_admin_and_disable_registration=Du kannst Selbst-Registrierungen nicht deaktivieren, ohne ein Administratorkonto zu erstellen.
err_empty_admin_password=Das Administrator-Passwort darf nicht leer sein.
err_empty_admin_email=Die Administrator-E-Mail darf nicht leer sein.
@ -463,7 +463,7 @@ openid_register_title=Neues Konto einrichten
openid_register_desc=Die gewählte OpenID-URI ist unbekannt. Ordne sie hier einem neuen Account zu.
openid_signin_desc=Gib deine OpenID-URI ein, zum Beispiel alice.openid.example.org oder https://openid.example.org/alice.
disable_forgot_password_mail=Die Kontowiederherstellung ist deaktiviert, da keine E-Mail eingerichtet ist. Bitte kontaktiere den zuständigen Administrator.
disable_forgot_password_mail_admin=Die Kontowiederherstellung ist nur verfügbar, wenn eine E-Mail eingerichtet wurde. Bitte richte eine E-Mail Adresse ein, um die Kontowiederherstellung freizuschalten.
disable_forgot_password_mail_admin=Die Kontowiederherstellung ist nur verfügbar, wenn eine E-Mail eingerichtet wurde. Bitte richte eine E-Mail-Adresse ein, um die Kontowiederherstellung freizuschalten.
email_domain_blacklisted=Du kannst dich nicht mit deiner E-Mail-Adresse registrieren.
authorize_application=Anwendung autorisieren
authorize_redirect_notice=Du wirst zu %s weitergeleitet, wenn du diese Anwendung autorisierst.
@ -530,8 +530,8 @@ issue.action.merge=<b>@%[1]s</b> hat #%[2]d in %[3]s zusammengeführt.
issue.action.approve=<b>@%[1]s</b> hat diesen Pull-Request genehmigt.
issue.action.reject=<b>@%[1]s</b> hat Änderungen auf diesem Pull-Request angefordert.
issue.action.review=<b>@%[1]s</b> hat diesen Pull-Request kommentiert.
issue.action.review_dismissed=<b>@%[1]s</b> hat das letzte Review von %[2]s für diesen Pull-Request verworfen.
issue.action.ready_for_review=<b>@%[1]s</b> hat diesen Pull-Request zum Review freigegeben.
issue.action.review_dismissed=<b>@%[1]s</b> hat die letzte Sichtung von %[2]s für diesen Pull-Request verworfen.
issue.action.ready_for_review=<b>@%[1]s</b> hat diesen Pull-Request für die Sichtung freigegeben.
issue.action.new=<b>@%[1]s</b> hat #%[2]d geöffnet.
issue.in_tree_path=In %s:
@ -540,8 +540,8 @@ release.new.text=<b>@%[1]s</b> hat %[2]s in %[3]s released
release.title=Titel: %s
release.note=Anmerkung:
release.downloads=Downloads:
release.download.zip=Quellcode (ZIP Datei)
release.download.targz=Quellcode (TAR.GZ Datei)
release.download.zip=Quellcode (ZIP)
release.download.targz=Quellcode (TAR.GZ)
repo.transfer.subject_to=%s möchte „%s“ an %s übertragen
repo.transfer.subject_to_you=%s möchte dir „%s“ übertragen
@ -787,7 +787,7 @@ comment_type_group_time_tracking=Zeiterfassung
comment_type_group_deadline=Frist
comment_type_group_dependency=Abhängigkeit
comment_type_group_lock=Sperrstatus
comment_type_group_review_request=Angeforderte Reviews
comment_type_group_review_request=Angeforderte Sichtungen
comment_type_group_pull_request_push=Hinzugefügte Commits
comment_type_group_project=Projekt
comment_type_group_issue_ref=Issue-Referenz
@ -873,7 +873,7 @@ gpg_key_matched_identities_long=Die eingebetteten Identitäten in diesem Schlüs
gpg_key_verified=Verifizierter Schlüssel
gpg_key_verified_long=Der Schlüssel wurde mit einem Token verifiziert. Er kann verwendet werden, um Commits zu verifizieren, die mit irgendeiner für diesen Nutzer aktivierten E-Mail-Adresse und irgendeiner Identität dieses Schlüssels übereinstimmen.
gpg_key_verify=Verifizieren
gpg_invalid_token_signature=Der GPG-Key, die Signatur, und das Token stimmen nicht überein, oder das Token ist veraltet.
gpg_invalid_token_signature=Der GPG-Key, die Signatur und das Token stimmen nicht überein, oder das Token ist veraltet.
gpg_token_required=Du musst eine Signatur für das folgende Token angeben
gpg_token=Token
gpg_token_help=Du kannst eine Signatur wie folgt generieren:
@ -902,10 +902,10 @@ add_principal_success=Die SSH-Zertifikatsidentität „%s“ wurde hinzugefügt.
delete_key=Entfernen
ssh_key_deletion=SSH-Schlüssel entfernen
gpg_key_deletion=GPG-Schlüssel entfernen
ssh_principal_deletion=SSH-Zertifik-Identität entfernen
ssh_principal_deletion=SSH-Zertifikats-Principal entfernen
ssh_key_deletion_desc=Wenn du einen SSH-Key entfernst, hast du mit diesem Key keinen Zugriff mehr. Fortfahren?
gpg_key_deletion_desc=Wenn du einen GPG-Schlüssel entfernst, können damit unterschriebene Commits nicht mehr verifiziert werden. Fortfahren?
ssh_principal_deletion_desc=Das Entfernen einer SSH-Zertifikat-Identität entzieht den Zugriff auf dein Konto. Fortfahren?
ssh_principal_deletion_desc=Das Entfernen eines SSH-Zertifikats-Principals entzieht den Zugriff auf dein Konto. Fortfahren?
ssh_key_deletion_success=Der SSH-Schlüssel wurde entfernt.
gpg_key_deletion_success=Der GPG-Schlüssel wurde entfernt.
ssh_principal_deletion_success=Die Identität wurde entfernt.
@ -931,7 +931,7 @@ unbind_success=Das soziale Konto wurde erfolgreich entfernt.
manage_access_token=Zugriffstokens
generate_new_token=Neuen Token erzeugen
tokens_desc=Diese Tokens gewähren vollen Zugriff auf dein Konto via die Forgejo-API.
tokens_desc=Diese Tokens gewähren vollen Zugriff auf dein Konto mit der Forgejo-API.
token_name=Token-Name
generate_token=Token generieren
generate_token_success=Ein neuer Token wurde generiert. Kopiere diesen jetzt, da er nicht erneut angezeigt wird.
@ -970,8 +970,8 @@ save_application=Speichern
oauth2_client_id=Client-ID
oauth2_client_secret=Client-Geheimnis
oauth2_regenerate_secret=Geheimnis neu generieren
oauth2_regenerate_secret_hint=Secret verloren?
oauth2_client_secret_hint=Das Secret wird nach dem Verlassen oder Aktualisieren dieser Seite nicht mehr angezeigt. Bitte stelle sicher, dass du es gespeichert hast.
oauth2_regenerate_secret_hint=Geheimnis verloren?
oauth2_client_secret_hint=Das Geheimnis wird nach dem Verlassen oder Aktualisieren dieser Seite nicht mehr angezeigt. Bitte stelle sicher, dass du es gespeichert hast.
oauth2_application_edit=Bearbeiten
oauth2_application_create_description=OAuth2-Anwendungen geben deiner Drittanwendung Zugriff auf Benutzeraccounts dieser Forgejo-Instanz.
oauth2_application_remove_description=Das Entfernen einer OAuth2-Anwendung hat zur Folge, dass diese nicht mehr auf autorisierte Benutzeraccounts auf dieser Instanz zugreifen kann. Möchtest Du fortfahren?
@ -996,11 +996,11 @@ twofa_disable_desc=Wenn du die Zwei-Faktor-Authentifizierung deaktivierst, wird
regenerate_scratch_token_desc=Wenn du deinen Wiederherstellungsschlüssel verlegst oder es bereits benutzt hast, kannst du es hier zurücksetzen.
twofa_disabled=Zwei-Faktor-Authentifizierung wurde deaktiviert.
scan_this_image=Scanne diese Grafik mit deiner Authentifizierungs-App:
or_enter_secret=Oder gib das Secret ein: %s
or_enter_secret=Oder gib das Geheimnis ein: %s
then_enter_passcode=Und gib dann die angezeigte PIN der Anwendung ein:
passcode_invalid=Die PIN ist falsch. Probiere es erneut.
twofa_enrolled=Die Zwei-Faktor-Authentifizierung wurde für dein Konto aktiviert. Bewahre deinen einmalig verwendbaren Wiederherstellungsschlüssel (%s) an einem sicheren Ort auf, da er nicht wieder angezeigt werden wird.
twofa_failed_get_secret=Fehler beim Abrufen des Secrets.
twofa_failed_get_secret=Fehler beim Abrufen des Geheimnisses.
webauthn_desc=Sicherheitsschlüssel sind Geräte, die kryptografische Schlüssel beeinhalten. Diese können für die Zwei-Faktor-Authentifizierung verwendet werden. Der Sicherheitsschlüssel muss den Standard „<a rel="noreferrer" target="_blank" href="%s">WebAuthn</a>“ unterstützen.
webauthn_register_key=Sicherheitsschlüssel hinzufügen
@ -1185,9 +1185,9 @@ blame.ignore_revs=Revisionen in <a href="%s">.git-blame-ignore-revs</a> werden i
blame.ignore_revs.failed=Fehler beim Ignorieren der Revisionen in <a href="%s">.git-blame-ignore-revs</a>.
author_search_tooltip=Zeigt maximal 30 Benutzer
tree_path_not_found_commit=Pfad %[1]s existiert nicht in Commit%[2]s
tree_path_not_found_branch=Pfad %[1]s existiert nicht in Branch %[2]s
tree_path_not_found_tag=Pfad %[1]s existiert nicht in Tag %[2]s
tree_path_not_found_commit=Pfad %[1]s existiert nicht im Commit %[2]s
tree_path_not_found_branch=Pfad %[1]s existiert nicht im Branch %[2]s
tree_path_not_found_tag=Pfad %[1]s existiert nicht im Tag %[2]s
transfer.accept=Übertragung akzeptieren
transfer.accept_desc=Übertragung nach „%s“
@ -1248,7 +1248,7 @@ migrate.clone_local_path=oder ein lokaler Serverpfad
migrate.permission_denied=Du hast keine Berechtigung zum Importieren lokaler Repositorys.
migrate.permission_denied_blocked=Du kannst von keinen nicht erlaubten Hosts importieren. Bitte fragen deinen Administrator, die Einstellungen ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS zu überprüfen.
migrate.invalid_local_path=Der lokale Pfad ist ungültig. Er existiert nicht oder ist kein Verzeichnis.
migrate.invalid_lfs_endpoint=Ungültiger LFS Endpunkt.
migrate.invalid_lfs_endpoint=Der LFS-Endpunkt ist nicht gültig.
migrate.failed=Fehler bei der Migration: %v
migrate.migrate_items_options=Zugangs-Token wird benötigt, um zusätzliche Elemente zu migrieren
migrated_from=Migriert von <a href="%[1]s">%[2]s</a>
@ -1324,7 +1324,7 @@ commit=Commit
release=Release
releases=Releases
tag=Tag
released_this=hat released
released_this=hat releast
tagged_this=hat getaggt
file.title=%s an %s
file_raw=Originalformat
@ -1448,7 +1448,7 @@ commits.older=Älter
commits.newer=Neuer
commits.signed_by=Signiert von
commits.signed_by_untrusted_user=Signiert von nicht vertrauenswürdigen Benutzern
commits.signed_by_untrusted_user_unmatched=Signiert von nicht vertrauenswürdigen Benutzern, der nicht mit dem Committer übereinstimmt
commits.signed_by_untrusted_user_unmatched=Von einem nicht vertrauenswürdigen Benutzer, der nicht auf den Committer passt, signiert
commits.gpg_key_id=GPG-Schlüssel-ID
commits.ssh_key_fingerprint=SSH-Schlüssel-Fingerabdruck
commits.view_path=An diesem Punkt im Verlauf anzeigen
@ -1459,7 +1459,7 @@ commit.revert-header=Setze zurück: %s
commit.revert-content=Branch auswählen, der zurückgesetzt werden soll:
commit.cherry-pick=Cherry-Pick
commit.cherry-pick-header=Cherry-Picke: %s
commit.cherry-pick-content=Branch auswählen, auf dem Cherry-Picked werden soll:
commit.cherry-pick-content=Branch auswählen, zu dem das Ergebnis des Cherry-Picks angewendet werden soll:
commitstatus.error=Fehler
commitstatus.failure=Fehler
@ -1515,7 +1515,7 @@ issues.filter_assignees=Verantwortliche filtern
issues.filter_milestones=Meilenstein filtern
issues.filter_projects=Projekt filtern
issues.filter_labels=Label filtern
issues.filter_reviewers=Reviewer filtern
issues.filter_reviewers=Sichter filtern
issues.new=Neues Issue
issues.new.title_empty=Der Titel kann nicht leer sein
issues.new.labels=Labels
@ -1535,7 +1535,7 @@ issues.new.closed_milestone=Geschlossene Meilensteine
issues.new.assignees=Zuständige
issues.new.clear_assignees=Zuständige entfernen
issues.new.no_assignees=Niemand zuständig
issues.new.no_reviewers=Keine Reviewer
issues.new.no_reviewers=Keine Sichter
issues.choose.get_started=Los geht's
issues.choose.open_external_link=Öffnen
issues.choose.blank=Standard
@ -1598,8 +1598,8 @@ issues.filter_type.all_issues=Alle Issues
issues.filter_type.assigned_to_you=Dir zugewiesen
issues.filter_type.created_by_you=Von dir erstellt
issues.filter_type.mentioning_you=Hat dich erwähnt
issues.filter_type.review_requested=Review angefordert
issues.filter_type.reviewed_by_you=Von dir gereviewt
issues.filter_type.review_requested=Sichtung angefordert
issues.filter_type.reviewed_by_you=Von dir gesichtet
issues.filter_sort=Sortieren
issues.filter_sort.latest=Neueste
issues.filter_sort.oldest=Älteste
@ -1621,8 +1621,8 @@ issues.action_milestone=Meilenstein
issues.action_milestone_no_select=Kein Meilenstein
issues.action_assignee=Zuständig
issues.action_assignee_no_select=Niemand zuständig
issues.action_check=Auswählen/Auswahl aufheben
issues.action_check_all=Alles auswählen/Auswahl aufheben
issues.action_check=Auswählen / Auswahl aufheben
issues.action_check_all=Alles auswählen / Auswahl aufheben
issues.opened_by=%[1]s von <a href="%[2]s">%[3]s</a> geöffnet
pulls.merged_by=von <a href="%[2]s">%[3]s</a> wurde %[1]s zusammengeführt
pulls.merged_by_fake=von %[2]s %[1]s zusammengeführt
@ -1673,12 +1673,12 @@ issues.role.first_time_contributor=Erstmaliger Mitwirkender
issues.role.first_time_contributor_helper=Dies ist der erste Beitrag dieses Benutzers zum Repository.
issues.role.contributor=Mitwirkender
issues.role.contributor_helper=Dieser Benutzer hat schon zuvor zu dem Repository beigetragen.
issues.re_request_review=Review erneut anfordern
issues.is_stale=Seit diesem Review gab es Änderungen an diesem PR
issues.remove_request_review=Review-Anfrage entfernen
issues.remove_request_review_block=Review-Anfrage kann nicht entfernt werden
issues.dismiss_review=Review verwerfen
issues.dismiss_review_warning=Bist du dir sicher, dass du dieses Review verwerfen willst?
issues.re_request_review=Sichtung erneut anfordern
issues.is_stale=Seit dieser Sichtung gab es Änderungen an diesem PR
issues.remove_request_review=Sichtungsanfrage entfernen
issues.remove_request_review_block=Sichtungsanfrage kann nicht entfernt werden
issues.dismiss_review=Sichtung verwerfen
issues.dismiss_review_warning=Bist du dir sicher, dass du diese Sichtung verwerfen willst?
issues.sign_in_require_desc=<a href="%s">Anmelden</a>, um an der Diskussion teilzunehmen.
issues.edit=Bearbeiten
issues.cancel=Abbrechen
@ -1809,20 +1809,20 @@ issues.dependency.add_error_dep_not_same_repo=Beide Issues müssen sich im selbe
issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen.
issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen.
issues.review.approve=hat die Änderungen %s genehmigt
issues.review.comment=hat %s gereviewt
issues.review.comment=hat %s gesichtet
issues.review.dismissed=verwarf %ss Review %s
issues.review.dismissed_label=Verworfen
issues.review.left_comment=hat einen Kommentar hinterlassen
issues.review.content.empty=Du musst einen Kommentar hinterlassen, der die gewünschte(n) Änderung(en) beschreibt.
issues.review.reject=hat %s Änderungen angefragt
issues.review.wait=wurde für ein Review %s angefragt
issues.review.add_review_request=hat ein Review von %[1]s %[2]s angefragt
issues.review.remove_review_request=hat die Aufforderung zum Review an %[1]s %[2]s entfernt
issues.review.remove_review_request_self=hat das Review verweigert %s
issues.review.wait=wurde für eine Sichtung von %s angefragt
issues.review.add_review_request=hat eine Sichtung von %[1]s %[2]s angefragt
issues.review.remove_review_request=hat die Sichtungsanfrage an %[1]s %[2]s entfernt
issues.review.remove_review_request_self=hat die Sichtung %s verweigert
issues.review.pending=Ausstehend
issues.review.pending.tooltip=Dieser Kommentar ist derzeit nicht für andere Benutzer sichtbar. Um deine ausstehenden Kommentare einzureichen, wähle „%s“ -> „%s/%s/%s“ oben auf der Seite.
issues.review.review=Review
issues.review.reviewers=Reviewer
issues.review.reviewers=Sichter
issues.review.outdated=Veraltet
issues.review.outdated_description=Der Inhalt hat sich geändert, seit dieser Kommentar abgegeben wurde
issues.review.option.show_outdated_comments=Veraltete Kommentare anzeigen
@ -1833,7 +1833,7 @@ issues.review.show_resolved=Erledigte anzeigen
issues.review.hide_resolved=Erledigte ausblenden
issues.review.resolve_conversation=Diskussion als „erledigt“ markieren
issues.review.un_resolve_conversation=Diskussion als „nicht erledigt“ markieren
issues.review.resolved_by=markierte diese Unterhaltung als gelöst
issues.review.resolved_by=markierte diese Unterhaltung als „erledigt“
issues.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Zuständigen hinzugefügt werden.
issues.reference_issue.body=Beschreibung
issues.content_history.deleted=gelöscht
@ -1847,7 +1847,7 @@ issues.reference_link=Referenz: %s
compare.compare_base=Basis
compare.compare_head=vergleichen
pulls.desc=Pull-Requests und Code-Reviews aktivieren.
pulls.desc=Pull-Requests und Code-Sichtungen aktivieren.
pulls.new=Neuer Pull-Request
pulls.view=Pull-Request ansehen
pulls.compare_changes=Neuer Pull-Request
@ -1856,7 +1856,7 @@ pulls.allow_edits_from_maintainers_desc=Nutzer mit Schreibzugriff auf den Basisb
pulls.allow_edits_from_maintainers_err=Aktualisieren fehlgeschlagen
pulls.compare_changes_desc=Wähle den Zielbranch, in das zusammengeführt werden soll, und den Quellbranch, von dem gepullt werden soll, aus.
pulls.has_viewed_file=Gesehen
pulls.has_changed_since_last_review=Seit deinem letzten Review geändert
pulls.has_changed_since_last_review=Seit deiner letzten Sichtung geändert
pulls.viewed_files_label=%[1]d / %[2]d Dateien betrachtet
pulls.expand_files=Alle Dateien ausklappen
pulls.collapse_files=Alle Dateien einklappen
@ -1867,11 +1867,11 @@ pulls.switch_head_and_base=Head und Base vertauschen
pulls.filter_branch=Branch filtern
pulls.no_results=Keine Ergebnisse verfügbar.
pulls.show_all_commits=Alle Commits anzeigen
pulls.show_changes_since_your_last_review=Zeige Änderungen seit deinem letzten Review
pulls.show_changes_since_your_last_review=Zeige Änderungen seit deiner letzten Sichtung
pulls.showing_only_single_commit=Nur Änderungen aus Commit %[1]s werden angezeigt
pulls.showing_specified_commit_range=Zeige nur die Änderungen zwischen %[1]s..%[2]s
pulls.select_commit_hold_shift_for_range=Commit auswählen. Halte Shift + klicke, um eine Reihe auszuwählen
pulls.review_only_possible_for_full_diff=Ein Review ist nur möglich, wenn das vollständige Diff angezeigt wird
pulls.review_only_possible_for_full_diff=Eine Sichtung ist nur möglich, wenn der vollständige Diff angezeigt wird
pulls.filter_changes_by_commit=Nach Commit filtern
pulls.nothing_to_compare=Diese Branches sind identisch. Es muss kein Pull-Request erstellt werden.
pulls.nothing_to_compare_and_allow_empty_pr=Diese Branches sind gleich. Der Pull-Request wird leer sein.
@ -1905,8 +1905,8 @@ pulls.required_status_check_failed=Einige erforderliche Prüfungen waren nicht e
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.
pulls.blocked_by_approvals=Dieser Pull-Request hat noch nicht genügend Genehmigungen. %d von %d Genehmigungen erteilt.
pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Reviewer angefragt wurden.
pulls.blocked_by_official_review_requests=Dieser Pull-Request ist blockiert, weil ihm die Genehmigung von einem oder mehreren offiziellen Reviewern fehlt.
pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Sichter angefragt wurden.
pulls.blocked_by_official_review_requests=Dieser Pull-Request ist blockiert, weil ihm die Genehmigung von einem oder mehreren offiziellen Sichtern fehlt.
pulls.blocked_by_outdated_branch=Dieser Pull-Request ist blockiert, da er veraltet ist.
pulls.blocked_by_changed_protected_files_1=Dieser Pull-Request ist blockiert, weil er eine geschützte Datei ändert:
pulls.blocked_by_changed_protected_files_n=Dieser Pull-Request ist blockiert, weil er geschützte Dateien ändert:
@ -1919,14 +1919,14 @@ pulls.approve_count_1=%d Genehmigung
pulls.approve_count_n=%d Genehmigungen
pulls.reject_count_1=%d Änderungsanfrage
pulls.reject_count_n=%d Änderungsanfragen
pulls.waiting_count_1=%d wartendes Review
pulls.waiting_count_n=%d wartende Reviews
pulls.waiting_count_1=%d wartende Sichtung
pulls.waiting_count_n=%d wartende Sichtungen
pulls.wrong_commit_id=die Commit-ID muss eine Commit-ID auf dem Zielbranch sein
pulls.no_merge_desc=Dieser Pull-Request kann nicht zusammengeführt werden, da alle Repository-Merge-Optionen deaktiviert sind.
pulls.no_merge_helper=Aktiviere Mergeoptionen in den Repositoryeinstellungen oder führe den Pull-Request manuell zusammen.
pulls.no_merge_wip=Dieser Pull-Request kann nicht zusammengeführt werden, da er als „Work in Progress“ (in Bearbeitung) markiert ist.
pulls.no_merge_not_ready=Dieser Pull-Request kann nicht zusammengeführt werden, überprüfe den Reviewstatus und die Statusprüfungen.
pulls.no_merge_not_ready=Dieser Pull-Request kann nicht zusammengeführt werden, überprüfe den Sichtungsstatus und die Statusprüfungen.
pulls.no_merge_access=Du bist nicht berechtigt, diesen Pull-Request zusammenzuführen.
pulls.merge_pull_request=Merge-Commit erstellen
pulls.rebase_merge_pull_request=Rebasen und dann fast-forwarden
@ -2202,7 +2202,7 @@ settings.tracker_issue_style.numeric=Numerisch
settings.tracker_issue_style.alphanumeric=Alphanumerisch
settings.tracker_issue_style.regexp=Regulärer Ausdruck
settings.tracker_issue_style.regexp_pattern=Regulärer Ausdruck
settings.tracker_issue_style.regexp_pattern_desc=Die erste gecapturte Gruppe wird statt <code>{index}</code> verwendet.
settings.tracker_issue_style.regexp_pattern_desc=Die erste gefundene Gruppe wird statt <code>{index}</code> verwendet.
settings.tracker_url_format_desc=Du kannst die Platzhalter <code>{user}</code>, <code>{repo}</code>, <code>{index}</code> für den Benutzernamen, den Namen des Repositorys und die Issue-Nummer verwenden.
settings.enable_timetracker=Zeiterfassung aktivieren
settings.allow_only_contributors_to_track_time=Nur Mitarbeitern erlauben, die Zeiterfassung zu nutzen
@ -2261,14 +2261,14 @@ settings.trust_model.collaborator=Mitarbeiter
settings.trust_model.collaborator.long=Mitarbeiter: Vertraue Signaturen von Mitarbeitern
settings.trust_model.collaborator.desc=Gültige Signaturen von Mitarbeitern dieses Projekts werden als „vertrauenswürdig“ markiert (egal, ob sie mit dem Committer übereinstimmen oder nicht). Andernfalls werden gültige Signaturen als „nicht vertrauenswürdig“ markiert, falls die Signatur zum Committer passt, ansonsten werden sie als „nicht übereinstimmend“ markiert.
settings.trust_model.committer=Committer
settings.trust_model.committer.long=Committer: Vertraue Signaturen, die zu Committern passen (Dies stimmt mit GitHub überein und zwingt signierte Commits von Forgejo dazu, Forgejo als Committer zu haben)
settings.trust_model.committer.long=Committer: Vertraue Signaturen, die zu Committern passen (dies stimmt mit GitHub überein und zwingt signierte Commits von Forgejo dazu, Forgejo als Committer zu haben)
settings.trust_model.committer.desc=Gültige Signaturen werden nur dann als „vertrauenswürdig“ gekennzeichnet, wenn sie mit ihrem Committer übereinstimmen. Ansonsten werden sie als „nicht übereinstimmend“ markiert. Das führt dazu, dass Forgejo auf signierten Commits, bei denen der echte Committer als „Co-authored-by:“ oder „Co-committed-by:“ in der Beschreibung eingetragen wurde, als Committer gilt. Der Forgejo-Standard-Key muss zu einem Benutzer in der Datenbank passen.
settings.trust_model.collaboratorcommitter=Mitarbeiter+Committer
settings.trust_model.collaboratorcommitter.long=Mitarbeiter+Committer: Signaturen der Mitarbeiter vertrauen die mit dem Committer übereinstimmen
settings.trust_model.collaboratorcommitter.desc=Gültige Signaturen von Mitarbeitern dieses Projekts werden als „vertrauenswürdig“ markiert, wenn sie mit dem Committer übereinstimmen. Andernfalls werden gültige Signaturen als „nicht vertrauenswürdig“ markiert, wenn die Signatur mit dem Committer übereinstimmt. Ansonsten werden sie als „nicht übereinstimmend“ markiert. Dies zwingt Forgejo, als Committer bei signierten Commits mit dem echten Committer als „Co-Authored-By:“ und „Co-Committed-By:“ im Commit zu markieren. Der Standard-Forgejo-Schlüssel muss mit einem Benutzer in der Datenbank übereinstimmen.
settings.wiki_delete=Wiki-Daten löschen
settings.wiki_delete_desc=Das Löschen von Wiki-Daten kann nicht rückgängig gemacht werden. Bitte sei vorsichtig.
settings.wiki_delete_notices_1= Dies löscht und deaktiviert das Wiki für %s.
settings.wiki_delete_notices_1= Dies wird das Repository-Wiki für %s dauerhaft löschen und deaktivieren.
settings.confirm_wiki_delete=Wiki-Daten löschen
settings.wiki_deletion_success=Repository-Wiki-Daten wurden gelöscht.
settings.delete=Dieses Repository löschen
@ -2327,7 +2327,7 @@ settings.add_webhook_desc=Forgejo sendet eine <code>POST</code>-Anfrage mit fest
settings.payload_url=Ziel-URL
settings.http_method=HTTP-Methode
settings.content_type=POST-Content-Type
settings.secret=Secret
settings.secret=Geheimnis
settings.slack_username=Benutzername
settings.slack_icon_url=Icon-URL
settings.slack_color=Farbe
@ -2374,12 +2374,12 @@ settings.event_pull_request_milestone=Meilensteine
settings.event_pull_request_milestone_desc=Meilenstein hinzugefügt, entfernt oder bearbeitet.
settings.event_pull_request_comment=Kommentare
settings.event_pull_request_comment_desc=Pull-Request-Kommentar angelegt, geändert oder gelöscht.
settings.event_pull_request_review=Reviews
settings.event_pull_request_review_desc=Pull-Request genehmigt, abgelehnt oder Review-Kommentare hinterlassen.
settings.event_pull_request_review=Sichtungen
settings.event_pull_request_review_desc=Pull-Request genehmigt, abgelehnt oder Sichtungskommentare hinterlassen.
settings.event_pull_request_sync=Synchronisiert
settings.event_pull_request_sync_desc=Branch automatisch mit Zielbranch aktualisiert.
settings.event_pull_request_review_request=Review-Anfragen
settings.event_pull_request_review_request_desc=Überprüfung des Pull-Requests angefragt oder die Anfrage entfernt.
settings.event_pull_request_review_request=Sichtungsanfragen
settings.event_pull_request_review_request_desc=Pull-Request-Sichtung angefragt oder Sichtungsanfrage entfernt.
settings.event_pull_request_approvals=Genehmigungen zum Pull-Request
settings.event_pull_request_merge=Pull-Request-Merge
settings.event_package=Paket
@ -2467,11 +2467,11 @@ settings.protect_status_check_matched=Übereinstimmung
settings.protect_invalid_status_check_pattern=Ungültiges Statusprüfungspattern: „%s“.
settings.protect_no_valid_status_check_patterns=Keine gültigen Statuscheck-Muster.
settings.protect_required_approvals=Erforderliche Genehmigungen
settings.protect_required_approvals_desc=Erlaube das Zusammenführen des Pull-Requests nur mit genügend positiven Reviews.
settings.protect_required_approvals_desc=Erlaube das Zusammenführen des Pull-Requests nur mit genügend positiven Sichtungen.
settings.protect_approvals_whitelist_enabled=Genehmigungen auf Benutzer oder Teams auf der Positivliste beschränken
settings.protect_approvals_whitelist_enabled_desc=Nur Reviews von Benutzern oder Teams auf der Positivliste zählen zu den erforderlichen Genehmigungen. Existiert keine Positivliste, so zählen Reviews von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen.
settings.protect_approvals_whitelist_users=Nutzer, die reviewen dürfen
settings.protect_approvals_whitelist_teams=Teams, die reviewen dürfen
settings.protect_approvals_whitelist_enabled_desc=Nur Sichtungen von Benutzern oder Teams auf der Positivliste zählen zu den erforderlichen Genehmigungen. Existiert keine Positivliste, so zählen Sichtungen von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen.
settings.protect_approvals_whitelist_users=Nutzer, die sichten dürfen
settings.protect_approvals_whitelist_teams=Teams, die sichten dürfen
settings.dismiss_stale_approvals=Entferne alte Genehmigungen
settings.dismiss_stale_approvals_desc=Wenn neue Commits gepusht werden, die den Inhalt des Pull-Requests ändern, werden alte Genehmigungen entfernt.
settings.require_signed_commits=Signierte Commits erforderlich
@ -2489,10 +2489,10 @@ settings.remove_protected_branch_success=Branchschutzregel „%s“ wurde entfer
settings.remove_protected_branch_failed=Entfernen der Branchschutzregel „%s“ fehlgeschlagen.
settings.protected_branch_deletion=Branch-Schutz löschen
settings.protected_branch_deletion_desc=Wenn du den Branch-Schutz deaktivierst, können alle Nutzer mit Schreibrechten auf den Branch pushen. Fortfahren?
settings.block_rejected_reviews=Zusammenführung bei abgelehnten Reviews blockieren
settings.block_rejected_reviews_desc=Merge ist nicht möglich, wenn Änderungen durch offizielle Reviewer angefragt werden, auch wenn genügend Genehmigungen existieren.
settings.block_on_official_review_requests=Merge bei offiziellen Review-Anfragen blockieren
settings.block_on_official_review_requests_desc=Merge ist nicht möglich, wenn offizielle Review-Anfrangen vorliegen, selbst wenn genügend Genehmigungen existieren.
settings.block_rejected_reviews=Zusammenführung bei abgelehnten Sichtungen blockieren
settings.block_rejected_reviews_desc=Merge ist nicht möglich, wenn Änderungen durch offizielle Sichter angefragt werden, auch wenn genügend Genehmigungen existieren.
settings.block_on_official_review_requests=Merge bei offiziellen Sichtungsanfragen blockieren
settings.block_on_official_review_requests_desc=Merge ist nicht möglich, wenn offizielle Sichtungsanfrangen vorliegen, selbst wenn genügend Genehmigungen existieren.
settings.block_outdated_branch=Merge blockieren, wenn der Pull-Request veraltet ist
settings.block_outdated_branch_desc=Merge ist nicht möglich, wenn der Head-Branch hinter dem Basis-Branch ist.
settings.default_branch_desc=Wähle einen Standardbranch für Pull-Requests und Code-Commits:
@ -2539,7 +2539,7 @@ settings.lfs_filelist=LFS-Dateien, die in diesem Repository gespeichert sind
settings.lfs_no_lfs_files=In diesem Repository sind keine LFS-Dateien gespeichert
settings.lfs_findcommits=Commits finden
settings.lfs_lfs_file_no_commits=Keine Commits für diese LFS-Datei gefunden
settings.lfs_noattribute=Dieser Pfad hat nicht das sperrbare Attribut im Standard-Branch
settings.lfs_noattribute=Dieser Pfad hat nicht das „lockable“-Attribut im Standard-Branch
settings.lfs_delete=LFS-Datei mit OID %s löschen
settings.lfs_delete_warning=Das Löschen einer LFS-Datei kann dazu führen, dass „Objekt existiert nicht“-Fehler beim Checkout auftreten. Bist du sicher?
settings.lfs_findpointerfiles=Pointer-Dateien finden
@ -2580,8 +2580,8 @@ diff.show_unified_view=Gesamtansicht
diff.whitespace_button=Leerzeichen
diff.whitespace_show_everything=Alle Änderungen anzeigen
diff.whitespace_ignore_all_whitespace=Ignoriere Leerzeichen beim Zeilen vergleichen
diff.whitespace_ignore_amount_changes=Ignoriere whitespace-Änderungen
diff.whitespace_ignore_at_eol=Ignoriere EOL-whitespace-Änderungen
diff.whitespace_ignore_amount_changes=Änderungen in der Anzahl der Leerzeichen und ähnlichen Zeichen ignorieren
diff.whitespace_ignore_at_eol=Änderungen an den Leerzeichen und ähnlichen Zeichen am Zeilenende ignorieren
diff.stats_desc=<strong> %d geänderte Dateien</strong> mit <strong>%d neuen</strong> und <strong>%d gelöschten</strong> Zeilen
diff.stats_desc_file=%d Änderungen: %d Ergänzungen und %d Löschungen
diff.bin=BIN
@ -2604,11 +2604,11 @@ diff.comment.placeholder=Kommentieren
diff.comment.markdown_info=Styling mit Markdown wird unterstützt.
diff.comment.add_single_comment=Einzelnen Kommentar hinzufügen
diff.comment.add_review_comment=Kommentar hinzufügen
diff.comment.start_review=Review starten
diff.comment.start_review=Sichtung starten
diff.comment.reply=Antworten
diff.review=Review abschließen
diff.review.header=Review einreichen
diff.review.placeholder=Kommentar zum Review
diff.review=Sichtung abschließen
diff.review.header=Sichtung einreichen
diff.review.placeholder=Kommentar zur Sichtung
diff.review.comment=Kommentieren
diff.review.approve=Genehmigen
diff.review.self_reject=Pull-Request-Autoren können keine Änderungen an ihren eigenen Pull-Request anfordern
@ -2656,7 +2656,7 @@ release.edit_release=Release aktualisieren
release.delete_release=Release löschen
release.delete_tag=Tag löschen
release.deletion=Release löschen
release.deletion_desc=Beim Entfernen wird ein Release nur von Forgejo gelöscht. Es betrifft weder den Git-Tag, noch den Inhalt des Repos oder seinen Änderungsverlauf. Fortfahren?
release.deletion_desc=Beim Entfernen wird ein Release nur von Forgejo gelöscht. Es betrifft weder den Git-Tag noch den Inhalt des Repos oder seinen Änderungsverlauf. Fortfahren?
release.deletion_success=Das Release wurde gelöscht.
release.deletion_tag_desc=Löscht dieses Tag aus dem Projektarchiv. Repository-Inhalt und Verlauf bleiben unverändert. Fortfahren?
release.deletion_tag_success=Der Tag wurde gelöscht.
@ -2666,7 +2666,7 @@ release.tag_name_protected=Der Tag-Name ist geschützt.
release.tag_already_exist=Dieser Tag-Name existiert bereits.
release.downloads=Downloads
release.download_count=Downloads: %s
release.add_tag_msg=Titel und Beschreibung des Releases als Tag Nachricht verwenden.
release.add_tag_msg=Titel und Beschreibung des Releases als Tag-Nachricht verwenden.
release.add_tag=Tag erstellen
release.releases_for=Releases für %s
release.tags_for=Tags für %s
@ -2766,7 +2766,7 @@ wiki.cancel = Abbrechen
settings.wiki_globally_editable = Allen erlauben, das Wiki zu bearbeiten
settings.protect_branch_name_pattern_desc = Geschützte Branch-Namens-Patterns. Siehe <a href="%s">die Dokumentation</a> für Pattern-Syntax. Beispiele: main, release/**
settings.ignore_stale_approvals = Abgestandene Genehmigungen ignorieren
settings.ignore_stale_approvals_desc = Genehmigungen, welche für ältere Commits gemacht wurden (abgestandene Reviews), nicht in die Gesamtzahl der Genehmigung des PRs mitzählen. Irrelevant, falls abgestandene Reviews bereits verworfen werden.
settings.ignore_stale_approvals_desc = Genehmigungen, welche für ältere Commits gemacht wurden (abgestandene Sichtungen), nicht in die Gesamtzahl der Genehmigung des PRs mitzählen. Irrelevant, falls abgestandene Sichtungen bereits verworfen werden.
pulls.commit_ref_at = `referenzierte diesen Pull-Request aus einem Commit %s`
pulls.fast_forward_only_merge_pull_request = Nur Fast-forward
pulls.cmd_instruction_checkout_desc = Checke einen neuen Branch aus deinem Projekt-Repository aus und teste die Änderungen.
@ -2782,7 +2782,7 @@ activity.navbar.contributors = Mitwirkende
contributors.contribution_type.deletions = Löschungen
contributors.contribution_type.additions = Einfügungen
contributors.contribution_type.filter_label = Art des Beitrags:
vendored = Vendored
vendored = Gevendort
activity.navbar.pulse = Puls
pulls.made_using_agit = AGit
settings.confirmation_string = Bestätigungsstring
@ -2795,7 +2795,7 @@ pulls.merged_title_desc_one = hat %[1]d Commit von <code>%[2]s</code> nach <code
pulls.title_desc_one = möchte %[1]d Commit von <code>%[2]s</code> nach <code id="%[4]s">%[3]s</code> zusammenführen
open_with_editor = Öffnen mit %s
commits.search_branch = Dieser Branch
pulls.ready_for_review = Bereit zum Review?
pulls.ready_for_review = Bereit zur Sichtung?
settings.rename_branch_failed_protected = Branch %s kann nicht umbenannt werden, weil er ein geschützter Branch ist.
editor.commit_id_not_matching = Die Datei wurde geändert, während du sie bearbeitet hast. Committe in einen neuen Branch, dann führe einen Merge durch.
editor.push_out_of_date = Der Push scheint veraltet zu sein.
@ -2824,7 +2824,7 @@ settings.sourcehut_builds.secrets_helper = Dem Job zugriff auf die Build-Geheimn
settings.web_hook_name_sourcehut_builds = SourceHut-Builds
settings.graphql_url = GraphQL-URL
settings.matrix.room_id_helper = Die Raum-ID kann über den Element-Webclient ermittelt werden: Raumeinstellungen > erweitert > interne Raum-ID. Beispielsweise %s.
settings.sourcehut_builds.access_token_helper = Zugangstoken, der die JOBS:RW-Freigabe hat. Generiere auf meta.sr.ht einen <a target="_blank" rel="noopener noreferrer" href="%s">builds.sr.ht-Token</a> oder einen <a target="_blank" rel="noopener noreferrer" href="%s">builds.sr.ht-Token mit Zugriff auf die Secrets</a>.
settings.sourcehut_builds.access_token_helper = Zugangstoken, der die JOBS:RW-Freigabe hat. Generiere auf meta.sr.ht einen <a target="_blank" rel="noopener noreferrer" href="%s">builds.sr.ht-Token</a> oder einen <a target="_blank" rel="noopener noreferrer" href="%s">builds.sr.ht-Token mit Zugriff auf die Geheimnisse</a>.
settings.matrix.access_token_helper = Es wird empfohlen, hierfür ein dediziertes Matrix-Konto anzulegen. Der Zugangstoken kann in einem Inkognito-Tab über den Element-Webclient geholt werden: Benutzermenü (oben links) > alle Einstellungen > Hilfe & Info > erweitert > Zugriffstoken (direkt unter der Heim-Server-URL). Schließe dann den Inkognito-Tab (Abmelden würde den Token ungültig machen).
release.hide_archive_links = Automatisch generierte Archive verstecken
release.hide_archive_links_helper = Verstecke automatisch generierte Quellcodearchive für diesen Release. Zum Beispiel, wenn du deine eigenen hochlädst.
@ -2873,9 +2873,9 @@ mirror_use_ssh.not_available = SSH-Authentifizierung ist nicht verfügbar.
issues.new.assign_to_me = Mir selbst zuweisen
issues.all_title = Alle
settings.discord_icon_url.exceeds_max_length = Die Icon-URL darf eine Länge von 2048 Zeichen nicht überschreiten
issues.review.add_review_requests = hat Reviews von %[1]s %[2]s angefragt
issues.review.remove_review_requests = hat Aufforderungen zum Review an %[1]s %[2]s entfernt
issues.review.add_remove_review_requests = hat Reviews von %[1]s angefragt und hat die Aufforderungen zum Review an %[2]s %[3]s entfernt
issues.review.add_review_requests = hat Sichtungen von %[1]s %[2]s angefragt
issues.review.remove_review_requests = hat die Sichtungsanfragen an %[1]s %[2]s entfernt
issues.review.add_remove_review_requests = hat Sichtungen von %[1]s angefragt und die Sichtungsanfragen an %[2]s %[3]s entfernt
pulls.delete_after_merge.head_branch.is_default = Der Head-Branch, den du löschen willst, ist der Standardbranch und kann nicht gelöscht werden.
pulls.delete_after_merge.head_branch.is_protected = Der Head-Branch, den du löschen willst, ist ein geschützter Branch und kann nicht gelöscht werden.
pulls.delete_after_merge.head_branch.insufficient_branch = Du hast keine Erlaubnis, den Head-Branch zu löschen.
@ -2883,9 +2883,9 @@ issues.filter_sort.relevance = Relevanz
diff.git-notes.add = Anmerkung hinzufügen
diff.git-notes.remove-header = Anmerkung entfernen
diff.git-notes.remove-body = Diese Anmerkung wird entfernt.
issues.num_reviews_one = %d Review
issues.num_reviews_one = %d Sichtung
issues.summary_card_alt = Zusammenfassung eines Issues mit dem Titel „%s“ im Repository %s
issues.num_reviews_few = %d Reviews
issues.num_reviews_few = %d Sichtungen
editor.add_tmpl.filename = Dateiname
settings.default_update_style_desc = Standard-Aktualisierungsart um Pull-Requests zu aktualisieren, die hinter dem Base-Branch sind.
new_advanced = Erweiterte Einstellungen
@ -2902,7 +2902,7 @@ issues.reaction.alt_add = Füge %[1]s Reaktion zum Kommentar hinzu.
issues.reaction.alt_remove = Entferne %[1]s Reaktion von diesem Kommentar.
summary_card_alt = Zusammenfassungskarte des Repositorys %s
release.summary_card_alt = Übersichtskarte eines Releases mit dem Titel „%s“ im Repository %s
archive.pull.noreview = Dieses Repository ist archiviert. Pull-Requests können nicht gereviewt werden.
archive.pull.noreview = Dieses Repository ist archiviert. Pull-Requests können nicht gesichtet werden.
editor.commit_email = Commit-E-Mail
commits.view_single_diff = Änderungen an dieser Datei, die in diesem Commit eingeführt wurden, betrachten
pulls.editable = Bearbeitbar
@ -2922,7 +2922,7 @@ settings.event_action_success_desc = Action-Run war erfolgreich.
settings.event_action_failure = Fehlschlag
settings.event_action_success = Erfolg
settings.event_header_action = Action-Run-Ereignisse
settings.event_action_recover_desc = Action-Run war erfolgreich, nachdem der letzte Action-Run im selben Arbeitsablauf fehlgeschlagen ist.
settings.event_action_recover_desc = Action-Run war erfolgreich, nachdem der letzte Action-Run im selben Workflow fehlgeschlagen ist.
settings.event_action_recover = Wiederherstellen
issues.filter_type.all_pull_requests = Alle Pull-Requests
@ -3116,7 +3116,7 @@ dashboard.repo_health_check=Healthchecks für alle Repositorys ausführen
dashboard.check_repo_stats=Überprüfe alle Repository-Statistiken
dashboard.archive_cleanup=Alte Repository-Archive löschen
dashboard.deleted_branches_cleanup=Gelöschte Branches bereinigen
dashboard.update_migration_poster_id=Migration Poster-IDs updaten
dashboard.update_migration_poster_id=Migrations-Poster-IDs aktualisieren
dashboard.git_gc_repos=Garbage-Collection für alle Repositorys ausführen
dashboard.resync_all_sshkeys=Die Datei „.ssh/authorized_keys“ mit Forgejo-SSH-Schlüsseln aktualisieren.
dashboard.resync_all_sshprincipals=Aktualisiere die Datei „.ssh/authorized_principals“ mit Forgejo-SSH-Principals.
@ -3159,7 +3159,7 @@ dashboard.delete_old_actions=Alle alten Aktivitäten aus der Datenbank löschen
dashboard.delete_old_actions.started=Löschen aller alten Aktivitäten aus der Datenbank gestartet.
dashboard.update_checker=Update-Checker
dashboard.delete_old_system_notices=Alle alten Systemmeldungen aus der Datenbank löschen
dashboard.gc_lfs=Garbage-Collection für LFS Meta-Objekte ausführen
dashboard.gc_lfs=Garbage-Collection für LFS-Meta-Objekte ausführen
dashboard.stop_zombie_tasks=Zombie-Actions-Aufgaben stoppen
dashboard.stop_endless_tasks=Endlose Actions-Aufgaben stoppen
dashboard.cancel_abandoned_jobs=Aufgegebene Actions-Jobs abbrechen
@ -3335,7 +3335,7 @@ auths.pam_email_domain=PAM-E-Mail-Domain (optional)
auths.oauth2_provider=OAuth2-Anbieter
auths.oauth2_icon_url=Symbol-URL
auths.oauth2_clientID=Client-ID (Schlüssel)
auths.oauth2_clientSecret=Client-Secret
auths.oauth2_clientSecret=Client-Geheimnis
auths.openIdConnectAutoDiscoveryURL=OpenID-Connect-Auto-Discovery-URL
auths.oauth2_use_custom_url=Benutzerdefinierte URLs anstelle von Standard-URLs verwenden
auths.oauth2_tokenURL=Token-URL
@ -3343,14 +3343,14 @@ auths.oauth2_authURL=Authorisierungs-URL
auths.oauth2_profileURL=Profil-URL
auths.oauth2_emailURL=E-Mail-URL
auths.skip_local_two_fa=Lokale 2FA überspringen
auths.skip_local_two_fa_helper=Leer lassen bedeutet, dass lokale User die 2FA immer noch bestehen müssen, um sich anzumelden
auths.skip_local_two_fa_helper=Das Leerlassen bedeutet, dass lokale User die 2FA immer noch bestehen müssen, um sich anzumelden
auths.oauth2_tenant=Inhaber
auths.oauth2_scopes=Zusätzliche Bereiche
auths.oauth2_required_claim_name=Benötigter Claim-Name
auths.oauth2_required_claim_name_helper=Setze diesen Namen, damit Nutzer aus dieser Quelle sich nur anmelden dürfen, wenn sie einen Claim mit diesem Namen besitzen
auths.oauth2_required_claim_value=Benötigter Claim-Wert
auths.oauth2_required_claim_value_helper=Setze diesen Wert, damit Nutzer aus dieser Quelle sich nur anmelden dürfen, wenn sie einen Claim mit diesem Namen und Wert besitzen
auths.oauth2_group_claim_name=Claim-Name, der Gruppennamen für diese Quelle angibt. (Optional)
auths.oauth2_group_claim_name=Claim-Name, der Gruppennamen für diese Quelle angibt (optional).
auths.oauth2_admin_group=Gruppen-Claim-Wert für Administratoren (optional erfordert Claim-Namen oben).
auths.oauth2_restricted_group=Gruppen-Claim-Wert für eingeschränkte User. (Optional erfordert Claim-Namen oben)
auths.oauth2_map_group_to_team=Gruppen aus OAuth-Claims den Organisationsteams zuordnen (optional oben muss der Name des Claims angegeben werden).
@ -3395,7 +3395,7 @@ auths.still_in_used=Diese Authentifizierungsquelle wird noch verwendet. Bearbeit
auths.deletion_success=Die Authentifizierungsquelle „%s“ wurde gelöscht.
auths.login_source_exist=Die Authentifizierungsquelle „%s“ existiert bereits.
auths.login_source_of_type_exist=Eine Authentifizierungart dieses Typs existiert bereits.
auths.unable_to_initialize_openid=OpenID Connect Provider konnte nicht initialisiert werden: %s
auths.unable_to_initialize_openid=Provider für OpenID Connect konnte nicht initialisiert werden: %s
auths.invalid_openIdConnectAutoDiscoveryURL=Ungültige Auto-Discovery-URL (dies muss eine gültige URL sein, die mit http:// oder https:// beginnt)
config.server_config=Serverkonfiguration
@ -3526,7 +3526,7 @@ config.git_pull_timeout=Zeitlimit für Pull
config.git_gc_timeout=Zeitlimit für GC
config.log_config=Protokollierungs-Konfiguration
config.logger_name_fmt=Logger: %s
config.logger_name_fmt=Protokollierer: %s
config.disabled_logger=Deaktiviert
config.access_log_mode=Zugriffsprotokoll-Modus
config.access_log_template=Zugriffsprotokoll-Vorlage
@ -3650,7 +3650,7 @@ mirror_sync_delete=hat die Referenz des Spiegels <code>%[2]s</code> in <a href="
approve_pull_request=`hat <a href="%[1]s">%[3]s#%[2]s</a> genehmigt`
reject_pull_request=`schlug Änderungen für <a href="%[1]s">%[3]s#%[2]s</a> vor`
publish_release=`veröffentlichte Release <a href="%[2]s">%[4]s</a> in <a href="%[1]s">%[3]s</a>`
review_dismissed=`verwarf das Review von <b>%[4]s</b> in <a href="%[1]s">%[3]s#%[2]s</a>`
review_dismissed=`verwarf die Sichtung von <b>%[4]s</b> in <a href="%[1]s">%[3]s#%[2]s</a>`
review_dismissed_reason=Grund:
create_branch=legte den Branch <a href="%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a> an
starred_repo=favorisierte <a href="%[1]s">%[2]s</a>
@ -3765,9 +3765,9 @@ conda.registry=Richte diese Registry als Conda-Repository in deiner <code>.conda
conda.install=Um das Paket mit Conda zu installieren, führe den folgenden Befehl aus:
container.details.type=Abbildtyp
container.details.platform=Plattform
container.pull=Downloade das Container-Image aus der Kommandozeile:
container.pull=Lade das Container-Image von der Kommandozeile aus herunter:
container.digest=Prüfsumme
container.multi_arch=Betriebsystem / Architektur
container.multi_arch=Betriebsystem/Architektur
container.layers=Abbildebenen
container.labels=Labels
container.labels.key=Schlüssel
@ -3781,7 +3781,7 @@ debian.repository=Repository-Informationen
debian.repository.distributions=Distributionen
debian.repository.components=Komponenten
debian.repository.architectures=Architekturen
generic.download=Downloade das Paket aus der Kommandozeile:
generic.download=Lade das Paket mit der Kommandozeile herunter:
go.install=Installiere das Paket über die Kommandozeile:
helm.registry=Diese Paketverwaltung über die Kommandozeile einrichten:
helm.install=Nutze folgenden Befehl, um das Paket zu installieren:
@ -3813,10 +3813,10 @@ rubygems.install=Um das Paket mit gem zu installieren, führe den folgenden Befe
rubygems.install2=oder füg es zum Gemfile hinzu:
rubygems.dependencies.runtime=Laufzeitabhängigkeiten
rubygems.dependencies.development=Entwicklungsabhängigkeiten
rubygems.required.ruby=Benötigt Ruby Version
rubygems.required.rubygems=Benötigt RubyGem Version
rubygems.required.ruby=Benötigt Ruby-Version
rubygems.required.rubygems=Benötigt RubyGem-Version
swift.registry=Diese Registry über die Kommandozeile einrichten:
swift.install=Füge das Paket deiner <code>Package.swift</code> Datei hinzu:
swift.install=Füge das Paket deiner <code>Package.swift</code>-Datei hinzu:
swift.install2=und führe den folgenden Befehl aus:
vagrant.install=Um eine Vagrant-Box hinzuzufügen, führe den folgenden Befehl aus:
settings.link=Dieses Paket einem Repository zuweisen
@ -3892,19 +3892,19 @@ alt.repository.architectures = Architekturen
alt.repository.multiple_groups = Dieses Paket ist in verschiedenen Gruppen verfügbar.
[secrets]
secrets=Secrets
description=Secrets werden an bestimmte Aktionen weitergegeben und können nicht anderweitig ausgelesen werden.
none=Noch keine Secrets vorhanden.
creation=Secret hinzufügen
secrets=Geheimnisse
description=Geheimnisse werden an bestimmte Aktionen weitergegeben und können nicht anderweitig ausgelesen werden.
none=Noch keine Geheimnisse vorhanden.
creation=Geheimnis hinzufügen
creation.name_placeholder=Groß-/Kleinschreibung wird ignoriert, nur alphanumerische Zeichen oder Unterstriche, darf nicht mit GITEA_ oder GITHUB_ beginnen
creation.value_placeholder=Beliebigen Inhalt eingeben. Leerzeichen am Anfang und Ende werden weggelassen.
creation.success=Das Secret „%s“ wurde hinzugefügt.
creation.failed=Secret konnte nicht hinzugefügt werden.
deletion=Secret entfernen
deletion.description=Das Entfernen eines Secrets kann nicht rückgängig gemacht werden. Fortfahren?
deletion.success=Das Secret wurde entfernt.
deletion.failed=Secret konnte nicht entfernt werden.
management=Secrets verwalten
creation.success=Das Geheimnis „%s“ wurde hinzugefügt.
creation.failed=Geheimnis konnte nicht hinzugefügt werden.
deletion=Geheimnis entfernen
deletion.description=Das Entfernen eines Geheimnisses kann nicht rückgängig gemacht werden. Fortfahren?
deletion.success=Das Geheimnis wurde entfernt.
deletion.failed=Geheimnis konnte nicht entfernt werden.
management=Geheimnisse verwalten
[actions]
actions=Actions

View file

@ -1882,7 +1882,6 @@ issues.content_history.created = created
issues.content_history.delete_from_history = Delete from history
issues.content_history.delete_from_history_confirm = Delete from history?
issues.content_history.options = Options
issues.reference_link = Reference: %s
issues.blocked_by_user = You cannot create issues in this repository because you are blocked by the repository owner.
comment.blocked_by_user = Commenting is not possible because you are blocked by the repository owner or the author.
issues.reopen.blocked_by_user = You cannot reopen this issue because you are blocked by the repository owner or the poster of this issue.

View file

@ -29,7 +29,7 @@ password=Пароль
access_token=Токен доступу
re_type=Підтвердження пароля
captcha=CAPTCHA
twofa=Двофакторна авторизація
twofa=Двофакторна автентифікація
twofa_scratch=Двофакторний одноразовий пароль
passcode=Код доступу
@ -112,7 +112,7 @@ webauthn_error_unknown = Сталася невідома помилка. Буд
webauthn_error_unable_to_process = Сервер не зміг обробити запит.
webauthn_error_duplicated = Запит із наданим ключем безпеки відхилено. Впевніться, що цього ключа ще не зареєстровано.
webauthn_error_empty = Ключ слід якось назвати.
new_project_column = Нова колонка
new_project_column = Новий стовпчик
retry = Повторити
rerun = Перезапустити
rerun_all = Перезапустити всі завдання
@ -201,7 +201,7 @@ buttons.mention.tooltip = Згадати користувача чи коман
buttons.ref.tooltip = Послатися на задачу чи на запит на злиття
buttons.enable_monospace_font = Увімкнути моноширинний шрифт
buttons.new_table.tooltip = Додати таблицю
table_modal.label.columns = Стовпці
table_modal.label.columns = Стовпчики
table_modal.header = Додати таблицю
table_modal.placeholder.header = Заголовок
table_modal.placeholder.content = Вміст
@ -343,13 +343,13 @@ enable_update_checker = Увімкнути перевірку оновлень
require_db_desc = Forgejo вимагає MySQL, PostgreSQL, SQLite3 чи TiDB (протокол MySQL).
allow_only_external_registration = Дозволити реєстрацію тільки через зовнішні сервіси
require_sign_in_view.description = Обмежити доступ до контенту лише користувачам, що увійшли. Гості зможуть лише відвідувати сторінки автентифікації.
password_algorithm_helper = Встановити алгоритм хешування паролів. Алгоритми мають різні вимоги та силу. Алгоритм argon2 є досить безпечним, проте споживає багато памʼяті та є недоречним для малих систем.
password_algorithm_helper = Установіть алгоритм хешування паролів. Алгоритми мають різні вимоги і стійкість. Алгоритм argon2 є досить безпечним, проте споживає багато памʼяті та є недоречним для малих систем.
app_slogan = Гасло екземпляра
app_slogan_helper = Уведіть гасло вашого екземпляра тут. Залиште порожнім, аби вимкнути.
run_user_helper = Імʼя користувача операційної системи, від якого запущено Forgejo. Зауважте, що цей користувач повинен мати доступ до кореневої теки репозиторію.
smtp_from_invalid = Адреса з «Відправляти email від імені» недійсна
allow_dots_in_usernames = Дозволити використання крапки в іменах користувачів. Не впливає на облікові записи, що вже існують.
invalid_password_algorithm = Недійсний варіант алгоритму хешування паролів
invalid_password_algorithm = Недійсний алгоритм хешування паролів
enable_update_checker_helper_forgejo = Наявність нових версій Forgejo періодично перевірятиметься через перевірку запису TXT DNS на release.forgejo.org.
[home]
@ -734,7 +734,7 @@ applications=Додатки
orgs=Організації
repos=Репозиторії
delete=Видалити обліковий запис
twofa=Двофакторна авторизація (TOTP)
twofa=Двофакторна автентифікація (TOTP)
account_link=Прив'язані облікові записи
organization=Організації
@ -1709,7 +1709,7 @@ pulls.reject_count_1=%d запит на зміну
pulls.reject_count_n=%d запити на зміну
pulls.waiting_count_1=очікується %d рецензія
pulls.waiting_count_n=очікується %d рецензії(й)
pulls.wrong_commit_id=ID коміта повинен бути ID коміта в цільовій гілці
pulls.wrong_commit_id=ID коміту повинен бути ID коміту в цільовій гілці
pulls.no_merge_desc=Цей запити на злиття неможливо злити, оскільки всі параметри об'єднання репозиторія вимкнено.
pulls.no_merge_helper=Увімкніть параметри злиття в налаштуваннях репозиторія або злийте запити на злиття вручну.
@ -1727,7 +1727,7 @@ pulls.require_signed_wont_sign=Гілка вимагає підписаних к
pulls.invalid_merge_option=Цей параметр злиття не можна використовувати для цього Pull Request'а.
pulls.merge_conflict=Не вдалося об'єднати: при об'єднанні виник конфлікт. Підказка: спробуйте іншу стратегію
pulls.merge_conflict_summary=Повідомлення про помилку
pulls.rebase_conflict=Не вдалося об'єднати: виник конфлікт під час перебазування коміта: %[1]s. Підказка: спробуйте іншу стратегію
pulls.rebase_conflict=Не вдалося об'єднати: виник конфлікт під час перебазування коміту: %[1]s. Підказка: спробуйте іншу стратегію
pulls.rebase_conflict_summary=Повідомлення про помилку
pulls.unrelated_histories=Не вдалося об'єднати: head та base злиття не мають спільної історії. Підказка: спробуйте іншу стратегію
pulls.merge_out_of_date=Не вдалося об'єднати: base було оновлено, поки відбувалося об'єднання. Підказка: спробуйте знову.
@ -1896,7 +1896,7 @@ settings.mirror_settings.direction=Напрямок
settings.mirror_settings.direction.pull=Pull
settings.mirror_settings.direction.push=Push
settings.mirror_settings.last_update=Останнє оновлення
settings.mirror_settings.push_mirror.none=Не налаштовано дзеркало push
settings.mirror_settings.push_mirror.none=Push-дзеркала не налаштовано
settings.mirror_settings.push_mirror.remote_url=URL віддаленого репозиторію Git
settings.mirror_settings.push_mirror.add=Додати push-дзеркало
@ -2343,7 +2343,7 @@ issues.author = Автор
issues.author_helper = Цей користувач - автор.
issues.close = Закрити задачу
issues.role.owner_helper = Цей користувач є власником цього репозиторію.
settings.mirror_settings.docs.more_information_if_disabled = Докладніше про push та pull дзеркала можна дізнатися тут:
settings.mirror_settings.docs.more_information_if_disabled = Докладніше про push- та pull-дзеркала можна дізнатися тут:
issues.comment.blocked_by_user = Ви не можете коментувати цю задачу, оскільки вас заблокував власник репозиторію або автор цієї задачі.
editor.add_file = Додати файл
from_comment = (коментар)
@ -2431,7 +2431,7 @@ pulls.is_ancestor = Цю гілку вже включено в цільову г
pulls.has_merged = Помилка: запит на злиття вже об'єднано, неможливо об'єднати знову чи змінити цільову гілку.
pulls.head_out_of_date = Не вдалося об'єднати: head було оновлено, поки відбувалося об'єднання. Підказка: спробуйте знову.
no_eol.tooltip = У цьому файлі відсутній символ закінчення рядка (EOL) у кінці.
settings.trust_model.committer.desc = Допустимі підписи будуть позначатися як «довірені», тільки якщо вони відповідають автору коміта, в іншому випадку вони позначатимуться як «невідповідні». Це змусить Forgejo бути автором підписаних комітів, а фактичного автора зазначати в трейлерах «Co-authored-by» і «Co-committed-by» в описі коміта. Типовий ключ Forgejo повинен відповідати користувачу в базі даних.
settings.trust_model.committer.desc = Допустимі підписи будуть позначатися як «довірені», тільки якщо вони відповідають авторові коміту, в іншому випадку вони позначатимуться як «невідповідні». Це змусить Forgejo бути автором підписаних комітів, а фактичного автора зазначати в трейлерах «Co-authored-by» і «Co-committed-by» в описі коміту. Типовий ключ Forgejo повинен відповідати користувачу в базі даних.
pulls.clear_merge_message_hint = Очищення повідомлення про об'єднання видалить лише вміст повідомлення коміту і збереже згенеровані git-трейлери, такі як «Co-Authored-By…».
branch.delete_branch_has_new_commits = Гілку «%s» не можна видалити, оскільки після об'єднання було додано нові коміти.
settings.graphql_url = Посилання GraphQL
@ -2514,7 +2514,7 @@ projects.column.set_default = Установити за замовчування
settings.federation_following_repos = URL-адреси відстежуваних репозиторіїв. Через «;», без пробілів.
settings.federation_not_enabled = Федерацію вимкнено у вашому екземплярі.
settings.federation_settings = Налаштування федерації
signing.wont_sign.nokey = Цей екземпляр не має ключа для підписання цього коміта.
signing.wont_sign.nokey = Цей екземпляр не має ключа для підписання цього коміту.
settings.federation_apapiurl = URL федерації цього репозиторію. Скопіюйте її та вставте в налаштування федерації іншого репозиторію як URL-адресу відстежуваного репозиторію.
fork_branch = Гілка, яку буде клоновано у форк
already_forked = Ви вже створили форк %s
@ -2554,7 +2554,7 @@ activity.navbar.pulse = Пульс
open_with_editor = Відкрити в %s
commits.view_single_diff = Переглянути зміни до цього файлу, внесені у цьому коміті
pulls.editable = Редаговане
pulls.editable_explanation = Цей запит на злиття дозволяє редагування від розробників. Ви можете зробити свій внесок безпосередньо до нього.
pulls.editable_explanation = Цей запит на злиття дозволено редагувати супроводжувачам. Ви можете зробити свій внесок безпосередньо до нього.
admin.failed_to_replace_flags = Не вдалося замінити прапорці репозиторія
admin.enabled_flags = Для репозиторія ввімкнено прапорці:
admin.flags_replaced = Прапорці репозиторія замінено
@ -2773,6 +2773,58 @@ issues.closed_by = від <a href="%[2]s">%[3]s</a> закрито %[1]s
issues.action_check = Поставити/зняти позначку
issues.action_check_all = Поставити/зняти позначку з усіх елементів
vendored = Сторонній
blame.ignore_revs.failed = Не вдалося проігнорувати зміни в <a href="%s">.git-blame-ignore-revs</a>.
blame.ignore_revs = Зміни в <a href="%s">.git-blame-ignore-revs</a> ігноруються. Натисніть <a href="%s">тут, щоб обійти це</a> і переглянути авторство у звичайному вигляді.
editor.new_branch_name = Укажіть назву нової гілки для цього коміту
projects.column.deletion_desc = Видалення стовпчика проєкту призведе до переміщення всіх пов'язаних із ним задач до стовпчика за замовчуванням. Продовжити?
signing.wont_sign.approved = Злиття не буде підписано, оскільки запит на злиття не схвалено.
pulls.allow_edits_from_maintainers = Дозволити редагування від супроводжувачів
pulls.showing_only_single_commit = Показано тільки зміни коміту %[1]s
pulls.showing_specified_commit_range = Показано тільки зміни між %[1]s..%[2]s
pulls.blocked_by_outdated_branch = Цей запит на злиття заблоковано, оскільки він застарів.
pulls.blocked_by_changed_protected_files_n = Цей запит на злиття заблоковано, оскільки він змінює захищені файли:
pulls.auto_merge_newly_scheduled = Заплановано об'єднати запит на злиття після успішного завершення всіх перевірок.
pulls.auto_merge_newly_scheduled_comment = `планує автоматично об'єднати цей запит на злиття після успішного завершення всіх перевірок %[1]s`
comments.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст коментаря. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін
pulls.auto_merge_canceled_schedule_comment = `скасовує автоматичне об'єднання цього запиту на злиття після успішного завершення всіх перевірок %[1]s`
signing.wont_sign.pubkey = Коміт не буде підписано, оскільки у вас немає публічного ключа, пов'язаного з вашим обліковим записом.
signing.wont_sign.basesigned = Злиття не буде підписано, оскільки не підписано базовий коміт.
signing.wont_sign.headsigned = Злиття не буде підписано, оскільки не підписано головний коміт.
projects.column.new_submit = Створити стовпчик
settings.authorization_header_desc = За наявності буде включено як заголовок авторизації для запитів. Приклади: %s.
template.git_hooks_tooltip = Наразі ви не можете змінювати або видаляти додані Git-хуки. Вибирайте лише якщо ви довіряєте репозиторію шаблону.
projects.column.edit = Редагувати стовпчик
projects.column.set_default_desc = Призначити цей стовпчик за замовчуванням для задач і запитів на злиття без категорії
projects.column.new = Новий стовпчик
projects.column.delete = Видалити стовпчик
pulls.blocked_by_official_review_requests = Цей запит на злиття заблоковано, оскільки йому бракує схвалення від одного або кількох офіційних рецензентів.
pulls.auto_merge_has_pending_schedule = %[1]s планує автоматично об'єднати цей запит на злиття після успішного завершення всіх перевірок %[2]s.
signing.wont_sign.commitssigned = Злиття не буде підписано, оскільки всі пов'язані з ним коміти не підписані.
settings.authorization_header = Заголовок авторизації
issues.archived_label_description = (Архівна) %s
issues.reaction.add = Додати реакцію
issues.reaction.alt_add = Додати реакцію %[1]s до коментаря.
issues.reaction.alt_remove = Прибрати реакцію %[1] з коментаря.
migrate.github_token_desc = Ви можете ввести тут один або кілька токенів через кому, щоб пришвидшити міграцію в обхід обмеження частоти звернень до API GitHub. ОБЕРЕЖНО: зловживання цією функцією може порушити політику постачальника послуг і призвести до блокування облікового запису.
issues.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст задачі. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін
pulls.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст запиту на злиття. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін
issues.reaction.alt_many = %[1]s і ще %[2]d реагують %[3]s.
settings.pulls.default_allow_edits_from_maintainers = За замовчуванням дозволити редагування від супроводжувачів
pulls.blocked_by_changed_protected_files_1 = Цей запит на злиття заблоковано, оскільки він змінює захищений файл:
pulls.delete.text = Ви дійсно хочете видалити цей запит на злиття? (Весь його вміст буде остаточно видалено. Можливо, варто його закрити і зберегти в архіві)
pulls.blocked_by_rejection = Цей запит на злиття містить зміни, запропоновані офіційним рецензентом.
signing.wont_sign.parentsigned = Цей коміт не буде підписано, оскільки не підписано батьківський коміт.
settings.ignore_stale_approvals = Ігнорувати застарілі схвалення
pulls.blocked_by_approvals = Цей запит на злиття ще не має достатньої кількості схвалень. Отримано %d з %d схвалень.
issues.delete.text = Ви дійсно хочете видалити цю задачу? (Весь її вміст буде остаточно видалено. Можливо, варто її закрити і зберегти в архіві)
signing.wont_sign.twofa = Щоб підписувати коміти, у вас повинна бути ввімкнена двофакторна автентифікація.
settings.mirror_settings.docs = Налаштуйте свій репозиторій на автоматичну синхронізацію комітів, тегів і гілок з іншим репозиторієм.
settings.mirror_settings.docs.disabled_push_mirror.instructions = Налаштуйте свій проєкт на автоматичне отримання комітів, тегів і гілок з іншого репозиторію.
settings.mirror_settings.docs.disabled_push_mirror.info = Push-дзеркала вимкнено адміністратором сайту.
settings.mirror_settings.docs.disabled_pull_mirror.instructions = Налаштуйте свій проєкт на автоматичне надсилання комітів, тегів і гілок до іншого репозиторію. Pull-дзеркала вимкнено адміністратором сайту.
issues.label_templates.fail_to_load_file = Не вдалося завантажити файл шаблону міток «%s»: %v
migrate.github.description = Перенесіть дані з github.com або сервера GitHub Enterprise.
migrate.cancel_migrating_confirm = Бажаєте скасувати перенесення?
[graphs]
contributors.what = внески
@ -3016,7 +3068,7 @@ users.prohibit_login=Заблокований обліковий запис
users.is_admin=Обліковий запис адміністратора
users.is_restricted=Обмежений
users.allow_git_hook=Може створювати Git-хуки
users.allow_git_hook_tooltip=Git хуки виконуються від імені користувача OS сервісу Forgejo і мають однаковий рівень доступу до хоста. Як результат, користувачі з доступом до Git-хуків можуть отримати доступ і змінювати всі репозиторії Forgejo, а також базу даних, що використовуються в Forgejo. Отже, вони також здатні отримати права адміністратора Forgejo.
users.allow_git_hook_tooltip=Git-хуки виконуються від імені користувача ОС, від якого запущено Forgejo, і мають той самий рівень доступу до хоста. Таким чином, користувачі зі спеціальними правами Git-хуків можуть отримати доступ і змінювати всі репозиторії Forgejo, а також базу даних Forgejo. Вони також здатні отримати права адміністратора Forgejo.
users.allow_import_local=Може імпортувати локальні репозиторії
users.allow_create_organization=Може створювати організації
users.update_profile=Оновити обліковий запис
@ -3170,7 +3222,7 @@ auths.tip.discord=Зареєструйте новий додаток на %s
auths.tip.yandex=Створіть новий додаток на %s. У розділі «Yandex.Passport API» виберіть такі дозволи: «Доступ до адреси електронної пошти», «Доступ до аватара» і «Доступ до імені користувача, імені та прізвища, статі»
auths.tip.mastodon=Введіть URL спеціального екземпляра для екземпляра mastodon, який ви хочете автентифікувати за допомогою (або використовувати за замовчуванням)
auths.edit=Редагувати джерело автентифікації
auths.activated=Це джерело авторизації активоване
auths.activated=Це джерело автентифікація активоване
auths.update_success=Параметри аутентифікації оновлені.
auths.update=Оновити джерело автентифікації
auths.delete=Видалити джерело автентифікації
@ -3196,7 +3248,7 @@ config.repo_root_path=Шлях до кореня репозиторію
config.lfs_root_path=Кореневий шлях LFS
config.log_file_root_path=Шлях до лог файлу
config.script_type=Тип скрипта
config.reverse_auth_user=Ім'я користувача для авторизації на reverse proxy
config.reverse_auth_user=Користувач для авторизації на зворотному проксі
config.ssh_config=Конфігурація SSH
config.ssh_enabled=Увімкнено
@ -3423,6 +3475,12 @@ config.mailer_smtp_addr = Адреса SMTP
dashboard.update_checker = Перевірка оновлень
auths.map_group_to_team_removal = Видаляти користувачів із синхронізованих команд, якщо користувачі не належать до відповідної групи LDAP
auths.enable_ldap_groups = Увімкнути групи LDAP
auths.unable_to_initialize_openid = Не вдалося ініціалізувати постачальника OpenID Connect: %s
auths.new_success = Метод автентифікації «%s» додано.
config.cache_test_slow = Перевірку кешу завершено успішно, але відповідь повільна: %s.
config.cache_test_succeeded = Перевірку кешу завершено успішно, відповідь отримано через %s.
config.cache_test = Перевірити кеш
config.cache_test_failed = Не вдалося перевірити кеш: %v.
[action]
@ -3704,7 +3762,7 @@ variables.management = Керування змінними
variables.id_not_exist = Змінної з ідентифікатором %d не існує.
variables.edit = Редагувати змінну
runs.expire_log_message = Журнали очищено, тому що вони були занадто старі.
runs.empty_commit_message = (порожнє повідомлення коміта)
runs.empty_commit_message = (порожнє повідомлення коміту)
runners.status.unspecified = Невідомо
runs.status_no_select = Усі стани
runs.status = Стан
@ -3826,6 +3884,7 @@ wiki.write = <b>Писати:</b> створювати, оновлювати т
issues.read = <b>Читати:</b> дивитись і створювати задачі та коментарі.
wiki.read = <b>Читати:</b> переглядати вбудовану вікі та її історію.
actions.write = <b>Писати:</b> вручну запускати, перезапускати, скасовувати або схвалювати конвеєри CI/CD в очікуванні.
projects.write = <b>Писати:</b> створювати проєкти і стовпчики та редагувати їх.
[munits.data]
pib = ПіБ

View file

@ -4,6 +4,138 @@
"home.explore_repos": "اكتشف المستودعات",
"home.explore_users": "اكتشف المستخدمين",
"home.explore_orgs": "اكتشف المنظمات",
"moderation.abuse_category.illegal_content": "المحتوى غير المشروع",
"moderation.abuse_category.malware": "برمجية خبيثة"
"moderation.abuse_category.illegal_content": "محتوى غير مشروع",
"moderation.abuse_category.malware": "برمجية خبيثة",
"relativetime.now": "الآن",
"relativetime.1month": "الشهر الفائت",
"relativetime.2weeks": "منذ أسبوعين",
"search.milestone_kind": "معالم البحث…",
"moderation.abuse_category.other_violations": "انتهاكات أخرى لقواعد المنصة",
"repo.issue_indexer.title": "مفهرس الإبلاغات",
"incorrect_root_url": "تم تكوين هذه النسخة من Forgejo لتعمل على العنوان \"%s\". أنت تقوم حاليًا بتصفّح Forgejo عبر رابط مختلف، مما قد يتسبب في تعطل بعض أجزاء التطبيق. يتم تحديد الرابط الرسمي (canonical URL) من قِبل مسؤولي Forgejo من خلال إعداد `ROOT_URL` في ملف `app.ini`.",
"error.not_found.title": "الصفحة غير موجودة",
"themes.names.forgejo-auto": "فورجيو (اتبع سمة النظام)",
"themes.names.forgejo-light": "فورجيجو المضيء",
"themes.names.forgejo-dark": "فورجيجو الداكن",
"stars.list.none": "لم يقم أحد بتمييز هذا المستودع بنجمة.",
"watch.list.none": "لا أحد يشاهد هذا المستودع.",
"followers.incoming.list.self.none": "لا أحد يتابع ملفك الشخصي.",
"followers.incoming.list.none": "لا أحد يتابع هذا المستخدم.",
"followers.outgoing.list.self.none": "أنت لا تتبع أي شخص.",
"relativetime.1day": "الأمس",
"followers.outgoing.list.none": "لا يتابع %s أي شخص.",
"relativetime.1week": "أخر أسبوع",
"relativetime.2days": "منذ يومين",
"relativetime.2months": "منذ شهرين",
"relativetime.1year": "السنة الفائتة",
"relativetime.2years": "منذ سنتين",
"repo.form.cannot_create": "بلغت جميع المساحات التي يمكنك إنشاء مستودعات بها حدها.",
"alert.asset_load_failed": "تعذّر تحميل ملفات الأصول من {path}. تأكد من أن الملفات متاحة للوصول.",
"settings.visibility.description": "رؤية ملفك الشخصي تؤثر في قدرة الآخرين على الوصول إلى مستودعاتك غير الخاصة. <a href=\"%s\" target=\"_blank\">اعرف المزيد</a>",
"relativetime.mins": {
"zero": "الآن",
"one": "منذ دقيقة مضت",
"two": "منذ دقيقتين مضت",
"few": "منذ %d دقائق مضت",
"many": "منذ %d دقيقة مضت",
"other": "منذ %d دقيقة مضت"
},
"relativetime.hours": {
"zero": "الآن",
"one": "منذ ساعة",
"two": "منذ ساعتين",
"few": "منذ %d ساعات",
"many": "منذ %d ساعة",
"other": "منذ %d ساعة"
},
"moderation.report_remarks": "الملاحظات",
"repo.diff.commit.next-short": "التالي",
"repo.diff.commit.previous-short": "السابق",
"admin.dashboard.cleanup_offline_runners": "تنظيف وحدات التشغيل غير المتصلة",
"relativetime.days": {
"zero": "اليوم",
"one": "منذ يوم واحد",
"two": "منذ يومين",
"few": "منذ %d أيام",
"many": "منذ %d يوماً",
"other": "منذ %d يوماً"
},
"relativetime.weeks": {
"zero": "هذا الأسبوع",
"one": "منذ أسبوع واحد",
"two": "منذ أسبوعين",
"few": "منذ %d أسابيع",
"many": "منذ %d أسبوعاً",
"other": "منذ %d أسبوعاً"
},
"relativetime.years": {
"zero": "هذه السنة",
"one": "منذ سنة واحدة",
"two": "منذ سنتين",
"few": "منذ %d سنوات",
"many": "منذ %d سنة",
"other": "منذ %d سنة"
},
"repo.settings.push_mirror.branch_filter.label": "تصفية الفرع (اختياري)",
"repo.settings.push_mirror.branch_filter.description": "الفروع المطلوب عكسها. اترك الحقل فارغًا لعكس جميع الفروع. راجع توثيق <a href=\"%[1]s\">%[2]s</a> للاطلاع على الصيغة. أمثلة: <code>main, release/*</code>",
"og.repo.summary_card.alt_description": "بطاقة تلخيصية للمستودع %[1]، موصوفة بـ %[1]: %[2]s",
"meta.last_line": "شكرًا لك على ترجمة Forgejo! هذا السطر لا يراه المستخدمون ولكنه يخدم أغراضًا أخرى في إدارة الترجمة. يمكنك وضع حقيقة ممتعة في الترجمة بدلاً من ترجمتها.",
"relativetime.future": "في المستقبل",
"avatar.constraints_hint": "لا يمكن أن يتجاوز حجم الصورة الشخصية المخصصة %[1]s، ولا أبعادها عن %[2]d×%[3]d بكسل",
"repo.pulls.merged_title_desc": {
"zero": "لم دمج أي إيداع من <code>%[2]s</code> إلى <code>%[3]s</code> %[4]s",
"one": "تم دمج إيداع واحد من <code>%[2]s</code> إلى <code>%[3]s</code> %[4]s",
"two": "تم دمج إيداعين اثنين من <code>%[2]s</code> إلى <code>%[3]s</code> %[4]s",
"few": "تم دمج %[1]d إيداعات من <code>%[2]s</code> إلى <code>%[3]s</code> %[4]s",
"many": "تم دمج %[1]d إيداعاً من <code>%[2]s</code> إلى <code>%[3]s</code> %[4]s",
"other": "تم دمج %[1]d إيداعاً من <code>%[2]s</code> إلى <code>%[3]s</code> %[4]s"
},
"relativetime.months": {
"zero": "هذا الشهر",
"one": "منذ شهر",
"two": "منذ شهرين",
"few": "منذ %d أشهر",
"many": "منذ %d شهراً",
"other": "منذ %d شهراً"
},
"repo.pulls.title_desc": {
"zero": "لا يريد دمج أي إيداع من <code>%[2]s</code> إلى <code id=\"%[4]s\">%[3]s</code>",
"one": "يريد دمج إيداع واحد من <code>%[2]s</code> إلى <code id=\"%[4]s\">%[3]s</code>",
"two": "يريد دمج إيداعين من <code>%[2]s</code> إلى <code id=\"%[4]s\">%[3]s</code>",
"few": "يريد دمج %[1]d إيداعات من <code>%[2]s</code> إلى <code id=\"%[4]s\">%[3]s</code>",
"many": "يريد دمج %[1]d إيداعًا من <code>%[2]s</code> إلى <code id=\"%[4]s\">%[3]s</code>",
"other": "يريد دمج %[1]d إيداعًا من <code>%[2]s</code> إلى <code id=\"%[4]s\">%[3]s</code>"
},
"mail.actions.run_info_sha": "إيداع: %[1]s",
"keys.gpg.link": "مفاتيح GPG",
"moderation.abuse_category.spam": "غير مرغوب به",
"keys.ssh.link": "مفاتيح SSH",
"profile.edit.link": "عدِّل ملف التعريف",
"moderation.abuse_category": "الفئة",
"moderation.abuse_category.placeholder": "حدد الفئة",
"moderation.report_remarks.placeholder": "يُرجى تقديم بعض التفاصيل المتعلقة بالإساءة التي أبلغت عنها.",
"moderation.submit_report": "إرسال التقرير",
"moderation.reporting_failed": "تعذر إرسال تقرير إساءة الاستخدام الجديد: %v",
"moderation.reported_thank_you": "شكراُ لك على تقريرك. وقد تم إبلاغ الإدارة به.",
"mail.actions.run_info_trigger": "تم تشغيله بسبب: %[1]s عبر: %[2]s",
"install.invalid_lfs_path": "غير قادر على إنشاء جذر LFS في المسار المحدد: %[1]s",
"alert.range_error": " يجب أن يكون رقمًا بين %[1]s و %[2]s.",
"mail.actions.not_successful_run_subject": "سير العمل %[1]s فشل في المستودت %[2]s",
"mail.actions.successful_run_after_failure": "تم استعادة سير العمل %[1]s في المستودع %[2]s",
"mail.actions.not_successful_run": "فشل سير العمل %[1]s في المستودع %[2]s",
"mail.actions.run_info_cur_status": "حالة هذا التشغيل: %[1]s (تم تحديثها من %[2]s للتو)",
"mail.actions.run_info_previous_status": "حالة التشغيل السابقة: %[1]s",
"discussion.locked": "تم إغلاق هذه المناقشة. يقتصر التعليق على المساهمين فقط.",
"editor.textarea.tab_hint": "السطر مُزاح بالفعل. اضغط <kbd>Tab</kbd> مرة أخرى أو <kbd>Escape</kbd> لمغادرة المحرر.",
"editor.textarea.shift_tab_hint": "لا توجد مسافة بادئة في هذا السطر. اضغط <kbd>Shift</kbd> + <kbd>Tab</kbd> مرة أخرى أو <kbd>Escape</kbd> لمغادرة المحرر.",
"profile.actions.tooltip": "إجراءات إضافية",
"moderation.report_content": "محتوى التقارير",
"moderation.report_abuse_form.header": "الإبلاغ عن الإساءة إلى المسؤول",
"moderation.report_abuse_form.details": "يتعين استخدام هذا النموذج للإبلاغ عن المستخدمين الذين ينشئون ملفات تعريف ، أو مستودعات ، أو إبلاغات ، أو تعليقات ، أو يتصرفون بشكل غير لائق.",
"moderation.report_abuse_form.invalid": "معاملا غير صالحة",
"moderation.report_abuse_form.already_reported": "‍لقد قمت بالفعل بالإبلاغ عن هذا المحتوى",
"moderation.report_abuse": "الإبلاغ عن إساءة الاستخدام",
"feed.atom.link": "موجز Atom",
"admin.config.moderation_config": "تهيئة الإشراف",
"mail.actions.successful_run_after_failure_subject": "تم استعادة سير العمل %[1]s في المستودع %[2]s"
}

View file

@ -1,3 +1,8 @@
{
"moderation.abuse_category.malware": "Шкодная праграма"
"moderation.abuse_category.malware": "Шкодная праграма",
"home.welcome.no_activity": "Няма падзей",
"home.welcome.activity_hint": "У вашай стужцы пакуль што нічога няма. Вашыя дзеяньні ды падзеі з рэпазыторыяў зьявяцца тут.",
"home.explore_repos": "Агляд рэпазыторыяў",
"home.explore_users": "Агляд карыстальнікаў",
"home.explore_orgs": "Агляд арганізацый"
}

View file

@ -111,5 +111,7 @@
"feed.atom.link": "Zdroj Atom",
"keys.ssh.link": "Klíče SSH",
"og.repo.summary_card.alt_description": "Karta se souhrnem repozitáře %[1]s, popsaným jako: %[2]s",
"mail.actions.run_info_sha": "Revize: %[1]s"
"mail.actions.run_info_sha": "Revize: %[1]s",
"repo.settings.push_mirror.branch_filter.label": "Filtr větve (nepovinný)",
"repo.settings.push_mirror.branch_filter.description": "Větve, které mají být zrcadleny. Ponechte prázdné pro zrcadlení všech větví. Syntaxi naleznete v <a href=\"%[1]s\">dokumentaci %[2]s</a>. Příklady: <code>main, release/*</code>"
}

View file

@ -90,11 +90,11 @@
"followers.outgoing.list.none": "%s folgt niemanden.",
"stars.list.none": "Niemand hat dieses Repo favorisiert.",
"followers.incoming.list.none": "Niemand folgt diesem Benutzer.",
"editor.textarea.tab_hint": "Zeile bereits eingerückt. Drücke nochmals <kbd>Tab</kbd> oder <kbd>Escape</kbd> um den Editor zu verlassen.",
"editor.textarea.shift_tab_hint": "Keine Einrückung auf dieser Zeile. Drücke nochmals <kbd>Shift</kbd> + <kbd>Tab</kbd> oder <kbd>Escape</kbd> um den Editor zu verlassen.",
"admin.dashboard.cleanup_offline_runners": "Aufräumen der offline Runner",
"editor.textarea.tab_hint": "Zeile bereits eingerückt. Drücke nochmals <kbd>Tab</kbd> oder <kbd>Escape</kbd>, um den Editor zu verlassen.",
"editor.textarea.shift_tab_hint": "Keine Einrückung auf dieser Zeile. Drücke nochmals <kbd>Shift</kbd> + <kbd>Tab</kbd> oder <kbd>Escape</kbd>, um den Editor zu verlassen.",
"admin.dashboard.cleanup_offline_runners": "Aufräumen der Offline-Runner",
"settings.visibility.description": "Die Profilsichtbarkeit beeinflusst die Möglichkeit anderer, auf deine nicht-privaten Repositorys zuzugreifen. <a href=\"%s\" target=\"_blank\">Erfahre mehr</a>",
"avatar.constraints_hint": "Individuelles Profilbild darf %[1]s in der Größe nicht überschreiten, und nicht größer als %[2]dx%[3]d Pixel sein",
"avatar.constraints_hint": "Individuelles Profilbild darf %[1]s in der Größe nicht überschreiten, und nicht größer als %[2]d×%[3]d Pixel sein",
"repo.diff.commit.next-short": "Nächste",
"repo.diff.commit.previous-short": "Vorherige",
"profile.edit.link": "Profil bearbeiten",
@ -103,5 +103,7 @@
"keys.gpg.link": "GPG-Schlüssel",
"profile.actions.tooltip": "Mehr Aktionen",
"og.repo.summary_card.alt_description": "Zusammenfassungskarte des Repositorys %[1]s, beschrieben als %[2]s",
"mail.actions.run_info_sha": "Commit: %[1]s"
"mail.actions.run_info_sha": "Commit: %[1]s",
"repo.settings.push_mirror.branch_filter.label": "Branch-Filter (optional)",
"repo.settings.push_mirror.branch_filter.description": "Zu spiegelnde Branches. Leer lassen, um alle Branches zu spiegeln. Siehe die <a href=\"%[1]s\">„%[2]s“-Dokumentation</a> für die Syntax. Beispiele: <code>main, release/*</code>"
}

View file

@ -56,7 +56,7 @@
"repo.issue_indexer.title": "Issue Indexer",
"search.milestone_kind": "Search milestones…",
"repo.settings.push_mirror.branch_filter.label": "Branch filter (optional)",
"repo.settings.push_mirror.branch_filter.description": "Branches to be mirrored. Leave blank to mirror all branches. See <a href=\"%[1]s\">%[2]s</a> documentation for syntax. Examples: <code>main, release/*</code>",
"repo.settings.push_mirror.branch_filter.description": "Branches to be mirrored. Leave blank to mirror all branches. See <a href=\"%[1]s\">%[2]s documentation</a> for syntax. Examples: <code>main, release/*</code>",
"incorrect_root_url": "This Forgejo instance is configured to be served on \"%s\". You are currently viewing Forgejo through a different URL, which may cause parts of the application to break. The canonical URL is controlled by Forgejo admins via the ROOT_URL setting in the app.ini.",
"themes.names.forgejo-auto": "Forgejo (follow system theme)",
"themes.names.forgejo-light": "Forgejo light",
@ -99,6 +99,7 @@
"repo.diff.commit.next-short": "Next",
"repo.diff.commit.previous-short": "Prev",
"discussion.locked": "This discussion has been locked. Commenting is limited to contributors.",
"discussion.sidebar.reference": "Reference",
"editor.textarea.tab_hint": "Line already indented. Press <kbd>Tab</kbd> again or <kbd>Escape</kbd> to leave the editor.",
"editor.textarea.shift_tab_hint": "No indentation on this line. Press <kbd>Shift</kbd> + <kbd>Tab</kbd> again or <kbd>Escape</kbd> to leave the editor.",
"admin.dashboard.cleanup_offline_runners": "Cleanup offline runners",

View file

@ -1,6 +1,6 @@
{
"repo.pulls.merged_title_desc": {
"one": "isinali ang %[1]d commit mula<code>%[2]s</code> patungong <code>%[3]s</code> %[4]s",
"one": "isinali ang %[1]d commit mula <code>%[2]s</code> patungong <code>%[3]s</code> %[4]s",
"other": "isinali ang %[1]d mga commit mula sa <code>%[2]s</code> patungong <code>%[3]s</code> %[4]s"
},
"repo.pulls.title_desc": {
@ -21,7 +21,7 @@
"alert.asset_load_failed": "Nabigong i-load ang mga asset file mula sa {path}. Siguraduhin na maa-access ang mga asset file.",
"install.invalid_lfs_path": "Nabigong gawin ang LFS root sa tinakdang path: %[1]s",
"alert.range_error": " dapat ay numero sa pagitan ng %[1]s at %[2]s.",
"meta.last_line": "Every day, I imagine a future where I can be with you. In my hand is a pen that will write a poem of me and you. The ink flows down into a dark puddle... Just move your hand, write the way into his heart. But in this world of infinite choices. What will it take just to find that special day? Have I found everybody a fun assignment to do today? When you're here, everything that we do is fun for them anyway... When I can't even read my own feelings. What good are words when a smile says it all? And if this world won't write me an ending... What will it take just for me to have it all? Does my pen only write bitter words for those who are dear to me? Is it love if I take you, or is it love if I set you free? The ink flows down into a dark puddle... How can I write love into reality? If I can't hear the sound of your heartbeat What do you call love in your reality? And in your reality, if I don't know how to love you... I'll leave you be.",
"meta.last_line": "Every day, I imagine a future where I can be with you. In my hand is a pen that will write a poem of me and you. The ink flows down into a dark puddle... Just move your hand, write the way into his heart. But in this world of infinite choices, what will it take just to find that special day? Have I found everybody a fun assignment to do today? When you're here, everything that we do is fun for them anyway... When I can't even read my own feelings, what good are words when a smile says it all? And if this world won't write me an ending, what will it take just for me to have it all? Does my pen only write bitter words for those who are dear to me? Is it love if I take you, or is it love if I set you free? The ink flows down into a dark puddle... How can I write love into reality? If I can't hear the sound of your heartbeat, what do you call love in your reality? And in your reality, if I don't know how to love you... I'll leave you be.",
"mail.actions.successful_run_after_failure": "Na-recover ang workflow na %[1]s sa repositoryong %[2]s",
"mail.actions.not_successful_run": "Nabigo ang workflow na %[1]s sa repositoryong %[2]s",
"mail.actions.run_info_previous_status": "Nakaraang Status ng Run: %[1]s",
@ -102,5 +102,8 @@
"keys.ssh.link": "Mga SSH key",
"keys.gpg.link": "Mga GPG key",
"profile.actions.tooltip": "Higit pang mga aksyon",
"og.repo.summary_card.alt_description": "Card ng pangkalahatang ideya ng repositoryong %[1]s, inilalarawan bilang: %[2]s"
"og.repo.summary_card.alt_description": "Card ng pangkalahatang ideya ng repositoryong %[1]s, inilalarawan bilang: %[2]s",
"mail.actions.run_info_sha": "Commit: %[1]s",
"repo.settings.push_mirror.branch_filter.label": "Filter ng branch (opsyonal)",
"repo.settings.push_mirror.branch_filter.description": "Mga branch na imi-mirror. Iwanang walang laman para i-mirror ang lahat ng mga branch. Tignan ang <a href=\"%[1]s\">dokumentasyon ng %[2]s</a> para sa syntax. Halimbawa: <code>main, your-reality, release/*</code>"
}

View file

@ -111,5 +111,7 @@
"keys.ssh.link": "SSH atslēgas",
"keys.gpg.link": "GPG atslēgas",
"og.repo.summary_card.alt_description": "Glabātavas %[1]s kopsavilkuma kartīte, aprakstīta kā: %[2]s",
"mail.actions.run_info_sha": "Iesūtījums: %[1]s"
"mail.actions.run_info_sha": "Iesūtījums: %[1]s",
"repo.settings.push_mirror.branch_filter.description": "Zarus, kurus spoguļot. Atstāt tukšu, lai spoguļotu visus zarus. Pierakstu skatīt <a href=\"%[1]s\">%[2]s dokumentācijā</a>. Piemēri: <code>main, release/*</code>",
"repo.settings.push_mirror.branch_filter.label": "Zaru atlasītājs (izvēles)"
}

View file

@ -103,5 +103,7 @@
"profile.actions.tooltip": "Mehr Aktioonen",
"profile.edit.link": "Profil bewarken",
"og.repo.summary_card.alt_description": "Tosamenfatens-Kaart vun de Repositorium %[1]s, beschrieven as: %[2]s",
"mail.actions.run_info_sha": "Kommitteren: %[1]s"
"mail.actions.run_info_sha": "Kommitteren: %[1]s",
"repo.settings.push_mirror.branch_filter.description": "Twiegen tum Spegeln. Laat dat leeg, um all Twiegen to spegeln. Lees de <a href=\"%[1]s\">%[2]s-Dokumenteren</a> för de Syntax. Bispölen: <code>main, release/*</code>",
"repo.settings.push_mirror.branch_filter.label": "Twieg-Filter (wenn du willst)"
}

View file

@ -103,5 +103,7 @@
"repo.diff.commit.previous-short": "Vorige",
"avatar.constraints_hint": "Eigen avatars mogen niet groter zijn dan %[1]s in grootte of groter zijn dan %[2]dx%[3]d pixels",
"og.repo.summary_card.alt_description": "Samenvattingsoverzicht van repositorie %[1]s, omschreven als: %[2]s",
"mail.actions.run_info_sha": "Commit: %[1]s"
"mail.actions.run_info_sha": "Commit: %[1]s",
"repo.settings.push_mirror.branch_filter.label": "Branch filter (optioneel)",
"repo.settings.push_mirror.branch_filter.description": "Branches die gespiegeld moeten worden. Laat het laag om alle branches te spiegelen. Zie <a href=\"%[1]s\">%[2]s</a> documentatie voor de syntax. Voorbeeld: <code>main, release/*</code>"
}

View file

@ -111,5 +111,7 @@
"og.repo.summary_card.alt_description": "Cartão de resumo do repositório %[1]s, descrito como: %[2]s",
"profile.actions.tooltip": "Mais Actions",
"keys.ssh.link": "Chaves SSH",
"mail.actions.run_info_sha": "Commit: %[1]s"
"mail.actions.run_info_sha": "Commit: %[1]s",
"repo.settings.push_mirror.branch_filter.label": "Filtro de branches (opcional)",
"repo.settings.push_mirror.branch_filter.description": "Branches para espelhar. Deixe em branco para espelhar todos os branches. Veja <a href=\"%[1]s\">%[2]s documentação</a> sobre a sintaxe. Exemplos: <code>main, release/*</code>"
}

View file

@ -83,7 +83,7 @@
"moderation.abuse_category": "Categoria",
"moderation.abuse_category.placeholder": "Escolha uma categoria",
"moderation.abuse_category.spam": "Spam",
"moderation.abuse_category.malware": "Malware",
"moderation.abuse_category.malware": "Software malicioso",
"moderation.abuse_category.illegal_content": "Conteúdo ilegal",
"moderation.abuse_category.other_violations": "Outras violações das regras da plataforma",
"moderation.report_remarks": "Observações",
@ -109,5 +109,9 @@
"feed.atom.link": "Feed Atom",
"keys.ssh.link": "Chaves SSH",
"keys.gpg.link": "Chaves GPG",
"repo.diff.commit.previous-short": "Ant."
"repo.diff.commit.previous-short": "Ant.",
"og.repo.summary_card.alt_description": "Cartão de resumo do repositório %[1]s, descrito como: %[2]s",
"repo.settings.push_mirror.branch_filter.label": "Filtro de ramos (opcional)",
"repo.settings.push_mirror.branch_filter.description": "Ramos a serem espelhados. Deixe em branco para espelhar todos os ramos. Veja a <a href=\"%[1]s\">%[2]s documentação</a> sobre a sintaxe. Exemplos: <code>main, release/*</code>",
"mail.actions.run_info_sha": "Cometimento: %[1]s"
}

View file

@ -23,7 +23,7 @@
"alert.asset_load_failed": "Не удалось получить ресурсы из {path}. Убедитесь, что файлы ресурсов доступны.",
"install.invalid_lfs_path": "Не удалось расположить корень LFS по указанному пути: %[1]s",
"alert.range_error": " - число должно быть в диапазоне от %[1]s-%[2]s.",
"meta.last_line": "Unskip..",
"meta.last_line": "...ъъ",
"mail.actions.not_successful_run_subject": "Провал раб. потока %[1]s в репозитории %[2]s",
"mail.actions.successful_run_after_failure_subject": "Возобновление раб. потока %[1]s в репозитории %[2]s",
"mail.actions.run_info_trigger": "Причина срабатывания: %[1]s by: %[2]s",
@ -111,5 +111,7 @@
"keys.gpg.link": "Ключи GPG",
"profile.edit.link": "Изменить профиль",
"og.repo.summary_card.alt_description": "Карточка со сводкой о репозитории %s. Описание: %[2]s",
"mail.actions.run_info_sha": "Коммит: %[1]s"
"mail.actions.run_info_sha": "Коммит: %[1]s",
"repo.settings.push_mirror.branch_filter.description": "Синхронизируемые ветви. Оставьте пустым, чтобы синхронизировать все. Ознакомьтесь с синтаксисом в <a href=\"%[1]s\">документации %[2]s</a>. Примеры: <code>main, release/*</code>",
"repo.settings.push_mirror.branch_filter.label": "Выбор ветвей (опционально)"
}

View file

@ -111,5 +111,7 @@
"feed.atom.link": "Стрічка Atom",
"profile.actions.tooltip": "Більше дій",
"og.repo.summary_card.alt_description": "Підсумкова картка репозиторію %[1]s з описом: %[2]s",
"mail.actions.run_info_sha": "Коміт: %[1]s"
"mail.actions.run_info_sha": "Коміт: %[1]s",
"repo.settings.push_mirror.branch_filter.description": "Гілки для дзеркалювання. Залиште порожнім, щоб віддзеркалити всі гілки. Дивіться синтаксис у <a href=\"%[1]s\">документації %[2]s</a>. Приклади: <code>main, release/*</code>",
"repo.settings.push_mirror.branch_filter.label": "Фільтр гілок (необов'язково)"
}

View file

@ -78,5 +78,8 @@
"repo.diff.commit.previous-short": "上一个",
"feed.atom.link": "Atom 订阅源",
"profile.edit.link": "编辑个人资料",
"og.repo.summary_card.alt_description": "仓库 %[1]s 的摘要卡片,描述为:%[2]s"
"og.repo.summary_card.alt_description": "仓库 %[1]s 的摘要卡片,描述为:%[2]s",
"repo.settings.push_mirror.branch_filter.label": "分支过滤器(可选)",
"repo.settings.push_mirror.branch_filter.description": "欲镜像的分支。留空以镜像所有分支。关于语法的更多信息,请参见 <a href=\"%[1]s\">%[2]s 文档</a>。例如:<code>main, release/*</code>",
"mail.actions.run_info_sha": "提交:%[1]s"
}

663
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -55,7 +55,7 @@
"vue-chartjs": "5.3.1",
"vue-loader": "17.4.2",
"vue3-calendar-heatmap": "2.0.5",
"webpack": "5.100.0",
"webpack": "5.100.2",
"webpack-cli": "6.0.1",
"wrap-ansi": "9.0.0"
},

View file

@ -0,0 +1,476 @@
<!--start release-notes-assistant-->
## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Breaking security features
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7924): <!--number 7924 --><!--line 0 --><!--description cmVtb3ZlIEFQSSBhdXRoZW50aWNhdGlvbiBtZXRob2RzIHRoYXQgdXNlcyB0aGUgVVJMIHF1ZXJ5-->remove API authentication methods that uses the URL query. They are disabled by default and this only has an impact if `[security].DISABLE_QUERY_AUTH_TOKEN=false` is explicitly set. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#removing-deprecated-api-authentication-methods).<!--description-->
- Security features
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7829): <!--number 7829 --><!--line 0 --><!--description cmVsYXggZW1haWwgcmVxdWlyZW1lbnRz-->relax email requirements. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#relaxing-the-requirements-on-email-addresses).<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7693): <!--number 7693 --><!--line 0 --><!--description Y29uc2lkZXIgV2ViQXV0aG4gJiBTU0ggZm9yIGluc3RhbmNlIHNpZ25pbmc=-->consider WebAuthn & SSH for instance signing.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/6897): <!--number 6897 --><!--line 0 --><!--description ZmVhdChzZWMpOiBBZGQgU1NIIHNpZ25pbmcgc3VwcG9ydCBmb3IgaW5zdGFuY2Vz-->add SSH signing support for instances. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#instance-signing-with-ssh).<!--description-->
- Breaking features
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8035): <!--number 8035 --><!--line 0 --><!--description VGhlIGBmb3JnZWpvIGRvY3NgIGNvbW1hbmQgaXMgZGVwcmVjYXRlZCBhbmQgQ0xJIGVycm9ycyBhcmUgbm93IGRpc3BsYXllZCBvbiBzdGRlcnIgaW5zdGVhZCBvZiBzdGRvdXQuIFRoZXNlIGJyZWFraW5nIGNoYW5nZXMgaGFwcGVuZWQgYmVjYXVzZSB0aGUgcGFja2FnZSB1c2VkIHRvIHBhcnNlIHRoZSBjb21tYW5kIGxpbmUgYXJndW1lbnRzIHdhcyBbdXBncmFkZWQgZnJvbSB2MiB0byB2M10oaHR0cHM6Ly9jbGkudXJmYXZlLm9yZy9taWdyYXRlLXYyLXRvLXYzLykuIEEgW3NlcGFyYXRlIHByb2plY3Qgd2FzIGluaXRpYXRlZF0oaHR0cHM6Ly9naXRodWIuY29tL3VyZmF2ZS9jbGktZG9jcykgdG8gcmUtaW1wbGVtZW50IHRoZSBgZG9jc2AgY29tbWFuZCwgYnV0IGl0IGlzIG5vdCB5ZXQgcHJvZHVjdGlvbiByZWFkeS4=-->The `forgejo docs` command is deprecated and CLI errors are now displayed on stderr instead of stdout. These breaking changes happened because the package used to parse the command line arguments was [upgraded from v2 to v3](https://cli.urfave.org/migrate-v2-to-v3/). A [separate project was initiated](https://github.com/urfave/cli-docs) to re-implement the `docs` command, but it is not yet production ready.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7745): <!--number 7745 --><!--line 0 --><!--description cmVtb3ZlIHRoZSBsZWdhY3kgYFRFU1RfQ09ORkxJQ1RJTkdfUEFUQ0hFU19XSVRIX0dJVF9BUFBMWWAgc2V0dGluZw==-->remove the legacy `TEST_CONFLICTING_PATCHES_WITH_GIT_APPLY` setting<!--description-->
- Breaking bug fixes
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8139): <!--number 8139 --><!--line 0 --><!--description QVBJOiBlbmZvcmNlIHNoYSByZXF1aXJlbWVudCBvbiBgUE9TVCAvcmVwb3Mve293bmVyfS97cmVwb30vY29udGVudHNg-->fail if `sha` is not provided to the `POST /repos/{owner}/{repo}/contents` API endpoint. Although it was documented to be required, it was not enforced and clients that do not set the `sha` will no longer succeed.<!--description-->
- User Interface features
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8185): <!--number 8185 --><!--line 0 --><!--description ZmVhdCh1aSk6IGZlZGl2ZXJzZSBoYW5kbGUgbWFya3VwIHZpYSByZWRpcmVjdCBzZXJ2ZXI=-->transform fediverse handles (ex. @forgejo@floss.social and !forgejo@programming.dev) into links to https://fedirect.toolforge.org. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#redirecting-fediverse-handles).<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8121): <!--number 8121 --><!--line 0 --><!--description YWRkIHVzZXIgdmlzaWJpbGl0eSBkZXNjcmlwdGlvbiBpbiBzZXR0aW5ncyBwYWdl-->add user visibility description in the settings page.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8111): <!--number 8111 --><!--line 0 --><!--description YWRkIG1vZGVsIHZpZXdlciBmb3IgYC5nbGJgIChHTFRGKSBtb2RlbCBpbiBmaWxlIHZpZXc=-->add model viewer for `.glb` (GLTF) model in file view. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#gltf-viewer).<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7998): <!--number 7998 --><!--line 0 --><!--description ZmVhdCh1aSk6IHNob3cgc2l6ZSBjb25zdHJhaW50cyBvZiBjdXN0b20gYXZhdGFy-->show size constraints of custom avatar.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7992): <!--number 7992 --><!--line 0 --><!--description ZmVhdCh1aSk6IGFkZCBsaW5rcyB0byBtaWxlc3RvbmVzIGFuZCBwcm9qZWN0cyBpbiBpc3N1ZSBjb21tZW50cw==-->add links to milestones and projects in issue comments.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7958): <!--number 7958 --><!--line 0 --><!--description ZmVhdCh1aSk6IGdsb2JhbCBzdHlsaW5nIGZvciBrYmQgdGFn-->global styling for the kbd tag.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7947): <!--number 7947 --><!--line 0 --><!--description ZmVhdCh1aSk6IGhpbnRzIGluIGVtcHR5IHVzZXJjYXJkcyBsaXN0cw==-->hints in empty usercards lists.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7906): <!--number 7906 --><!--line 0 --><!--description ZmVhdCh1aSk6IHJlZGVzaWduIHVzZXIgcHJvZmlsZSBhY3Rpb25zIGxheW91dA==-->the user profile has been redesigned. The most notable change is that actions have been moved to a dropdown and several new actions were added.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7531): <!--number 7531 --><!--line 0 --><!--description ZmVhdCh1aSk6IGltcHJvdmUgZGVzYyBpbiBQYWNrYWdlcyBzZXR0aW5ncw==-->improve the description in the packages settings.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7516): <!--number 7516 --><!--line 0 --><!--description aW5saW5lIHB1YmxpYyBzc2gga2V5IGluIHZlcmlmaWNhdGlvbiBjb21tYW5k-->inline public ssh key in verification command.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7481): <!--number 7481 --><!--line 0 --><!--description ZmVhdCh1aSk6IHVzZSBzd2l0Y2ggZWxlbWVudCBmb3IgbWFya2Rvd24gZWRpdG9yIG1vZGVz-->use switch element for markdown editor modes.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7388): <!--number 7388 --><!--line 0 --><!--description ZmVhdCh1aSk6IG1ha2UgSlMgYXNzZXQgbG9hZCBlcnJvciBtZXNzYWdlIHRyYW5zbGF0YWJsZQ==-->make JS asset load error message translatable.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7385): <!--number 7385 --><!--line 0 --><!--description VXNlIGBnaXQgc3dpdGNoIC1jYCBpbnN0ZWFkIG9mIGBnaXQgY2hlY2tvdXQgLWJg-->improve performances by using `git switch -c` instead of `git checkout -b`.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7377): <!--number 7377 --><!--line 0 --><!--description ZmVhdCh1aS9taWdyYXRpb25zKTogY2xhcmlmeSBkZXNpcmVkIGF1dG9jb21wbGV0aW9uIHR5cGUgZm9yIGNsb25lX2FkZHI=-->clarify the desired autocompletion type for the clone address in migrations.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7373): <!--number 7373 --><!--line 0 --><!--description TWlncmF0aW9ucyBVSTogSW1wcm92ZSBDbGFyaXR5IGZvciBNaWdyYXRpb24gRGVzY3JpcHRpb24gVGV4dGFyZWE=-->improve the clarity of the migration description textarea.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7361): <!--number 7361 --><!--line 0 --><!--description ZmVhdCh1aSk6IEF1dG9tYXRpY2FsbHkgcmVmcmVzaCB3b3JrZmxvd3MgaW4gdGhlICJBY3Rpb25zIiBsaXN0-->automatically refresh workflows in the "Actions" list. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#automatically-refreshing-workflows).<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7274): <!--number 7274 --><!--line 0 --><!--description dWk6IGltcHJvdmUgZXJyb3IgcGFnZXM=-->improve error pages.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7155): <!--number 7155 --><!--line 0 --><!--description ZmVhdCh1aSk6IGltcHJvdmUgdGhlIHVzZXIgZXhwZXJpZW5jZSB0byByZXZpZXcgaW5kaXZpZHVhbCBjb21taXRzIGluIGEgcHVsbCByZXF1ZXN0-->improve the user experience to review individual commits in a pull request. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#improved-ux-for-per-commit-reviews).<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7128): <!--number 7128 --><!--line 0 --><!--description VXNlIGF2YWlsYWJsZSBzY3JlZW4gd2lkdGggZm9yIEFjdGlvbnMgbG9ncw==-->use the available screen width when displaying Forgejo Actions logs.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/6933): <!--number 6933 --><!--line 0 --><!--description U2hvdyBpZiBjb21taXQgaXMgdmVyaWZpZWQgaW4gYWN0aXZpdHkgZmVlZCBvZiBhbiB1c2VyIG9yIGFuIG9yZ2FuaXphdGlvbiBmb3IgbmV3IGFjdGl2aXR5-->show if a commit is verified in the activity feed of a user or an organization.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/6813): <!--number 6813 --><!--line 0 --><!--description UmVpbXBsZW1lbnRlZCBlZGl0b3IgVGFiIGtleSBoYW5kbGluZyB3aXRoIGFjY2Vzc2liaWxpdHkgc2FmZWd1YXJkcy4gQmFsYW5jZSBoYXZpbmcgdGhlIGVkaXRvciB3b3JrIGFzIGV4cGVjdGVkIGJ5IGRldmVsb3BlcnMgKHdpdGggVGFiIGtleSBhZmZlY3RpbmcgaW5kZW50YXRpb24pIHdoaWxlIGFsc28gbm90IGltcGVkaW5nIGtleWJvYXJkIG5hdmlnYXRpb24u-->reimplemented editor Tab key handling with accessibility safeguards. Balance having the editor work as expected by developers (with Tab key affecting indentation) while also not impeding keyboard navigation. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#tabs-indentations-in-the-comment-editor)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/6795): <!--number 6795 --><!--line 0 --><!--description ZmVhdCh1aSk6IHJlZGVzaWduIG1pZ3JhdGlvbiBzZWxlY3Rpb24gc2NyZWVu-->redesign the migration selection page.<!--description-->
- User Interface bug fixes
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8417) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8514)): <!--number 8514 --><!--line 0 --><!--description Zml4KHVpKTogbXVsdGlwbGUgQ29tYm9NYXJrZG93bkVkaXRvcnMgb24gb25lIHBhZ2UgaW50ZXJmZXJl-->multiple ComboMarkdownEditors on one page interfere with each other.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7749) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8362)): <!--number 8362 --><!--line 0 --><!--description Zml4KHVpKTogQWRkIHBhc3RlZCBpbWFnZXMgdG8gZHJvcHpvbmU=-->pasting images into the comment editor will now show that image in the dropzone.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8296) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8317)): <!--number 8317 --><!--line 0 --><!--description Zml4OiBhZGQgbWlzc2luZyB0cnVzdCBzdGF0dXMgdG8gcHVsbCByZXZpZXcgY29tbWl0cw==-->add missing trust status to pull review commits.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8246) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8282)): <!--number 8282 --><!--line 0 --><!--description Zml4KHVpKTogYWRkIG1pc3NpbmcgbGF6eSBsb2FkIGF0dHJpYnV0ZSB0byBpbWFnZXM=-->add missing lazy load attribute to images.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8067): <!--number 8067 --><!--line 0 --><!--description cG9ydChnaXRlYSk6IFJldGFpbiBpc3N1ZS9wdWxsIHNvcnQgdHlwZQ==-->retain sort type when viewing issue or pull requests.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7893): <!--number 7893 --><!--line 0 --><!--description Zml4KHVpKTogaW5jbHVkZSBlbm91Z2ggYWN0aXZpdHkgZm9yIHRoZSBlbnRpcmUgaGVhdG1hcA==-->include enough activity for the entire heatmap.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7726): <!--number 7726 --><!--line 0 --><!--description U2hvdyB3YXJuaW5nIGluIGxvY2tlZCBpc3N1ZSBkaXNjdXNzaW9u-->show warning in locked issue discussion.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7492): <!--number 7492 --><!--line 0 --><!--description Zml4KHVpKTogZW5zdXJlIGNvbnNpc3RlbnQgc3dpdGNoIHBvc2l0aW9uIGluIG1hcmtkb3duIGVkaXRvcg==-->ensure consistent switch position in the markdown editor.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7420): <!--number 7420 --><!--line 0 --><!--description Zml4KHVpKTogZGlzcGxheSB1c2VyLWZyaWVuZGx5IG1lc3NhZ2UgZm9yIHJhbmdlIGVycm9y-->display user-friendly message for range error.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7402): <!--number 7402 --><!--line 0 --><!--description Zml4KHVpKTogbWFrZSBsaW1pdHMgY2xlYXJlciBpbiBjcmVhdGUgcmVwbyBmb3Jt-->make limits clearer in the create repository form.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7307): <!--number 7307 --><!--line 0 --><!--description RG9uJ3QgcHV0IHRyYWlsaW5nIHNsYXNoIGluIGF1dG9nZW5lcmF0ZWQgbmFtZSBkdXJpbmcgaW1wb3J0-->don't put trailing slash in autogenerated name in the migration form.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7245): <!--number 7245 --><!--line 0 --><!--description YWxsb3cgdXNlciB3aXRoIGFjdGlvbnMgd3JpdGUgcGVybWlzc2lvbiB0byBydW4gYSB3b3JrZmxvdyBmcm9tIHRoZSB3ZWIgVUk=-->allow user with actions write permission to run a workflow from the web UI.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/6799): <!--number 6799 --><!--line 0 --><!--description Zml4KHVpKTogZW5zdXJlIHNhbWUgd2lkdGggb2YgdXNlcmNhcmRzIGluIGdyaWQ=-->ensure usercards in grid have the same width.<!--description-->
- Localization
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8238) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8294)): <!--number 8294 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8178): <!--number 8178 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8077): <!--number 8077 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7969): <!--number 7969 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7895): <!--number 7895 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7819): <!--number 7819 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7742): <!--number 7742 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7637): <!--number 7637 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7572): <!--number 7572 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7507): <!--number 7507 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7438): <!--number 7438 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7363): <!--number 7363 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7275): <!--number 7275 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
- Features
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8179): <!--number 8179 --><!--line 0 --><!--description QVBJOiBuZXcgYEdFVCAvcmVwb3Mve293bmVyfS97cmVwb30vZ2l0L2Jsb2JzYCBlbmRwb2ludCB0byByZXRyaWV2ZSBtdWx0aXBsZSBibG9icyBhdCBvbmNl-->new `GET /repos/{owner}/{repo}/git/blobs` API endpoint to retrieve multiple blobs at once.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8177): <!--number 8177 --><!--line 0 --><!--description YWx3YXlzIHB1Ymxpc2ggdGhlIGxpbmsgdG8gdGhlIGNvbW1pdCBzdGF0dXM=-->always publish the link to the commit status.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8143): <!--number 8143 --><!--line 0 --><!--description aW1wcm92ZSBnZW5lcmF0aW9uIG9mIGJ1bmRsZWQgYXNzZXRz-->improve the performances of the generation of bundled assets.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8115): <!--number 8115 --><!--line 0 --><!--description ZW5hYmxlIGBtbGtlbTc2OHgyNTUxOS1zaGEyNTZgIGJ5IGRlZmF1bHQgZm9yIGJ1aWx0aW4gc3No-->enable `mlkem768x25519-sha256` by default for builtin ssh.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8070): <!--number 8070 --><!--line 0 --><!--description c3VwcG9ydCBhcnRpZmFjdCB1cGxvYWRzIGZvciBPQ0kgY29udGFpbmVyIHBhY2thZ2Vz-->support artifact uploads for OCI container packages.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8047): <!--number 8047 --><!--line 0 --><!--description YWRkIGBhZG1pbiB1c2VyIHJlc2V0LW1mYWAgQ0xJIGNvbW1hbmQ=-->add `admin user reset-mfa` CLI command.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7988): <!--number 7988 --><!--line 0 --><!--description dXBkYXRlIGFtYmlnaW91cyBjaGFyYWN0ZXJz-->update the list of ambigious characters.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7986): <!--number 7986 --><!--line 0 --><!--description bWFrZSBGb3JnZWpvIEFjdGlvbnMgc2VydmVyIGxvZ3MgbGVzcyBub2lzeQ==-->make Forgejo Actions server logs less noisy.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7968): <!--number 7968 --><!--line 0 --><!--description QWxsb3cgc2VhcmNoaW5nIGlzc3VlcyBieSBudW1iZXIsIHByaW9yaXRpemUgdGl0bGUgbWF0Y2hlcyB3aGVuIHNvcnRlZCBieSByZWxldmFuY2U=-->allow searching issues by number, prioritize title matches when sorted by relevance.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7953): <!--number 7953 --><!--line 0 --><!--description cmVwbGFjZSBnby1ycG11dGlscyBsaWJyYXJ5IHdpdGggb3VyIG93bg==-->replace go-rpmutils library with our own. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#forgejo-build-time-optimization).<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7902): <!--number 7902 --><!--line 0 --><!--description Y29uZmlndXJhYmxlIGRlZmF1bHQgdW5pdHMgZm9yIG1pcnJvcnM=-->configurable default units for mirrors.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7896): <!--number 7896 --><!--line 0 --><!--description ZW5oOiBhbGxvdyBwZXIgcmVwbyBpc3N1ZSByZWluZGV4aW5nIGZvciBhZG1pbnM=-->a repository administrator has control over reindexing the issues.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7803): <!--number 7803 --><!--line 0 --><!--description YXV0byBjbGVhbnVwIG9mIG9mZmxpbmUgcnVubmVycw==-->auto cleanup of offline runners.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7727): <!--number 7727 --><!--line 0 --><!--description aW1wcm92ZWQgcGVyZm9ybWFuY2VzIHdoZW4gY2hlY2tpbmcgZm9yIGNvbmZsaWN0cyBvbiBwdWxsIHJlcXVlc3Rz-->improved performances when checking for conflicts on pull requests. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#faster-conflict-checking).<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7716): <!--number 7716 --><!--line 0 --><!--description YWxsb3cgYWNjZXNzIHRvIGAvYXBpL3YxL3BhY2thZ2VzL3t1c2VybmFtZX1gIHdpdGhvdXQgdG9rZW4=-->allow access to publicly available `/api/v1/packages/{username}` without a token.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7699): <!--number 7699 --><!--line 0 --><!--description aW1wbGVtZW50IGBHRVQgL3JlcG9zL3tvd25lcn0ve3JlcG99L2FjdGlvbnMvcnVuc2AgYW5kIGBHRVQgL3JlcG9zL3tvd25lcn0ve3JlcG99L2FjdGlvbnMvcnVucy97cnVuX2lkfWA=-->implement the `GET /repos/{owner}/{repo}/actions/runs` and `GET /repos/{owner}/{repo}/actions/runs/{run_id}` API endpoints.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7527): <!--number 7527 --><!--line 0 --><!--description dXNlIGdpdC1yZXBsYXkgZm9yIHJlYmFzaW5n-->use git-replay for rebasing for better performances.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7509): <!--number 7509 --><!--line 0 --><!--description c2VuZCBtYWlsIG9uIGZhaWxlZCBvciByZWNvdmVyZWQgRm9yZ2VqbyBBY3Rpb25zIHJ1bg==-->send mail on failed or recovered Forgejo Actions run. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#forgejo-actions-email-notifications-on-failure).<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7508): <!--number 7508 --><!--line 0 --><!--description QWN0aW9ucyBGYWlsdXJlLCBTdWNjZXNzLCBSZWNvdmVyIFdlYmhvb2tz-->Forgejo Actions failure, success, recover webhooks.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7418): <!--number 7418 --><!--line 0 --><!--description YWRkIGBsYXN0X2NvbW1pdF93aGVuYCB0byBjb250ZW50cyByZXNwb25zZQ==-->add `last_commit_when` to API contents responses.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7387): <!--number 7387 --><!--line 0 --><!--description aW5jbHVkZSBhIGRlZmF1bHQgcm9ib3RzLnR4dCB0byByZWR1Y2UgdGhlIGltcGFjdCBvZiBjcmF3bGVycw==-->include a default robots.txt to reduce the impact of crawlers. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#default-robotstxt).<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7212): <!--number 7212 --><!--line 0 --><!--description dXNlIFhPUk0gRW5naW5lR3JvdXAgaW5zdGVhZCBvZiBzaW5nbGUgRW5naW5lIGNvbm5lY3Rpb24=-->use XORM EngineGroup instead of single Engine connection. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#xorm-enginegroup-connections-for-optimized-database-query-routing-and-load-balancing).<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/2364): <!--number 2364 --><!--line 0 --><!--description c3luYyBmb3Jrcw==-->sync forks. [Read more in the v12.0 companion blog post](https://forgejo.org/2025-07-release-v12-0/#keeping-forks-in-sync).<!--description-->
- Bug fixes
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8511) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8516)): <!--number 8516 --><!--line 0 --><!--description Zml4OiBQUiBub3QgYmxvY2tlZCBieSByZXZpZXcgcmVxdWVzdCBmb3IgYSB3aGl0ZWxpc3RlZCB0ZWFt-->pull requests were not blocked by review request for a whitelisted team.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8475) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8480)): <!--number 8480 --><!--line 0 --><!--description c2V2ZXJhbCBmaXhlcyBvZiBBTFQgUGFja2FnZSByZWdpc3RyeQ==-->several fixes of the ALT RPM package registry.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8391) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8459)): <!--number 8459 --><!--line 0 --><!--description QXV0aCBIZWFkZXI6IEFsbG93IGxvd2VyY2FzZSBhcyB3ZWxsIGFzIHVwcGVyY2FzZSB0b2tlbg==-->allow lowercase as well as uppercase token keyword in the auth header.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8450) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8454)): <!--number 8454 --><!--line 0 --><!--description Zml4OiBjb3JyZWN0bHkgbWFyayByZXZpZXdzIGFzIHN0YWxlIGZvciBBR2l0IFBScw==-->correctly mark reviews as stale for AGit pull requests.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8367) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8386)): <!--number 8386 --><!--line 0 --><!--description Zml4OiB1c2VyIGFjdGl2YXRpb24gd2l0aCB1cHBlcmNhc2UgZW1haWwgYWRkcmVzcw==-->user activation failed when an email address contained uppercase letters.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8330) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8349)): <!--number 8349 --><!--line 0 --><!--description Zml4OiBsb2FkIE9sZE1pbGVzdG9uZSBiYXNlZCBvbiBPbGRNaWxlc3RvbmVJRCwgbm90IE1pbGVzdG9uZUlE-->fix: load OldMilestone based on OldMilestoneID, not MilestoneID<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8037): <!--number 8037 --><!--line 0 --><!--description b21pdCBDb250ZW50LUxlbmd0aCBvbiAzMDcgcmVkaXJlY3RzIHdoZW4gc2VydmluZyBkaXJlY3QgbWFuaWZlc3QgZm9yIGNvbnRhaW5lcnM=-->omit Content-Length on 307 redirects when serving direct manifest for containers.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8034): <!--number 8034 --><!--line 0 --><!--description Y2FtZWwgYW5kIHBhc2NhbCBjYXNlIHRyYW5zZm9ybWVy-->fix a bug causing the PASCAL-modifier to return camel-case.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8028): <!--number 8028 --><!--line 0 --><!--description QWRkcyBjb2RlIHRvIHJlbW92ZSB0aGUgdHJhaWxpbmcgc2xhc2ggZnJvbSB0aGUgaXNzdWVyIGluIG9hdXRoIGNsYWltcw==-->remove the trailing slash from the issuer in OAuth claims.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8025): <!--number 8025 --><!--line 0 --><!--description cmV0dXJuIHRoZSBjb3JyZWN0IGFnaXQgdHlwZSBpbiBzc2hfaW5mbw==-->return the correct AGit type in ssh_info.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7932): <!--number 7932 --><!--line 0 --><!--description W2dpdGVhXSBGaXggdXJsIHZhbGlkYXRpb24gaW4gd2ViaG9vayBhZGQvZWRpdCBBUEk=-->fix url validation in the webhook add/edit API.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7930): <!--number 7930 --><!--line 0 --><!--description QWRkIGVycm9yIHJlcG9ydGluZyB0byBQUnMgd2l0aCBpbnZhbGlkIHdvcmtmbG93cw==-->add error reporting to pull requests with invalid Forgejo Actions workflow files.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7644): <!--number 7644 --><!--line 0 --><!--description YWxsb3cgaW5zdGFuY2UgQVBJIFVSTHMgaW4gcmVsZWFzZSBhc3NldHM=-->allow instance API URLs in release assets.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7604): <!--number 7604 --><!--line 0 --><!--description aW1wcm92ZSBkYXNoYm9hcmQgbG9hZGluZyBwZXJmb3JtYW5jZXM=-->improve the dashboard loading performances.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7454): <!--number 7454 --><!--line 0 --><!--description Q2FuY2VsIGEgcmV2aWV3-->fix a border case where it was not possible to cancel a pull request review.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7409): <!--number 7409 --><!--line 0 --><!--description Zml4IGFjbWUgcmVuZXdhbA==-->fix acme renewal.<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/6352): <!--number 6352 --><!--line 0 --><!--description bWlncmF0ZSBNYXZlbiBwYWNrYWdlcyB0byAiZ3JvdXBJZDphcnRpZmFjdElkIiBuYW1lIGNvbmNhdGVuYXRpb24sIHJlZ2VuZXJhdGUgbWV0YWRhdGEgYW5kIGZpeCBtaXNzaW5nIGdyb3VwSWQ=-->migrate Maven packages to "groupId:artifactId" name concatenation, regenerate metadata and fix missing groupId.<!--description-->
- Included for completeness but not worth a release note
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8534): <!--number 8534 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8530) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8532)): <!--number 8532 --><!--line 0 --><!--description Zml4KHBhY2thZ2VzKTogc2tpcCBhbm90aGVyIHN0YWNrIGZyYW1lIGZyb20gbG9nZ2luZw==-->fix(packages): skip another stack frame from logging<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8527) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8528)): <!--number 8528 --><!--line 0 --><!--description Zml4OiBpZ25vcmUgIkNsb3NlIiBlcnJvciB3aGVuIHVwbG9hZGluZyBjb250YWluZXIgYmxvYg==-->fix: ignore "Close" error when uploading container blob<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8524) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8526)): <!--number 8526 --><!--line 0 --><!--description Y2hvcmU6IGZhaWxlZCBhdXRoZW50aWNhdGlvbiBhdHRlbXB0cyBhcmUgbm90IGVycm9ycyBhbmQgYXJlIGRpc3BsYXllZCBhdCB0aGUgbG9nIGluZm8gbGV2ZWw=-->chore: failed authentication attempts are not errors and are displayed at the log info level<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8519) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8523)): <!--number 8523 --><!--line 0 --><!--description Zml4OiBleHBhbmRpbmcgZXhhY3RseSAyMCBsaW5lcyBiZXR3ZWVuIGRpZmYgc2VjdGlvbnMgbGVhdmVzIHZpc3VhbCBhcnRpZmFjdA==-->fix: expanding exactly 20 lines between diff sections leaves visual artifact<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8301) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8518)): <!--number 8518 --><!--line 0 --><!--description Y2hvcmU6IHVzZSBldmVudHVhbGx5IGZvciBteXNxbCBjb2xsYXRpb24gdGVzdA==-->chore: use eventually for mysql collation test<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8492) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8498)): <!--number 8498 --><!--line 0 --><!--description Zml4KGNvZGUtc2VhcmNoKTogSGlnaGxpZ2h0U2VhcmNoUmVzdWx0Q29kZSBzaG91bGQgY291bnQgdGhlIG51bWJlciBvZiBieXRlcyBhbmQgbm90IHRoZSBudW1iZXIgb2YgcnVuZXM=-->fix(code-search): HighlightSearchResultCode should count the number of bytes and not the number of runes<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8464) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8474)): <!--number 8474 --><!--line 0 --><!--description Zml4OiB1c2UgcGFyZW50IGNvbnRleHQgZm9yIG5ldyB0cmFuc2FjdGlvbnM=-->fix: use parent context for new transactions<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8460) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8466)): <!--number 8466 --><!--line 0 --><!--description Y2hvcmU6IGRpc2FibGUgbWlzbWF0Y2hlZCByb290IFVSTCBlMmUgdGVzdCBmb3Igc2FmYXJp-->chore: disable mismatched root URL e2e test for safari<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8461) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8463)): <!--number 8463 --><!--line 0 --><!--description Y2hvcmU6IGRvIG5vdCBuYXZpZ2F0ZSB0byBzYW1lIFVSTCBpbiBFMkUgdGVzdA==-->chore: do not navigate to same URL in E2E test<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8448) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8455)): <!--number 8455 --><!--line 0 --><!--description Zml4KGVtYWlsKTogYWN0aW9ucyBub3RpZmljYXRpb24gdGVtcGxhdGUgY29uZnVzZXMgYnJhbmNoIHdpdGggUFI=-->fix(email): actions notification template confuses branch with PR<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8258) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8439)): <!--number 8439 --><!--line 0 --><!--description Zml4OiBjb3JydXB0ZWQgd2lraSB1bml0IGRlZmF1bHQgcGVybWlzc2lvbiAoIzgyMzQgZm9sbG93LXVwKQ==-->fix: corrupted wiki unit default permission (#8234 follow-up)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8366) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8404)): <!--number 8404 --><!--line 0 --><!--description Zml4OiBjYW5jZWxsZWQgb3Igc2tpcHBlZCBydW5zIGFyZSBub3QgZmFpbHVyZXMgZm9yIG5vdGlmaWNhdGlvbnM=-->fix: cancelled or skipped runs are not failures for notifications<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8400) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8402)): <!--number 8402 --><!--line 0 --><!--description Y2hvcmU6IGltcHJvdmUgcmVsaWFiaWxpdHkgb2Ygd2ViYXV0aG4gZTJlIHRlc3Q=-->chore: improve reliability of webauthn e2e test<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8261) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8398)): <!--number 8398 --><!--line 0 --><!--description Zml4OiBza2lwIGVtcHR5IHRva2VucyBpbiBTZWFyY2hPcHRpb25zLlRva2Vucygp-->fix: skip empty tokens in SearchOptions.Tokens()<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8374) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8390)): <!--number 8390 --><!--line 0 --><!--description Zml4OiBkaXNhYmxlIEZvcmdlam8gQWN0aW9ucyBlbWFpbCBub3RpZmljYXRpb25zIG9uIHJlY292ZXJ5-->fix: disable Forgejo Actions email notifications on recovery<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8326) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8331)): <!--number 8331 --><!--line 0 --><!--description Zml4OiBtYWtlIEFQSSAvcmVwb3Mve293bmVyfS97cmVwb30vY29tcGFyZS97YmFzZWhlYWR9IHdvcmsgd2l0aCBmb3Jrcw==-->fix: make API /repos/{owner}/{repo}/compare/{basehead} work with forks<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8320) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8321)): <!--number 8321 --><!--line 0 --><!--description Y2hvcmU6IHNvcnQgYmxvY2tlZCB1c2VycyBsaXN0IGZvciBkZXRlcm1pc3RpYyByZXN1bHRz-->chore: sort blocked users list for determistic results<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8267) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8319)): <!--number 8319 --><!--line 0 --><!--description Zml4OiBhYnVzZSByZXBvcnRzIHN0cmluZyBkYXRhIHR5cGVz-->fix: abuse reports string data types<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8304) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8318)): <!--number 8318 --><!--line 0 --><!--description Zml4OiBwYXNzIGRvZXIncyBJRCBmb3IgQ1JVRCBpbnN0YW5jZSBzaWduaW5n-->fix: pass doer's ID for CRUD instance signing<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8002) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8290)): <!--number 8290 --><!--line 0 --><!--description Zml4KHVpKTogcmVsZWFzZTogbmFtZSBpcyBvdmVycmlkZGVuIHdpdGggdGFnIG5hbWUgb24gZWRpdA==-->fix(ui): release: name is overridden with tag name on edit<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8286) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8288)): <!--number 8288 --><!--line 0 --><!--description UmV2ZXJ0ICJmaXgoYXBpKTogZG9jdW1lbnQgYGlzX3N5c3RlbV93ZWJob29rYCBmaWVsZCAoIzc3ODQpIg==-->Revert "fix(api): document `is_system_webhook` field (#7784)"<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8271) ([backported](https://codeberg.org/forgejo/forgejo/pulls/8277)): <!--number 8277 --><!--line 0 --><!--description Q0kgZGVidWc6IHRlc3RTbGVlcDogc2hvdyBhY3R1YWwgdGltZXMgb24gZmFpbHVyZXM=-->CI debug: testSleep: show actual times on failures<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8268): <!--number 8268 --><!--line 0 --><!--description Y2hvcmU6IHVwZGF0ZSBzZWN1cml0eSBvcHRpb24gaW4gaXNzdWUgdGVtcGxhdGVz-->chore: update security option in issue templates<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8260): <!--number 8260 --><!--line 0 --><!--description VG9rZW4uUGFyc2VJc3N1ZVJlZmVyZW5jZSBjcmFzaGluZyBvbiBlbXB0eSBzdHJpbmc=-->Token.ParseIssueReference crashing on empty string<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8256): <!--number 8256 --><!--line 0 --><!--description VXBkYXRlIHJlbm92YXRlIHRvIHY0MS4xLjQgKGZvcmdlam8p-->Update renovate to v41.1.4 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8253): <!--number 8253 --><!--line 0 --><!--description VXBkYXRlIHJlbm92YXRlIHRvIHY0MSAoZm9yZ2VqbykgKG1ham9yKQ==-->Update renovate to v41 (forgejo) (major)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8252): <!--number 8252 --><!--line 0 --><!--description YWRkIGFuIGluZGV4IHRvIHRoZSBBY3Rpb25SdW4uc3RvcHBlZCBjb2x1bW4=-->add an index to the ActionRun.stopped column<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8250): <!--number 8250 --><!--line 0 --><!--description YnVnOiB1bmlmeSBSZXBvQWN0aW9uUnVuIGFuZCBBY3Rpb25SdW4gc3RydWN0cw==-->bug: unify RepoActionRun and ActionRun structs<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8248): <!--number 8248 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2dvLWNoaS9jaGkvdjUgdG8gdjUuMi4yIChmb3JnZWpvKQ==-->Update module github.com/go-chi/chi/v5 to v5.2.2 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8245): <!--number 8245 --><!--line 0 --><!--description RG93bmdyYWRlIHBsYXl3cmlnaHQgdGVtcG9yYXJpbHkgYW5kIGFsbG93IHJ1bm5pbmcgYWxsIGUyZSB0ZXN0cw==-->Downgrade playwright temporarily and allow running all e2e tests<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8243): <!--number 8243 --><!--line 0 --><!--description Z2l0L2Jsb2IgdXNlIE5ld1RydW5jYXRlZFJlYWRlciBmb3IgcHJvZmlsZSBhbmQgY29kZW93bmVycw==-->git/blob use NewTruncatedReader for profile and codeowners<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8242): <!--number 8242 --><!--line 0 --><!--description YnVnOiBGb3JnZWpvIEFjdGlvbnMgZW1haWwgbm90aWZpY2F0aW9ucyBhcmUgb3B0LWlu-->bug: Forgejo Actions email notifications are opt-in<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8236): <!--number 8236 --><!--line 0 --><!--description cHJldmVudCA1MDAgbWVzc2FnZSBvbiBpbnZhbGlkIHVzZXJuYW1l-->prevent 500 message on invalid username<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8227): <!--number 8227 --><!--line 0 --><!--description b25seSBzZW5kIEZvcmdlam8gQWN0aW9ucyBub3RpZmljYXRpb25zIHRvIG9uZSB1c2Vy-->only send Forgejo Actions notifications to one user<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8226): <!--number 8226 --><!--line 0 --><!--description Y2hvcmU6IHNvcnQgbWFpbGVyIG1lc3NhZ2VzIGluIHRlc3QgYXNzZXJ0aW9u-->chore: sort mailer messages in test assertion<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8224): <!--number 8224 --><!--line 0 --><!--description Y2hvcmUocmVsZWFzZS1ub3Rlcyk6IEZvcmdlam8gdjExLjAuMg==-->chore(release-notes): Forgejo v11.0.2<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8223): <!--number 8223 --><!--line 0 --><!--description YmxvYjogR2V0QmxvYkNvbnRlbnQ6IHJlZHVjZSBhbGxvY2F0aW9ucw==-->blob: GetBlobContent: reduce allocations<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8220): <!--number 8220 --><!--line 0 --><!--description Zml4KHRlc3RzKTogVGVzdEluaXRJbnN0cnVjdGlvbnMgbXVzdCB1c2UgZm9yRWFjaE9iamVjdEZvcm1hdA==-->fix(tests): TestInitInstructions must use forEachObjectFormat<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8219): <!--number 8219 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBjb2RlLmZvcmdlam8ub3JnL2Zvcmdlam8vYWN0IHRvIHYxLjI4LjAgKGZvcmdlam8p-->Update module code.forgejo.org/forgejo/act to v1.28.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8218): <!--number 8218 --><!--line 0 --><!--description VXBkYXRlIGRhdGEuZm9yZ2Vqby5vcmcvb2NpL2FscGluZSBEb2NrZXIgdGFnIHRvIHYzLjIyIChmb3JnZWpvKQ==-->Update data.forgejo.org/oci/alpine Docker tag to v3.22 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8217): <!--number 8217 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL21pbmlvL21pbmlvLWdvL3Y3IHRvIHY3LjAuOTQgKGZvcmdlam8p-->Update module github.com/minio/minio-go/v7 to v7.0.94 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8216): <!--number 8216 --><!--line 0 --><!--description Y2hvcmU6IG1pZ3JhdGUgdG8gYEBzdHlsaXN0aWMvZXNsaW50LXBsdWdpbmA=-->chore: migrate to `@stylistic/eslint-plugin`<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8215): <!--number 8215 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgZXNsaW50LXBsdWdpbi13YyB0byB2MyAoZm9yZ2Vqbyk=-->Update dependency eslint-plugin-wc to v3 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8214): <!--number 8214 --><!--line 0 --><!--description Zml4KHVpKTogaXNzdWUgY29tbWVudCBhbmNob3Igb24gdGltZSBzdGFtcA==-->fix(ui): issue comment anchor on time stamp<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8209): <!--number 8209 --><!--line 0 --><!--description Z2l0X21vZGVsLkNvbW1pdFN0YXR1c2VzSGlkZUFjdGlvbnNVUkwgaXMgb2Jzb2xldGU=-->git_model.CommitStatusesHideActionsURL is obsolete<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8207): <!--number 8207 --><!--line 0 --><!--description UmVtb3ZlIDFtcyBkZWxheSBiZWZvcmUgaW5zZXJ0aW5nIGxpc3QgcHJlZml4LCBmaXggcmFjZSBjb25kaXRpb24gaW4gdGVzdHM=-->Remove 1ms delay before inserting list prefix, fix race condition in tests<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8205): <!--number 8205 --><!--line 0 --><!--description Y2hvcmU6IHJlbW92ZSBnb3BscyBpbiBNYWtlZmlsZQ==-->chore: remove gopls in Makefile<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8202): <!--number 8202 --><!--line 0 --><!--description ZG8gbm90IGNoZWNrIGZvciBgb2JqZWN0X2Zvcm1hdF9uYW1lYCBmaWVsZA==-->do not check for `object_format_name` field<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8199): <!--number 8199 --><!--line 0 --><!--description ZmVhdCh1aSk6IHVzZSBrYmQgaW4gbGFiZWwgc2VsZWN0b3IgaGludCwgcmVtb3ZlIGVudGVy-->feat(ui): use kbd in label selector hint, remove enter<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8198): <!--number 8198 --><!--line 0 --><!--description W2dpdGVhXSB3ZWVrIDIwMjUtMjIgY2hlcnJ5IHBpY2sgKGdpdGVhL21haW4gLT4gZm9yZ2Vqbyk=-->[gitea] week 2025-22 cherry pick (gitea/main -> forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8197): <!--number 8197 --><!--line 0 --><!--description Rml4IHNlbnRlbmNlIHN0cnVjdHVyZSBtZW50aW9uaW5nIGNvb2xkb3duIHBlcmlvZA==-->Fix sentence structure mentioning cooldown period<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8195): <!--number 8195 --><!--line 0 --><!--description TG9jayBmaWxlIG1haW50ZW5hbmNlIChmb3JnZWpvKQ==-->Lock file maintenance (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8194): <!--number 8194 --><!--line 0 --><!--description VXBkYXRlIHJlbm92YXRlIHRvIHY0MC41Ny4xIChmb3JnZWpvKQ==-->Update renovate to v40.57.1 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8190): <!--number 8190 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgY2hhcnQuanMgdG8gdjQuNS4wIChmb3JnZWpvKQ==-->Update dependency chart.js to v4.5.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8188): <!--number 8188 --><!--line 0 --><!--description bWFrZSB0ZXN0IHN1aXRlIHJ1biBvbiBvbGRlciBnaXQgdmVyc2lvbg==-->make test suite run on older git version<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8186): <!--number 8186 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2dvLXNxbC1kcml2ZXIvbXlzcWwgdG8gdjEuOS4zIChmb3JnZWpvKQ==-->Update module github.com/go-sql-driver/mysql to v1.9.3 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8183): <!--number 8183 --><!--line 0 --><!--description VXBkYXRlIGVudmlyb25tZW50LXRvLWluaSBSRUFETUU=-->Update environment-to-ini README<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8181): <!--number 8181 --><!--line 0 --><!--description VXBkYXRlIGh0dHBzOi8vZGF0YS5mb3JnZWpvLm9yZy9mb3JnZWpvL2Zvcmdlam8tYnVpbGQtcHVibGlzaCBhY3Rpb24gdG8gdjUuMy41IChmb3JnZWpvKQ==-->Update https://data.forgejo.org/forgejo/forgejo-build-publish action to v5.3.5 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8176): <!--number 8176 --><!--line 0 --><!--description RG9ja2VyZmlsZSBzaG91bGQgcmUtdXNlIGJpbmRhdGEgZmlsZXMgd2hlbiBwb3NzaWJsZQ==-->Dockerfile should re-use bindata files when possible<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8174): <!--number 8174 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgbWluaW1hdGNoIHRvIHYxMC4wLjMgKGZvcmdlam8p-->Update dependency minimatch to v10.0.3 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8172): <!--number 8172 --><!--line 0 --><!--description dXNlIHpzdGQuV2l0aExvd2VyRW5jb2Rlck1lbSBmb3IgZ2VuZXJhdGUtYmluZGF0YQ==-->use zstd.WithLowerEncoderMem for generate-bindata<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8168): <!--number 8168 --><!--line 0 --><!--description ZG8gbm90IG1peCB1cmZhdmUgdjIgd2l0aCB1cmZhdmUgdjM=-->do not mix urfave v2 with urfave v3<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8166): <!--number 8166 --><!--line 0 --><!--description Y2hvcmUoY2kpOiBza2lwIHRlc3RzIGlmIFRFU1Rfe01JTklPX0VORFBPSU5ULEVMQVNUSUNTRUFSQ0hfVVJMfSBpcyBub3Qgc2V0-->chore(ci): skip tests if TEST_{MINIO_ENDPOINT,ELASTICSEARCH_URL} is not set<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8163): <!--number 8163 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRsYWIuY29tL2dpdGxhYi1vcmcvYXBpL2NsaWVudC1nbyB0byB2MC4xMzAuMSAoZm9yZ2Vqbyk=-->Update module gitlab.com/gitlab-org/api/client-go to v0.130.1 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8162): <!--number 8162 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL21pbmlvL21pbmlvLWdvL3Y3IHRvIHY3LjAuOTMgKGZvcmdlam8p-->Update module github.com/minio/minio-go/v7 to v7.0.93 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8160): <!--number 8160 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgcG9zdGNzcyB0byB2OC41LjUgKGZvcmdlam8p-->Update dependency postcss to v8.5.5 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8159): <!--number 8159 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgbWluaW1hdGNoIHRvIHYxMC4wLjIgKGZvcmdlam8p-->Update dependency minimatch to v10.0.2 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8157): <!--number 8157 --><!--line 0 --><!--description Y2hvcmUoY2kpOiB1c2UgY29kZS5mb3JnZWpvLm9yZy9mb3JnZWpvL21pZ3JhdGlvbi10ZXN0IGZvciBtaWdyYXRpb24gdGVzdHM=-->chore(ci): use code.forgejo.org/forgejo/migration-test for migration tests<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8150): <!--number 8150 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgaGFwcHktZG9tIHRvIHYxOCAoZm9yZ2Vqbyk=-->Update dependency happy-dom to v18 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8149): <!--number 8149 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgdHlwZXNjcmlwdC1lc2xpbnQgdG8gdjguMzQuMCAoZm9yZ2Vqbyk=-->Update dependency typescript-eslint to v8.34.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8148): <!--number 8148 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgQHBsYXl3cmlnaHQvdGVzdCB0byB2MS41My4wIChmb3JnZWpvKQ==-->Update dependency @playwright/test to v1.53.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8147): <!--number 8147 --><!--line 0 --><!--description VXBkYXRlIGdoY3IuaW8vZGV2Y29udGFpbmVycy9mZWF0dXJlcy9naXQtbGZzIERvY2tlciB0YWcgdG8gdjEuMi40IChmb3JnZWpvKQ==-->Update ghcr.io/devcontainers/features/git-lfs Docker tag to v1.2.4 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8146): <!--number 8146 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgcG9zdGNzcy1uZXN0aW5nIHRvIHYxMy4wLjIgKGZvcmdlam8p-->Update dependency postcss-nesting to v13.0.2 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8145): <!--number 8145 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgQHZpdGVzdC9lc2xpbnQtcGx1Z2luIHRvIHYxLjIuMiAoZm9yZ2Vqbyk=-->Update dependency @vitest/eslint-plugin to v1.2.2 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8144): <!--number 8144 --><!--line 0 --><!--description Y2hvcmU6IGZpeCB0ZXN0cyBmb3Igb2xkIGdpdCB2ZXJzaW9ucw==-->chore: fix tests for old git versions<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8140): <!--number 8140 --><!--line 0 --><!--description Y2hvcmUoY2kpOiBydW4gYWRkaXRpb25hbCB0ZXN0cyBpbiBpbnRlZ3JhdGlvbg==-->chore(ci): run additional tests in integration<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8137): <!--number 8137 --><!--line 0 --><!--description UmVtb3ZlIHNoZWJhbmcgZnJvbSBiYXNoIGF1dG9jb21wbGV0aW9u-->Remove shebang from bash autocompletion<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8134): <!--number 8134 --><!--line 0 --><!--description TWFrZSByZWxhdGl2ZS10aW1lIGEgc2VsZi1tYWludGFpbmluZyBjdXN0b20gZWxlbWVudA==-->Make relative-time a self-maintaining custom element<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8129): <!--number 8129 --><!--line 0 --><!--description Y3JlYXRlIHNoZWxsLm5peDsgdXBkYXRlIGZsYWtlLio=-->create shell.nix; update flake.*<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8128): <!--number 8128 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBjb25uZWN0cnBjLmNvbS9jb25uZWN0IHRvIHYxLjE4LjEgKGZvcmdlam8p-->Update module connectrpc.com/connect to v1.18.1 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8127): <!--number 8127 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgbWFya2Rvd25saW50LWNsaSB0byB2MC40NS4wIChmb3JnZWpvKQ==-->Update dependency markdownlint-cli to v0.45.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8126): <!--number 8126 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgZXNsaW50LXBsdWdpbi1yZWdleHAgdG8gdjIuOS4wIChmb3JnZWpvKQ==-->Update dependency eslint-plugin-regexp to v2.9.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8125): <!--number 8125 --><!--line 0 --><!--description VXBkYXRlIHZpdGVzdCBtb25vcmVwbyB0byB2My4yLjMgKGZvcmdlam8p-->Update vitest monorepo to v3.2.3 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8118): <!--number 8118 --><!--line 0 --><!--description TG9jayBmaWxlIG1haW50ZW5hbmNlIChmb3JnZWpvKQ==-->Lock file maintenance (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8117): <!--number 8117 --><!--line 0 --><!--description VXBkYXRlIHJlbm92YXRlIHRvIHY0MC40OC40IChmb3JnZWpvKQ==-->Update renovate to v40.48.4 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8112): <!--number 8112 --><!--line 0 --><!--description cmVtb3ZlIGRvd25sb2FkIGF0dHJpYnV0ZSBmcm9tIGV4dGVybmFsIGFzc2V0cw==-->remove download attribute from external assets<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8108): <!--number 8108 --><!--line 0 --><!--description YWx3YXlzIHJlbmRlciBkZXRhaWxlZCB0ZWFtIHBlcm1pc3Npb25zIHRhYmxlIGluIHNpZGViYXI=-->always render detailed team permissions table in sidebar<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8103): <!--number 8103 --><!--line 0 --><!--description VXBkYXRlIHgvdG9vbHMgdG8gdjAuMzQuMCAoZm9yZ2Vqbyk=-->Update x/tools to v0.34.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8102): <!--number 8102 --><!--line 0 --><!--description VXBkYXRlIHZpdGVzdCBtb25vcmVwbyB0byB2My4yLjIgKGZvcmdlam8p-->Update vitest monorepo to v3.2.2 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8101): <!--number 8101 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2VkaXRvcmNvbmZpZy1jaGVja2VyL2VkaXRvcmNvbmZpZy1jaGVja2VyL3YzL2NtZC9lZGl0b3Jjb25maWctY2hlY2tlciB0byB2My4zLjAgKGZvcmdlam8p-->Update module github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker to v3.3.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8099): <!--number 8099 --><!--line 0 --><!--description VXBkYXRlIGxpbnRlcnMgKGZvcmdlam8p-->Update linters (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8098): <!--number 8098 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgaGFwcHktZG9tIHRvIHYxNy42LjMgKGZvcmdlam8p-->Update dependency happy-dom to v17.6.3 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8097): <!--number 8097 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgQHZpdGVqcy9wbHVnaW4tdnVlIHRvIHY1LjIuNCAoZm9yZ2Vqbyk=-->Update dependency @vitejs/plugin-vue to v5.2.4 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8096): <!--number 8096 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgQGF4ZS1jb3JlL3BsYXl3cmlnaHQgdG8gdjQuMTAuMiAoZm9yZ2Vqbyk=-->Update dependency @axe-core/playwright to v4.10.2 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8094): <!--number 8094 --><!--line 0 --><!--description c2hvdyBtZW1iZXJzaGlwIG9mIGxpbWl0ZWQgb3Jncw==-->show membership of limited orgs<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8091): <!--number 8091 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnb2xhbmcub3JnL3gvY3J5cHRvIHRvIHYwLjM5LjAgKGZvcmdlam8p-->Update module golang.org/x/crypto to v0.39.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8090): <!--number 8090 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL3NlcmdpL2dvLWRpZmYgdG8gdjEuNC4wIChmb3JnZWpvKQ==-->Update module github.com/sergi/go-diff to v1.4.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8086): <!--number 8086 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgZ28gdG8gdjEuMjQuNCAoZm9yZ2Vqbyk=-->Update dependency go to v1.24.4 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8078): <!--number 8078 --><!--line 0 --><!--description RmVkZXJhdGVkIHVzZXIgYWN0aXZpdHkgZm9sbG93aW5nOiBJc29sYXRlZCBtb2RlbCBjaGFuZ2Vz-->Federated user activity following: Isolated model changes<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8066): <!--number 8066 --><!--line 0 --><!--description cmVuYW1lIGFwaS57TGlzdCx9QWN0aW9uUnVuIHRvIGFwaS57TGlzdCx9UmVwb0FjdGlvblJ1bg==-->rename api.{List,}ActionRun to api.{List,}RepoActionRun<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8065): <!--number 8065 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnby51YmVyLm9yZy9tb2NrL21vY2tnZW4gdG8gdjAuNS4yIChmb3JnZWpvKQ==-->Update module go.uber.org/mock/mockgen to v0.5.2 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8064): <!--number 8064 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2JsZXZlc2VhcmNoL2JsZXZlL3YyIHRvIHYyLjUuMiAoZm9yZ2Vqbyk=-->Update module github.com/blevesearch/bleve/v2 to v2.5.2 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8063): <!--number 8063 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgdnVlIHRvIHYzLjUuMTYgKGZvcmdlam8p-->Update dependency vue to v3.5.16 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8062): <!--number 8062 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgcG9zdGNzcyB0byB2OC41LjQgKGZvcmdlam8p-->Update dependency postcss to v8.5.4 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8061): <!--number 8061 --><!--line 0 --><!--description Y2hvcmU6IGV4dHJhY3QgY29tbWl0IGhlYWRlciB0ZW1wbGF0ZQ==-->chore: extract commit header template<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8058): <!--number 8058 --><!--line 0 --><!--description Y2hvcmU6IGRyb3AgdW51c2VkIGBtaXNzcGVsbGA=-->chore: drop unused `misspell`<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8056): <!--number 8056 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBjb2RlLmZvcmdlam8ub3JnL2YzL2dvZjMvdjMgdG8gdjMuMTEuMCAoZm9yZ2Vqbyk=-->Update module code.forgejo.org/f3/gof3/v3 to v3.11.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8055): <!--number 8055 --><!--line 0 --><!--description Y2hvcmUocmVub3ZhdGUpOiBkaXNhYmxlIGluZGlyZWN0IGRpZ2VzdCB1cGRhdGVzIGZvciBzdGFibGU=-->chore(renovate): disable indirect digest updates for stable<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8054): <!--number 8054 --><!--line 0 --><!--description Y2hvcmU6IGRyb3AgdW51c2VkIGBAdHlwZXNjcmlwdC1lc2xpbnQvcGFyc2VyYCBwYWNrYWdl-->chore: drop unused `@typescript-eslint/parser` package<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8052): <!--number 8052 --><!--line 0 --><!--description VXBkYXRlIGxpbnRlcnMgKGZvcmdlam8p-->Update linters (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8051): <!--number 8051 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgaGFwcHktZG9tIHRvIHYxNy42LjEgKGZvcmdlam8p-->Update dependency happy-dom to v17.6.1 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8050): <!--number 8050 --><!--line 0 --><!--description Y2hvcmUocmVub3ZhdGUpOiBidW1wIHRvIHY0MC40MC4wIGFuZCBkaXNhYmxlIHY3-->chore(renovate): bump to v40.40.0 and disable v7<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8049): <!--number 8049 --><!--line 0 --><!--description Q0xJIGlzIGZvcmdlam8gbm90IEZvcmdlam8=-->CLI is forgejo not Forgejo<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8044): <!--number 8044 --><!--line 0 --><!--description YWRkIG1pc3NpbmcgYm90dG9tIG1hcmdpbiBmb3IgdmVyaWZpY2F0aW9uLWJ1dHRvbiBpbiByZWxlYXNlIHZpZXc=-->add missing bottom margin for verification-button in release view<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8041): <!--number 8041 --><!--line 0 --><!--description W2dpdGVhXSBBbHdheXMgdXNlIGFuIGVtcHR5IGxpbmUgdG8gc2VwYXJhdGUgdGhlIGNvbW1pdCBtZXNzYWdlIGFuZCB0cmFpbGVy-->[gitea] Always use an empty line to separate the commit message and trailer<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8040): <!--number 8040 --><!--line 0 --><!--description W2dpdGVhXSB3ZWVrIDIwMjUtMjEgY2hlcnJ5IHBpY2sgKGdpdGVhL21haW4gLT4gZm9yZ2Vqbyk=-->[gitea] week 2025-21 cherry pick (gitea/main -> forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8038): <!--number 8038 --><!--line 0 --><!--description QWRkIGEgR05VIEd1aXggbWFuaWZlc3Q=-->Add a GNU Guix manifest<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8021): <!--number 8021 --><!--line 0 --><!--description Y2hvcmUoY2xlYW51cCk6IHN1cHByZXNzIG5vbiBhY3Rpb25hYmxlIFhPUk0gd2FybmluZ3M=-->chore(cleanup): suppress non actionable XORM warnings<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8019): <!--number 8019 --><!--line 0 --><!--description bWlncmF0ZSByZXBvc2l0b3J5LnRvcGljcyBjb2x1bW4gZm9yIFNRTGl0ZQ==-->migrate repository.topics column for SQLite<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7994): <!--number 7994 --><!--line 0 --><!--description Y2hvcmUoY2xlYW51cCk6IHJlcGxhY2VzIHVubmVjZXNzYXJ5IGNhbGxzIHRvIGZvcm1hdHRpbmcgZnVuY3Rpb25zIGJ5IG5vbi1mb3JtYXR0aW5nIGVxdWl2YWxlbnRz-->chore(cleanup): replaces unnecessary calls to formatting functions by non-formatting equivalents<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7987): <!--number 7987 --><!--line 0 --><!--description YWdncmVnYXRlIGRlbGV0ZWQgdGVhbSBhcyBnaG9zdCB0ZWFt-->aggregate deleted team as ghost team<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7981): <!--number 7981 --><!--line 0 --><!--description YWRkIHZhbGlkYXRpbmcgdXNlciBwYXNzd29yZCBhcyB0cmFjZSByZWdpb24=-->add validating user password as trace region<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7978): <!--number 7978 --><!--line 0 --><!--description Y2hvcmUoY2xlYW51cCk6IGZpeCBhbmQgc2ltcGxpZnkgQVBJIGNvbXBhcmlzb24gaGVscGVy-->chore(cleanup): fix and simplify API comparison helper<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7975): <!--number 7975 --><!--line 0 --><!--description Zml4KGkxOG4pOiB1c2UgY29ycmVjdCBiYXNlIGNhcGl0YWxpemF0aW9uIHN0eWxl-->fix(i18n): use correct base capitalization style<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7961): <!--number 7961 --><!--line 0 --><!--description TG9jayBmaWxlIG1haW50ZW5hbmNlIChmb3JnZWpvKQ==-->Lock file maintenance (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7959): <!--number 7959 --><!--line 0 --><!--description VXBkYXRlIHJlbm92YXRlIHRvIHY0MC4zMS4wIChmb3JnZWpvKQ==-->Update renovate to v40.31.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7954): <!--number 7954 --><!--line 0 --><!--description VXBkYXRlIGZvcmdlam8gZ28tY2hpIHBhY2thZ2VzIChmb3JnZWpvKQ==-->Update forgejo go-chi packages (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7952): <!--number 7952 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL1Byb3Rvbk1haWwvZ28tY3J5cHRvIHRvIHYxLjMuMCAoZm9yZ2Vqbyk=-->Update module github.com/ProtonMail/go-crypto to v1.3.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7950): <!--number 7950 --><!--line 0 --><!--description Zml4KHVpKTogcmVsYXRpdmUgdGltZSBlbGVtZW50cyB3ZXJlIHJlc2V0IG9uIGh0bXggc3dhcA==-->fix(ui): relative time elements were reset on htmx swap<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7945): <!--number 7945 --><!--line 0 --><!--description YG92ZXJmbG93LXdyYXBgIHN0cmF0ZWd5IGluIGAubWFya3VwYCBDU1MgY2xhc3M=-->`overflow-wrap` strategy in `.markup` CSS class<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7944): <!--number 7944 --><!--line 0 --><!--description Zml4KHVpKTogY2hhbmdlIGVzY2FwaW5nIGJ1dHRvbiBiZyBvbiBzZWxlY3RlZCBsaW5lcw==-->fix(ui): change escaping button bg on selected lines<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7933): <!--number 7933 --><!--line 0 --><!--description ZG8gbm90IHVuY29uZGl0aW9uYWxseSBhcHBlbmQgJChHSVRFQV9DT01QQVRJQklMSVRZKSBpbiB2ZXJzaW9u-->do not unconditionally append $(GITEA_COMPATIBILITY) in version<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7925): <!--number 7925 --><!--line 0 --><!--description Zml4KHVpKTogY2VudGVyIGZvb3RlciBsaW5rcw==-->fix(ui): center footer links<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7922): <!--number 7922 --><!--line 0 --><!--description VXBkYXRlIGdvLW9wZW5hcGkgcGFja2FnZXMgKGZvcmdlam8p-->Update go-openapi packages (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7920): <!--number 7920 --><!--line 0 --><!--description dW5pZnkgcmVwb3NpdG9yeSB0b3BpY3MgZmllbGQgYnkgcmVwbGFjaW5nIEpTT04gbnVsbCB3aXRoIGVtcHR5IGFycmF5-->unify repository topics field by replacing JSON null with empty array<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7919): <!--number 7919 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgd2VicGFjayB0byB2NS45OS45IChmb3JnZWpvKQ==-->Update dependency webpack to v5.99.9 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7918): <!--number 7918 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgc2hhcnAgdG8gdjAuMzQuMiAoZm9yZ2Vqbyk=-->Update dependency sharp to v0.34.2 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7917): <!--number 7917 --><!--line 0 --><!--description Y2hvcmU6IFFvTCBpbXByb3ZlbWVudHMgdG8gdGVzdHM=-->chore: QoL improvements to tests<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7914): <!--number 7914 --><!--line 0 --><!--description Y2hvcmUocmVub3ZhdGUpOiBkaXNhYmxlIGluZGlyZWN0IG1ham9yIHVwZGF0ZXMgZm9yIHN0YWJsZSBicmFuY2hlcw==-->chore(renovate): disable indirect major updates for stable branches<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7910): <!--number 7910 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgY2xpcHBpZSB0byB2NC4xLjcgKGZvcmdlam8p-->Update dependency clippie to v4.1.7 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7908): <!--number 7908 --><!--line 0 --><!--description TG9jayBmaWxlIG1haW50ZW5hbmNlIChmb3JnZWpvKQ==-->Lock file maintenance (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7907): <!--number 7907 --><!--line 0 --><!--description VXBkYXRlIHJlbm92YXRlIHRvIHY0MC4yNi4wIChmb3JnZWpvKQ==-->Update renovate to v40.26.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7901): <!--number 7901 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL3l1aW4vZ29sZG1hcmsgdG8gdjEuNy4xMiAoZm9yZ2Vqbyk=-->Update module github.com/yuin/goldmark to v1.7.12 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7898): <!--number 7898 --><!--line 0 --><!--description Y2hvcmUodWkpOiBjbGVhbnVwIHVudXNlZCBjb2xvciBDU1M=-->chore(ui): cleanup unused color CSS<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7894): <!--number 7894 --><!--line 0 --><!--description Zml4KHVpKTogZml4IGZvcmNlLXB1c2ggY29tcGFyZSBsaW5lIGxheW91dA==-->fix(ui): fix force-push compare line layout<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7890): <!--number 7890 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2FsZWN0aG9tYXMvY2hyb21hL3YyIHRvIHYyLjE4LjAgKGZvcmdlam8p-->Update module github.com/alecthomas/chroma/v2 to v2.18.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7884): <!--number 7884 --><!--line 0 --><!--description cGFyc2UgYGNoYW5nZS1pZGAgaW4gdGhlIGdpdCBjb21taXQgaGVhZGVy-->parse `change-id` in the git commit header<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7881): <!--number 7881 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2JsZXZlc2VhcmNoL2JsZXZlL3YyIHRvIHYyLjUuMSAoZm9yZ2Vqbyk=-->Update module github.com/blevesearch/bleve/v2 to v2.5.1 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7879): <!--number 7879 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgdnVlIHRvIHYzLjUuMTQgKGZvcmdlam8p-->Update dependency vue to v3.5.14 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7877): <!--number 7877 --><!--line 0 --><!--description TWlncmF0ZSByZW5vdmF0ZSBjb25maWc=-->Migrate renovate config<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7872): <!--number 7872 --><!--line 0 --><!--description Zml4KHVpKTogZGlzYWJsZSBhdXRvY2FwaXRhbGl6YXRpb24vYXV0b2NvcnJlY3QgZm9yIHVzZXJuYW1lIGlucHV0cw==-->fix(ui): disable autocapitalization/autocorrect for username inputs<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7868): <!--number 7868 --><!--line 0 --><!--description Zml4KHVpKTogZGlzYWJsZSBzcGVsbGNoZWNrIG9uIFRPVFAgZm9ybSBmaWVsZHM=-->fix(ui): disable spellcheck on TOTP form fields<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7866): <!--number 7866 --><!--line 0 --><!--description Y2hvcmU6IHJlbW92ZSB1bnVzZWQgdXBkYXRlLWxvY2FsZXMuc2g=-->chore: remove unused update-locales.sh<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7857): <!--number 7857 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL21zdGVpbmVydC9wYW0vdjIgdG8gdjIuMS4wIChmb3JnZWpvKQ==-->Update module github.com/msteinert/pam/v2 to v2.1.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7856): <!--number 7856 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBjb2RlLmZvcmdlam8ub3JnL2Zvcmdlam8vYWN0IHRvIHYxLjI2LjAgKGZvcmdlam8p-->Update module code.forgejo.org/forgejo/act to v1.26.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7852): <!--number 7852 --><!--line 0 --><!--description Rml4IFRlc3RTU0hQdXNoTWlycm9yL05vcm1hbC9DaGVja19taXJyb3JlZF9jb250ZW50IHRlc3Q=-->Fix TestSSHPushMirror/Normal/Check_mirrored_content test<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7849): <!--number 7849 --><!--line 0 --><!--description VXBkYXRlIGdpdGh1Yi5jb20vZ29sYW5nLWp3dC9qd3QvdjQgKGluZGlyZWN0KSB0byB2NC41LjIgW1NFQ1VSSVRZXSAoZm9yZ2Vqbyk=-->Update github.com/golang-jwt/jwt/v4 (indirect) to v4.5.2 [SECURITY] (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7848): <!--number 7848 --><!--line 0 --><!--description Y2kocmVub3ZhdGUpOiBvbmx5IGZhaWwgb24gZXJyb3I=-->ci(renovate): only fail on error<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7847): <!--number 7847 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL25pa2xhc2Zhc2NoaW5nL2dvLW9yZyB0byB2MS44LjAgKGZvcmdlam8p-->Update module github.com/niklasfasching/go-org to v1.8.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7840): <!--number 7840 --><!--line 0 --><!--description TG9jayBmaWxlIG1haW50ZW5hbmNlIChmb3JnZWpvKQ==-->Lock file maintenance (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7835): <!--number 7835 --><!--line 0 --><!--description cmVtb3ZlIHJlZHVuZGFudCBwZXJtaXNzaW9uIGNoZWNrIGluIFJlbW92ZUxhYmVs-->remove redundant permission check in RemoveLabel<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7833): <!--number 7833 --><!--line 0 --><!--description dGVzdCh1aS1lMmUpOiBmaXggZmxha3kgcmVwbyB3aWtpIHRlc3Q=-->test(ui-e2e): fix flaky repo wiki test<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7830): <!--number 7830 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2dvLXdlYmF1dGhuL3dlYmF1dGhuIHRvIHYwLjEzLjAgKGZvcmdlam8p-->Update module github.com/go-webauthn/webauthn to v0.13.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7822): <!--number 7822 --><!--line 0 --><!--description Y2hvcmUodWkpOiBjbGVhbiB1cCBoYXNoYm94IENTUywgc21hbGwgZGVzaWduIGNoYW5nZXM=-->chore(ui): clean up hashbox CSS, small design changes<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7817): <!--number 7817 --><!--line 0 --><!--description cmVwbGFjZSDDnyB3aXRoIHNzIGluIG5vcm1hbGl6ZVVzZXJOYW1l-->replace ß with ss in normalizeUserName<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7814): <!--number 7814 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgZ2xvYmFscyB0byB2MTYuMS4wIChmb3JnZWpvKQ==-->Update dependency globals to v16.1.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7809): <!--number 7809 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnb2xhbmcub3JnL3gvb2F1dGgyIHRvIHYwLjMwLjAgKGZvcmdlam8p-->Update module golang.org/x/oauth2 to v0.30.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7807): <!--number 7807 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgZ28gdG8gdjEuMjQuMyAoZm9yZ2Vqbyk=-->Update dependency go to v1.24.3 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7799): <!--number 7799 --><!--line 0 --><!--description YWRkIG1pc3NpbmcgbG9hZGJhbGFuY2luZyBwb2xpY2llcyBmb3IgRW5naW5lR3JvdXAgY29ubmVjdGlvbnM=-->add missing loadbalancing policies for EngineGroup connections<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7798): <!--number 7798 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnb2xhbmcub3JnL3gvbmV0IHRvIHYwLjQwLjAgKGZvcmdlam8p-->Update module golang.org/x/net to v0.40.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7797): <!--number 7797 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnb2xhbmcub3JnL3gvaW1hZ2UgdG8gdjAuMjcuMCAoZm9yZ2Vqbyk=-->Update module golang.org/x/image to v0.27.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7792): <!--number 7792 --><!--line 0 --><!--description W3NraXAgY2ldIGNob3JlOiByZW1vdmUgYmFja3BvcnQgc2NyaXB0-->[skip ci] chore: remove backport script<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7789): <!--number 7789 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2dvbGFuZ2NpL2dvbGFuZ2NpLWxpbnQvdjIvY21kL2dvbGFuZ2NpLWxpbnQgdG8gdjIuMS42IChmb3JnZWpvKQ==-->Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.1.6 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7785): <!--number 7785 --><!--line 0 --><!--description W1NLSVAgQ0ldIGNob3JlOiB1cGRhdGUgQ09ERU9XTkVSUw==-->[SKIP CI] chore: update CODEOWNERS<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7784): <!--number 7784 --><!--line 0 --><!--description Zml4KGFwaSk6IGRvY3VtZW50IGBpc19zeXN0ZW1fd2ViaG9va2AgZmllbGQ=-->fix(api): document `is_system_webhook` field<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7778): <!--number 7778 --><!--line 0 --><!--description TG9jayBmaWxlIG1haW50ZW5hbmNlIChmb3JnZWpvKQ==-->Lock file maintenance (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7777): <!--number 7777 --><!--line 0 --><!--description VXBkYXRlIHJlbm92YXRlIHRvIHY0MCAoZm9yZ2VqbykgKG1ham9yKQ==-->Update renovate to v40 (forgejo) (major)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7776): <!--number 7776 --><!--line 0 --><!--description VXBkYXRlIHJlbm92YXRlIHRvIHYzOS4yNjQuMCAoZm9yZ2Vqbyk=-->Update renovate to v39.264.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7773): <!--number 7773 --><!--line 0 --><!--description cmVtb3ZlIGFydGlmaWNpYWwgZGVsYXkgZm9yIFBSIHVwZGF0ZQ==-->remove artificial delay for PR update<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7772): <!--number 7772 --><!--line 0 --><!--description Y2hvcmU6IEZpeCBvdXRkYXRlZCB1c2FnZSBvZiB1bml0dGVzdC5PdmVycmlkZUZpeHR1cmVz-->chore: Fix outdated usage of unittest.OverrideFixtures<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7770): <!--number 7770 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tLzQyd2ltL2h0dHBzaWcgdG8gdjEuMi4zIChmb3JnZWpvKQ==-->Update module github.com/42wim/httpsig to v1.2.3 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7769): <!--number 7769 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgQGdpdGh1Yi9yZWxhdGl2ZS10aW1lLWVsZW1lbnQgdG8gdjQuNC42IChmb3JnZWpvKQ==-->Update dependency @github/relative-time-element to v4.4.6 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7768): <!--number 7768 --><!--line 0 --><!--description VXBkYXRlIGdpdGh1Yi5jb20vNDJ3aW0vc3Noc2lnIGRpZ2VzdCB0byA1MTAwNjMyIChmb3JnZWpvKQ==-->Update github.com/42wim/sshsig digest to 5100632 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7765): <!--number 7765 --><!--line 0 --><!--description Y2hvcmUocmVsZWFzZS1ub3Rlcyk6IEZvcmdlam8gdjcuMC4xNQ==-->chore(release-notes): Forgejo v7.0.15<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7764): <!--number 7764 --><!--line 0 --><!--description Y2hvcmUocmVsZWFzZS1ub3Rlcyk6IEZvcmdlam8gdjExLjAuMQ==-->chore(release-notes): Forgejo v11.0.1<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7762): <!--number 7762 --><!--line 0 --><!--description ZG9jdW1lbnQgdGhhdCAvcmVwb3Mve293bmVyfS97cmVwb30vcHVsbHMgbWF5IGNvbnRhaW4gbnVsbHM=-->document that /repos/{owner}/{repo}/pulls may contain nulls<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7760): <!--number 7760 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgZm9yZ2Vqby9yZWxlYXNlLW5vdGVzLWFzc2lzdGFudCB0byB2MS4yLjUgKGZvcmdlam8p-->Update dependency forgejo/release-notes-assistant to v1.2.5 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7759): <!--number 7759 --><!--line 0 --><!--description Y2hvcmUocmVub3ZhdGUpOiB1c2UgYGdpdGVhLXJlbGVhc2VzYCBkYXRhc291cmNlIGZvciBybmE=-->chore(renovate): use `gitea-releases` datasource for rna<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7758): <!--number 7758 --><!--line 0 --><!--description Y2hvcmUocmVub3ZhdGUpOiBhbGxvdyB1cGRhdGluZyByZWxlYXNlIG5vdGVzIGFzc2lzdGFudA==-->chore(renovate): allow updating release notes assistant<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7746): <!--number 7746 --><!--line 0 --><!--description Zml4KHVpKTogaW1wcm92ZSBmb3JjZS1wdXNoIGNvbXBhcmUgbGluZSBsYXlvdXQ=-->fix(ui): improve force-push compare line layout<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7744): <!--number 7744 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgZXNsaW50LXBsdWdpbi11bmljb3JuIHRvIHY1OSAoZm9yZ2Vqbyk=-->Update dependency eslint-plugin-unicorn to v59 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7740): <!--number 7740 --><!--line 0 --><!--description Zml4KHVpKTogbXVsdGlwbGUgZml4ZXMgb2Ygc3luYyBmb3JrIFVJ-->fix(ui): multiple fixes of sync fork UI<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7739): <!--number 7739 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL3JlZGlzL2dvLXJlZGlzL3Y5IHRvIHY5LjguMCAoZm9yZ2Vqbyk=-->Update module github.com/redis/go-redis/v9 to v9.8.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7738): <!--number 7738 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2FsZWN0aG9tYXMvY2hyb21hL3YyIHRvIHYyLjE3LjIgKGZvcmdlam8p-->Update module github.com/alecthomas/chroma/v2 to v2.17.2 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7736): <!--number 7736 --><!--line 0 --><!--description VXBkYXRlIGxpbnRlcnMgKGZvcmdlam8p-->Update linters (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7735): <!--number 7735 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgaGFwcHktZG9tIHRvIHYxNy40LjYgKGZvcmdlam8p-->Update dependency happy-dom to v17.4.6 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7732): <!--number 7732 --><!--line 0 --><!--description Y2k6IGFkZCBjbGFyaWZpY2F0aW9uIHJlZ2FyZGluZyB0ZXN0IGxhYmVs-->ci: add clarification regarding test label<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7731): <!--number 7731 --><!--line 0 --><!--description Y2hvcmU6IGZpeCB0ZXN0IHRvIGF2b2lkIGRhdGEgcmFjZQ==-->chore: fix test to avoid data race<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7728): <!--number 7728 --><!--line 0 --><!--description cmVmYWN0b3IgJiBlbmhhbmNlIEFQIGVsZW1lbnRzIHVzZWQ=-->refactor & enhance AP elements used<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7720): <!--number 7720 --><!--line 0 --><!--description Y2hvcmUocmVsZWFzZSk6IG5leHQtZGlnZXN0IG1vdmVkIHRvIGludmlzaWJsZS5mb3JnZWpvLm9yZw==-->chore(release): next-digest moved to invisible.forgejo.org<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7715): <!--number 7715 --><!--line 0 --><!--description Y2hvcmU6IHJlcGxhY2UgYGdpdGh1Yi5jb20vZ28tdGVzdGZpeHR1cmVzL3Rlc3RmaXh0dXJlc2A=-->chore: replace `github.com/go-testfixtures/testfixtures`<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7714): <!--number 7714 --><!--line 0 --><!--description ZW5oYW5jZSB2YWxpZGF0ZWFibGUgaW50ZXJmYWNl-->enhance validateable interface<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7713): <!--number 7713 --><!--line 0 --><!--description W2dpdGVhXSB3ZWVrIDIwMjUtMTcgY2hlcnJ5IHBpY2sgKGdpdGVhL21haW4gLT4gZm9yZ2Vqbyk=-->[gitea] week 2025-17 cherry pick (gitea/main -> forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7707): <!--number 7707 --><!--line 0 --><!--description ZG8gbm90IHNldCBHT1BST1hZPWRpcmVjdCBpbiBEb2NrZXJmaWxlKg==-->do not set GOPROXY=direct in Dockerfile*<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7705): <!--number 7705 --><!--line 0 --><!--description Y2hvcmUodWkpOiByZW1vdmUgdW51c2VkIGZvbWFudGljIGZvbnQgc2l6ZSBjbGFzc2Vz-->chore(ui): remove unused fomantic font size classes<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7697): <!--number 7697 --><!--line 0 --><!--description YmV0dGVyIGNvbW1lbnRzIGFuZCB2YXJpYWJsZSBuYW1lcyBmb3IgQWN0aW9uUnVuTm93RG9uZQ==-->better comments and variable names for ActionRunNowDone<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7696): <!--number 7696 --><!--line 0 --><!--description TG9jayBmaWxlIG1haW50ZW5hbmNlIChmb3JnZWpvKQ==-->Lock file maintenance (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7695): <!--number 7695 --><!--line 0 --><!--description VXBkYXRlIHJlbm92YXRlIHRvIHYzOS4yNjEuNCAoZm9yZ2Vqbyk=-->Update renovate to v39.261.4 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7690): <!--number 7690 --><!--line 0 --><!--description Zml4IHZhcmlvdXMgdHlwb3M=-->fix various typos<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7689): <!--number 7689 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL3l1aW4vZ29sZG1hcmsgdG8gdjEuNy4xMSAoZm9yZ2Vqbyk=-->Update module github.com/yuin/goldmark to v1.7.11 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7687): <!--number 7687 --><!--line 0 --><!--description Y2hvcmU6IHR1bmUgZG93biByZW1vdGUgdXNlciBwcm9tb3Rpb24gZGVidWcgbWVzc2FnZSBzaG93biBhcyBlcnJvcg==-->chore: tune down remote user promotion debug message shown as error<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7685): <!--number 7685 --><!--line 0 --><!--description dXNlIGBsaW5ndWlzdC1nZW5lcmF0ZWRgIGZvciBsYW5ndWFnZSBzdGF0cw==-->use `linguist-generated` for language stats<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7683): <!--number 7683 --><!--line 0 --><!--description c2V0IGRlZmF1bHQgcmVzdHJpY3RlZCBmb3IgT0F1dGgyIHVzZXI=-->set default restricted for OAuth2 user<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7682): <!--number 7682 --><!--line 0 --><!--description Y2hvcmU6IHNpbXBsaWZ5IGBHZXREaWZmYA==-->chore: simplify `GetDiff`<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7680): <!--number 7680 --><!--line 0 --><!--description Y2hvcmU6IHJlbW92ZSB1bnVzZWQgbGludGVycw==-->chore: remove unused linters<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7678): <!--number 7678 --><!--line 0 --><!--description YWRkIGxhYmVsIGZvciBhdmF0YXIgc2V0dGluZ3M=-->add label for avatar settings<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7667): <!--number 7667 --><!--line 0 --><!--description aTE4bihlbik6IGZpeCB0eXBvIGluIGFyY2hpdmUgbm90aWNl-->i18n(en): fix typo in archive notice<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7665): <!--number 7665 --><!--line 0 --><!--description VXBkYXRlIGxpbnRlcnMgKGZvcmdlam8p-->Update linters (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7663): <!--number 7663 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgQHBsYXl3cmlnaHQvdGVzdCB0byB2MS41Mi4wIChmb3JnZWpvKQ==-->Update dependency @playwright/test to v1.52.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7662): <!--number 7662 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL1B1ZXJraXRvQmlvL2dvcXVlcnkgdG8gdjEuMTAuMyAoZm9yZ2Vqbyk=-->Update module github.com/PuerkitoBio/goquery to v1.10.3 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7661): <!--number 7661 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2dvbGFuZ2NpL2dvbGFuZ2NpLWxpbnQvdjIvY21kL2dvbGFuZ2NpLWxpbnQgdG8gdjIuMS41IChmb3JnZWpvKQ==-->Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.1.5 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7659): <!--number 7659 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgd2VicGFjayB0byB2NS45OS43IChmb3JnZWpvKQ==-->Update dependency webpack to v5.99.7 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7648): <!--number 7648 --><!--line 0 --><!--description Y2hvcmU6IG1lcmdlIHRlc3RzLkFkZEZpeHR1cmVzIGFuZCB1bml0dGVzdC5PdmVycmlkZUZpeHR1cmVz-->chore: merge tests.AddFixtures and unittest.OverrideFixtures<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7640): <!--number 7640 --><!--line 0 --><!--description UmVtb3ZlICJjcmVhdGUgYnJhbmNoIiBidXR0b24gb24gbWlycm9yZWQgcmVwb3M=-->Remove "create branch" button on mirrored repos<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7616): <!--number 7616 --><!--line 0 --><!--description Zml4KHVpKTogbWFrZSBwYWdpbmF0aW9uIGxhYmVscyBhbHdheXMgdmlzaWJsZSB0byBzY3JlZW5yZWFkZXI=-->fix(ui): make pagination labels always visible to screenreader<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7613): <!--number 7613 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBtdmRhbi5jYy9nb2Z1bXB0IHRvIHYwLjguMCAoZm9yZ2Vqbyk=-->Update module mvdan.cc/gofumpt to v0.8.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7612): <!--number 7612 --><!--line 0 --><!--description VXBkYXRlIGxpbnRlcnMgKGZvcmdlam8p-->Update linters (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7611): <!--number 7611 --><!--line 0 --><!--description VXBkYXRlIHZpdGVzdCBtb25vcmVwbyB0byB2My4xLjIgKGZvcmdlam8p-->Update vitest monorepo to v3.1.2 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7610): <!--number 7610 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgQHZpdGVzdC9lc2xpbnQtcGx1Z2luIHRvIHYxLjEuNDMgKGZvcmdlam8p-->Update dependency @vitest/eslint-plugin to v1.1.43 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7607): <!--number 7607 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2FsZWN0aG9tYXMvY2hyb21hL3YyIHRvIHYyLjE3LjAgKGZvcmdlam8p-->Update module github.com/alecthomas/chroma/v2 to v2.17.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7606): <!--number 7606 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL21pbmlvL21pbmlvLWdvL3Y3IHRvIHY3LjAuOTEgKGZvcmdlam8p-->Update module github.com/minio/minio-go/v7 to v7.0.91 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7602): <!--number 7602 --><!--line 0 --><!--description W2dpdGVhXSB3ZWVrIDIwMjUtMTYgY2hlcnJ5IHBpY2sgKGdpdGVhL21haW4gLT4gZm9yZ2Vqbyk=-->[gitea] week 2025-16 cherry pick (gitea/main -> forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7597): <!--number 7597 --><!--line 0 --><!--description ZGVsYXktd3JpdGUgdHJhY2UuZGF0IGZvciBmb3JnZWpvIGRpYWdub3Npcw==-->delay-write trace.dat for forgejo diagnosis<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7588): <!--number 7588 --><!--line 0 --><!--description TG9jayBmaWxlIG1haW50ZW5hbmNlIChmb3JnZWpvKQ==-->Lock file maintenance (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7587): <!--number 7587 --><!--line 0 --><!--description VXBkYXRlIHJlbm92YXRlIHRvIHYzOS4yNTIuMCAoZm9yZ2Vqbyk=-->Update renovate to v39.252.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7580): <!--number 7580 --><!--line 0 --><!--description ZmVhdCh1aSk6IGVubGFyZ2UgbWV0YWRhdGEgbGluZSBnYXBzIGluIGlzc3VlIGxpc3QgYW5kIHJlZmFjdG9y-->feat(ui): enlarge metadata line gaps in issue list and refactor<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7579): <!--number 7579 --><!--line 0 --><!--description aTE4bihlbik6IGFkZCBwb3NpdGlvbmFsIGhpbnRzIHRvIHN5bmNfZm9yayBwbGFjZWhvbGRlcnM=-->i18n(en): add positional hints to sync_fork placeholders<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7578): <!--number 7578 --><!--line 0 --><!--description Zml4KHVpKTogb3ZlcmZsb3cgdGFidWxhciBtZW51IENTUyBmaXhlcw==-->fix(ui): overflow tabular menu CSS fixes<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7577): <!--number 7577 --><!--line 0 --><!--description ZmVhdCh1aSk6IG1ha2UgZm9yayByZWxhdGVkIGJhbm5lcnMgbW9yZSBjb25zaXN0ZW50-->feat(ui): make fork related banners more consistent<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7575): <!--number 7575 --><!--line 0 --><!--description Zml4KHVpKTogdXNlIGNvcnJlY3QgYnJhbmNoIG5hbWUgaW4gYnJhbmNoIHRhZyBzZWxlY3Rvcg==-->fix(ui): use correct branch name in branch tag selector<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7573): <!--number 7573 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgd2VicGFjayB0byB2NS45OS42IChmb3JnZWpvKQ==-->Update dependency webpack to v5.99.6 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7571): <!--number 7571 --><!--line 0 --><!--description Zml4KHVpKTogc2hvdyBjb21taXQgaWNvbiBpbiBicmFuY2ggZHJvcGRvd24gYnV0dG9uIHdoZW4gdmlld2luZyBhIGNvbW1pdA==-->fix(ui): show commit icon in branch dropdown button when viewing a commit<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7565): <!--number 7565 --><!--line 0 --><!--description Z3JhbW1hciBpbiBhIHJlbGVhc2UgQVBJIGVycm9yIG1lc3NhZ2U=-->grammar in a release API error message<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7561): <!--number 7561 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2NhZGR5c2VydmVyL2NlcnRtYWdpYyB0byB2MC4yMy4wIChmb3JnZWpvKQ==-->Update module github.com/caddyserver/certmagic to v0.23.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7560): <!--number 7560 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL21hdHRuL2dvLXNxbGl0ZTMgdG8gdjEuMTQuMjggKGZvcmdlam8p-->Update module github.com/mattn/go-sqlite3 to v1.14.28 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7558): <!--number 7558 --><!--line 0 --><!--description Zml4KHVpKTogbWFrZSB0YWcgZHJvcGRvd24gY2xpY2thYmxlIGFnYWlu-->fix(ui): make tag dropdown clickable again<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7557): <!--number 7557 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgcHl0aG9uIHRvIHYzLjEzLjMgKGZvcmdlam8p-->Update dependency python to v3.13.3 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7555): <!--number 7555 --><!--line 0 --><!--description Y2hvcmUocmVsZWFzZSk6IHYxMC4wIGlzIEVPTCBbc2tpcCBjaV0=-->chore(release): v10.0 is EOL [skip ci]<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7554): <!--number 7554 --><!--line 0 --><!--description Y2hvcmUocmVub3ZhdGUpOiBmaXggcGFja2FnZSBuYW1lIG1hdGNoaW5nIGZvciBnbyBtYWpvcnMgW3NraXAgY2ld-->chore(renovate): fix package name matching for go majors [skip ci]<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7552): <!--number 7552 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2dvbGFuZ2NpL2dvbGFuZ2NpLWxpbnQvdjIvY21kL2dvbGFuZ2NpLWxpbnQgdG8gdjIuMS4yIChmb3JnZWpvKQ==-->Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.1.2 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7551): <!--number 7551 --><!--line 0 --><!--description VXBkYXRlIGh0dHBzOi8vZGF0YS5mb3JnZWpvLm9yZy9hY3Rpb25zL2dpdC1iYWNrcG9ydGluZyBhY3Rpb24gdG8gdjQuOC41IChmb3JnZWpvKQ==-->Update https://data.forgejo.org/actions/git-backporting action to v4.8.5 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7550): <!--number 7550 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgY2hhcnQuanMgdG8gdjQuNC45IChmb3JnZWpvKQ==-->Update dependency chart.js to v4.4.9 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7545): <!--number 7545 --><!--line 0 --><!--description VXBkYXRlIE5vZGUuanMgdG8gdjIyIChmb3JnZWpvKQ==-->Update Node.js to v22 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7543): <!--number 7543 --><!--line 0 --><!--description bWF0Y2ggUGFja2FnZUJsb2IuSGFzaEJsYWtlMmIgZGVmaW5pdGlvbiBhbmQgbWlncmF0aW9u-->match PackageBlob.HashBlake2b definition and migration<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7539): <!--number 7539 --><!--line 0 --><!--description Zml4KFVJKTogaTE4bjogaW1wcm92ZSBuYW1pbmc=-->fix(UI): i18n: improve naming<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7538): <!--number 7538 --><!--line 0 --><!--description W2dpdGVhXSB3ZWVrIDIwMjUtMTUgY2hlcnJ5IHBpY2sgKGdpdGVhL21haW4gLT4gZm9yZ2Vqbyk=-->[gitea] week 2025-15 cherry pick (gitea/main -> forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7534): <!--number 7534 --><!--line 0 --><!--description TG9jayBmaWxlIG1haW50ZW5hbmNlIChmb3JnZWpvKQ==-->Lock file maintenance (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7532): <!--number 7532 --><!--line 0 --><!--description VXBkYXRlIHJlbm92YXRlIHRvIHYzOS4yNDAuMSAoZm9yZ2Vqbyk=-->Update renovate to v39.240.1 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7528): <!--number 7528 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2dvbGFuZ2NpL2dvbGFuZ2NpLWxpbnQvdjIvY21kL2dvbGFuZ2NpLWxpbnQgdG8gdjIuMS4xIChmb3JnZWpvKQ==-->Update module github.com/golangci/golangci-lint/v2/cmd/golangci-lint to v2.1.1 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7520): <!--number 7520 --><!--line 0 --><!--description cGFja2FnZV9ibG9iLmhhc19ibGFrZTJiIG1heSBiZSBudWxs-->package_blob.has_blake2b may be null<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7512): <!--number 7512 --><!--line 0 --><!--description Y2hvcmU6IHVzZSBgc2hhcnBgIHRvIGdlbmVyYXRlIGltYWdlcw==-->chore: use `sharp` to generate images<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7510): <!--number 7510 --><!--line 0 --><!--description Y2hvcmU6IHJlZmFjdG9yIGZvciBBY3Rpb25zIERvbmUgTm90aWZpY2F0aW9u-->chore: refactor for Actions Done Notification<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7505): <!--number 7505 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnb2xhbmcub3JnL3gvdG9vbHMvY21kL2RlYWRjb2RlIHRvIHYwLjMyLjAgKGZvcmdlam8p-->Update module golang.org/x/tools/cmd/deadcode to v0.32.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7504): <!--number 7504 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnb2xhbmcub3JnL3gvbmV0IHRvIHYwLjM5LjAgKGZvcmdlam8p-->Update module golang.org/x/net to v0.39.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7503): <!--number 7503 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kga2F0ZXggdG8gdjAuMTYuMjIgKGZvcmdlam8p-->Update dependency katex to v0.16.22 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7499): <!--number 7499 --><!--line 0 --><!--description Y2hvcmUoaTE4bik6IHVwZGF0ZSBjb250cmlidXRpbmcgZG9jdW1lbnRhdGlvbiB3aXRoIEpTT04gZm9ybWF0-->chore(i18n): update contributing documentation with JSON format<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7497): <!--number 7497 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnb2xhbmcub3JnL3gvaW1hZ2UgdG8gdjAuMjYuMCAoZm9yZ2Vqbyk=-->Update module golang.org/x/image to v0.26.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7495): <!--number 7495 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL21pbmlvL21pbmlvLWdvL3Y3IHRvIHY3LjAuOTAgKGZvcmdlam8p-->Update module github.com/minio/minio-go/v7 to v7.0.90 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7494): <!--number 7494 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgd2VicGFjayB0byB2NS45OS41IChmb3JnZWpvKQ==-->Update dependency webpack to v5.99.5 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7491): <!--number 7491 --><!--line 0 --><!--description QWN0aW9ucyBEb25lIE5vdGlmaWNhdGlvbg==-->Actions Done Notification<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7490): <!--number 7490 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgd2VicGFjayB0byB2NS45OS4xIChmb3JnZWpvKQ==-->Update dependency webpack to v5.99.1 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7488): <!--number 7488 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnby51YmVyLm9yZy9tb2NrIHRvIHYwLjUuMSAoZm9yZ2Vqbyk=-->Update module go.uber.org/mock to v0.5.1 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7486): <!--number 7486 --><!--line 0 --><!--description W2dpdGVhXSB3ZWVrIDIwMjUtMTQgY2hlcnJ5IHBpY2sgKGdpdGVhL21haW4gLT4gZm9yZ2Vqbyk=-->[gitea] week 2025-14 cherry pick (gitea/main -> forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7485): <!--number 7485 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnb2xhbmcub3JnL3gvY3J5cHRvIHRvIHYwLjM3LjAgKGZvcmdlam8p-->Update module golang.org/x/crypto to v0.37.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7484): <!--number 7484 --><!--line 0 --><!--description VXBkYXRlIHJlbm92YXRlIHRvIHYzOS4yMzMuNSAoZm9yZ2Vqbyk=-->Update renovate to v39.233.5 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7479): <!--number 7479 --><!--line 0 --><!--description TWlub3IgZ3JhbW1hdGljYWwgZml4IHRvIHJ1bm5lciBkZWxldGlvbiBtZXNzYWdl-->Minor grammatical fix to runner deletion message<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7475): <!--number 7475 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnb2xhbmcub3JnL3gvb2F1dGgyIHRvIHYwLjI5LjAgKGZvcmdlam8p-->Update module golang.org/x/oauth2 to v0.29.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7473): <!--number 7473 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2Zzbm90aWZ5L2Zzbm90aWZ5IHRvIHYxLjkuMCAoZm9yZ2Vqbyk=-->Update module github.com/fsnotify/fsnotify to v1.9.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7472): <!--number 7472 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgY2xpcHBpZSB0byB2NC4xLjYgKGZvcmdlam8p-->Update dependency clippie to v4.1.6 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7471): <!--number 7471 --><!--line 0 --><!--description Zml4KHVpKTogZW5zdXJlIGRpbW1lciBhbHdheXMgY292ZXJzIHdob2xlIHBhZ2U=-->fix(ui): ensure dimmer always covers whole page<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7470): <!--number 7470 --><!--line 0 --><!--description Zml4KHVpKTogb25seSBydW4gYXV0aF9uYW1lIGNvZGUgb24gbmV3IGFuZCBlZGl0IHBhZ2U=-->fix(ui): only run auth_name code on new and edit page<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7469): <!--number 7469 --><!--line 0 --><!--description Y2hvcmUocmVsZWFzZS1ub3Rlcyk6IEZvcmdlam8gdjExLjAuMA==-->chore(release-notes): Forgejo v11.0.0<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7468): <!--number 7468 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2JsZXZlc2VhcmNoL2JsZXZlL3YyIHRvIHYyLjUuMCAoZm9yZ2Vqbyk=-->Update module github.com/blevesearch/bleve/v2 to v2.5.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7467): <!--number 7467 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgdHlwZXNjcmlwdCB0byB2NS44LjMgKGZvcmdlam8p-->Update dependency typescript to v5.8.3 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7465): <!--number 7465 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgYW5zaV91cCB0byB2Ni4wLjUgKGZvcmdlam8p-->Update dependency ansi_up to v6.0.5 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7463): <!--number 7463 --><!--line 0 --><!--description Y2hvcmU6IGFsd2F5cyBlbmFibGUgd2VicGFjayBwcm9ncmVzcw==-->chore: always enable webpack progress<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7462): <!--number 7462 --><!--line 0 --><!--description Y2hvcmUoaTE4bik6IGFkZCBhIG1ldGEgbGluZSB0byB0aGUgYmFzZSBqc29uIHRyYW5zbGF0aW9u-->chore(i18n): add a meta line to the base json translation<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7459): <!--number 7459 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2FsZWN0aG9tYXMvY2hyb21hL3YyIHRvIHYyLjE2LjAgKGZvcmdlam8p-->Update module github.com/alecthomas/chroma/v2 to v2.16.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7458): <!--number 7458 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBjb2RlLmdpdGVhLmlvL3Nkay9naXRlYSB0byB2MC4yMS4wIChmb3JnZWpvKQ==-->Update module code.gitea.io/sdk/gitea to v0.21.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7456): <!--number 7456 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgYW5zaV91cCB0byB2Ni4wLjMgKGZvcmdlam8p-->Update dependency ansi_up to v6.0.3 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7455): <!--number 7455 --><!--line 0 --><!--description TWFrZWZpbGUgJiBCU0RtYWtlZmlsZSBjaGFuZ2Vz-->Makefile & BSDmakefile changes<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7453): <!--number 7453 --><!--line 0 --><!--description Y2hvcmUoc2VjKTogdW5pZnkgdXNhZ2Ugb2YgYGNyeXB0by9yYW5kLlJlYWRg-->chore(sec): unify usage of `crypto/rand.Read`<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7452): <!--number 7452 --><!--line 0 --><!--description bWFrZSBpbnN0YWxsaW5nIEZvcmdlam8gd29yayBhZ2Fpbg==-->make installing Forgejo work again<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7451): <!--number 7451 --><!--line 0 --><!--description Y2hvcmUoaTE4bik6IGNsZWFudXAgYHNldHRpbmdzLmFkb3B0YCBzdHJpbmc=-->chore(i18n): cleanup `settings.adopt` string<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7434): <!--number 7434 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL21hdHRuL2dvLXNxbGl0ZTMgdG8gdjEuMTQuMjcgKGZvcmdlam8p-->Update module github.com/mattn/go-sqlite3 to v1.14.27 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7433): <!--number 7433 --><!--line 0 --><!--description ZmVhdChidWlsZCk6IHJ1biBsaW50LWxvY2FsZS11c2FnZSB3L28gLS1hbGxvdy1taXNzaW5nLW1zZ2lkcw==-->feat(build): run lint-locale-usage w/o --allow-missing-msgids<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7431): <!--number 7431 --><!--line 0 --><!--description bWFrZSByZXBvIGNsb25lIGh0dHBzL3NzaCBsaXN0ZW5lciBjb25kaXRpb25hbA==-->make repo clone https/ssh listener conditional<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7429): <!--number 7429 --><!--line 0 --><!--description ZmVhdChidWlsZCk6IHVuaWZvcm0gaW5pIHBhcnNpbmc=-->feat(build): uniform ini parsing<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7425): <!--number 7425 --><!--line 0 --><!--description ZmVhdChsb2NhbGUgSXRlcik6IHByb3Blcmx5IHN1cHBvcnQgdHJQbHVyYWxTdHJpbmc=-->feat(locale Iter): properly support trPluralString<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7424): <!--number 7424 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL21hdHRuL2dvLXNxbGl0ZTMgdG8gdjEuMTQuMjUgKGZvcmdlam8p-->Update module github.com/mattn/go-sqlite3 to v1.14.25 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7423): <!--number 7423 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2dvLXdlYmF1dGhuL3dlYmF1dGhuIHRvIHYwLjEyLjMgKGZvcmdlam8p-->Update module github.com/go-webauthn/webauthn to v0.12.3 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7422): <!--number 7422 --><!--line 0 --><!--description Zml4KGkxOG4pOiBmaXggc2V2ZXJhbCB1c2FnZXMgb2YgaTE4bg==-->fix(i18n): fix several usages of i18n<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7421): <!--number 7421 --><!--line 0 --><!--description Zml4KG1pZ3JhdGlvbnMpOiB0cmFuc2ZlciBQUiBmbG93IGluZm9ybWF0aW9u-->fix(migrations): transfer PR flow information<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7419): <!--number 7419 --><!--line 0 --><!--description Y2hvcmU6IGFkZCBlbXB0eSBgYWN0aW9uX3ZhcmlhYmxlYCBmaXh0dXJl-->chore: add empty `action_variable` fixture<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7417): <!--number 7417 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgZ28gdG8gdjEuMjQuMiAoZm9yZ2Vqbyk=-->Update dependency go to v1.24.2 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7416): <!--number 7416 --><!--line 0 --><!--description Y2hvcmUodWkpOiByZW1vdmUgZm9tYW50aWMncyBkaW1tZXIgbW9kdWxl-->chore(ui): remove fomantic's dimmer module<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7414): <!--number 7414 --><!--line 0 --><!--description Y2hvcmUodWkpOiByZW1vdmUgaW5lZmZlY3RpdmUgY2xhc3Mgc21hbGwgaW4gaW5saW5lIGNvZGUgcHJldmlldw==-->chore(ui): remove ineffective class small in inline code preview<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7408): <!--number 7408 --><!--line 0 --><!--description Y2hvcmU6IGVuYWJsZSBzZXZlcmFsIG5vLWpxdWVyeSBydWxlcw==-->chore: enable several no-jquery rules<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7407): <!--number 7407 --><!--line 0 --><!--description VXBkYXRlIHZpdGVzdCBtb25vcmVwbyB0byB2My4xLjEgKGZvcmdlam8p-->Update vitest monorepo to v3.1.1 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7406): <!--number 7406 --><!--line 0 --><!--description VXBkYXRlIGxpbnRlcnMgdG8gdjguMjkuMCAoZm9yZ2Vqbyk=-->Update linters to v8.29.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7400): <!--number 7400 --><!--line 0 --><!--description TG9jayBmaWxlIG1haW50ZW5hbmNlIChmb3JnZWpvKQ==-->Lock file maintenance (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7399): <!--number 7399 --><!--line 0 --><!--description VXBkYXRlIHJlbm92YXRlIHRvIHYzOS4yMjIuMSAoZm9yZ2Vqbyk=-->Update renovate to v39.222.1 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7397): <!--number 7397 --><!--line 0 --><!--description W2dpdGVhXSB3ZWVrIDIwMjUtMTMgY2hlcnJ5IHBpY2sgKGdpdGVhL21haW4gLT4gZm9yZ2Vqbyk=-->[gitea] week 2025-13 cherry pick (gitea/main -> forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7396): <!--number 7396 --><!--line 0 --><!--description Y2hvcmU6IHVzZSBkeW5hbWljIGlk-->chore: use dynamic id<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7394): <!--number 7394 --><!--line 0 --><!--description dXNlIGNvcnJlY3QgaW5pdCBpbnN0cnVjdGlvbiBmb3Igc2hhMjU2-->use correct init instruction for sha256<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7392): <!--number 7392 --><!--line 0 --><!--description TG9jayBmaWxlIG1haW50ZW5hbmNlIChmb3JnZWpvKQ==-->Lock file maintenance (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7391): <!--number 7391 --><!--line 0 --><!--description VXBkYXRlIGdpdGh1Yi5jb20vZ29vZ2xlL3Bwcm9mIGRpZ2VzdCB0byBhNGIwM2VjIChmb3JnZWpvKQ==-->Update github.com/google/pprof digest to a4b03ec (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7390): <!--number 7390 --><!--line 0 --><!--description QWNjZXNzaWJpbGl0eTogZml4IHVucmVhZGFibGUgY2FwdGNoYSB3aXRoIGRhcmsgdGhlbWVz-->Accessibility: fix unreadable captcha with dark themes<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7384): <!--number 7384 --><!--line 0 --><!--description VXBkYXRlIGxpbnRlcnMgKGZvcmdlam8p-->Update linters (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7383): <!--number 7383 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgeWFtbGxpbnQgdG8gdjEuMzcuMCAoZm9yZ2Vqbyk=-->Update dependency yamllint to v1.37.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7382): <!--number 7382 --><!--line 0 --><!--description VXBkYXRlIHZpdGVzdCBtb25vcmVwbyB0byB2My4wLjkgKGZvcmdlam8p-->Update vitest monorepo to v3.0.9 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7381): <!--number 7381 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgQHZpdGVzdC9lc2xpbnQtcGx1Z2luIHRvIHYxLjEuMzggKGZvcmdlam8p-->Update dependency @vitest/eslint-plugin to v1.1.38 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7379): <!--number 7379 --><!--line 0 --><!--description cHJlcGVuZCBBcHBTdWJVUkwgdG8gdmlzaWJpbGl0eSBoaW50IFVSTHM=-->prepend AppSubURL to visibility hint URLs<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7378): <!--number 7378 --><!--line 0 --><!--description Y2hvcmU6IGltcHJvdmUgcmVwbyBtaWdyYXRlIGUyZSB0ZXN0-->chore: improve repo migrate e2e test<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7376): <!--number 7376 --><!--line 0 --><!--description Y2hvcmUodGVzdHMpOiBmaXggdGVzdGluZyBmYWlsdXJlIGNhdXNlZCBieSBkZXAgdXBkYXRl-->chore(tests): fix testing failure caused by dep update<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7374): <!--number 7374 --><!--line 0 --><!--description Y2hvcmUodGVzdHMpOiByZWZhY3RvciBtaWdyYXRpb24gZm9ybSB0ZXN0-->chore(tests): refactor migration form test<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7372): <!--number 7372 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgZXNsaW50LXBsdWdpbi11bmljb3JuIHRvIHY1OCAoZm9yZ2Vqbyk=-->Update dependency eslint-plugin-unicorn to v58 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7371): <!--number 7371 --><!--line 0 --><!--description Y2hvcmU6IHVzZSBjb3JyZWN0IGltcG9ydA==-->chore: use correct import<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7367): <!--number 7367 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnaXRodWIuY29tL2dvbGFuZ2NpL2dvbGFuZ2NpLWxpbnQvY21kL2dvbGFuZ2NpLWxpbnQgdG8gdjIgKGZvcmdlam8p-->Update module github.com/golangci/golangci-lint/cmd/golangci-lint to v2 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7366): <!--number 7366 --><!--line 0 --><!--description VXBkYXRlIGh0dHBzOi8vZGF0YS5mb3JnZWpvLm9yZy90ai1hY3Rpb25zL2NoYW5nZWQtZmlsZXMgYWN0aW9uIHRvIHY0NiAoZm9yZ2Vqbyk=-->Update https://data.forgejo.org/tj-actions/changed-files action to v46 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7362): <!--number 7362 --><!--line 0 --><!--description VXBkYXRlIG1vZHVsZSBnb2xhbmcub3JnL3gvbmV0IHRvIHYwLjM4LjAgKGZvcmdlam8p-->Update module golang.org/x/net to v0.38.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7356): <!--number 7356 --><!--line 0 --><!--description Zml4KHVpKTogaW1wcm92ZSB2ZXJ0aWNhbCBhbGlnbm1lbnQgb2YgaWNvbnMgd2l0aCB0ZXh0IGluIHRoZSBvdmVyZmxvdyBtZW51ICgjNzMxNCk=-->fix(ui): improve vertical alignment of icons with text in the overflow menu (#7314)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7355): <!--number 7355 --><!--line 0 --><!--description NDEwOC1lbXB0eS1zbGljZS1lbmNvZGVkLXRvLW51bGw=-->4108-empty-slice-encoded-to-null<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7350): <!--number 7350 --><!--line 0 --><!--description cmVmYWN0b3IoY2xpKTogaW1wcm92ZSBkdW1wJ3MgdGVtcG9yYXJ5IGZpbGUgaGFuZGxpbmc=-->refactor(cli): improve dump's temporary file handling<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7348): <!--number 7348 --><!--line 0 --><!--description aW50cm9kdWNlIGdpdE5lZWRlZCBib29sIGluIHNldHVw-->introduce gitNeeded bool in setup<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7344): <!--number 7344 --><!--line 0 --><!--description Zml4KHVpKTogRG8gbm90IGNoZWNrIGZvciBgdmVydGljYWwtYWxpZ25g-->fix(ui): Do not check for `vertical-align`<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7339): <!--number 7339 --><!--line 0 --><!--description VXBkYXRlIGRlcGVuZGVuY3kgbWVybWFpZCB0byB2MTEuNi4wIChmb3JnZWpvKQ==-->Update dependency mermaid to v11.6.0 (forgejo)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7337): <!--number 7337 --><!--line 0 --><!--description Y2hvcmU6IGJyYW5kaW5nIGltcG9ydCBwYXRo-->chore: branding import path<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7314): <!--number 7314 --><!--line 0 --><!--description Zml4KHVpKTogaW1wcm92ZSB2ZXJ0aWNhbCBhbGlnbm1lbnQgb2YgaWNvbnMgd2l0aCB0ZXh0IGluIHRoZSBvdmVyZmxvdyBtZW51-->fix(ui): improve vertical alignment of icons with text in the overflow menu<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7278): <!--number 7278 --><!--line 0 --><!--description ZmVhdChidWlsZCk6IGxpbnQtbG9jYWxlLXVzYWdlIHNob3VsZCBkZXRlY3QgbW9yZSBUciBmdW5jdGlvbnM=-->feat(build): lint-locale-usage should detect more Tr functions<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7271): <!--number 7271 --><!--line 0 --><!--description ZmVhdCh1aSk6IGltcHJvdmUgYnV0dG9uIGdhcCBjb25zaXN0ZW5jeSwgbWFrZSBpdCB2YXJpYWJsZSwgbGFyZ2VyIG9uIHRvdWNoc2NyZWVucw==-->feat(ui): improve button gap consistency, make it variable, larger on touchscreens<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7261): <!--number 7261 --><!--line 0 --><!--description cmVkaXJlY3QgdG8gc3VibW9kdWxlIGluc3RlYWQgb2YgdGhyb3dpbmcgNTAwIGVycm9yIHdoZW4gdmlld2luZyBzdWJtb2R1bGUgZW50cnk=-->redirect to submodule instead of throwing 500 error when viewing submodule entry<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7203): <!--number 7203 --><!--line 0 --><!--description YWRkIHBvcnQgYW5kIHNjaGVtYSB0byBmZWRlcmF0aW9uIGhvc3Q=-->add port and schema to federation host<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7092): <!--number 7092 --><!--line 0 --><!--description ZmVhdChyZXBvLGxvY2FsZSk6IG1lcmdlIFBSL2lzc3VlcyBjYXNlcyBmb3Igc29tZSByZXBvL2lzc3VlIHN0cmluZ3M=-->feat(repo,locale): merge PR/issues cases for some repo/issue strings<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7035): <!--number 7035 --><!--line 0 --><!--description ZW5hYmxlIEhUVFAgc2lnbmF0dXJlcyBvbiBhbGwgQWN0aXZpdHlQdWIgZW5kcG9pbnRz-->enable HTTP signatures on all ActivityPub endpoints<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/6977): <!--number 6977 --><!--line 0 --><!--description ZmVhdCE6IEFidXNpdmUgY29udGVudCByZXBvcnRpbmc=-->feat!: Abusive content reporting<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/6154): <!--number 6154 --><!--line 0 --><!--description UmVwbGFjZSB0aGUgJ3JlbGF0aXZlLXRpbWUnIGVsZW1lbnQgc2NyaXB0aW5nIHdpdGggY3VzdG9tLCB0cmFuc2xhdGFibGUgcmV3cml0ZQ==-->Replace the 'relative-time' element scripting with custom, translatable rewrite<!--description-->
- Already announced in the release notes of an older stable release
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8234): <!--number 8234 --><!--line 0 --><!--description Y29sbGFib3JhdG9yIGNhbiBlZGl0IHdpa2kgd2l0aCB3cml0ZSBhY2Nlc3M=-->collaborator can edit wiki with write access<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8189): <!--number 8189 --><!--line 0 --><!--description ZG8gbm90IGlnbm9yZSBhdXRvbWVyZ2Ugd2hpbGUgYSBQUiBpcyBjaGVja2luZyBmb3IgY29uZmxpY3Rz-->do not ignore automerge while a PR is checking for conflicts<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8170): <!--number 8170 --><!--line 0 --><!--description ZXJyb25lb3VzIGxpc3QgY29udGludWF0aW9uIG9uIENtZCtFbnRlciAoIzgxNTMp-->erroneous list continuation on Cmd+Enter (#8153)<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/8155): <!--number 8155 --><!--line 0 --><!--description ZG8gbm90IGZhaWwgd2hlbiByZWxlYXNlIG9yIHdpa2kgaXMgc2V0IGluIGAvcmVwb3MvbWlncmF0ZWAgQVBJ-->do not fail when release or wiki is set in `/repos/migrate` API<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7979): <!--number 7979 --><!--line 0 --><!--description cHVsbCByZXF1ZXN0IGNyb3NzIHJlZmVyZW5jZXM=-->pull request cross references<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7976): <!--number 7976 --><!--line 0 --><!--description aWdub3JlIGV4cGlyZWQgYXJ0aWZhY3RzIGZvciBxdW90YSBjYWxjdWxhdGlvbg==-->ignore expired artifacts for quota calculation<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7883): <!--number 7883 --><!--line 0 --><!--description cXVvdGUgcmVwbHkgaW4gQ2hyb21pdW0=-->quote reply in Chromium<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7775): <!--number 7775 --><!--line 0 --><!--description bWFrZSBoYXNoIHBhdHRlcm4gbW9yZSBzdHJpY3Q=-->make hash pattern more strict<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7755): <!--number 7755 --><!--line 0 --><!--description Zml4KHNlYyk6IGFkZCB0ZXN0cyBmb3IgT0F1dGgyIHNpZ251cA==-->fix(sec): add tests for OAuth2 signup<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7752): <!--number 7752 --><!--line 0 --><!--description Zml4KHNlYyk6IG9ubHkgZGVncmFkZSBwZXJtaXNzaW9uIGNoZWNrIGZvciBnaXQgcHVzaA==-->fix(sec): only degrade permission check for git push<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7650): <!--number 7650 --><!--line 0 --><!--description ZGlzcGxheSB0aGUgbGlzdCBvZiB0YXNrcyBpbiB0aGUgcnVubmVyIGVkaXQgcGFnZQ==-->display the list of tasks in the runner edit page<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7594): <!--number 7594 --><!--line 0 --><!--description Zml4KGkxOG4pOiBwcmV2ZW50IGluY29ycmVjdCBsb2dnaW5nIG9uIHN0cmluZ3MgbWlzc2luZyBpbiBKU09OIGxvY2FsZXM=-->fix(i18n): prevent incorrect logging on strings missing in JSON locales<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7584): <!--number 7584 --><!--line 0 --><!--description Zml4KHVpL3ByKTogdXNlIGV5ZSBpY29uIGZvciByZXZpZXdz-->fix(ui/pr): use eye icon for reviews<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7581): <!--number 7581 --><!--line 0 --><!--description Zml4KHVpKTogdXNlIGdhcCBpbiBzd2l0Y2ggaXRlbXM=-->fix(ui): use gap in switch items<!--description-->
- [PR](https://codeberg.org/forgejo/forgejo/pulls/7395): <!--number 7395 --><!--line 0 --><!--description dmFsaWRhdGUgaW5wdXQgZm9yIGRlZmF1bHRfe21lcmdlLHVwZGF0ZX1fc3R5bGU=-->validate input for default_{merge,update}_style<!--description-->
<!--end release-notes-assistant-->

View file

@ -8,7 +8,8 @@
],
"baseBranches": [
"$default",
"/^v11\\.\\d+/forgejo$/"
"/^v11\\.\\d+/forgejo$/",
"/^v12\\.\\d+/forgejo$/"
],
"postUpdateOptions": ["gomodTidy", "gomodUpdateImportPaths", "npmDedupe"],
"prConcurrentLimit": 10,

View file

@ -117,7 +117,7 @@ func verifyAuth(r *web.Route, authMethods []auth.Method) {
var err error
ctx.Doer, err = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
if err != nil {
log.Error("Failed to verify user: %v", err)
log.Info("Failed to verify user: %v", err)
ctx.Error(http.StatusUnauthorized, "authGroup.Verify")
return
}

View file

@ -437,11 +437,11 @@ func EndUploadBlob(ctx *context.Context) {
return
}
doClose = false
if err := uploader.Close(); err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
doClose = false
if err := container_service.RemoveBlobUploadByID(ctx, uploader.ID); err != nil {
apiError(ctx, http.StatusInternalServerError, err)

View file

@ -25,7 +25,8 @@ func LogAndProcessError(ctx *context.Context, status int, obj any, cb func(strin
message = fmt.Sprintf("%s", obj)
}
if status == http.StatusInternalServerError {
log.ErrorWithSkip(1, message)
// LogAndProcessError is always wrapped in a `apiError` call, so we need to skip two frames
log.ErrorWithSkip(2, message)
if setting.IsProd && (ctx.Doer == nil || !ctx.Doer.IsAdmin) {
message = ""

View file

@ -241,7 +241,7 @@ func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEn
return nil, nil, nil
}
if entry.IsDir() || entry.IsSubModule() {
if entry.IsDir() || entry.IsSubmodule() {
ctx.NotFound("getBlobForEntry", nil)
return nil, nil, nil
}

View file

@ -920,25 +920,29 @@ func ExcerptBlob(ctx *context.Context) {
ctx.Error(http.StatusInternalServerError, "getExcerptLines")
return
}
if idxRight > lastRight {
// After the "up" or "down" expansion, check if there's any remaining content in the diff and add a line that will
// be rendered into a new expander at either the top, or bottom.
lineSection := &gitdiff.DiffLine{
Type: gitdiff.DiffLineSection,
SectionInfo: &gitdiff.DiffLineSectionInfo{
Path: filePath,
LastLeftIdx: lastLeft,
LastRightIdx: lastRight,
LeftIdx: idxLeft,
RightIdx: idxRight,
LeftHunkSize: leftHunkSize,
RightHunkSize: rightHunkSize,
},
}
if lineSection.GetExpandDirection() != gitdiff.DiffLineExpandNone {
lineText := " "
if rightHunkSize > 0 || leftHunkSize > 0 {
lineText = fmt.Sprintf("@@ -%d,%d +%d,%d @@\n", idxLeft, leftHunkSize, idxRight, rightHunkSize)
}
lineText = html.EscapeString(lineText)
lineSection := &gitdiff.DiffLine{
Type: gitdiff.DiffLineSection,
Content: lineText,
SectionInfo: &gitdiff.DiffLineSectionInfo{
Path: filePath,
LastLeftIdx: lastLeft,
LastRightIdx: lastRight,
LeftIdx: idxLeft,
RightIdx: idxRight,
LeftHunkSize: leftHunkSize,
RightHunkSize: rightHunkSize,
},
}
lineSection.Content = lineText
switch direction {
case "up":
section.Lines = append([]*gitdiff.DiffLine{lineSection}, section.Lines...)

View file

@ -92,7 +92,7 @@ func getBlobForEntry(ctx *context.Context) (*git.Blob, *time.Time) {
return nil, nil
}
if entry.IsDir() || entry.IsSubModule() {
if entry.IsDir() || entry.IsSubmodule() {
ctx.NotFound("getBlobForEntry", nil)
return nil, nil
}

View file

@ -2401,10 +2401,6 @@ func UpdateIssueMilestone(ctx *context.Context) {
}
if ctx.FormBool("htmx") {
renderMilestones(ctx)
if ctx.Written() {
return
}
prepareHiddenCommentType(ctx)
if ctx.Written() {
return

View file

@ -42,7 +42,7 @@ func isExcludedEntry(entry *git.TreeEntry) bool {
return true
}
if entry.IsSubModule() {
if entry.IsSubmodule() {
return true
}

View file

@ -1057,14 +1057,13 @@ func renderHomeCode(ctx *context.Context) {
return
}
if entry.IsSubModule() {
subModuleURL, err := ctx.Repo.Commit.GetSubModule(entry.Name())
if entry.IsSubmodule() {
submodule, err := ctx.Repo.Commit.GetSubmodule(ctx.Repo.TreePath, entry)
if err != nil {
HandleGitError(ctx, "Repo.Commit.GetSubModule", err)
HandleGitError(ctx, "Repo.Commit.GetSubmodule", err)
return
}
subModuleFile := git.NewSubModuleFile(ctx.Repo.Commit, subModuleURL, entry.ID.String())
ctx.Redirect(subModuleFile.RefURL(setting.AppURL, ctx.Repo.Repository.FullName(), setting.SSH.Domain))
ctx.Redirect(submodule.ResolveUpstreamURL(ctx.Repo.Repository.HTMLURL()))
} else if entry.IsDir() {
renderDirectory(ctx)
} else {

View file

@ -118,7 +118,7 @@ func webAuth(authMethod auth_service.Method) func(*context.Context) {
return func(ctx *context.Context) {
ar, err := common.AuthShared(ctx.Base, ctx.Session, authMethod)
if err != nil {
log.Error("Failed to verify user: %v", err)
log.Info("Failed to verify user: %v", err)
ctx.Error(http.StatusUnauthorized, ctx.Locale.TrString("auth.unauthorized_credentials", "https://codeberg.org/forgejo/forgejo/issues/2809"))
return
}

View file

@ -85,11 +85,20 @@ type DiffLine struct {
// DiffLineSectionInfo represents diff line section meta data
type DiffLineSectionInfo struct {
Path string
LastLeftIdx int
LastRightIdx int
LeftIdx int
RightIdx int
Path string
// Last(Left/Right)Idx do not directly relate to this diff section, but indicate the last line number in the
// previous diff section. Set to 0 for the first diff section of a file, and 1 for the first line of code in the
// file.
LastLeftIdx int
LastRightIdx int
// (Left/Right)Idx are the first line number in this diff section
LeftIdx int
RightIdx int
// Number of lines contained within each diff section. In the UI, these fields are set to 0 in cases where a
// section is being used as a placeholder at the end of a diff to allow expansion into the remainder of the file.
LeftHunkSize int
RightHunkSize int
}
@ -157,7 +166,7 @@ func (d *DiffLine) GetExpandDirection() DiffLineExpandDirection {
}
if d.SectionInfo.LastLeftIdx <= 0 && d.SectionInfo.LastRightIdx <= 0 {
return DiffLineExpandUp
} else if d.SectionInfo.RightIdx-d.SectionInfo.LastRightIdx > BlobExcerptChunkSize && d.SectionInfo.RightHunkSize > 0 {
} else if d.SectionInfo.RightIdx-d.SectionInfo.LastRightIdx-1 > BlobExcerptChunkSize && d.SectionInfo.RightHunkSize > 0 {
return DiffLineExpandUpDown
} else if d.SectionInfo.LeftHunkSize <= 0 && d.SectionInfo.RightHunkSize <= 0 {
return DiffLineExpandDown

View file

@ -717,6 +717,163 @@ func TestGetDiffFull(t *testing.T) {
})
}
func TestDiffLine_GetExpandDirection(t *testing.T) {
tests := []struct {
name string
diffLine *DiffLine
expectedResult DiffLineExpandDirection
}{
{
name: "non-section line - no expansion",
diffLine: &DiffLine{
Type: DiffLineAdd,
SectionInfo: &DiffLineSectionInfo{},
},
expectedResult: DiffLineExpandNone,
},
{
name: "nil section info - no expansion",
diffLine: &DiffLine{
Type: DiffLineSection,
SectionInfo: nil,
},
expectedResult: DiffLineExpandNone,
},
{
name: "no lines between",
diffLine: &DiffLine{
Type: DiffLineSection,
SectionInfo: &DiffLineSectionInfo{
// Previous section of the diff displayed up to line 530...
LastRightIdx: 530,
LastLeftIdx: 530,
// This section of the diff starts at line 531...
RightIdx: 531,
LeftIdx: 531,
},
},
// There are zero lines between 530 and 531, so we should have nothing to expand.
expectedResult: DiffLineExpandNone,
},
{
name: "first diff section is the start of the file",
diffLine: &DiffLine{
Type: DiffLineSection,
SectionInfo: &DiffLineSectionInfo{
// Last[...]Idx is set to zero when it's the first section in the file (and not 1, which would be
// the first section -is- the first line of the file).
LastRightIdx: 0,
LastLeftIdx: 0,
// The diff section is showing line 1, the top of th efile.
RightIdx: 1,
LeftIdx: 1,
},
},
// We're at the top of the file; no expansion.
expectedResult: DiffLineExpandNone,
},
{
name: "first diff section doesn't start at the top of the file",
diffLine: &DiffLine{
Type: DiffLineSection,
SectionInfo: &DiffLineSectionInfo{
// Last[...]Idx is set to zero when it's the first section in the file (and not 1, which would be
// the first section -is- the first line of the file).
LastRightIdx: 0,
LastLeftIdx: 0,
RightIdx: 531,
LeftIdx: 531,
},
},
// We're at the top of the diff but there's content above, so can only expand up.
expectedResult: DiffLineExpandUp,
},
{
name: "middle of the file with single expansion",
diffLine: &DiffLine{
Type: DiffLineSection,
SectionInfo: &DiffLineSectionInfo{
// Previous section ended at ~500...
LastRightIdx: 500,
LastLeftIdx: 500,
// Next section starts one line away...
RightIdx: 502,
LeftIdx: 502,
// The next block has content (> 0)
RightHunkSize: 50,
LeftHunkSize: 50,
},
},
// Can be expanded in a single direction, displaying the missing line (501).
expectedResult: DiffLineExpandSingle,
},
{
name: "middle of the file with multi line expansion",
diffLine: &DiffLine{
Type: DiffLineSection,
SectionInfo: &DiffLineSectionInfo{
// Previous section ended at ~500...
LastRightIdx: 500,
LastLeftIdx: 500,
// Lines 501-520 are hidden, exactly 20 lines, matching BlobExcerptChunkSize (20)...
RightIdx: 521,
LeftIdx: 521,
// The next block has content (> 0)
RightHunkSize: 50,
LeftHunkSize: 50,
},
},
// Can be expanded in a single direction, displaying all the hidden 20 lines.
expectedResult: DiffLineExpandSingle,
},
{
name: "middle of the file with multi direction expansion",
diffLine: &DiffLine{
Type: DiffLineSection,
SectionInfo: &DiffLineSectionInfo{
// Previous section ended at ~500...
LastRightIdx: 500,
LastLeftIdx: 500,
// Lines 501-521 are hidden, exactly 21 lines, exceeding BlobExcerptChunkSize (20)...
RightIdx: 522,
LeftIdx: 522,
// The next block has content (> 0)
RightHunkSize: 50,
LeftHunkSize: 50,
},
},
// Now can be expanded down to display from 501-520 (521 remains hidden), or up to display 502-521 (501
// remains hidden).
expectedResult: DiffLineExpandUpDown,
},
{
name: "end of the diff but still file content to display",
diffLine: &DiffLine{
Type: DiffLineSection,
SectionInfo: &DiffLineSectionInfo{
// We had a previous diff section, of any size/location...
LastRightIdx: 200,
LastLeftIdx: 200,
RightIdx: 531,
LeftIdx: 531,
// Hunk size size 0 is a placeholder value for the end or beginning of a file...
RightHunkSize: 0,
LeftHunkSize: 0,
},
},
// Combination of conditions says we're at the end of the last diff section, can only expand down.
expectedResult: DiffLineExpandDown,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.diffLine.GetExpandDirection()
assert.Equal(t, tt.expectedResult, result)
})
}
}
func TestNoCrashes(t *testing.T) {
type testcase struct {
gitdiff string

View file

@ -404,6 +404,10 @@ func CheckPRsForBaseBranch(ctx context.Context, baseRepo *repo_model.Repository,
// Init runs the task queue to test all the checking status pull requests
func Init() error {
if err := LoadMergeMessageTemplates(); err != nil {
return err
}
prPatchCheckerQueue = queue.CreateUniqueQueue(graceful.GetManager().ShutdownContext(), "pr_patch_checker", handler)
if prPatchCheckerQueue == nil {

View file

@ -35,6 +35,30 @@ import (
notify_service "forgejo.org/services/notify"
)
var mergeMessageTemplates = make(map[repo_model.MergeStyle]string, len(repo_model.MergeStyles))
func LoadMergeMessageTemplates() error {
// Load templates for all known merge styles
for _, mergeStyle := range repo_model.MergeStyles {
templateFilename := filepath.Join(
setting.CustomPath,
"default_merge_message",
fmt.Sprintf("%s_TEMPLATE.md", strings.ToUpper(string(mergeStyle))),
)
content, err := os.ReadFile(templateFilename)
if err == nil {
mergeMessageTemplates[mergeStyle] = string(content)
} else if os.IsNotExist(err) {
// The file no longer exists, so delete any previous content
delete(mergeMessageTemplates, mergeStyle)
} else {
return err
}
}
return nil
}
// getMergeMessage composes the message used when merging a pull request.
func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issues_model.PullRequest, mergeStyle repo_model.MergeStyle, extraVars map[string]string) (message, body string, err error) {
if err := pr.LoadBaseRepo(ctx); err != nil {
@ -79,6 +103,13 @@ func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issue
if _, ok := err.(git.ErrNotExist); ok {
templateContent, err = commit.GetFileContent(templateFilepathGitea, setting.Repository.PullRequest.DefaultMergeMessageSize)
}
if _, ok := err.(git.ErrNotExist); ok {
if preloadedContent, ok := mergeMessageTemplates[mergeStyle]; ok {
templateContent, err = preloadedContent, nil
}
}
if err != nil {
if !git.IsErrNotExist(err) {
return "", "", err

View file

@ -4,9 +4,17 @@
package pull
import (
"os"
"path"
"strings"
"testing"
repo_model "forgejo.org/models/repo"
"forgejo.org/modules/setting"
"forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_expandDefaultMergeMessage(t *testing.T) {
@ -90,3 +98,51 @@ func TestAddCommitMessageTailer(t *testing.T) {
assert.Equal(t, "title\n\nTest-tailer: v1\nTest-tailer: v2", AddCommitMessageTrailer("title\n\nTest-tailer: v1", "Test-tailer", "v2"))
assert.Equal(t, "title\n\nTest-tailer: v1\nTest-tailer: v2", AddCommitMessageTrailer("title\n\nTest-tailer: v1\n", "Test-tailer", "v2"))
}
func prepareLoadMergeMessageTemplates(targetDir string) error {
for _, template := range []string{"MERGE", "REBASE", "REBASE-MERGE", "SQUASH", "MANUALLY-MERGED", "REBASE-UPDATE-ONLY"} {
file, err := os.Create(path.Join(targetDir, template+"_TEMPLATE.md"))
defer file.Close()
if err == nil {
_, err = file.WriteString("Contents for " + template)
}
if err != nil {
return err
}
}
return nil
}
func TestLoadMergeMessageTemplates(t *testing.T) {
defer test.MockVariableValue(&setting.CustomPath, t.TempDir())()
templateTemp := path.Join(setting.CustomPath, "default_merge_message")
require.NoError(t, os.MkdirAll(templateTemp, 0o755))
require.NoError(t, prepareLoadMergeMessageTemplates(templateTemp))
testStyles := []repo_model.MergeStyle{
repo_model.MergeStyleMerge,
repo_model.MergeStyleRebase,
repo_model.MergeStyleRebaseMerge,
repo_model.MergeStyleSquash,
repo_model.MergeStyleManuallyMerged,
repo_model.MergeStyleRebaseUpdate,
}
// Load all templates
require.NoError(t, LoadMergeMessageTemplates())
// Check their correctness
assert.Len(t, mergeMessageTemplates, len(testStyles))
for _, mergeStyle := range testStyles {
assert.Equal(t, "Contents for "+strings.ToUpper(string(mergeStyle)), mergeMessageTemplates[mergeStyle])
}
// Unload all templates
require.NoError(t, os.RemoveAll(templateTemp))
require.NoError(t, LoadMergeMessageTemplates())
assert.Empty(t, mergeMessageTemplates)
}

View file

@ -106,12 +106,15 @@ func (t *testPatchContext) LoadHeadRevision(ctx context.Context, pr *issues_mode
}
// getTestPatchCtx constructs a new testpatch context for the given pull request.
func getTestPatchCtx(ctx context.Context, pr *issues_model.PullRequest) (*testPatchContext, error) {
// If `onBare` is true, then the context will use the base repository that does
// not contain a working tree. Otherwise a temprorary repository is created that
// contains a working tree.
func getTestPatchCtx(ctx context.Context, pr *issues_model.PullRequest, onBare bool) (*testPatchContext, error) {
testPatchCtx := &testPatchContext{
close: func() {},
}
if git.SupportGitMergeTree {
if onBare {
if err := pr.LoadBaseRepo(ctx); err != nil {
return testPatchCtx, fmt.Errorf("LoadBaseRepo: %w", err)
}
@ -157,7 +160,7 @@ func getTestPatchCtx(ctx context.Context, pr *issues_model.PullRequest) (*testPa
}
func testPatch(ctx context.Context, pr *issues_model.PullRequest) (*testPatchContext, error) {
testPatchCtx, err := getTestPatchCtx(ctx, pr)
testPatchCtx, err := getTestPatchCtx(ctx, pr, git.SupportGitMergeTree)
if err != nil {
return testPatchCtx, fmt.Errorf("getTestPatchCtx: %w", err)
}

View file

@ -4,11 +4,9 @@
package pull
import (
"bytes"
"context"
"fmt"
"io"
"os"
"regexp"
"strings"
"time"
@ -30,7 +28,6 @@ import (
repo_module "forgejo.org/modules/repository"
"forgejo.org/modules/setting"
"forgejo.org/modules/sync"
"forgejo.org/modules/util"
gitea_context "forgejo.org/services/context"
issue_service "forgejo.org/services/issue"
notify_service "forgejo.org/services/notify"
@ -384,98 +381,51 @@ func TestPullRequest(ctx context.Context, doer *user_model.User, repoID, olderTh
// Update commit divergence.
func ValidatePullRequest(ctx context.Context, pr *issues_model.PullRequest, newCommitID, oldCommitID string, doer *user_model.User) {
objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName)
if newCommitID != "" && newCommitID != objectFormat.EmptyObjectID().String() {
changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID)
if newCommitID == "" || newCommitID == objectFormat.EmptyObjectID().String() {
return
}
testPatchCtx, err := getTestPatchCtx(ctx, pr, true)
defer testPatchCtx.close()
if err != nil {
log.Error("testPatchCtx: %v", err)
return
}
changed, err := testPatchCtx.gitRepo.CheckIfDiffDiffers(testPatchCtx.baseRev, oldCommitID, newCommitID, testPatchCtx.env)
if err != nil {
log.Error("CheckIfDiffDiffers: %v", err)
}
if changed {
if err := issues_model.MarkReviewsAsStale(ctx, pr.IssueID); err != nil {
log.Error("MarkReviewsAsStale: %v", err)
}
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
if err != nil {
log.Error("checkIfPRContentChanged: %v", err)
log.Error("GetFirstMatchProtectedBranchRule: %v", err)
}
if changed {
if err := issues_model.MarkReviewsAsStale(ctx, pr.IssueID); err != nil {
log.Error("MarkReviewsAsStale: %v", err)
if pb != nil && pb.DismissStaleApprovals {
if err := DismissApprovalReviews(ctx, doer, pr); err != nil {
log.Error("DismissApprovalReviews: %v", err)
}
}
}
if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, newCommitID); err != nil {
log.Error("MarkReviewsAsNotStale: %v", err)
}
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
if err != nil {
log.Error("GetFirstMatchProtectedBranchRule: %v", err)
}
if pb != nil && pb.DismissStaleApprovals {
if err := DismissApprovalReviews(ctx, doer, pr); err != nil {
log.Error("DismissApprovalReviews: %v", err)
}
}
}
if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, newCommitID); err != nil {
log.Error("MarkReviewsAsNotStale: %v", err)
}
divergence, err := GetDiverging(ctx, pr)
divergence, err := git.GetDivergingCommits(ctx, testPatchCtx.gitRepo.Path, testPatchCtx.baseRev, testPatchCtx.headRev, testPatchCtx.env)
if err != nil {
log.Error("GetDivergingCommits: %v", err)
} else {
err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind)
if err != nil {
log.Error("GetDiverging: %v", err)
} else {
err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind)
if err != nil {
log.Error("UpdateCommitDivergence: %v", err)
}
log.Error("UpdateCommitDivergence: %v", err)
}
}
}
// checkIfPRContentChanged checks if diff to target branch has changed by push
// A commit can be considered to leave the PR untouched if the patch/diff with its merge base is unchanged
func checkIfPRContentChanged(ctx context.Context, pr *issues_model.PullRequest, oldCommitID, newCommitID string) (hasChanged bool, err error) {
prCtx, cancel, err := createTemporaryRepoForPR(ctx, pr)
if err != nil {
log.Error("CreateTemporaryRepoForPR %-v: %v", pr, err)
return false, err
}
defer cancel()
tmpRepo, err := git.OpenRepository(ctx, prCtx.tmpBasePath)
if err != nil {
return false, fmt.Errorf("OpenRepository: %w", err)
}
defer tmpRepo.Close()
// Find the merge-base
_, base, err := tmpRepo.GetMergeBase("", "base", "tracking")
if err != nil {
return false, fmt.Errorf("GetMergeBase: %w", err)
}
cmd := git.NewCommand(ctx, "diff", "--name-only", "-z").AddDynamicArguments(newCommitID, oldCommitID, base)
stdoutReader, stdoutWriter, err := os.Pipe()
if err != nil {
return false, fmt.Errorf("unable to open pipe for to run diff: %w", err)
}
stderr := new(bytes.Buffer)
if err := cmd.Run(&git.RunOpts{
Dir: prCtx.tmpBasePath,
Stdout: stdoutWriter,
Stderr: stderr,
PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error {
_ = stdoutWriter.Close()
defer func() {
_ = stdoutReader.Close()
}()
return util.IsEmptyReader(stdoutReader)
},
}); err != nil {
if err == util.ErrNotEmpty {
return true, nil
}
err = git.ConcatenateError(err, stderr.String())
log.Error("Unable to run diff on %s %s %s in tempRepo for PR[%d]%s/%s...%s/%s: Error: %v",
newCommitID, oldCommitID, base,
pr.ID, pr.BaseRepo.FullName(), pr.BaseBranch, pr.HeadRepo.FullName(), pr.HeadBranch,
err)
return false, fmt.Errorf("Unable to run git diff --name-only -z %s %s %s: %w", newCommitID, oldCommitID, base, err)
}
return false, nil
}
// PushToBaseRepo pushes commits from branches of head repository to
// corresponding branches of base repository.
// FIXME: Only push branches that are actually updates?

View file

@ -92,3 +92,39 @@ func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) {
assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo2:branch2 into master", mergeMessage)
}
func TestPullRequest_GetDefaultMergeMessage_GlobalTemplate(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
require.NoError(t, pr.LoadBaseRepo(t.Context()))
gitRepo, err := gitrepo.OpenRepository(t.Context(), pr.BaseRepo)
require.NoError(t, err)
defer gitRepo.Close()
templateRepo, err := git.OpenRepository(t.Context(), "./../../modules/git/tests/repos/templates_repo")
require.NoError(t, err)
defer templateRepo.Close()
mergeMessageTemplates[repo_model.MergeStyleMerge] = "${PullRequestTitle} (${PullRequestReference})\n${PullRequestDescription}"
// Check template is used for Merge...
mergeMessage, body, err := GetDefaultMergeMessage(t.Context(), gitRepo, pr, repo_model.MergeStyleMerge)
require.NoError(t, err)
assert.Equal(t, "issue3 (#3)", mergeMessage)
assert.Equal(t, "content for the third issue", body)
// ...but not for RebaseMerge
mergeMessage, _, err = GetDefaultMergeMessage(t.Context(), gitRepo, pr, repo_model.MergeStyleRebaseMerge)
require.NoError(t, err)
assert.Equal(t, "Merge pull request 'issue3' (#3) from branch2 into master", mergeMessage)
// ...and that custom Merge template takes priority
mergeMessage, body, err = GetDefaultMergeMessage(t.Context(), templateRepo, pr, repo_model.MergeStyleMerge)
require.NoError(t, err)
assert.Equal(t, "Default merge message template", mergeMessage)
assert.Equal(t, "This line was read from .forgejo/default_merge_message/MERGE_TEMPLATE.md", body)
}

View file

@ -298,10 +298,16 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos
if headCommitID == commitID {
stale = false
} else {
stale, err = checkIfPRContentChanged(ctx, pr, commitID, headCommitID)
testPatchCtx, err := getTestPatchCtx(ctx, pr, true)
defer testPatchCtx.close()
if err != nil {
return nil, nil, err
}
stale, err = testPatchCtx.gitRepo.CheckIfDiffDiffers(testPatchCtx.baseRev, commitID, headCommitID, testPatchCtx.env)
if err != nil {
return nil, nil, fmt.Errorf("CheckIfDiffDiffers: %w", err)
}
}
}

View file

@ -108,7 +108,7 @@ func GetObjectTypeFromTreeEntry(entry *git.TreeEntry) ContentType {
switch {
case entry.IsDir():
return ContentTypeDir
case entry.IsSubModule():
case entry.IsSubmodule():
return ContentTypeSubmodule
case entry.IsExecutable(), entry.IsRegular():
return ContentTypeRegular
@ -211,14 +211,14 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref
return nil, err
}
contentsResponse.Target = &targetFromContent
} else if entry.IsSubModule() {
} else if entry.IsSubmodule() {
contentsResponse.Type = string(ContentTypeSubmodule)
submoduleURL, err := commit.GetSubModule(treePath)
submodule, err := commit.GetSubmodule(treePath, entry)
if err != nil {
return nil, err
}
if submoduleURL != "" {
contentsResponse.SubmoduleGitURL = &submoduleURL
if submodule.URL != "" {
contentsResponse.SubmoduleGitURL = &submodule.URL
}
}
// Handle links
@ -230,7 +230,7 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref
downloadURLString := downloadURL.String()
contentsResponse.DownloadURL = &downloadURLString
}
if !entry.IsSubModule() {
if !entry.IsSubmodule() {
htmlURL, err := url.Parse(repo.HTMLURL() + "/src/" + url.PathEscape(string(refType)) + "/" + util.PathEscapeSegments(ref) + "/" + util.PathEscapeSegments(treePath))
if err != nil {
return nil, err

View file

@ -87,7 +87,7 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
if entries[e].IsDir() {
copy(treeURL[copyPos:], entries[e].ID.String())
tree.Entries[i].URL = string(treeURL)
} else if entries[e].IsSubModule() {
} else if entries[e].IsSubmodule() {
// In Github Rest API Version=2022-11-28, if a tree entry is a submodule,
// its url will be returned as an empty string.
// So the URL will be set to "" here.

View file

@ -1,4 +1,4 @@
<div id="insert-timeline" hx-swap-oob="beforebegin">
{{template "repo/issue/view_content/comments" .}}
</div>
{{template "repo/issue/view_content/sidebar/milestones" .}}
{{template "repo/issue/milestone/selected" .}}

View file

@ -0,0 +1,11 @@
<div id="selected-milestone" class="ui select-milestone list">
<span class="no-select item {{if .Issue.Milestone}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_milestone"}}</span>
<div class="selected">
{{if .Issue.Milestone}}
<a class="item muted sidebar-item-link" href="{{.RepoLink}}/milestone/{{.Issue.Milestone.ID}}">
{{svg "octicon-milestone" 18 "tw-mr-2"}}
{{.Issue.Milestone.Name}}
</a>
{{end}}
</div>
</div>

View file

@ -1,4 +1,4 @@
<div id="milestone-section" hx-swap="morph" hx-target="this" hx-indicator="this">
<div id="milestone-section" hx-swap="morph" hx-target="#selected-milestone" hx-indicator="this">
<div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-milestone dropdown">
<a class="text muted flex-text-block">
<strong>{{ctx.Locale.Tr "repo.issues.new.milestone"}}</strong>
@ -10,15 +10,5 @@
{{template "repo/issue/milestone/select_menu" .}}
</div>
</div>
<div class="ui select-milestone list">
<span class="no-select item {{if .Issue.Milestone}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_milestone"}}</span>
<div class="selected">
{{if .Issue.Milestone}}
<a class="item muted sidebar-item-link" href="{{.RepoLink}}/milestone/{{.Issue.Milestone.ID}}">
{{svg "octicon-milestone" 18 "tw-mr-2"}}
{{.Issue.Milestone.Name}}
</a>
{{end}}
</div>
</div>
{{template "repo/issue/milestone/selected" .}}
</div>

View file

@ -1,7 +1,14 @@
<div class="ui equal width compact grid">
<div class="ui reference">
{{$issueReferenceLink := printf "%s#%d" .Issue.Repo.FullName .Issue.Index}}
<div class="row tw-items-center" data-tooltip-content="{{$issueReferenceLink}}">
<span class="text column truncate">{{ctx.Locale.Tr "repo.issues.reference_link" $issueReferenceLink}}</span>
<button class="ui two wide button column tw-p-2" data-clipboard-text="{{$issueReferenceLink}}">{{svg "octicon-copy" 14}}</button>
<span class="text">
<strong>
{{ctx.Locale.Tr "discussion.sidebar.reference"}}
</strong>
</span>
<div class="ui equal width compact grid">
<div class="row tw-items-center" data-tooltip-content="{{$issueReferenceLink}}">
<span class="text column truncate">{{$issueReferenceLink}}</span>
<button class="ui two wide button column tw-p-2" data-clipboard-text="{{$issueReferenceLink}}">{{svg "octicon-copy" 14}}</button>
</div>
</div>
</div>

View file

@ -292,7 +292,7 @@
<div class="field {{if .Err_PushMirrorBranchFilter}}error{{end}}">
<label for="push_mirror_branch_filter">{{ctx.Locale.Tr "repo.settings.push_mirror.branch_filter.label"}}</label>
<input id="push_mirror_branch_filter" name="push_mirror_branch_filter" value="{{.push_mirror_branch_filter}}" maxlength="2048">
<p class="help">{{ctx.Locale.Tr "repo.settings.push_mirror.branch_filter.description" "https://forgejo.org/docs/latest/user/repo-mirror/#branch-filter" "forgejo"}}</p>
<p class="help">{{ctx.Locale.Tr "repo.settings.push_mirror.branch_filter.description" "https://forgejo.org/docs/latest/user/repo-mirror/#branch-filter" "Forgejo"}}</p>
</div>
<details class="ui optional field" {{if or .Err_PushMirrorAuth .push_mirror_username}}open{{end}}>
<summary class="tw-p-1">

View file

@ -24,7 +24,7 @@
<div class="ui small input">
<input id="push-mirror-edit-branch-filter" name="push_mirror_branch_filter" maxlength="2048">
</div>
<p class="help">{{ctx.Locale.Tr "repo.settings.push_mirror.branch_filter.description" "https://forgejo.org/docs/latest/user/repo-mirror/#branch-filter" "forgejo"}}</p>
<p class="help">{{ctx.Locale.Tr "repo.settings.push_mirror.branch_filter.description" "https://forgejo.org/docs/latest/user/repo-mirror/#branch-filter" "Forgejo"}}</p>
</div>
<div class="actions">
<button class="ui small basic cancel button">

View file

@ -20,17 +20,17 @@
{{range $item := .Files}}
{{$entry := $item.Entry}}
{{$commit := $item.Commit}}
{{$subModuleFile := $item.SubModuleFile}}
<tr data-entryname="{{$entry.Name}}" data-ready="{{if $commit}}true{{else}}false{{end}}" class="{{if not $commit}}not{{end}}ready entry">
<td class="name four wide">
<span class="truncate">
{{if $entry.IsSubModule}}
{{$refURL := $subModuleFile.RefURL AppUrl $.Repository.FullName $.SSHDomain}} {{/* FIXME: the usage of AppUrl seems incorrect, it would be fixed in the future, use AppSubUrl instead */}}
{{if $entry.IsSubmodule}}
{{$submodule := $item.Submodule}}
{{$refURL := $submodule.ResolveUpstreamURL $.Repository.HTMLURL}}
{{$icon := (svg "octicon-file-submodule" 16 "tw-mr-2")}}
{{if $refURL}}
<a class="muted" href="{{$refURL}}">{{$icon}}{{$entry.Name}}</a><span class="at">@</span><a href="{{$refURL}}/commit/{{PathEscape $subModuleFile.RefID}}">{{ShortSha $subModuleFile.RefID}}</a>
<a class="muted" href="{{$refURL}}">{{$icon}}{{$entry.Name}}</a><span class="at">@</span><a href="{{$refURL}}/commit/{{PathEscape $submodule.Commit.String}}">{{ShortSha $submodule.Commit.String}}</a>
{{else}}
{{$icon}}{{$entry.Name}}<span class="at">@</span>{{ShortSha $subModuleFile.RefID}}
{{$icon}}{{$entry.Name}}<span class="at">@</span>{{ShortSha $submodule.Commit.String}}
{{end}}
{{else}}
{{if $entry.IsDir}}

View file

@ -83,6 +83,22 @@ func DeclareGitRepos(t *testing.T) func() {
Filename: "a-file",
Versions: []string{"{a}{а}"},
}}, nil),
newRepo(t, 2, "multiple-combo-boxes", nil, []FileChanges{{
Filename: ".forgejo/issue_template/multi-combo-boxes.yaml",
Versions: []string{`
name: "Multiple combo-boxes"
description: "To show something"
body:
- type: textarea
id: textarea-one
attributes:
label: one
- type: textarea
id: textarea-two
attributes:
label: two
`},
}}, nil),
newRepo(t, 11, "dependency-test", &tests.DeclarativeRepoOptions{
UnitConfig: optional.Some(map[unit_model.Type]convert.Conversion{
unit_model.TypeIssues: &repo_model.IssuesConfig{

View file

@ -221,7 +221,7 @@ test('Issue: Milestone', async ({page}, workerInfo) => {
const response = await page.goto('/user2/repo1/issues/1');
expect(response?.status()).toBe(200);
const selectedMilestone = page.locator('.issue-content-right .select-milestone.list');
const selectedMilestone = page.locator('.issue-content-right #selected-milestone');
const milestoneDropdown = page.locator('.issue-content-right .select-milestone.dropdown');
await expect(selectedMilestone).toContainText('No milestone');

View file

@ -456,3 +456,62 @@ test('Combo Markdown: preview mode switch', async ({page}) => {
await expect(previewPanel).toBeHidden();
await save_visual(page);
});
test('Multiple combo markdown: insert table', async ({page}) => {
const response = await page.goto('/user2/multiple-combo-boxes/issues/new?template=.forgejo%2fissue_template%2fmulti-combo-boxes.yaml');
expect(response?.status()).toBe(200);
// check that there are two textareas
const textareaOne = page.locator('textarea[name=form-field-textarea-one]');
const comboboxOne = page.locator('textarea#_combo_markdown_editor_0');
await expect(textareaOne).toBeVisible();
await expect(comboboxOne).toBeHidden();
const textareaTwo = page.locator('textarea[name=form-field-textarea-two]');
const comboboxTwo = page.locator('textarea#_combo_markdown_editor_1');
await expect(textareaTwo).toBeVisible();
await expect(comboboxTwo).toBeHidden();
// focus first one and add table to it
await textareaOne.click();
await expect(comboboxOne).toBeVisible();
await expect(comboboxTwo).toBeHidden();
const newTableButtonOne = page.locator('[for="_combo_markdown_editor_0"] button[data-md-action="new-table"]');
await newTableButtonOne.click();
const newTableModalOne = page.locator('div[data-markdown-table-modal-id="0"]');
await expect(newTableModalOne).toBeVisible();
await newTableModalOne.locator('input[name="table-rows"]').fill('3');
await newTableModalOne.locator('input[name="table-columns"]').fill('2');
await newTableModalOne.locator('button[data-selector-name="ok-button"]').click();
await expect(newTableModalOne).toBeHidden();
await expect(comboboxOne).toHaveValue('| Header | Header |\n|---------|---------|\n| Content | Content |\n| Content | Content |\n| Content | Content |\n');
await expect(comboboxTwo).toBeEmpty();
await save_visual(page);
// focus second one and add table to it
await textareaTwo.click();
await expect(comboboxOne).toBeHidden();
await expect(comboboxTwo).toBeVisible();
const newTableButtonTwo = page.locator('[for="_combo_markdown_editor_1"] button[data-md-action="new-table"]');
await newTableButtonTwo.click();
const newTableModalTwo = page.locator('div[data-markdown-table-modal-id="1"]');
await expect(newTableModalTwo).toBeVisible();
await newTableModalTwo.locator('input[name="table-rows"]').fill('2');
await newTableModalTwo.locator('input[name="table-columns"]').fill('3');
await newTableModalTwo.locator('button[data-selector-name="ok-button"]').click();
await expect(newTableModalTwo).toBeHidden();
await expect(comboboxOne).toHaveValue('| Header | Header |\n|---------|---------|\n| Content | Content |\n| Content | Content |\n| Content | Content |\n');
await expect(comboboxTwo).toHaveValue('| Header | Header | Header |\n|---------|---------|---------|\n| Content | Content | Content |\n| Content | Content | Content |\n');
await save_visual(page);
});

View file

@ -784,47 +784,66 @@ func TestPullRequestStaleReview(t *testing.T) {
)
defer f()
// Clone it.
dstPath := t.TempDir()
r := fmt.Sprintf("%suser2/%s.git", u.String(), repo.Name)
cloneURL, _ := url.Parse(r)
cloneURL.User = url.UserPassword("user2", userPassword)
require.NoError(t, git.CloneWithArgs(t.Context(), nil, cloneURL.String(), dstPath, git.CloneRepoOptions{}))
clone := func(t *testing.T, clone string) string {
t.Helper()
// Create first commit.
require.NoError(t, os.WriteFile(path.Join(dstPath, "README.md"), []byte("## test content"), 0o600))
require.NoError(t, git.AddChanges(dstPath, true))
require.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{
Committer: &git.Signature{
Email: "user2@example.com",
Name: "user2",
When: time.Now(),
},
Author: &git.Signature{
Email: "user2@example.com",
Name: "user2",
When: time.Now(),
},
Message: "Add README.",
}))
stdout := &bytes.Buffer{}
require.NoError(t, git.NewCommand(t.Context(), "rev-parse", "HEAD").Run(&git.RunOpts{Dir: dstPath, Stdout: stdout}))
firstCommitID := strings.TrimSpace(stdout.String())
dstPath := t.TempDir()
cloneURL, _ := url.Parse(clone)
cloneURL.User = url.UserPassword("user2", userPassword)
require.NoError(t, git.CloneWithArgs(t.Context(), nil, cloneURL.String(), dstPath, git.CloneRepoOptions{}))
// Create agit PR.
require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr").Run(&git.RunOpts{Dir: dstPath}))
return dstPath
}
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{Index: 1, BaseRepoID: repo.ID})
firstCommit := func(t *testing.T, dstPath string) string {
t.Helper()
req := NewRequest(t, "GET", "/"+repo.FullName()+"/pulls/1/files/reviews/new_comment")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
require.NoError(t, os.WriteFile(path.Join(dstPath, "README.md"), []byte("## test content"), 0o600))
require.NoError(t, git.AddChanges(dstPath, true))
require.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{
Committer: &git.Signature{
Email: "user2@example.com",
Name: "user2",
When: time.Now(),
},
Author: &git.Signature{
Email: "user2@example.com",
Name: "user2",
When: time.Now(),
},
Message: "Add README.",
}))
stdout := &bytes.Buffer{}
require.NoError(t, git.NewCommand(t.Context(), "rev-parse", "HEAD").Run(&git.RunOpts{Dir: dstPath, Stdout: stdout}))
t.Run("Mark review as stale", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
return strings.TrimSpace(stdout.String())
}
// Create a approved review against against this commit.
req = NewRequestWithValues(t, "POST", "/"+repo.FullName()+"/pulls/1/files/reviews/comments", map[string]string{
secondCommit := func(t *testing.T, dstPath string) {
require.NoError(t, os.WriteFile(path.Join(dstPath, "README.md"), []byte("## I prefer this heading"), 0o600))
require.NoError(t, git.AddChanges(dstPath, true))
require.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{
Committer: &git.Signature{
Email: "user2@example.com",
Name: "user2",
When: time.Now(),
},
Author: &git.Signature{
Email: "user2@example.com",
Name: "user2",
When: time.Now(),
},
Message: "Add README.",
}))
}
firstReview := func(t *testing.T, firstCommitID string, index int64) {
t.Helper()
resp := session.MakeRequest(t, NewRequest(t, "GET", fmt.Sprintf("/%s/pulls/%d/files/reviews/new_comment", repo.FullName(), index)), http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/pulls/%d/files/reviews/comments", repo.FullName(), index), map[string]string{
"_csrf": doc.GetCSRF(),
"origin": doc.GetInputValueByName("origin"),
"latest_commit_id": firstCommitID,
@ -846,77 +865,166 @@ func TestPullRequestStaleReview(t *testing.T) {
"type": "comment",
})
session.MakeRequest(t, req, http.StatusOK)
}
// Review is not stale.
review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID})
assert.False(t, review.Stale)
// Create second commit
require.NoError(t, os.WriteFile(path.Join(dstPath, "README.md"), []byte("## I prefer this heading"), 0o600))
require.NoError(t, git.AddChanges(dstPath, true))
require.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{
Committer: &git.Signature{
Email: "user2@example.com",
Name: "user2",
When: time.Now(),
},
Author: &git.Signature{
Email: "user2@example.com",
Name: "user2",
When: time.Now(),
},
Message: "Add README.",
}))
// Push to agit PR.
require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr").Run(&git.RunOpts{Dir: dstPath}))
// Review is stale.
review = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID})
assert.True(t, review.Stale)
})
t.Run("Create stale review", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
staleReview := func(t *testing.T, firstCommitID string, index int64) {
// Review based on the first commit, which is a stale review because the
// PR's head is at the seconnd commit.
req := NewRequestWithValues(t, "POST", "/"+repo.FullName()+"/pulls/1/files/reviews/submit", map[string]string{
"_csrf": doc.GetCSRF(),
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/pulls/%d/files/reviews/submit", repo.FullName(), index), map[string]string{
"_csrf": GetCSRF(t, session, fmt.Sprintf("/%s/pulls/%d/files/reviews/new_comment", repo.FullName(), index)),
"commit_id": firstCommitID,
"content": "looks good",
"type": "approve",
})
session.MakeRequest(t, req, http.StatusOK)
}
// There does not exist a review that is not stale, because all reviews
// are based on the first commit and the PR's head is at the second commit.
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID}, "stale = false")
t.Run("Across repositories", func(t *testing.T) {
testRepoFork(t, session, "user2", repo.Name, "org3", "forked-repo")
// Clone it.
dstPath := clone(t, fmt.Sprintf("%sorg3/forked-repo.git", u.String()))
// Create first commit.
firstCommitID := firstCommit(t, dstPath)
// Create PR across repositories.
require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "main").Run(&git.RunOpts{Dir: dstPath}))
session.MakeRequest(t, NewRequestWithValues(t, "POST", repo.FullName()+"/compare/main...org3/forked-repo:main", map[string]string{
"_csrf": GetCSRF(t, session, repo.FullName()+"/compare/main...org3/forked-repo:main"),
"title": "pull request",
}), http.StatusOK)
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{Index: 1, BaseRepoID: repo.ID})
t.Run("Mark review as stale", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
// Create first review
firstReview(t, firstCommitID, pr.Index)
// Review is not stale.
review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID})
assert.False(t, review.Stale)
// Create second commit
secondCommit(t, dstPath)
// Push to PR.
require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "main").Run(&git.RunOpts{Dir: dstPath}))
// Review is stale.
assert.Eventually(t, func() bool {
return unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID}).Stale == true
}, time.Second*10, time.Microsecond*100)
})
t.Run("Create stale review", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
// Review based on the first commit, which is a stale review because the
// PR's head is at the seconnd commit.
staleReview(t, firstCommitID, pr.Index)
// There does not exist a review that is not stale, because all reviews
// are based on the first commit and the PR's head is at the second commit.
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID}, "stale = false")
})
t.Run("Mark unstale", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
// Force push the PR to the first commit.
require.NoError(t, git.NewCommand(t.Context(), "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath}))
require.NoError(t, git.NewCommand(t.Context(), "push", "--force-with-lease", "origin", "main").Run(&git.RunOpts{Dir: dstPath}))
// There does not exist a review that is stale, because all reviews
// are based on the first commit and thus all reviews are no longer marked
// as stale.
assert.Eventually(t, func() bool {
return !unittest.BeanExists(t, &issues_model.Review{IssueID: pr.IssueID}, "stale = true")
}, time.Second*10, time.Microsecond*100)
})
t.Run("Diff did not change", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
// Create a empty commit and push it to the PR.
require.NoError(t, git.NewCommand(t.Context(), "commit", "--allow-empty", "-m", "Empty commit").Run(&git.RunOpts{Dir: dstPath}))
require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "main").Run(&git.RunOpts{Dir: dstPath}))
// There does not exist a review that is stale, because the diff did not
// change.
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID}, "stale = true")
})
})
t.Run("Mark unstale", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
t.Run("AGit", func(t *testing.T) {
dstPath := clone(t, fmt.Sprintf("%suser2/%s.git", u.String(), repo.Name))
// Force push the PR to the first commit.
require.NoError(t, git.NewCommand(t.Context(), "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath}))
require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr", "-o", "force-push").Run(&git.RunOpts{Dir: dstPath}))
// Create first commit.
firstCommitID := firstCommit(t, dstPath)
// There does not exist a review that is stale, because all reviews
// are based on the first commit and thus all reviews are no longer marked
// as stale.
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID}, "stale = true")
})
t.Run("Diff did not change", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
// Create a empty commit and push it to the PR.
require.NoError(t, git.NewCommand(t.Context(), "commit", "--allow-empty", "-m", "Empty commit").Run(&git.RunOpts{Dir: dstPath}))
// Create agit PR.
require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr").Run(&git.RunOpts{Dir: dstPath}))
// There does not exist a review that is stale, because the diff did not
// change.
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID}, "stale = true")
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{Index: 2, BaseRepoID: repo.ID})
t.Run("Mark review as stale", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
firstReview(t, firstCommitID, pr.Index)
// Review is not stale.
review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID})
assert.False(t, review.Stale)
// Create second commit
secondCommit(t, dstPath)
// Push to agit PR.
require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr").Run(&git.RunOpts{Dir: dstPath}))
// Review is stale.
review = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID})
assert.True(t, review.Stale)
})
t.Run("Create stale review", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
// Review based on the first commit, which is a stale review because the
// PR's head is at the seconnd commit.
staleReview(t, firstCommitID, pr.Index)
// There does not exist a review that is not stale, because all reviews
// are based on the first commit and the PR's head is at the second commit.
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID}, "stale = false")
})
t.Run("Mark unstale", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
// Force push the PR to the first commit.
require.NoError(t, git.NewCommand(t.Context(), "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath}))
require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr", "-o", "force-push").Run(&git.RunOpts{Dir: dstPath}))
// There does not exist a review that is stale, because all reviews
// are based on the first commit and thus all reviews are no longer marked
// as stale.
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID}, "stale = true")
})
t.Run("Diff did not change", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
// Create a empty commit and push it to the PR.
require.NoError(t, git.NewCommand(t.Context(), "commit", "--allow-empty", "-m", "Empty commit").Run(&git.RunOpts{Dir: dstPath}))
require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr").Run(&git.RunOpts{Dir: dstPath}))
// There does not exist a review that is stale, because the diff did not
// change.
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID}, "stale = true")
})
})
})
}

View file

@ -132,9 +132,9 @@
}
},
"node_modules/@octokit/core": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.2.tgz",
"integrity": "sha512-ODsoD39Lq6vR6aBgvjTnA3nZGliknKboc9Gtxr7E4WDNqY24MxANKcuDQSF0jzapvGb3KWOEDrKfve4HoWGK+g==",
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.3.tgz",
"integrity": "sha512-oNXsh2ywth5aowwIa7RKtawnkdH6LgU1ztfP9AIUCQCvzysB+WeU8o2kyyosDPwBZutPpjZDKPQGIzzrfTWweQ==",
"license": "MIT",
"peer": true,
"dependencies": {
@ -494,9 +494,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "24.0.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz",
"integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==",
"version": "24.0.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.13.tgz",
"integrity": "sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==",
"license": "MIT",
"dependencies": {
"undici-types": "~7.8.0"
@ -2005,9 +2005,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.179",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.179.tgz",
"integrity": "sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==",
"version": "1.5.182",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.182.tgz",
"integrity": "sha512-Lv65Btwv9W4J9pyODI6EWpdnhfvrve/us5h1WspW8B2Fb0366REPtY3hX7ounk1CkV/TBjWCEvCBBbYbmV0qCA==",
"license": "ISC"
},
"node_modules/emoji-regex": {
@ -5850,9 +5850,9 @@
"license": "ISC"
},
"node_modules/nan": {
"version": "2.22.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz",
"integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==",
"version": "2.23.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz",
"integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==",
"license": "MIT",
"optional": true
},

View file

@ -226,7 +226,7 @@ export async function initDropzone(dropzoneEl, zone = undefined) {
inputPath.name = `files_fullpath[${data.uuid}]`;
inputPath.type = 'hidden';
inputPath.value = htmlEscape(file.fullPath || file.name);
dropzoneEl.querySelector('.files').append(input).append(inputPath);
dropzoneEl.querySelector('.files').append(input, inputPath);
// Create a "Copy Link" element, to conveniently copy the image
// or file link as Markdown to the clipboard

View file

@ -11,8 +11,6 @@ import {initTextExpander} from './TextExpander.js';
import {showErrorToast, showHintToast} from '../../modules/toast.js';
import {POST} from '../../modules/fetch.js';
let elementIdCounter = 0;
/**
* validate if the given textarea is non-empty.
* @param {HTMLElement} textarea - The textarea element to be validated.
@ -39,10 +37,13 @@ export function validateTextareaNonEmpty(textarea) {
const listPrefixRegex = /^\s*((\d+)[.)]\s|[-*+]\s{1,4}\[[ x]\]\s?|[-*+]\s|(>\s?)+)?/;
class ComboMarkdownEditor {
static idSuffixCounter = 0;
constructor(container, options = {}) {
container._giteaComboMarkdownEditor = this;
this.options = options;
this.container = container;
this.elementIdSuffix = ComboMarkdownEditor.idSuffixCounter++;
}
async init() {
@ -55,8 +56,6 @@ class ComboMarkdownEditor {
this.setupLinkInserter();
await this.switchToUserPreference();
elementIdCounter++;
}
applyEditorHeights(el, heights) {
@ -74,7 +73,7 @@ class ComboMarkdownEditor {
setupTextarea() {
this.textarea = this.container.querySelector('.markdown-text-editor');
this.textarea._giteaComboMarkdownEditor = this;
this.textarea.id = `_combo_markdown_editor_${elementIdCounter}`;
this.textarea.id = `_combo_markdown_editor_${this.elementIdSuffix}`;
this.textarea.addEventListener('input', (e) => this.options?.onContentChanged?.(this, e));
this.applyEditorHeights(this.textarea, this.options.editorHeights);
@ -96,8 +95,8 @@ class ComboMarkdownEditor {
this.textareaMarkdownToolbar.querySelector('button[data-md-action="unindent"]')?.addEventListener('click', () => {
this.indentSelection(true, false);
});
this.textareaMarkdownToolbar.querySelector('button[data-md-action="new-table"]')?.setAttribute('data-modal', `div[data-markdown-table-modal-id="${elementIdCounter}"]`);
this.textareaMarkdownToolbar.querySelector('button[data-md-action="new-link"]')?.setAttribute('data-modal', `div[data-markdown-link-modal-id="${elementIdCounter}"]`);
this.textareaMarkdownToolbar.querySelector('button[data-md-action="new-table"]')?.setAttribute('data-modal', `div[data-markdown-table-modal-id="${this.elementIdSuffix}"]`);
this.textareaMarkdownToolbar.querySelector('button[data-md-action="new-link"]')?.setAttribute('data-modal', `div[data-markdown-link-modal-id="${this.elementIdSuffix}"]`);
// Track whether any actual input or pointer action was made after focusing, and only intercept Tab presses after that.
this.tabEnabled = false;
@ -195,7 +194,7 @@ class ComboMarkdownEditor {
setupDropzone() {
const dropzoneParentContainer = this.container.getAttribute('data-dropzone-parent-container');
if (dropzoneParentContainer) {
this.dropzone = this.container.closest(this.container.getAttribute('data-dropzone-parent-container'))?.querySelector('.dropzone');
this.dropzone = this.container.closest(dropzoneParentContainer)?.querySelector('.dropzone');
}
}
@ -207,13 +206,13 @@ class ComboMarkdownEditor {
// So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic.
const tabEditor = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-writer');
const tabPreviewer = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-previewer');
tabEditor.setAttribute('data-tab', `markdown-writer-${elementIdCounter}`);
tabPreviewer.setAttribute('data-tab', `markdown-previewer-${elementIdCounter}`);
tabEditor.setAttribute('data-tab', `markdown-writer-${this.elementIdSuffix}`);
tabPreviewer.setAttribute('data-tab', `markdown-previewer-${this.elementIdSuffix}`);
const toolbar = $container[0].querySelector('markdown-toolbar');
const panelEditor = $container[0].querySelector('.ui.tab[data-tab-panel="markdown-writer"]');
const panelPreviewer = $container[0].querySelector('.ui.tab[data-tab-panel="markdown-previewer"]');
panelEditor.setAttribute('data-tab', `markdown-writer-${elementIdCounter}`);
panelPreviewer.setAttribute('data-tab', `markdown-previewer-${elementIdCounter}`);
panelEditor.setAttribute('data-tab', `markdown-writer-${this.elementIdSuffix}`);
panelPreviewer.setAttribute('data-tab', `markdown-previewer-${this.elementIdSuffix}`);
tabEditor.addEventListener('click', () => {
toolbar.classList.remove('markdown-toolbar-hidden');
@ -276,10 +275,10 @@ class ComboMarkdownEditor {
setupTableInserter() {
const newTableModal = this.container.querySelector('div[data-modal-name="new-markdown-table"]');
newTableModal.setAttribute('data-markdown-table-modal-id', elementIdCounter);
newTableModal.setAttribute('data-markdown-table-modal-id', this.elementIdSuffix);
const button = newTableModal.querySelector('button[data-selector-name="ok-button"]');
button.setAttribute('data-element-id', elementIdCounter);
button.setAttribute('data-element-id', this.elementIdSuffix);
button.addEventListener('click', this.addNewTable);
}
@ -311,8 +310,8 @@ class ComboMarkdownEditor {
setupLinkInserter() {
const newLinkModal = this.container.querySelector('div[data-modal-name="new-markdown-link"]');
newLinkModal.setAttribute('data-markdown-link-modal-id', elementIdCounter);
const textarea = document.getElementById(`_combo_markdown_editor_${elementIdCounter}`);
newLinkModal.setAttribute('data-markdown-link-modal-id', this.elementIdSuffix);
const textarea = document.getElementById(`_combo_markdown_editor_${this.elementIdSuffix}`);
$(newLinkModal).modal({
// Pre-fill the description field from the selection to create behavior similar
@ -331,7 +330,7 @@ class ComboMarkdownEditor {
});
const button = newLinkModal.querySelector('button[data-selector-name="ok-button"]');
button.setAttribute('data-element-id', elementIdCounter);
button.setAttribute('data-element-id', this.elementIdSuffix);
button.addEventListener('click', this.addNewLink);
}