Compare commits

..

59 commits

Author SHA1 Message Date
Earl Warren
c0eeb75322 fix: disable Forgejo Actions email notifications on recovery (#8374)
Some checks are pending
/ release (push) Waiting to run
testing-integration / test-unit (push) Has been skipped
testing-integration / test-sqlite (push) Has been skipped
testing / backend-checks (push) Has been skipped
testing / frontend-checks (push) Has been skipped
testing / test-unit (push) Has been skipped
testing / test-e2e (push) Has been skipped
testing / test-mysql (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
testing / test-remote-cacher (redis) (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / security-check (push) Has been skipped
- Make the migration to add an index a noop - do not remove it as it would break v12.next & v13.next
- Keep the logic that relies on finding the last run, only always fail to find which is the same as assuming each run is one of a kind

Refs forgejo/forgejo#8373

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8374
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Earl Warren <contact@earl-warren.org>
Co-committed-by: Earl Warren <contact@earl-warren.org>
2025-07-02 19:23:07 +02:00
Renovate Bot
bcde3aea4f Update module github.com/go-chi/cors to v1.2.2 (forgejo) (#8380)
Some checks are pending
/ release (push) Waiting to run
testing / test-unit (push) Has been skipped
testing / test-mysql (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
testing-integration / test-unit (push) Has been skipped
testing-integration / test-sqlite (push) Has been skipped
testing / backend-checks (push) Has been skipped
testing / frontend-checks (push) Has been skipped
testing / test-e2e (push) Has been skipped
testing / test-remote-cacher (redis) (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / security-check (push) Has been skipped
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8380
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-02 16:40:48 +02:00
Renovate Bot
abb95c8c92 Update dependency globals to v16.3.0 (forgejo) (#8381)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8381
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-07-02 16:34:56 +02:00
Gusted
0ecd9d9682 fix: user activation with uppercase email address (#8367)
- Right before the call to user email activation, the user is updated [^1]. This causes the email to be lowered, which in turn makes the call to activate the user activation fail (on database where collation is case sensitive, which is the recommend collation by Forgejo).
- The code in `BeforeUpdate` is quite confusing, the comment has become slightly out of date and was reworded to reflect reality and its purpose. The code is also slightly reworked to only lower the email for the `AvatarEmail` field to avoid causing side-effect.
- Added unit test.
- Resolves forgejo/forgejo#8354

[^1]: 4927d4ee3d/routers/web/auth/auth.go (L785)

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8367
Reviewed-by: Otto <otto@codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-07-02 13:04:22 +02:00
Bojidar Marinov
1ed750a33a feat: detect Interlisp sources as text (#8377)
Some checks are pending
/ release (push) Waiting to run
testing-integration / test-unit (push) Has been skipped
testing-integration / test-sqlite (push) Has been skipped
testing / backend-checks (push) Has been skipped
testing / frontend-checks (push) Has been skipped
testing / test-unit (push) Has been skipped
testing / test-e2e (push) Has been skipped
testing / test-mysql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / security-check (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / test-remote-cacher (redis) (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
This PR detects Interlisp files (files that include "(DEFINE-FILE-INFO" somewhere near the start, and do not have an .LCOM extension) as text files and displays them as such in the web UI.
To check for extensions, I had to extend the `typesniffer.DetectContentType` function to accept an extra filename parameter—which could be useful for future filetype detection features. It is possible that a few of the places I modified pass a full file path instead of just passing a file name.

Implements #8184

## Checklist

### 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... - NA
  - [ ] 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.

<!--start release-notes-assistant-->

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Features
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/8377): <!--number 8377 --><!--line 0 --><!--description ZGV0ZWN0IEludGVybGlzcCBzb3VyY2VzIGFzIHRleHQ=-->detect Interlisp sources as text<!--description-->
<!--end release-notes-assistant-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8377
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Bojidar Marinov <bojidar.marinov.bg@gmail.com>
Co-committed-by: Bojidar Marinov <bojidar.marinov.bg@gmail.com>
2025-07-02 07:38:46 +02:00
Michael Jerger
6f501b1fdf Improved signature handling & instance actor (#8275)
Some checks are pending
testing / test-mysql (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
testing / test-remote-cacher (redis) (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / security-check (push) Has been skipped
/ release (push) Waiting to run
testing-integration / test-unit (push) Has been skipped
testing-integration / test-sqlite (push) Has been skipped
testing / backend-checks (push) Has been skipped
testing / frontend-checks (push) Has been skipped
testing / test-unit (push) Has been skipped
testing / test-e2e (push) Has been skipped
This PR is part of https://codeberg.org/forgejo/forgejo/pulls/4767

It improves the signature handling:
1. move logic to a service (might be used from other services as well)
2. make a clear difference between ` ReqHTTPUserSignature` and `ReqHTTPUserOrInstanceSignature`
3. improve test ability (activitypub/client & distant_federation_server_mock

Adjust instance actor
1. name &
2. webfinger

## Strategy for next PRs is

Integration tests are in the driving seat.

I will step by step add integration tests form original PR and add code required by the integration test changes.

## Meta

Proposal howto process large PRs can be discussed here: https://codeberg.org/forgejo-contrib/federation/pulls/37

Current state with rendered diagrams can be found here: https://codeberg.org/meissa/federation/src/branch/merge-large-pr/doc/merge-large-pr.md

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8275
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Michael Jerger <michael.jerger@meissa-gmbh.de>
Co-committed-by: Michael Jerger <michael.jerger@meissa-gmbh.de>
2025-07-01 19:49:00 +02:00
Renovate Bot
7a8ff20bf3 Update github.com/google/pprof digest to 6e76a2b (forgejo) (#8368)
Some checks are pending
/ release (push) Waiting to run
testing-integration / test-unit (push) Has been skipped
testing-integration / test-sqlite (push) Has been skipped
testing / test-unit (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / backend-checks (push) Has been skipped
testing / frontend-checks (push) Has been skipped
testing / test-e2e (push) Has been skipped
testing / test-mysql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
testing / test-remote-cacher (redis) (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / security-check (push) Has been skipped
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8368
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-01 07:17:12 +02:00
Renovate Bot
8f28942233 Update ghcr.io/devcontainers/features/git-lfs Docker tag to v1.2.5 (forgejo) (#8369)
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-07-01 06:31:22 +02:00
Renovate Bot
2b5123a90f Update dependency @playwright/test to v1.53.2 (forgejo) (#8372)
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-07-01 06:09:20 +02:00
Renovate Bot
0730e5481f Update vitest monorepo to v3.2.4 (forgejo) (#8371)
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-07-01 06:08:49 +02:00
Robert Wolff
2f0a993a33 fix(ui): repo settings units overview anchor (#8322)
This fixes (or features?) a small nit that I found disturbing when clicking through repository settings.

# Test
- open repo settings
- click in navigation bar on "Units" and then on "Overview"
- the page opens now at top (not jumping down as before that change)
- click on "Issues", etc. -> the page still jumps to the related sections

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8322
Reviewed-by: Otto <otto@codeberg.org>
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: Robert Wolff <mahlzahn@posteo.de>
Co-committed-by: Robert Wolff <mahlzahn@posteo.de>
2025-07-01 01:07:19 +02:00
Gusted
0ecb25fdcb chore: do not require empty fixtures to clean tables (#8353)
Some checks are pending
/ release (push) Waiting to run
testing / frontend-checks (push) Has been skipped
testing / test-unit (push) Has been skipped
testing-integration / test-unit (push) Has been skipped
testing-integration / test-sqlite (push) Has been skipped
testing / backend-checks (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / security-check (push) Has been skipped
testing / test-e2e (push) Has been skipped
testing / test-mysql (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
testing / test-remote-cacher (redis) (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
- A mistake that is often made is to not put a empty fixture for tables that gets populated in a test and should be cleaned up between runs. The CI does not detect such issues as these never arise on the first run of the test suite and are only noticed when developers run a test for a second time, unless you are aware of this behavior (which very few do as this is not documented and pure folk knowledge) can end up in chasing a bug that does not exist for hours. forgejo/forgejo#7041, forgejo/forgejo#7419, meissa/forgejo#21, forgejo/forgejo#5771
- Because we now own the fixture loader (forgejo/forgejo#7715), its rather simple to ensure that all tables that did not receive fixtures should be cleaned between runs and removes the need to place empty fixture files.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8353
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Reviewed-by: Otto <otto@codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-06-30 23:04:16 +02:00
0ko
6e58d285c7 merge commit: fix(ui): Add pasted images to dropzone (#7749)
Some checks are pending
/ release (push) Waiting to run
testing-integration / test-unit (push) Has been skipped
testing-integration / test-sqlite (push) Has been skipped
testing / backend-checks (push) Has been skipped
testing / frontend-checks (push) Has been skipped
testing / test-unit (push) Has been skipped
testing / test-e2e (push) Has been skipped
testing / test-mysql (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
testing / test-remote-cacher (redis) (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / security-check (push) Has been skipped
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7749
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
2025-06-30 14:30:25 +02:00
Beowulf
6e66380408
fix(ui): Add pasted images to dropzone
This adds pasted images to the dropzone to provide the same experience
as when using the dropzone. This gives the possibility to preview and
delete the image. Additionally it provides a copy button to copy the
markdown code for inserting the image.

Removed the old implementation in `repo-legacy.js` and generalized
everything in `common-global.js` as common implementation.

Replaced old jquery code with plain JS.

Fixes #4588
2025-06-30 12:42:22 +02:00
Renovate Bot
06bff3bb7e Lock file maintenance (forgejo) (#8356)
Some checks failed
testing / test-remote-cacher (redis) (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / security-check (push) Has been skipped
/ release (push) Waiting to run
testing-integration / test-unit (push) Has been skipped
testing-integration / test-sqlite (push) Has been skipped
testing / backend-checks (push) Has been skipped
testing / frontend-checks (push) Has been skipped
testing / test-unit (push) Has been skipped
testing / test-e2e (push) Has been skipped
testing / test-mysql (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
Integration tests for the release process / release-simulation (push) Has been cancelled
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:eyJjcmVhdGVkSW5WZXIiOiI0MS4xLjQiLCJ1cGRhdGVkSW5WZXIiOiI0MS4xLjQiLCJ0YXJnZXRCcmFuY2giOiJmb3JnZWpvIiwibGFiZWxzIjpbImRlcGVuZGVuY3ktdXBncmFkZSIsInRlc3Qvbm90LW5lZWRlZCJdfQ==-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8356
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-06-30 06:53:08 +02:00
Renovate Bot
a943271205 Update renovate to v41.17.2 (forgejo) (#8355)
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-06-30 06:44:04 +02:00
Gusted
4927d4ee3d feat: detect incorrect integration test functions (#8352)
- I have seen multiple times where a test function tries to prepare the
testing environment more than once, this can lead to bugs and false
positives of testing code. I would attribute this to lack of
documentation on how to write integration tests.
- To detect such cases, keep track when we are in a prepared test
environment and fail when some testing code is tries to once again
prepare the test environment.
- The message is logged to the function call that is requesting to
prepare the test environment, for example: `change_default_branch_test.go:19: Cannot prepare a test environment if you are already in a test environment. This is a bug in your testing code.`

A example of what this will be able to catch, 6226f464ce:

```go
func TestFoo(t *testing.T) {
	defer PrepareTestEnv(t)()

	t.Run("Bar", func(t *testing.T) {
		defer PrepareTestEnv(t)() // Should very likely be PrintCurrentTest
	})
}
```

```go
func TestBar(t *testing.T) {
	onGiteaRun(t, func(t *testing.T, _ *url.URL) {
		defer PrepareTestEnv(t)() // Already called by onGiteaRun.
	})
}
```

```go
func TestFooBar(t *testing.T) {
	defer PrepareTestEnv(t)() // This will be called by onGiteaRun later on and very unlikely to do this before the call to onGiteaRun.
	onGiteaRun(t, func(t *testing.T, _ *url.URL) {
		// [...]
	})
}
```

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8352
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-06-29 23:09:29 +02:00
0ko
c57dea336c fix(ui): small org dashboard ui cleanup (#8327)
Some checks are pending
/ release (push) Waiting to run
testing-integration / test-unit (push) Has been skipped
testing / backend-checks (push) Has been skipped
testing / frontend-checks (push) Has been skipped
testing / test-unit (push) Has been skipped
testing / test-e2e (push) Has been skipped
testing / test-remote-cacher (redis) (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / test-mysql (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
testing / security-check (push) Has been skipped
testing-integration / test-sqlite (push) Has been skipped
Small UI cleanups in this area with no visual changes:
https://codeberg.org/attachments/4282f225-63e0-41b7-9edb-8b64877092b2

* remove classes `ui`, `top`, `attached`: following https://codeberg.org/forgejo/forgejo/pulls/2593, it is no longer a fomantic ui segment for those classes to be relevant to it
* use gap in flexbox as it is a cleaner way than setting margins

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8327
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Reviewed-by: Robert Wolff <mahlzahn@posteo.de>
Reviewed-by: Beowulf <beowulf@beocode.eu>
2025-06-29 16:22:07 +02:00
Renovate Bot
31fc02332a Update dependency svgo to v4 (forgejo) (#8343)
Some checks are pending
/ release (push) Waiting to run
testing-integration / test-unit (push) Has been skipped
testing-integration / test-sqlite (push) Has been skipped
testing / backend-checks (push) Has been skipped
testing / frontend-checks (push) Has been skipped
testing / test-unit (push) Has been skipped
testing / test-e2e (push) Has been skipped
testing / test-mysql (push) Has been skipped
testing / test-remote-cacher (redis) (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
testing / security-check (push) Has been skipped
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8343
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-06-29 13:57:15 +02:00
Renovate Bot
878ce241a4 Update dependency htmx.org to v2 (forgejo) (#8342)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8342
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-06-29 13:52:24 +02:00
Earl Warren
447c5789bd fix(ci): add install-minimum-git-version helper for workflows (#8345)
https://codeberg.org/forgejo-integration/forgejo/actions/runs/10592#jobstep-3-14 failed with

> E: Packages were downgraded and -y was used without --allow-downgrades.

Running the tests is done following the instructions in the workflow:

```
# - uncomment [on].pull_request
# - swap 'forgejo-integration' and 'forgejo-coding'
# - open a pull request at https://codeberg.org/forgejo/forgejo and fix things
# - swap 'forgejo-integration' and 'forgejo-coding'
# - comment [on].pull_request
```

The result of the test is available at https://codeberg.org/forgejo/forgejo/actions/runs/85408/jobs/0

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8345
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Earl Warren <contact@earl-warren.org>
Co-committed-by: Earl Warren <contact@earl-warren.org>
2025-06-29 13:04:28 +02:00
floss4good
920f6d24d2 fix: load OldMilestone based on OldMilestoneID, not MilestoneID (#8330)
Fixes #8329

## 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...
  - [ ] in their respective `*_test.go` for unit tests.
  - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server.

### Documentation

- [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.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8330
Reviewed-by: Robert Wolff <mahlzahn@posteo.de>
Co-authored-by: floss4good <floss4good@disroot.org>
Co-committed-by: floss4good <floss4good@disroot.org>
2025-06-29 12:08:03 +02:00
Renovate Bot
2160741221 Update dependency @vitejs/plugin-vue to v6 (forgejo) (#8341)
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-06-29 11:50:41 +02:00
Renovate Bot
3feceb10c7 Update dependency @stylistic/eslint-plugin to v5 (forgejo) (#8340)
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-06-29 11:40:47 +02:00
Renovate Bot
7a881e2f26 Update dependency happy-dom to v18.0.1 (forgejo) (#8337)
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-06-29 10:59:35 +02:00
Renovate Bot
ad1adabcbb Update linters (forgejo) (#8338)
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-06-29 09:37:44 +02:00
Renovate Bot
33217a3633 Update dependency @stylistic/stylelint-plugin to v3.1.3 (forgejo) (#8336)
Co-authored-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
Co-committed-by: Renovate Bot <forgejo-renovate-action@forgejo.org>
2025-06-29 09:37:43 +02:00
Gusted
84ed8aa740 chore: use standard library function (#8334)
- As mentioned in the comment, use the standard library function now its available.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8334
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-06-29 08:06:38 +02:00
Earl Warren
ba37b69252 feat: make API pull and compare endpoint references to head more robust (#8332)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8332
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
2025-06-29 07:35:47 +02:00
Mathieu Fenniak
b6c6981c30 feat(ui): add repository description to og:image:alt (#8325)
Some checks are pending
/ release (push) Waiting to run
testing / backend-checks (push) Has been skipped
testing-integration / test-unit (push) Has been skipped
testing-integration / test-sqlite (push) Has been skipped
testing / frontend-checks (push) Has been skipped
testing / test-unit (push) Has been skipped
testing / test-e2e (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / test-mysql (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
testing / test-remote-cacher (redis) (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
testing / security-check (push) Has been skipped
Followup to https://codeberg.org/forgejo/forgejo/pulls/6053

Adds the repository description to the "alt" tag of the OpenGraph summary card, improving accessibility when these images are displayed. Fixes #8192.

Other summary cards, for issues and releases, are not modified as they already contain the issue title or release title, which seems reasonable.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8325
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
2025-06-29 05:54:07 +02:00
Renovate Bot
14309837d4 Update module github.com/niklasfasching/go-org to v1.9.0 (forgejo) (#8335)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8335
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-06-29 02:52:27 +02:00
Gusted
b5e608f3e2 feat: bump the minimum required Git version from 2.0.0 to 2.34.1 (#8328)
- Resolves forgejo/discussions#324
- Remove all checks of `CheckGitVersionAtLeast` that checked for a version below 2.34.1
- The version was chosen because Debian stable supports 2.39.5 and Ubuntu 22.04 LTS supports 2.34.1

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8328
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-06-29 00:44:18 +02:00
Earl Warren
66e0988a43
feat: make API pull and compare endpoint references to head more robust
It is best to prefix the reference to resolve any ambiguity once it
has been determined that it is a branch or a tag.

It was done in forgejo/forgejo#5991 for the base reference but not for
the head reference.

Refs forgejo/forgejo#5991
2025-06-28 23:30:26 +02:00
Earl Warren
b8e66a5552
feat: improve API /repos/{owner}/{repo}/compare/{basehead} error messages 2025-06-28 23:30:24 +02:00
Earl Warren
a300c0b9fd
chore(cleanup): GitRepo.GetCommit is equivalent to GitRepo.IsBranchExist
Since go-git was dropped in a21128a734,
IsBranchExist relies on git-cat-file to figure out if a commit ID
exists.

There is no need to fallback to GetCommit if IsBranchExist determines
the commit does not exist and it will come to the same conclusion.
2025-06-28 23:30:20 +02:00
Earl Warren
d6e4342353 fix: make API /repos/{owner}/{repo}/compare/{basehead} work with forks (#8326)
- fix: API must use headGitRepo instead of ctx.Repo.GitRepo for comparing
- fix: make API /repos/{owner}/{repo}/compare/{basehead} work with forks
- add test coverage for both fixes and the underlying function `parseCompareInfo`
- refactor and improve part of the helpers from `tests/integration/api_helper_for_declarative_test.go`
- remove a few wrong or misleading comments

Refs forgejo/forgejo#7978

## Note on the focus of the PR

It was initially created to address a regression introduced in v12. But the tests that verify it is fixed discovered a v11.0 bug. They cannot conveniently be separated because they both relate to the same area of code that was previously not covered by any test.

## Note on v11.0 backport

It must be manually done by cherry-picking all commits up to and not including `fix: API must use headGitRepo instead of ctx.Repo.GitRepo for comparing` because it is v12 specific.

## 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 the `tests/integration` directory if it involves interactions with a live Forgejo server.

### Documentation

- [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.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8326
Reviewed-by: Otto <otto@codeberg.org>
Co-authored-by: Earl Warren <contact@earl-warren.org>
Co-committed-by: Earl Warren <contact@earl-warren.org>
2025-06-28 23:28:12 +02:00
oliverpool
225a0f7026 git/TreeEntry: LinkTarget simplification (#8323)
Some checks are pending
testing / frontend-checks (push) Has been skipped
testing / test-e2e (push) Has been skipped
/ release (push) Waiting to run
testing-integration / test-unit (push) Has been skipped
testing-integration / test-sqlite (push) Has been skipped
testing / backend-checks (push) Has been skipped
testing / test-unit (push) Has been skipped
testing / test-mysql (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
testing / test-remote-cacher (redis) (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / security-check (push) Has been skipped
See #8222 for context.

This PR removes a call to `Blob.GetBlobContent` and `Blob.DataAsync` and unifies symlink resolution:
- length was unlimited in one case
- length was truncated to 1024 chars in the other case

Now it is hard-limited to 4096 chars (ie error if larger), which is a length which seems appropriate according to https://stackoverflow.com/a/22575737.

### Tests

- Tests are already present in `tests/integration/repo_test.go:972`: `TestRepoFollowSymlink` (it caught a cap/len stupid mistake).
- [x] I did not document these changes and I do not expect someone else to do it.
- [x] I do not want this change to show in the release notes.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8323
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-06-27 23:10:09 +02:00
Gusted
6b27fa66b9 chore: sort blocked users list for determistic results (#8320)
Some checks are pending
/ release (push) Waiting to run
testing-integration / test-unit (push) Has been skipped
testing-integration / test-sqlite (push) Has been skipped
testing / backend-checks (push) Has been skipped
testing / frontend-checks (push) Has been skipped
testing / test-unit (push) Has been skipped
testing / test-e2e (push) Has been skipped
testing / test-mysql (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / security-check (push) Has been skipped
testing / test-remote-cacher (redis) (push) Has been skipped
- Ref https://codeberg.org/forgejo/forgejo/issues/8221#issuecomment-5515478

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8320
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-06-27 17:37:29 +02:00
floss4good
69d374435b fix: abuse reports string data types (#8267)
Follow-up of !6977

I was fooled by the fact that for SQLite the columns corresponding to `string` fields were created as `TEXT`; but this is not the case for PostgreSQL and MariaDB/MySQL. According to XORM default mapping rules[^1] _String is corresponding to varchar(255)_.

Therefore `abuse_report`.`remarks` should be of type `VARCHAR(500)` and `abuse_report_shadow_copy`.`raw_value` of type `LONGTEXT`.

### Testing

I have dropped the affected columns (or the entire tables) and checked that for PostgreSQL and MariaDB they are created with the correct type and also manually tested the abusive content reporting functionality in order to make sure that no DB error will be returned if for 'Remarks' a text longer than 255 characters is submitted or when a (big) shadow copy is created.

[^1]: https://xorm.io/docs/chapter-02/4.columns/

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8267
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: floss4good <floss4good@disroot.org>
Co-committed-by: floss4good <floss4good@disroot.org>
2025-06-27 15:47:58 +02:00
Gusted
c085d6c9ac fix: pass doer's ID for CRUD instance signing (#8304)
- When doing CRUD actions, the commiter and author are reconstructed and
do not contain the doer's ID. Make sure to pass this ID along so it can
be used to verify the rules of instance signing for CRUD actions.
- Regression of forgejo/forgejo#7693. It seems that previously this
didn't work correctly as it would not care about a empty ID.
- Resolves forgejo/forgejo#8278

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8304
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Reviewed-by: Beowulf <beowulf@beocode.eu>
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-06-27 15:43:31 +02:00
Lucas Schwiderski
3fb6e17105 fix: add missing trust status to pull review commits (#8296)
Closes: #8293

## 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...
  - [ ] in their respective `*_test.go` for unit tests.
  - [x] 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

- [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/8296
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Lucas Schwiderski <lucas@lschwiderski.de>
Co-committed-by: Lucas Schwiderski <lucas@lschwiderski.de>
2025-06-27 15:29:44 +02:00
Michael Jerger
aee161ff25 [gitea] week 2025-19 cherry pick (gitea/main -> forgejo) (#7909)
## Checklist

- [x] go to the last cherry-pick PR (forgejo/forgejo#7804) to figure out how far it went: [gitea@a2024953c5](a2024953c5)
- [x] cherry-pick and open PR (forgejo/forgejo#7909)
- [ ] have the PR pass the CI
- end-to-end (specially important if there are actions related changes)
  - [ ] add `run-end-to-end` label
  - [ ] check the result
- [ ] write release notes
- [ ] assign reviewers
- [ ] 48h later, last call
- merge 1 hour after the last call

## Legend

-  - No decision about the commit has been made.
- 🍒 - The commit has been cherry picked.
-  - The commit has been skipped.
- 💡 - The commit has been skipped, but should be ported to Forgejo.
- ✍️ - The commit has been skipped, and a port to Forgejo already exists.

## Commits

- 🍒 [`gitea`](e92c4f1808) -> [`forgejo`](56fa2caef3) Add missing setting load in dump-repo command ([gitea#34479](https://github.com/go-gitea/gitea/pull/34479))
- 🍒 [`gitea`](7b518bc6c7) -> [`forgejo`](6e5299606a) Change "rejected" to "changes requested" in 3rd party PR review notification ([gitea#34481](https://github.com/go-gitea/gitea/pull/34481))

## TODO

- 💡 [`gitea`](972381097c) Fix url validation in webhook add/edit API ([gitea#34492](https://github.com/go-gitea/gitea/pull/34492))
  Relevant input validation but test needs more backport.
------
- 💡 [`gitea`](59df03b554) Fix get / delete runner to use consistent http 404 and 500 status ([gitea#34480](https://github.com/go-gitea/gitea/pull/34480))
  It may be relevant to Forgejo as well
------
- 💡 [`gitea`](1e2f3514b9) Add endpoint deleting workflow run ([gitea#34337](https://github.com/go-gitea/gitea/pull/34337))
  Actions, it would be worth having in Forgejo as well.
------
- 💡 [`gitea`](5cb4cbf044) Fix repo broken check ([gitea#34444](https://github.com/go-gitea/gitea/pull/34444))
  Check wether this is relevant to us, port if yes.
------
- 💡 [`gitea`](355e9a9d54) Add a webhook push test for dev branch ([gitea#34421](https://github.com/go-gitea/gitea/pull/34421))
  Enhances webhook integration tests.
------
- 💡 [`gitea`](34281bc198) Fix bug webhook milestone is not right. ([gitea#34419](https://github.com/go-gitea/gitea/pull/34419))
  Testcode diverged, port required.
------
- 💡 [`gitea`](780e92ea99) Only git operations should update `last changed` of a repository ([gitea#34388](https://github.com/go-gitea/gitea/pull/34388))
  Port required, would benefit from additional tests.
------
- 💡 [`gitea`](b07e03956a) When updating comment, if the content is the same, just return and not update the databse ([gitea#34422](https://github.com/go-gitea/gitea/pull/34422))
  Codebase diverged, port required.
------
- 💡 [`gitea`](71a1187209) Fix incorrect divergence cache after switching default branch ([gitea#34370](https://github.com/go-gitea/gitea/pull/34370))
  Depends on previous gitea changes, port needed.
------
- 💡 [`gitea`](4c611bf280) Add a button editing action secret ([gitea#34348](https://github.com/go-gitea/gitea/pull/34348))
  This is an interesting feature and it has tests as well. Feature request covering this: https://codeberg.org/forgejo/forgejo/issues/7882
------
- 💡 [`gitea`](2fbc8f9e87) Fix LFS file not stored in LFS when uploaded/edited via API or web UI ([gitea#34367](https://github.com/go-gitea/gitea/pull/34367))
  Our code diverged - pls. check relevance & maybe port.
------
- 💡 [`gitea`](020e774b91) feat: add label 'state' to metric 'gitea_users' ([gitea#34326](https://github.com/go-gitea/gitea/pull/34326))
  Adjust our existing tests while porting this.
------

## Skipped

-  [`gitea`](ec10c6ba5a) [skip ci] Updated translations via Crowdin
------
-  [`gitea`](d89eed998f) Fix edithook api can not update package, status and workflow_job events ([gitea#34495](https://github.com/go-gitea/gitea/pull/34495))

  - gitea actions specific specific
------
-  [`gitea`](b6c0667474)  Add R-HNF to the TRANSLATORS file ([gitea#34494](https://github.com/go-gitea/gitea/pull/34494))

  - gitea translators update specific
------
-  [`gitea`](6fbf0e6738) nix flake update ([gitea#34476](https://github.com/go-gitea/gitea/pull/34476))

  - gitea dependency update specific
------
-  [`gitea`](c24f4b3d29) Add migrations tests ([gitea#34456](https://github.com/go-gitea/gitea/pull/34456))
------
-  [`gitea`](bf338bb9e2) Fix project board view ([gitea#34470](https://github.com/go-gitea/gitea/pull/34470))

  - gitea ui specific specific
------
-  [`gitea`](319d03fbc0) [skip ci] Updated translations via Crowdin
------
-  [`gitea`](dd500ce559) Fix Workflow run Not Found page ([gitea#34459](https://github.com/go-gitea/gitea/pull/34459))

  - gitea actions specific specific
------
-  [`gitea`](b6bf128f1e) [skip ci] Updated translations via Crowdin
------
-  [`gitea`](a0595add72) Fix remove org user failure on mssql ([gitea#34449](https://github.com/go-gitea/gitea/pull/34449))
------
-  [`gitea`](b5fd3e7210) Fix comment textarea scroll issue in Firefox ([gitea#34438](https://github.com/go-gitea/gitea/pull/34438))

  - gitea ui specific specific
------
-  [`gitea`](4011e2245b) Fix releases sidebar navigation link ([gitea#34436](https://github.com/go-gitea/gitea/pull/34436))

  - gitea ui specific specific
------
-  [`gitea`](0902d42fc7) [skip ci] Updated translations via Crowdin
------
-  [`gitea`](4a98ab0540) Remove legacy template helper functions ([gitea#34426](https://github.com/go-gitea/gitea/pull/34426))

  - gitea specific specific
------
-  [`gitea`](9b8609e017) Fix GetUsersByEmails ([gitea#34423](https://github.com/go-gitea/gitea/pull/34423))

  - gitea specific specific
------
-  [`gitea`](0f63a5ef48) [skip ci] Updated translations via Crowdin
------
-  [`gitea`](ad271444e9) Fix a bug when uploading file via lfs ssh command ([gitea#34408](https://github.com/go-gitea/gitea/pull/34408))
  :skiP: present with PR #7752
------
-  [`gitea`](8b16ab719c) Merge and tweak markup editor expander CSS ([gitea#34409](https://github.com/go-gitea/gitea/pull/34409))

  - gitea ui specific specific
------
-  [`gitea`](2ecd73d2e5) Bump `@github/relative-time-element` to v4.4.8 ([gitea#34413](https://github.com/go-gitea/gitea/pull/34413))

  - gitea dependency update specific
------
-  [`gitea`](179068fddb) Refactor commit message rendering and fix bugs ([gitea#34412](https://github.com/go-gitea/gitea/pull/34412))

  - gitea ui specific specific
------
-  [`gitea`](44aadc37c9) [skip ci] Updated translations via Crowdin
------
-  [`gitea`](f63822fe64) Fix autofocus behavior ([gitea#34397](https://github.com/go-gitea/gitea/pull/34397))

  - gitea ui specific specific
------
-  [`gitea`](82071ee730) [skip ci] Updated translations via Crowdin
------
-  [`gitea`](bbfc21e74f) Fix "The sidebar of the repository file list does not have a fixed height #34298" ([gitea#34321](https://github.com/go-gitea/gitea/pull/34321))

  - gitea ui specific specific
------
-  [`gitea`](dd886d729f) Update JS and PY dependencies ([gitea#34391](https://github.com/go-gitea/gitea/pull/34391))

  - gitea dependency update specific
------
-  [`gitea`](2a660b4a1b) Upgrade go-github v61 -> v71 ([gitea#34385](https://github.com/go-gitea/gitea/pull/34385))

  - gitea dependency update specific
------
-  [`gitea`](6bd8fe5353) Bump `@github/relative-time-element` to v4.4.7 ([gitea#34384](https://github.com/go-gitea/gitea/pull/34384))

  - gitea dependency update specific
------

<details>
<summary><h2>Stats</h2></summary>

<br>

Between [`gitea@a2024953c5`](a2024953c5) and [`gitea@ec10c6ba5a`](ec10c6ba5a), **41** commits have been reviewed. We picked **2**, skipped **27**, and decided to port **12**.

</details>

Co-authored-by: Sebastian Weigand <s.weigand.phy@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7909
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Michael Jerger <michael.jerger@meissa-gmbh.de>
Co-committed-by: Michael Jerger <michael.jerger@meissa-gmbh.de>
2025-06-27 13:59:07 +02:00
Gusted
a2e7446fe7 chore: use eventually for mysql collation test (#8301)
Some checks are pending
/ release (push) Waiting to run
testing-integration / test-unit (push) Has been skipped
testing-integration / test-sqlite (push) Has been skipped
testing / backend-checks (push) Has been skipped
testing / frontend-checks (push) Has been skipped
testing / test-e2e (push) Has been skipped
testing / test-mysql (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
testing / test-remote-cacher (redis) (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / security-check (push) Has been skipped
testing / test-unit (push) Has been skipped
- Regression of removing `time.Sleep(5 * time.Second)` in forgejo/forgejo#7917.
- Ref: https://codeberg.org/forgejo/forgejo/issues/8221#issuecomment-5532035

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8301
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-06-27 11:44:59 +02:00
oliverpool
7ad20a2730 git/blob: GetContentBase64 with fewer allocations and no goroutine (#8297)
See #8222 for context.i

`GetBlobContentBase64` was using a pipe and a goroutine to read the blob content as base64. This can be replace by a pre-allocated buffer and a direct copy.

Note that although similar to `GetBlobContent`, it does not truncate the content if the blob size is over the limit (but returns an error). I think that `GetBlobContent` should adopt the same behavior at some point (error instead of truncating).

### Tests

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
- [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.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8297
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-06-27 11:22:10 +02:00
Danko Aleksejevs
184e068f37 feat: show more relevant results for 'dependencies' dropdown (#8003)
Some checks are pending
/ release (push) Waiting to run
testing-integration / test-sqlite (push) Has been skipped
testing-integration / test-unit (push) Has been skipped
testing / backend-checks (push) Has been skipped
testing / test-e2e (push) Has been skipped
testing / test-pgsql (push) Has been skipped
testing / test-sqlite (push) Has been skipped
testing / test-remote-cacher (valkey) (push) Has been skipped
testing / frontend-checks (push) Has been skipped
testing / test-unit (push) Has been skipped
testing / test-mysql (push) Has been skipped
testing / test-remote-cacher (redis) (push) Has been skipped
testing / test-remote-cacher (garnet) (push) Has been skipped
testing / test-remote-cacher (redict) (push) Has been skipped
testing / security-check (push) Has been skipped
- Fix issue dropdown breaking when currently selected issue is included in results.
- Add `sort` parameter to `/issues/search` API.
- Sort dropdown by relevance.
- Make priority_repo_id work again.
- Added E2E test.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8003
Reviewed-by: Shiny Nematoda <snematoda@noreply.codeberg.org>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: Danko Aleksejevs <danko@very.lv>
Co-committed-by: Danko Aleksejevs <danko@very.lv>
2025-06-26 20:06:21 +02:00
Codeberg Translate
414199fc66 i18n: update of translations from Codeberg Translate (#8238)
Translations update from [Codeberg Translate](https://translate.codeberg.org) for [Forgejo/forgejo](https://translate.codeberg.org/projects/forgejo/forgejo/).

It also includes following components:

* [Forgejo/forgejo-next](https://translate.codeberg.org/projects/forgejo/forgejo-next/)

Current translation status:

![Weblate translation status](https://translate.codeberg.org/widget/forgejo/forgejo/horizontal-auto.svg)

<!--start release-notes-assistant-->

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Localization
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/8238): <!--number 8238 --><!--line 0 --><!--description aTE4bjogdXBkYXRlIG9mIHRyYW5zbGF0aW9ucyBmcm9tIENvZGViZXJnIFRyYW5zbGF0ZQ==-->i18n: update of translations from Codeberg Translate<!--description-->
<!--end release-notes-assistant-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8238
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Reviewed-by: Robert Wolff <mahlzahn@posteo.de>
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Co-authored-by: Codeberg Translate <translate@codeberg.org>
Co-committed-by: Codeberg Translate <translate@codeberg.org>
2025-06-26 10:44:26 +02:00
Renovate Bot
aee5e1fb94 Update module github.com/jhillyerd/enmime/v2 to v2.2.0 (forgejo) (#8254)
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [github.com/jhillyerd/enmime/v2](https://github.com/jhillyerd/enmime) | `v2.1.0` -> `v2.2.0` | [![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fjhillyerd%2fenmime%2fv2/v2.2.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fjhillyerd%2fenmime%2fv2/v2.1.0/v2.2.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>jhillyerd/enmime (github.com/jhillyerd/enmime/v2)</summary>

### [`v2.2.0`](https://github.com/jhillyerd/enmime/releases/tag/v2.2.0)

[Compare Source](https://github.com/jhillyerd/enmime/compare/v2.1.0...v2.2.0)

#### What's Changed

- chore: bump golangci to last 1.x release by [@&#8203;jhillyerd](https://github.com/jhillyerd) in https://github.com/jhillyerd/enmime/pull/368
- chore: fix linter warnings by [@&#8203;jhillyerd](https://github.com/jhillyerd) in https://github.com/jhillyerd/enmime/pull/370
- chore: bump golangci to 2.x by [@&#8203;jhillyerd](https://github.com/jhillyerd) in https://github.com/jhillyerd/enmime/pull/369
- chore: bump go deps by [@&#8203;jhillyerd](https://github.com/jhillyerd) in https://github.com/jhillyerd/enmime/pull/371
- build(deps): bump golangci/golangci-lint-action from 7 to 8 by [@&#8203;dependabot](https://github.com/dependabot) in https://github.com/jhillyerd/enmime/pull/373
- Switch to fork of html2text for tablewriter 1.0 by [@&#8203;jhillyerd](https://github.com/jhillyerd) in https://github.com/jhillyerd/enmime/pull/375
- Release for go 1.23 support by [@&#8203;jhillyerd](https://github.com/jhillyerd) in https://github.com/jhillyerd/enmime/pull/376

**Full Changelog**: https://github.com/jhillyerd/enmime/compare/v2.1.0...v2.2.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (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.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0MS4xLjMiLCJ1cGRhdGVkSW5WZXIiOiI0MS4xLjMiLCJ0YXJnZXRCcmFuY2giOiJmb3JnZWpvIiwibGFiZWxzIjpbImRlcGVuZGVuY3ktdXBncmFkZSIsInRlc3Qvbm90LW5lZWRlZCJdfQ==-->

Co-authored-by: Gusted <postmaster@gusted.xyz>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8254
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
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-06-26 08:49:20 +02:00
Renovate Bot
d3c712fe2a Lock file maintenance (forgejo) (#8257)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8257
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-06-26 02:27:54 +02:00
Otto
4a1f4acf76 fix(ui): release: name is overridden with tag name on edit (#8002)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8002
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Reviewed-by: Otto <otto@codeberg.org>
2025-06-26 01:22:48 +02:00
Earl Warren
30bfa13308 git/blob: refactor line-counting logic (#8270)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8270
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
2025-06-26 01:09:01 +02:00
Gusted
507a12bf82 Revert "fix(api): document is_system_webhook field (#7784)" (#8286)
The field is not part of the struct, it is instead part of the config field. See https://codeberg.org/forgejo/forgejo/pulls/7784#issuecomment-5511212

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8286
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: Gusted <postmaster@gusted.xyz>
Co-committed-by: Gusted <postmaster@gusted.xyz>
2025-06-26 00:36:18 +02:00
Renovate Bot
69bd7a1f1b Update dependency mermaid to v11.7.0 (forgejo) (#8249)
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8249
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-06-25 23:58:26 +02:00
Bente Groh
7ab27a7a7f fix(ui): add missing lazy load attribute to images (#8246)
closes #8076

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8246
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: Bente Groh <mail@bentegroh.de>
Co-committed-by: Bente Groh <mail@bentegroh.de>
2025-06-25 18:31:03 +02:00
oliverpool
2bca029f6f chore(ci): testSleep: show actual times on failures (#8271)
I just experienced a spurious error on `testSleep`.
The current assertion only showed `expected false to be truthy`. With this PR it should show the actual elapsed time (to be able to knowingly adjust the expected delay).

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8271
Reviewed-by: floss4good <floss4good@noreply.codeberg.org>
Reviewed-by: Beowulf <beowulf@beocode.eu>
Co-authored-by: oliverpool <git@olivier.pfad.fr>
Co-committed-by: oliverpool <git@olivier.pfad.fr>
2025-06-25 15:58:55 +02:00
Earl Warren
8844b6b8e5
chore: 12.0 is now stable (take 2) 2025-06-25 06:41:35 +02:00
oliverpool
6ed62c14d3 move blobLineCount to disencentive its usage 2025-06-24 17:36:13 +02:00
oliverpool
744363597d test: before refatoring count function 2025-06-24 17:36:13 +02:00
oliverpool
7a6b5b6dd9 blame: count lines without reading blob 2025-06-24 17:36:13 +02:00
Beowulf
43fb63a063
fix(ui): release: name is overridden with tag name on edit
Fixes #8001

Regression: f66a6b12cd
2025-06-23 19:11:43 +02:00
239 changed files with 4007 additions and 2615 deletions

View file

@ -27,15 +27,13 @@ forgejo.org/models/db
TruncateBeans TruncateBeans
InTransaction InTransaction
DumpTables DumpTables
GetTableNames
forgejo.org/models/dbfs forgejo.org/models/dbfs
file.renameTo file.renameTo
Create Create
Rename Rename
forgejo.org/models/forgefed
GetFederationHost
forgejo.org/models/forgejo/semver forgejo.org/models/forgejo/semver
GetVersion GetVersion
SetVersionString SetVersionString
@ -67,7 +65,6 @@ forgejo.org/models/user
DeleteUserSetting DeleteUserSetting
GetFederatedUser GetFederatedUser
GetFederatedUserByUserID GetFederatedUserByUserID
UpdateFederatedUser
GetFollowersForUser GetFollowersForUser
AddFollower AddFollower
RemoveFollower RemoveFollower
@ -248,6 +245,9 @@ forgejo.org/routers/web/org
forgejo.org/services/context forgejo.org/services/context
GetPrivateContext GetPrivateContext
forgejo.org/services/federation
Init
forgejo.org/services/repository forgejo.org/services/repository
IsErrForkAlreadyExist IsErrForkAlreadyExist

View file

@ -6,7 +6,7 @@
"ghcr.io/devcontainers/features/node:1": { "ghcr.io/devcontainers/features/node:1": {
"version": "22" "version": "22"
}, },
"ghcr.io/devcontainers/features/git-lfs:1.2.4": {}, "ghcr.io/devcontainers/features/git-lfs:1.2.5": {},
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {} "ghcr.io/warrenbuckley/codespace-features/sqlite:1": {}
}, },
"customizations": { "customizations": {

View file

@ -0,0 +1,22 @@
#
# Install the minimal version of Git supported by Forgejo
#
runs:
using: "composite"
steps:
- name: install git and git-lfs
run: |
set -x
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get -q install -y -qq curl ca-certificates
curl -sS -o /tmp/git-man.deb http://archive.ubuntu.com/ubuntu/pool/main/g/git/git-man_2.34.1-1ubuntu1_all.deb
curl -sS -o /tmp/git.deb https://archive.ubuntu.com/ubuntu/pool/main/g/git/git_2.34.1-1ubuntu1_amd64.deb
curl -sS -o /tmp/git-lfs.deb https://archive.ubuntu.com/ubuntu/pool/universe/g/git-lfs/git-lfs_3.0.2-1_amd64.deb
apt-get -q install --allow-downgrades -y -qq /tmp/git-man.deb
apt-get -q install --allow-downgrades -y -qq /tmp/git.deb
apt-get -q install --allow-downgrades -y -qq /tmp/git-lfs.deb

View file

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

View file

@ -33,11 +33,8 @@ jobs:
steps: steps:
- uses: https://data.forgejo.org/actions/checkout@v4 - uses: https://data.forgejo.org/actions/checkout@v4
- uses: ./.forgejo/workflows-composite/setup-env - uses: ./.forgejo/workflows-composite/setup-env
- name: install git 2.30 - name: install git 2.34.1 and git-lfs 3.0.2
uses: ./.forgejo/workflows-composite/apt-install-from uses: ./.forgejo/workflows-composite/install-minimum-git-version
with:
packages: git/bullseye git-lfs/bullseye
release: bullseye
- uses: ./.forgejo/workflows-composite/build-backend - uses: ./.forgejo/workflows-composite/build-backend
- run: | - run: |
su forgejo -c 'make test-backend test-check' su forgejo -c 'make test-backend test-check'
@ -55,11 +52,8 @@ jobs:
steps: steps:
- uses: https://data.forgejo.org/actions/checkout@v4 - uses: https://data.forgejo.org/actions/checkout@v4
- uses: ./.forgejo/workflows-composite/setup-env - uses: ./.forgejo/workflows-composite/setup-env
- name: install git 2.30 - name: install git 2.34.1 and git-lfs 3.0.2
uses: ./.forgejo/workflows-composite/apt-install-from uses: ./.forgejo/workflows-composite/install-minimum-git-version
with:
packages: git/bullseye git-lfs/bullseye
release: bullseye
- uses: ./.forgejo/workflows-composite/build-backend - uses: ./.forgejo/workflows-composite/build-backend
- run: | - run: |
su forgejo -c 'make test-sqlite-migration test-sqlite' su forgejo -c 'make test-sqlite-migration test-sqlite'

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 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 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 GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.5.2 # renovate: datasource=go
RENOVATE_NPM_PACKAGE ?= renovate@41.1.4 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate RENOVATE_NPM_PACKAGE ?= renovate@41.17.2 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate
# https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/
DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ...

View file

@ -595,8 +595,8 @@
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Huan Du\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n" "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Huan Du\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
}, },
{ {
"name": "github.com/jaytaylor/html2text", "name": "github.com/inbucket/html2text",
"path": "github.com/jaytaylor/html2text/LICENSE", "path": "github.com/inbucket/html2text/LICENSE",
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Jay Taylor\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n" "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Jay Taylor\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
}, },
{ {
@ -779,6 +779,16 @@
"path": "github.com/nwaples/rardecode/LICENSE", "path": "github.com/nwaples/rardecode/LICENSE",
"licenseText": "Copyright (c) 2015, Nicholas Waples\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" "licenseText": "Copyright (c) 2015, Nicholas Waples\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
}, },
{
"name": "github.com/olekukonko/errors",
"path": "github.com/olekukonko/errors/LICENSE",
"licenseText": "MIT License\n\nCopyright (c) 2025 Oleku Konko\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
},
{
"name": "github.com/olekukonko/ll",
"path": "github.com/olekukonko/ll/LICENSE",
"licenseText": "MIT License\n\nCopyright (c) 2025 Oleku Konko\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
},
{ {
"name": "github.com/olekukonko/tablewriter", "name": "github.com/olekukonko/tablewriter",
"path": "github.com/olekukonko/tablewriter/LICENSE.md", "path": "github.com/olekukonko/tablewriter/LICENSE.md",

View file

@ -82,6 +82,11 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
} }
func runDumpRepository(stdCtx context.Context, ctx *cli.Command) error { func runDumpRepository(stdCtx context.Context, ctx *cli.Command) error {
setupConsoleLogger(log.INFO, log.CanColorStderr, os.Stderr)
// setting.DisableLoggerInit()
setting.LoadSettings() // cannot access skip_tls_verify settings otherwise
stdCtx, cancel := installSignals(stdCtx) stdCtx, cancel := installSignals(stdCtx)
defer cancel() defer cancel()

View file

@ -231,8 +231,6 @@ Forgejo or set your environment appropriately.`, "")
} }
} }
supportProcReceive := git.CheckGitVersionAtLeast("2.29") == nil
for scanner.Scan() { for scanner.Scan() {
// TODO: support news feeds for wiki // TODO: support news feeds for wiki
if isWiki { if isWiki {
@ -250,10 +248,7 @@ Forgejo or set your environment appropriately.`, "")
total++ total++
lastline++ lastline++
// If the ref is a branch or tag, check if it's protected // All references should be checked because permission check was delayed.
// if supportProcReceive all ref should be checked because
// permission check was delayed
if supportProcReceive || refFullName.IsBranch() || refFullName.IsTag() {
oldCommitIDs[count] = oldCommitID oldCommitIDs[count] = oldCommitID
newCommitIDs[count] = newCommitID newCommitIDs[count] = newCommitID
refFullNames[count] = refFullName refFullNames[count] = refFullName
@ -273,9 +268,6 @@ Forgejo or set your environment appropriately.`, "")
count = 0 count = 0
lastline = 0 lastline = 0
} }
} else {
fmt.Fprint(out, ".")
}
if lastline >= hookBatchSize { if lastline >= hookBatchSize {
fmt.Fprint(out, "\n") fmt.Fprint(out, "\n")
lastline = 0 lastline = 0
@ -513,10 +505,6 @@ Forgejo or set your environment appropriately.`, "")
return nil return nil
} }
if git.CheckGitVersionAtLeast("2.29") != nil {
return fail(ctx, "No proc-receive support", "current git version doesn't support proc-receive.")
}
reader := bufio.NewReader(os.Stdin) reader := bufio.NewReader(os.Stdin)
repoUser := os.Getenv(repo_module.EnvRepoUsername) repoUser := os.Getenv(repo_module.EnvRepoUsername)
repoName := os.Getenv(repo_module.EnvRepoName) repoName := os.Getenv(repo_module.EnvRepoName)

View file

@ -193,13 +193,11 @@ func runServ(ctx context.Context, c *cli.Command) error {
} }
if len(words) < 2 { if len(words) < 2 {
if git.CheckGitVersionAtLeast("2.29") == nil {
// for AGit Flow // for AGit Flow
if cmd == "ssh_info" { if cmd == "ssh_info" {
fmt.Print(`{"type":"agit","version":1}`) fmt.Print(`{"type":"agit","version":1}`)
return nil return nil
} }
}
return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd) return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd)
} }

18
go.mod
View file

@ -42,7 +42,7 @@ require (
github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9 github.com/go-ap/activitypub v0.0.0-20231114162308-e219254dc5c9
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
github.com/go-chi/chi/v5 v5.2.2 github.com/go-chi/chi/v5 v5.2.2
github.com/go-chi/cors v1.2.1 github.com/go-chi/cors v1.2.2
github.com/go-co-op/gocron v1.37.0 github.com/go-co-op/gocron v1.37.0
github.com/go-enry/go-enry/v2 v2.9.2 github.com/go-enry/go-enry/v2 v2.9.2
github.com/go-git/go-git/v5 v5.13.2 github.com/go-git/go-git/v5 v5.13.2
@ -56,15 +56,15 @@ require (
github.com/golang-jwt/jwt/v5 v5.2.2 github.com/golang-jwt/jwt/v5 v5.2.2
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/google/go-github/v64 v64.0.0 github.com/google/go-github/v64 v64.0.0
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/gorilla/feeds v1.2.0 github.com/gorilla/feeds v1.2.0
github.com/gorilla/sessions v1.4.0 github.com/gorilla/sessions v1.4.0
github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/huandu/xstrings v1.5.0 github.com/huandu/xstrings v1.5.0
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 github.com/inbucket/html2text v0.9.0
github.com/jhillyerd/enmime/v2 v2.1.0 github.com/jhillyerd/enmime/v2 v2.2.0
github.com/json-iterator/go v1.1.12 github.com/json-iterator/go v1.1.12
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/klauspost/compress v1.18.0 github.com/klauspost/compress v1.18.0
@ -79,7 +79,7 @@ require (
github.com/minio/minio-go/v7 v7.0.94 github.com/minio/minio-go/v7 v7.0.94
github.com/msteinert/pam/v2 v2.1.0 github.com/msteinert/pam/v2 v2.1.0
github.com/nektos/act v0.2.52 github.com/nektos/act v0.2.52
github.com/niklasfasching/go-org v1.8.0 github.com/niklasfasching/go-org v1.9.0
github.com/olivere/elastic/v7 v7.0.32 github.com/olivere/elastic/v7 v7.0.32
github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.1 github.com/opencontainers/image-spec v1.1.1
@ -158,7 +158,7 @@ require (
github.com/dlclark/regexp2 v1.11.5 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
github.com/emirpasic/gods v1.18.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.16.0 // indirect github.com/fatih/color v1.18.0 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/fxamacker/cbor/v2 v2.8.0 // indirect
github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect github.com/go-ap/errors v0.0.0-20231003111023-183eef4b31b7 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
@ -192,7 +192,7 @@ require (
github.com/libdns/libdns v1.0.0-beta.1 // indirect github.com/libdns/libdns v1.0.0-beta.1 // indirect
github.com/mailru/easyjson v0.9.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect
github.com/markbates/going v1.0.3 // indirect github.com/markbates/going v1.0.3 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mholt/acmez/v3 v3.1.2 // indirect github.com/mholt/acmez/v3 v3.1.2 // indirect
github.com/miekg/dns v1.1.63 // indirect github.com/miekg/dns v1.1.63 // indirect
@ -205,7 +205,9 @@ require (
github.com/mschoch/smat v0.2.0 // indirect github.com/mschoch/smat v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect github.com/nwaples/rardecode v1.1.3 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/olekukonko/tablewriter v1.0.7 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/ginkgo v1.16.5 // indirect
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect

39
go.sum
View file

@ -192,8 +192,8 @@ github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTe
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
@ -215,8 +215,8 @@ github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkPro
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618= github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY= github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY=
github.com/go-enry/go-enry/v2 v2.9.2 h1:giOQAtCgBX08kosrX818DCQJTCNtKwoPBGu0qb6nKTY= github.com/go-enry/go-enry/v2 v2.9.2 h1:giOQAtCgBX08kosrX818DCQJTCNtKwoPBGu0qb6nKTY=
@ -307,8 +307,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 h1:xhMrHhTJ6zxu3gA4enFM9MLn9AY7613teCdFnlUVbSQ=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@ -341,12 +341,12 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA= github.com/inbucket/html2text v0.9.0 h1:ULJmVcBEMAcmLE+/rN815KG1Fx6+a4HhbUxiDiN+qks=
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= github.com/inbucket/html2text v0.9.0/go.mod h1:QDaumzl+/OzlSVbNohhmg+yAy5pKjUjzCKW2BMvztKE=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jhillyerd/enmime/v2 v2.1.0 h1:c8Qwi5Xq5EdtMN6byQWoZ/8I2RMTo6OJ7Xay+s1oPO0= github.com/jhillyerd/enmime/v2 v2.2.0 h1:Pe35MB96eZK5Q0XjlvPftOgWypQpd1gcbfJKAt7rsB8=
github.com/jhillyerd/enmime/v2 v2.1.0/go.mod h1:EJ74dcRbBcqHSP2TBu08XRoy6y3Yx0cevwb1YkGMEmQ= github.com/jhillyerd/enmime/v2 v2.2.0/go.mod h1:SOBXlCemjhiV2DvHhAKnJiWrtJGS/Ffuw4Iy7NjBTaI=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@ -389,12 +389,10 @@ github.com/markbates/going v1.0.3 h1:mY45T5TvW+Xz5A6jY7lf4+NLg9D8+iuStIHyR7M8qsE
github.com/markbates/going v1.0.3/go.mod h1:fQiT6v6yQar9UD6bd/D4Z5Afbk9J6BBVBtLiyY4gp2o= github.com/markbates/going v1.0.3/go.mod h1:fQiT6v6yQar9UD6bd/D4Z5Afbk9J6BBVBtLiyY4gp2o=
github.com/markbates/goth v1.80.0 h1:NnvatczZDzOs1hn9Ug+dVYf2Viwwkp/ZDX5K+GLjan8= github.com/markbates/goth v1.80.0 h1:NnvatczZDzOs1hn9Ug+dVYf2Viwwkp/ZDX5K+GLjan8=
github.com/markbates/goth v1.80.0/go.mod h1:4/GYHo+W6NWisrMPZnq0Yr2Q70UntNLn7KXEFhrIdAY= github.com/markbates/goth v1.80.0/go.mod h1:4/GYHo+W6NWisrMPZnq0Yr2Q70UntNLn7KXEFhrIdAY=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
@ -428,16 +426,20 @@ github.com/msteinert/pam/v2 v2.1.0 h1:er5F9TKV5nGFuTt12ubtqPHEUdeBwReP7vd3wovidG
github.com/msteinert/pam/v2 v2.1.0/go.mod h1:KT28NNIcDFf3PcBmNI2mIGO4zZJ+9RSs/At2PB3IDVc= github.com/msteinert/pam/v2 v2.1.0/go.mod h1:KT28NNIcDFf3PcBmNI2mIGO4zZJ+9RSs/At2PB3IDVc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/niklasfasching/go-org v1.8.0 h1:WyGLaajLLp8JbQzkmapZ1y0MOzKuKV47HkZRloi+HGY= github.com/niklasfasching/go-org v1.9.0 h1:4/Sr68Qx06hjC9MVDB/4etGP67JionLHGscLMOClpnk=
github.com/niklasfasching/go-org v1.8.0/go.mod h1:e2A9zJs7cdONrEGs3gvxCcaAEpwwPNPG7csDpXckMNg= github.com/niklasfasching/go-org v1.9.0/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
github.com/olekukonko/tablewriter v1.0.7 h1:HCC2e3MM+2g72M81ZcJU11uciw6z/p82aEnm4/ySDGw=
github.com/olekukonko/tablewriter v1.0.7/go.mod h1:H428M+HzoUXC6JU2Abj9IT9ooRmdq9CxuDmKMtrOCMs=
github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E= github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E=
github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k= github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@ -645,7 +647,6 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View file

@ -284,16 +284,10 @@ func GetLatestRun(ctx context.Context, repoID int64) (*ActionRun, error) {
return &run, nil return &run, nil
} }
// GetRunBefore returns the last run that completed a given timestamp (not inclusive). func GetRunBefore(ctx context.Context, _ *ActionRun) (*ActionRun, error) {
func GetRunBefore(ctx context.Context, repoID int64, timestamp timeutil.TimeStamp) (*ActionRun, error) { // TODO return the most recent run related to the run given in argument
var run ActionRun // see https://codeberg.org/forgejo/user-research/issues/63 for context
has, err := db.GetEngine(ctx).Where("repo_id=? AND stopped IS NOT NULL AND stopped<?", repoID, timestamp).OrderBy("stopped DESC").Limit(1).Get(&run) return nil, nil
if err != nil {
return nil, err
} else if !has {
return nil, fmt.Errorf("run before: %w", util.ErrNotExist)
}
return &run, nil
} }
func GetLatestRunForBranchAndWorkflow(ctx context.Context, repoID int64, branch, workflowFile, event string) (*ActionRun, error) { func GetLatestRunForBranchAndWorkflow(ctx context.Context, repoID int64, branch, workflowFile, event string) (*ActionRun, error) {

View file

@ -5,92 +5,7 @@ package actions
import ( import (
"testing" "testing"
"time"
"forgejo.org/models/db"
"forgejo.org/models/unittest"
"forgejo.org/modules/timeutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestGetRunBefore(t *testing.T) { func TestGetRunBefore(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
// this repo is part of the test database requiring loading "repository.yml" in main_test.go
var repoID int64 = 1
workflowID := "test_workflow"
// third completed run
time1, err := time.Parse(time.RFC3339, "2024-07-31T15:47:55+08:00")
require.NoError(t, err)
timeutil.MockSet(time1)
run1 := ActionRun{
ID: 1,
Index: 1,
RepoID: repoID,
Stopped: timeutil.TimeStampNow(),
WorkflowID: workflowID,
}
// fourth completed run
time2, err := time.Parse(time.RFC3339, "2024-08-31T15:47:55+08:00")
require.NoError(t, err)
timeutil.MockSet(time2)
run2 := ActionRun{
ID: 2,
Index: 2,
RepoID: repoID,
Stopped: timeutil.TimeStampNow(),
WorkflowID: workflowID,
}
// second completed run
time3, err := time.Parse(time.RFC3339, "2024-07-31T15:47:54+08:00")
require.NoError(t, err)
timeutil.MockSet(time3)
run3 := ActionRun{
ID: 3,
Index: 3,
RepoID: repoID,
Stopped: timeutil.TimeStampNow(),
WorkflowID: workflowID,
}
// first completed run
time4, err := time.Parse(time.RFC3339, "2024-06-30T15:47:54+08:00")
require.NoError(t, err)
timeutil.MockSet(time4)
run4 := ActionRun{
ID: 4,
Index: 4,
RepoID: repoID,
Stopped: timeutil.TimeStampNow(),
WorkflowID: workflowID,
}
require.NoError(t, db.Insert(db.DefaultContext, &run1))
runBefore, err := GetRunBefore(db.DefaultContext, repoID, run1.Stopped)
// there is no run before run1
require.Error(t, err)
require.Nil(t, runBefore)
// now there is only run3 before run1
require.NoError(t, db.Insert(db.DefaultContext, &run3))
runBefore, err = GetRunBefore(db.DefaultContext, repoID, run1.Stopped)
require.NoError(t, err)
assert.Equal(t, run3.ID, runBefore.ID)
// there still is only run3 before run1
require.NoError(t, db.Insert(db.DefaultContext, &run2))
runBefore, err = GetRunBefore(db.DefaultContext, repoID, run1.Stopped)
require.NoError(t, err)
assert.Equal(t, run3.ID, runBefore.ID)
// run4 is further away from run1
require.NoError(t, db.Insert(db.DefaultContext, &run4))
runBefore, err = GetRunBefore(db.DefaultContext, repoID, run1.Stopped)
require.NoError(t, err)
assert.Equal(t, run3.ID, runBefore.ID)
} }

View file

@ -15,8 +15,6 @@ func TestMain(m *testing.M) {
"gpg_key.yml", "gpg_key.yml",
"public_key.yml", "public_key.yml",
"TestParseCommitWithSSHSignature/public_key.yml", "TestParseCommitWithSSHSignature/public_key.yml",
"deploy_key.yml",
"gpg_key_import.yml",
"user.yml", "user.yml",
"email_address.yml", "email_address.yml",
}, },

View file

@ -15,6 +15,7 @@ import (
"strings" "strings"
"time" "time"
"forgejo.org/modules/container"
"forgejo.org/modules/log" "forgejo.org/modules/log"
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
@ -438,3 +439,12 @@ func GetMasterEngine(x Engine) (*xorm.Engine, error) {
return engine, nil return engine, nil
} }
// GetTableNames returns the table name of all registered models.
func GetTableNames() container.Set[string] {
names := make(container.Set[string])
for _, table := range tables {
names.Add(x.TableName(table))
}
return names
}

View file

@ -0,0 +1,40 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package db
import (
"slices"
"testing"
"forgejo.org/modules/test"
"github.com/stretchr/testify/assert"
)
func TestGetTableNames(t *testing.T) {
t.Run("Simple", func(t *testing.T) {
defer test.MockVariableValue(&tables, []any{new(GPGKey)})()
assert.Equal(t, []string{"gpg_key"}, GetTableNames().Values())
})
t.Run("Multiple tables", func(t *testing.T) {
defer test.MockVariableValue(&tables, []any{new(GPGKey), new(User), new(BlockedUser)})()
tableNames := GetTableNames().Values()
slices.Sort(tableNames)
assert.Equal(t, []string{"forgejo_blocked_user", "gpg_key", "user"}, tableNames)
})
}
type GPGKey struct{}
type User struct{}
type BlockedUser struct{}
func (*BlockedUser) TableName() string {
return "forgejo_blocked_user"
}

View file

@ -0,0 +1,7 @@
-
id: 1001
uid: 1001
email: AnotherTestUserWithUpperCaseEmail@otto.splvs.net
lower_email: anothertestuserwithuppercaseemail@otto.splvs.net
is_activated: false
is_primary: true

View file

@ -0,0 +1,12 @@
-
id: 1001
lower_name: user1001
name: user1001
full_name: User That loves Upper Cases
email: AnotherTestUserWithUpperCaseEmail@otto.splvs.net
passwd: ZogKvWdyEx:password
passwd_hash_algo: dummy
avatar: ''
avatar_email: anothertestuserwithuppercaseemail@otto.splvs.net
login_name: user1
created_unix: 1672578000

View file

@ -1 +0,0 @@
[] # empty

View file

@ -186,10 +186,46 @@
type: 8 # milestone type: 8 # milestone
poster_id: 1 poster_id: 1
issue_id: 1 # in repo_id 1 issue_id: 1 # in repo_id 1
milestone_id: 10 # not exsting milestone milestone_id: 10 # not existing milestone
old_milestone_id: 0 old_milestone_id: 0
created_unix: 946685080 created_unix: 946685080
-
id: 2004
type: 8 # milestone
poster_id: 1
issue_id: 1 # in repo_id 1
milestone_id: 1
old_milestone_id: 10 # not existing (ghost) milestone
created_unix: 946685085
-
id: 2005
type: 8 # milestone
poster_id: 1
issue_id: 1 # in repo_id 1
milestone_id: 10 # not existing (ghost) milestone
old_milestone_id: 1
created_unix: 946685090
-
id: 2006
type: 8 # milestone
poster_id: 1
issue_id: 1 # in repo_id 1
milestone_id: 11 # not existing (ghost) milestone
old_milestone_id: 10 # not existing (ghost) milestone
created_unix: 946685095
-
id: 2007
type: 8 # milestone
poster_id: 1
issue_id: 1 # in repo_id 1
milestone_id: 0
old_milestone_id: 11 # not existing (ghost) milestone
created_unix: 946685100
- -
id: 2010 id: 2010
type: 30 # project type: 30 # project

View file

@ -1 +0,0 @@
[] # empty

View file

@ -1 +0,0 @@
[] # empty

View file

@ -1 +0,0 @@
[] # empty

View file

@ -1 +0,0 @@
[] # empty

View file

@ -1 +0,0 @@
[] # empty

View file

@ -1 +0,0 @@
[] # empty

View file

@ -1 +0,0 @@
[] # empty

View file

@ -1 +0,0 @@
[] # empty

View file

@ -1 +0,0 @@
[] # empty

View file

@ -1 +0,0 @@
[] # empty

View file

@ -1 +0,0 @@
[] # empty

View file

@ -1 +0,0 @@
[] # empty

View file

@ -108,7 +108,7 @@ var migrations = []*Migration{
// v33 -> v34 // v33 -> v34
NewMigration("Add `notify-email` column to `action_run` table", AddNotifyEmailToActionRun), NewMigration("Add `notify-email` column to `action_run` table", AddNotifyEmailToActionRun),
// v34 -> v35 // v34 -> v35
NewMigration("Add index to `stopped` column in `action_run` table", AddIndexToActionRunStopped), NewMigration("Noop because of https://codeberg.org/forgejo/forgejo/issues/8373", NoopAddIndexToActionRunStopped),
} }
// GetCurrentDBVersion returns the current Forgejo database version. // GetCurrentDBVersion returns the current Forgejo database version.

View file

@ -4,16 +4,10 @@
package forgejo_migrations //nolint:revive package forgejo_migrations //nolint:revive
import ( import (
"forgejo.org/modules/timeutil"
"xorm.io/xorm" "xorm.io/xorm"
) )
func AddIndexToActionRunStopped(x *xorm.Engine) error { // see https://codeberg.org/forgejo/forgejo/issues/8373
type ActionRun struct { func NoopAddIndexToActionRunStopped(x *xorm.Engine) error {
ID int64 return nil
Stopped timeutil.TimeStamp `xorm:"index"`
}
return x.Sync(&ActionRun{})
} }

View file

@ -101,7 +101,7 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
return nil return nil
} }
milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs)) milestones := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs) left := len(milestoneIDs)
for left > 0 { for left > 0 {
limit := db.DefaultMaxInSize limit := db.DefaultMaxInSize
@ -110,7 +110,7 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
} }
err := db.GetEngine(ctx). err := db.GetEngine(ctx).
In("id", milestoneIDs[:limit]). In("id", milestoneIDs[:limit]).
Find(&milestoneMaps) Find(&milestones)
if err != nil { if err != nil {
return err return err
} }
@ -118,8 +118,8 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
milestoneIDs = milestoneIDs[limit:] milestoneIDs = milestoneIDs[limit:]
} }
for _, issue := range comments { for _, comment := range comments {
issue.Milestone = milestoneMaps[issue.MilestoneID] comment.Milestone = milestones[comment.MilestoneID]
} }
return nil return nil
} }
@ -140,7 +140,7 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error {
return nil return nil
} }
milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs)) milestones := make(map[int64]*Milestone, len(milestoneIDs))
left := len(milestoneIDs) left := len(milestoneIDs)
for left > 0 { for left > 0 {
limit := db.DefaultMaxInSize limit := db.DefaultMaxInSize
@ -149,7 +149,7 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error {
} }
err := db.GetEngine(ctx). err := db.GetEngine(ctx).
In("id", milestoneIDs[:limit]). In("id", milestoneIDs[:limit]).
Find(&milestoneMaps) Find(&milestones)
if err != nil { if err != nil {
return err return err
} }
@ -157,8 +157,8 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error {
milestoneIDs = milestoneIDs[limit:] milestoneIDs = milestoneIDs[limit:]
} }
for _, issue := range comments { for _, comment := range comments {
issue.OldMilestone = milestoneMaps[issue.MilestoneID] comment.OldMilestone = milestones[comment.OldMilestoneID]
} }
return nil return nil
} }

View file

@ -48,6 +48,8 @@ type IssuesOptions struct { //nolint
UpdatedBeforeUnix int64 UpdatedBeforeUnix int64
// prioritize issues from this repo // prioritize issues from this repo
PriorityRepoID int64 PriorityRepoID int64
// if this issue index (not ID) exists and matches the filters, *and* priorityrepo sort is used, show it first
PriorityIssueIndex int64
IsArchived optional.Option[bool] IsArchived optional.Option[bool]
// If combined with AllPublic, then private as well as public issues // If combined with AllPublic, then private as well as public issues
@ -60,7 +62,7 @@ type IssuesOptions struct { //nolint
// applySorts sort an issues-related session based on the provided // applySorts sort an issues-related session based on the provided
// sortType string // sortType string
func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) { func applySorts(sess *xorm.Session, sortType string, priorityRepoID, priorityIssueIndex int64) {
switch sortType { switch sortType {
case "oldest": case "oldest":
sess.Asc("issue.created_unix").Asc("issue.id") sess.Asc("issue.created_unix").Asc("issue.id")
@ -97,8 +99,11 @@ func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) {
case "priorityrepo": case "priorityrepo":
sess.OrderBy("CASE "+ sess.OrderBy("CASE "+
"WHEN issue.repo_id = ? THEN 1 "+ "WHEN issue.repo_id = ? THEN 1 "+
"ELSE 2 END ASC", priorityRepoID). "ELSE 2 END ASC", priorityRepoID)
Desc("issue.created_unix"). if priorityIssueIndex != 0 {
sess.OrderBy("issue.index = ? DESC", priorityIssueIndex)
}
sess.Desc("issue.created_unix").
Desc("issue.id") Desc("issue.id")
case "project-column-sorting": case "project-column-sorting":
sess.Asc("project_issue.sorting").Desc("issue.created_unix").Desc("issue.id") sess.Asc("project_issue.sorting").Desc("issue.created_unix").Desc("issue.id")
@ -470,7 +475,7 @@ func Issues(ctx context.Context, opts *IssuesOptions) (IssueList, error) {
Join("INNER", "repository", "`issue`.repo_id = `repository`.id") Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
applyLimit(sess, opts) applyLimit(sess, opts)
applyConditions(sess, opts) applyConditions(sess, opts)
applySorts(sess, opts.SortType, opts.PriorityRepoID) applySorts(sess, opts.SortType, opts.PriorityRepoID, opts.PriorityIssueIndex)
issues := IssueList{} issues := IssueList{}
if err := sess.Find(&issues); err != nil { if err := sess.Find(&issues); err != nil {
@ -494,7 +499,7 @@ func IssueIDs(ctx context.Context, opts *IssuesOptions, otherConds ...builder.Co
} }
applyLimit(sess, opts) applyLimit(sess, opts)
applySorts(sess, opts.SortType, opts.PriorityRepoID) applySorts(sess, opts.SortType, opts.PriorityRepoID, opts.PriorityIssueIndex)
var res []int64 var res []int64
total, err := sess.Select("`issue`.id").Table(&Issue{}).FindAndCount(&res) total, err := sess.Select("`issue`.id").Table(&Issue{}).FindAndCount(&res)

View file

@ -149,7 +149,7 @@ func PullRequests(ctx context.Context, baseRepoID int64, opts *PullRequestsOptio
} }
findSession := listPullRequestStatement(ctx, baseRepoID, opts) findSession := listPullRequestStatement(ctx, baseRepoID, opts)
applySorts(findSession, opts.SortType, 0) applySorts(findSession, opts.SortType, 0, 0)
findSession = db.SetSessionPagination(findSession, opts) findSession = db.SetSessionPagination(findSession, opts)
prs := make([]*PullRequest, 0, opts.PageSize) prs := make([]*PullRequest, 0, opts.PageSize)
found := findSession.Find(&prs) found := findSession.Find(&prs)

View file

@ -96,6 +96,7 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu
if err := unittest.InitFixtures( if err := unittest.InitFixtures(
unittest.FixturesOptions{ unittest.FixturesOptions{
Dir: fixturesDir, Dir: fixturesDir,
SkipCleanRegistedModels: true,
}, x); err != nil { }, x); err != nil {
t.Errorf("error whilst initializing fixtures from %s: %v", fixturesDir, err) t.Errorf("error whilst initializing fixtures from %s: %v", fixturesDir, err)
return x, deferFn return x, deferFn

View file

@ -100,7 +100,7 @@ type AbuseReport struct {
// The abuse category selected by the reporter. // The abuse category selected by the reporter.
Category AbuseCategoryType `xorm:"INDEX NOT NULL"` Category AbuseCategoryType `xorm:"INDEX NOT NULL"`
// Remarks provided by the reporter. // Remarks provided by the reporter.
Remarks string Remarks string `xorm:"VARCHAR(500)"`
// The ID of the corresponding shadow-copied content when exists; otherwise null. // The ID of the corresponding shadow-copied content when exists; otherwise null.
ShadowCopyID sql.NullInt64 `xorm:"DEFAULT NULL"` ShadowCopyID sql.NullInt64 `xorm:"DEFAULT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`

View file

@ -17,7 +17,7 @@ import (
type AbuseReportShadowCopy struct { type AbuseReportShadowCopy struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
RawValue string `xorm:"NOT NULL"` RawValue string `xorm:"LONGTEXT NOT NULL"` // A JSON with relevant fields from user, repository, issue or comment table.
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
} }

View file

@ -12,6 +12,8 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"forgejo.org/modules/container"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@ -32,13 +34,15 @@ type loader struct {
fixtureFiles []*fixtureFile fixtureFiles []*fixtureFile
} }
func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string) (*loader, error) { func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string, allTableNames container.Set[string]) (*loader, error) {
l := &loader{ l := &loader{
db: db, db: db,
dialect: dialect, dialect: dialect,
fixtureFiles: []*fixtureFile{}, fixtureFiles: []*fixtureFile{},
} }
tablesWithoutFixture := allTableNames
// Load fixtures // Load fixtures
for _, fixturePath := range fixturePaths { for _, fixturePath := range fixturePaths {
stat, err := os.Stat(fixturePath) stat, err := os.Stat(fixturePath)
@ -60,6 +64,7 @@ func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string) (*loade
return nil, err return nil, err
} }
l.fixtureFiles = append(l.fixtureFiles, fixtureFile) l.fixtureFiles = append(l.fixtureFiles, fixtureFile)
tablesWithoutFixture.Remove(fixtureFile.name)
} }
} }
} else { } else {
@ -71,6 +76,14 @@ func newFixtureLoader(db *sql.DB, dialect string, fixturePaths []string) (*loade
} }
} }
// Even though these tables have no fixtures, they can still be used and ensure
// they are cleaned.
for table := range tablesWithoutFixture.Seq() {
l.fixtureFiles = append(l.fixtureFiles, &fixtureFile{
name: table,
})
}
return l, nil return l, nil
} }
@ -178,13 +191,13 @@ func (l *loader) Load() error {
}() }()
// Clean the table and re-insert the fixtures. // Clean the table and re-insert the fixtures.
tableDeleted := map[string]struct{}{} tableDeleted := make(container.Set[string])
for _, fixture := range l.fixtureFiles { for _, fixture := range l.fixtureFiles {
if _, ok := tableDeleted[fixture.name]; !ok { if !tableDeleted.Contains(fixture.name) {
if _, err := tx.Exec(fmt.Sprintf("DELETE FROM %s", l.quoteKeyword(fixture.name))); err != nil { if _, err := tx.Exec(fmt.Sprintf("DELETE FROM %s", l.quoteKeyword(fixture.name))); err != nil {
return fmt.Errorf("cannot delete table %s: %w", fixture.name, err) return fmt.Errorf("cannot delete table %s: %w", fixture.name, err)
} }
tableDeleted[fixture.name] = struct{}{} tableDeleted.Add(fixture.name)
} }
for _, insertSQL := range fixture.insertSQLs { for _, insertSQL := range fixture.insertSQLs {

View file

@ -7,10 +7,12 @@ package unittest
import ( import (
"fmt" "fmt"
"path/filepath" "path/filepath"
"sync"
"time" "time"
"forgejo.org/models/db" "forgejo.org/models/db"
"forgejo.org/modules/auth/password/hash" "forgejo.org/modules/auth/password/hash"
"forgejo.org/modules/container"
"forgejo.org/modules/setting" "forgejo.org/modules/setting"
"xorm.io/xorm" "xorm.io/xorm"
@ -44,6 +46,8 @@ func OverrideFixtures(dir string) func() {
} }
} }
var allTableNames = sync.OnceValue(db.GetTableNames)
// InitFixtures initialize test fixtures for a test database // InitFixtures initialize test fixtures for a test database
func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) { func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) {
e, err := GetXORMEngine(engine...) e, err := GetXORMEngine(engine...)
@ -75,7 +79,12 @@ func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) {
panic("Unsupported RDBMS for test") panic("Unsupported RDBMS for test")
} }
fixturesLoader, err = newFixtureLoader(e.DB().DB, dialect, fixturePaths) var allTables container.Set[string]
if !opts.SkipCleanRegistedModels {
allTables = allTableNames().Clone()
}
fixturesLoader, err = newFixtureLoader(e.DB().DB, dialect, fixturePaths, allTables)
if err != nil { if err != nil {
return err return err
} }

View file

@ -217,6 +217,10 @@ type FixturesOptions struct {
Files []string Files []string
Dirs []string Dirs []string
Base string Base string
// By default all registered models are cleaned, even if they do not have
// fixture. Enabling this will skip that and only models with fixtures are
// considered.
SkipCleanRegistedModels bool
} }
// CreateTestEngine creates a memory database and loads the fixture data from fixturesDir // CreateTestEngine creates a memory database and loads the fixture data from fixturesDir

View file

@ -19,7 +19,7 @@ func (u *User) APActorID() string {
return fmt.Sprintf("%sapi/v1/activitypub/user-id/%s", setting.AppURL, url.PathEscape(fmt.Sprintf("%d", u.ID))) return fmt.Sprintf("%sapi/v1/activitypub/user-id/%s", setting.AppURL, url.PathEscape(fmt.Sprintf("%d", u.ID)))
} }
// APActorKeyID returns the ID of the user's public key // KeyID returns the ID of the user's public key
func (u *User) APActorKeyID() string { func (u *User) KeyID() string {
return u.APActorID() + "#main-key" return u.APActorID() + "#main-key"
} }

View file

@ -181,3 +181,20 @@ func TestDeletePrimaryEmailAddressOfUser(t *testing.T) {
assert.True(t, user_model.IsErrEmailAddressNotExist(err)) assert.True(t, user_model.IsErrEmailAddressNotExist(err))
assert.Nil(t, email) assert.Nil(t, email)
} }
func TestActivateUserEmail(t *testing.T) {
defer unittest.OverrideFixtures("models/fixtures/TestActivateUserEmail")()
require.NoError(t, unittest.PrepareTestDatabase())
t.Run("Activate email", func(t *testing.T) {
require.NoError(t, user_model.ActivateUserEmail(t.Context(), 1001, "AnotherTestUserWithUpperCaseEmail@otto.splvs.net", true))
unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{UID: 1001}, "is_activated = true")
})
t.Run("Deactivate email", func(t *testing.T) {
require.NoError(t, user_model.ActivateUserEmail(t.Context(), 1001, "AnotherTestUserWithUpperCaseEmail@otto.splvs.net", false))
unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{UID: 1001}, "is_activated = false")
})
}

View file

@ -182,11 +182,11 @@ func (u *User) BeforeUpdate() {
u.MaxRepoCreation = -1 u.MaxRepoCreation = -1
} }
// Organization does not need email // Ensure AvatarEmail is set for non-organization users, because organization
u.Email = strings.ToLower(u.Email) // are not required to have a email set.
if !u.IsOrganization() { if !u.IsOrganization() {
if len(u.AvatarEmail) == 0 { if len(u.AvatarEmail) == 0 {
u.AvatarEmail = u.Email u.AvatarEmail = strings.ToLower(u.Email)
} }
} }

View file

@ -57,14 +57,6 @@ func CreateFederatedUser(ctx context.Context, user *User, federatedUser *Federat
return committer.Commit() return committer.Commit()
} }
func (federatedUser *FederatedUser) UpdateFederatedUser(ctx context.Context) error {
if _, err := validation.IsValid(federatedUser); err != nil {
return err
}
_, err := db.GetEngine(ctx).ID(federatedUser.ID).Cols("inbox_path").Update(federatedUser)
return err
}
func FindFederatedUser(ctx context.Context, externalID string, federationHostID int64) (*User, *FederatedUser, error) { func FindFederatedUser(ctx context.Context, externalID string, federationHostID int64) (*User, *FederatedUser, error) {
federatedUser := new(FederatedUser) federatedUser := new(FederatedUser)
user := new(User) user := new(User)
@ -219,7 +211,6 @@ func RemoveFollower(ctx context.Context, followedUser *User, followingUser *Fede
return err return err
} }
// TODO: We should unify Activity-pub-following and classical following (see models/user/follow.go)
func IsFollowingAp(ctx context.Context, followedUser *User, followingUser *FederatedUser) (bool, error) { func IsFollowingAp(ctx context.Context, followedUser *User, followingUser *FederatedUser) (bool, error) {
if res, err := validation.IsValid(followedUser); !res { if res, err := validation.IsValid(followedUser); !res {
return false, err return false, err

View file

@ -150,7 +150,7 @@ func TestAPActorID_APActorID(t *testing.T) {
func TestKeyID(t *testing.T) { func TestKeyID(t *testing.T) {
user := user_model.User{ID: 1} user := user_model.User{ID: 1}
url := user.APActorKeyID() url := user.KeyID()
expected := "https://try.gitea.io/api/v1/activitypub/user-id/1#main-key" expected := "https://try.gitea.io/api/v1/activitypub/user-id/1#main-key"
assert.Equal(t, expected, url) assert.Equal(t, expected, url)
} }

View file

@ -89,6 +89,7 @@ func NewClientFactory() (c *ClientFactory, err error) {
type APClientFactory interface { type APClientFactory interface {
WithKeys(ctx context.Context, user *user_model.User, pubID string) (APClient, error) WithKeys(ctx context.Context, user *user_model.User, pubID string) (APClient, error)
WithKeysDirect(ctx context.Context, privateKey, pubID string) (APClient, error)
} }
// Client struct // Client struct
@ -103,12 +104,8 @@ type Client struct {
} }
// NewRequest function // NewRequest function
func (cf *ClientFactory) WithKeys(ctx context.Context, user *user_model.User, pubID string) (APClient, error) { func (cf *ClientFactory) WithKeysDirect(ctx context.Context, privateKey, pubID string) (APClient, error) {
priv, err := GetPrivateKey(ctx, user) privPem, _ := pem.Decode([]byte(privateKey))
if err != nil {
return nil, err
}
privPem, _ := pem.Decode([]byte(priv))
privParsed, err := x509.ParsePKCS1PrivateKey(privPem.Bytes) privParsed, err := x509.ParsePKCS1PrivateKey(privPem.Bytes)
if err != nil { if err != nil {
return nil, err return nil, err
@ -126,6 +123,14 @@ func (cf *ClientFactory) WithKeys(ctx context.Context, user *user_model.User, pu
return &c, nil return &c, nil
} }
func (cf *ClientFactory) WithKeys(ctx context.Context, user *user_model.User, pubID string) (APClient, error) {
priv, err := GetPrivateKey(ctx, user)
if err != nil {
return nil, err
}
return cf.WithKeysDirect(ctx, priv, pubID)
}
// NewRequest function // NewRequest function
func (c *Client) newRequest(method string, b []byte, to string) (req *http.Request, err error) { func (c *Client) newRequest(method string, b []byte, to string) (req *http.Request, err error) {
buf := bytes.NewBuffer(b) buf := bytes.NewBuffer(b)
@ -149,6 +154,7 @@ func (c *Client) Post(b []byte, to string) (resp *http.Response, err error) {
return nil, err return nil, err
} }
if c.pubID != "" {
signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.postHeaders, httpsig.Signature, httpsigExpirationTime) signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.postHeaders, httpsig.Signature, httpsigExpirationTime)
if err != nil { if err != nil {
return nil, err return nil, err
@ -156,6 +162,7 @@ func (c *Client) Post(b []byte, to string) (resp *http.Response, err error) {
if err := signer.SignRequest(c.priv, c.pubID, req, b); err != nil { if err := signer.SignRequest(c.priv, c.pubID, req, b); err != nil {
return nil, err return nil, err
} }
}
resp, err = c.client.Do(req) resp, err = c.client.Do(req)
return resp, err return resp, err
@ -167,6 +174,8 @@ func (c *Client) Get(to string) (resp *http.Response, err error) {
if req, err = c.newRequest(http.MethodGet, nil, to); err != nil { if req, err = c.newRequest(http.MethodGet, nil, to); err != nil {
return nil, err return nil, err
} }
if c.pubID != "" {
signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.getHeaders, httpsig.Signature, httpsigExpirationTime) signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.getHeaders, httpsig.Signature, httpsigExpirationTime)
if err != nil { if err != nil {
return nil, err return nil, err
@ -174,6 +183,7 @@ func (c *Client) Get(to string) (resp *http.Response, err error) {
if err := signer.SignRequest(c.priv, c.pubID, req, nil); err != nil { if err := signer.SignRequest(c.priv, c.pubID, req, nil); err != nil {
return nil, err return nil, err
} }
}
resp, err = c.client.Do(req) resp, err = c.client.Do(req)
return resp, err return resp, err

View file

@ -74,3 +74,8 @@ func (s Set[T]) Values() []T {
func (s Set[T]) Seq() iter.Seq[T] { func (s Set[T]) Seq() iter.Seq[T] {
return maps.Keys(s) return maps.Keys(s)
} }
// Clone returns a identical shallow copy of this set.
func (s Set[T]) Clone() Set[T] {
return maps.Clone(s)
}

View file

@ -47,4 +47,11 @@ func TestSet(t *testing.T) {
assert.False(t, s.IsSubset([]string{"key1"})) assert.False(t, s.IsSubset([]string{"key1"}))
assert.True(t, s.IsSubset([]string{})) assert.True(t, s.IsSubset([]string{}))
t.Run("Clone", func(t *testing.T) {
clonedSet := s.Clone()
clonedSet.Remove("key6")
assert.False(t, clonedSet.Contains("key6"))
assert.True(t, s.Contains("key6"))
})
} }

View file

@ -132,7 +132,7 @@ func (r *BlameReader) Close() error {
// CreateBlameReader creates reader for given repository, commit and file // CreateBlameReader creates reader for given repository, commit and file
func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (*BlameReader, error) { func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (*BlameReader, error) {
var ignoreRevsFile *string var ignoreRevsFile *string
if CheckGitVersionAtLeast("2.23") == nil && !bypassBlameIgnore { if !bypassBlameIgnore {
ignoreRevsFile = tryCreateBlameIgnoreRevsFile(commit) ignoreRevsFile = tryCreateBlameIgnoreRevsFile(commit)
} }

View file

@ -8,6 +8,7 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"fmt"
"io" "io"
"forgejo.org/modules/log" "forgejo.org/modules/log"
@ -172,60 +173,43 @@ func (b *Blob) GetBlobContent(limit int64) (string, error) {
return string(buf), err return string(buf), err
} }
// GetBlobLineCount gets line count of the blob type BlobTooLargeError struct {
func (b *Blob) GetBlobLineCount() (int, error) { Size, Limit int64
reader, err := b.DataAsync()
if err != nil {
return 0, err
}
defer reader.Close()
buf := make([]byte, 32*1024)
count := 1
lineSep := []byte{'\n'}
c, err := reader.Read(buf)
if c == 0 && err == io.EOF {
return 0, nil
}
for {
count += bytes.Count(buf[:c], lineSep)
switch {
case err == io.EOF:
return count, nil
case err != nil:
return count, err
}
c, err = reader.Read(buf)
}
} }
// GetBlobContentBase64 Reads the content of the blob with a base64 encode and returns the encoded string func (b BlobTooLargeError) Error() string {
func (b *Blob) GetBlobContentBase64() (string, error) { return fmt.Sprintf("blob: content larger than limit (%d > %d)", b.Size, b.Limit)
dataRc, err := b.DataAsync() }
// GetContentBase64 Reads the content of the blob and returns it as base64 encoded string.
// Returns [BlobTooLargeError] if the (unencoded) content is larger than the limit.
func (b *Blob) GetContentBase64(limit int64) (string, error) {
if b.Size() > limit {
return "", BlobTooLargeError{
Size: b.Size(),
Limit: limit,
}
}
rc, size, err := b.NewTruncatedReader(limit)
if err != nil { if err != nil {
return "", err return "", err
} }
defer dataRc.Close() defer rc.Close()
pr, pw := io.Pipe() encoding := base64.StdEncoding
encoder := base64.NewEncoder(base64.StdEncoding, pw) buf := bytes.NewBuffer(make([]byte, 0, encoding.EncodedLen(int(size))))
go func() { encoder := base64.NewEncoder(encoding, buf)
_, err := io.Copy(encoder, dataRc)
_ = encoder.Close()
if err != nil { if _, err := io.Copy(encoder, rc); err != nil {
_ = pw.CloseWithError(err)
} else {
_ = pw.Close()
}
}()
out, err := io.ReadAll(pr)
if err != nil {
return "", err return "", err
} }
return string(out), nil if err := encoder.Close(); err != nil {
return "", err
}
return buf.String(), nil
} }
// GuessContentType guesses the content type of the blob. // GuessContentType guesses the content type of the blob.
@ -236,7 +220,7 @@ func (b *Blob) GuessContentType() (typesniffer.SniffedType, error) {
} }
defer r.Close() defer r.Close()
return typesniffer.DetectContentTypeFromReader(r) return typesniffer.DetectContentTypeFromReader(r, b.Name())
} }
// GetBlob finds the blob object in the repository. // GetBlob finds the blob object in the repository.

View file

@ -63,6 +63,24 @@ func TestBlob(t *testing.T) {
require.Equal(t, "file2\n", r) require.Equal(t, "file2\n", r)
}) })
t.Run("GetContentBase64", func(t *testing.T) {
r, err := testBlob.GetContentBase64(100)
require.NoError(t, err)
require.Equal(t, "ZmlsZTIK", r)
r, err = testBlob.GetContentBase64(-1)
require.ErrorAs(t, err, &BlobTooLargeError{})
require.Empty(t, r)
r, err = testBlob.GetContentBase64(4)
require.ErrorAs(t, err, &BlobTooLargeError{})
require.Empty(t, r)
r, err = testBlob.GetContentBase64(6)
require.NoError(t, err)
require.Equal(t, "ZmlsZTIK", r)
})
t.Run("NewTruncatedReader", func(t *testing.T) { t.Run("NewTruncatedReader", func(t *testing.T) {
// read fewer than available // read fewer than available
rc, size, err := testBlob.NewTruncatedReader(100) rc, size, err := testBlob.NewTruncatedReader(100)

View file

@ -412,11 +412,7 @@ func (c *Commit) GetSubModule(entryname string) (string, error) {
// GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only') // GetBranchName gets the closest branch name (as returned by 'git name-rev --name-only')
func (c *Commit) GetBranchName() (string, error) { func (c *Commit) GetBranchName() (string, error) {
cmd := NewCommand(c.repo.Ctx, "name-rev") cmd := NewCommand(c.repo.Ctx, "name-rev", "--exclude", "refs/tags/*", "--name-only", "--no-undefined").AddDynamicArguments(c.ID.String())
if CheckGitVersionAtLeast("2.13.0") == nil {
cmd.AddArguments("--exclude", "refs/tags/*")
}
cmd.AddArguments("--name-only", "--no-undefined").AddDynamicArguments(c.ID.String())
data, _, err := cmd.RunStdString(&RunOpts{Dir: c.repo.Path}) data, _, err := cmd.RunStdString(&RunOpts{Dir: c.repo.Path})
if err != nil { if err != nil {
// handle special case where git can not describe commit // handle special case where git can not describe commit

View file

@ -23,7 +23,7 @@ import (
) )
// RequiredVersion is the minimum Git version required // RequiredVersion is the minimum Git version required
const RequiredVersion = "2.0.0" const RequiredVersion = "2.34.1"
var ( var (
// GitExecutable is the command name of git // GitExecutable is the command name of git
@ -33,7 +33,6 @@ var (
// DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx // DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx
DefaultContext context.Context DefaultContext context.Context
SupportProcReceive bool // >= 2.29
SupportHashSha256 bool // >= 2.42, SHA-256 repositories no longer an experimental curiosity SupportHashSha256 bool // >= 2.42, SHA-256 repositories no longer an experimental curiosity
InvertedGitFlushEnv bool // 2.43.1 InvertedGitFlushEnv bool // 2.43.1
SupportCheckAttrOnBare bool // >= 2.40 SupportCheckAttrOnBare bool // >= 2.40
@ -113,7 +112,7 @@ func VersionInfo() string {
format := "%s" format := "%s"
args := []any{GitVersion.Original()} args := []any{GitVersion.Original()}
// Since git wire protocol has been released from git v2.18 // Since git wire protocol has been released from git v2.18
if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil { if setting.Git.EnableAutoGitWireProtocol {
format += ", Wire Protocol %s Enabled" format += ", Wire Protocol %s Enabled"
args = append(args, "Version 2") // for focus color args = append(args, "Version 2") // for focus color
} }
@ -172,16 +171,13 @@ func InitFull(ctx context.Context) (err error) {
_ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg")) _ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg"))
} }
// Since git wire protocol has been released from git v2.18 if setting.Git.EnableAutoGitWireProtocol {
if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2") globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
} }
// Explicitly disable credential helper, otherwise Git credentials might leak // Explicitly disable credential helper, otherwise Git credentials might leak
if CheckGitVersionAtLeast("2.9") == nil {
globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=") globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=")
}
SupportProcReceive = CheckGitVersionAtLeast("2.29") == nil
SupportHashSha256 = CheckGitVersionAtLeast("2.42") == nil SupportHashSha256 = CheckGitVersionAtLeast("2.42") == nil
SupportCheckAttrOnBare = CheckGitVersionAtLeast("2.40") == nil SupportCheckAttrOnBare = CheckGitVersionAtLeast("2.40") == nil
if SupportHashSha256 { if SupportHashSha256 {
@ -195,9 +191,6 @@ func InitFull(ctx context.Context) (err error) {
SupportGrepMaxCount = CheckGitVersionAtLeast("2.38") == nil SupportGrepMaxCount = CheckGitVersionAtLeast("2.38") == nil
if setting.LFS.StartServer { if setting.LFS.StartServer {
if CheckGitVersionAtLeast("2.1.2") != nil {
return errors.New("LFS server support requires Git >= 2.1.2")
}
globalCommandArgs = append(globalCommandArgs, "-c", "filter.lfs.required=", "-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=") globalCommandArgs = append(globalCommandArgs, "-c", "filter.lfs.required=", "-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=")
} }
@ -234,18 +227,15 @@ func syncGitConfig() (err error) {
} }
} }
// Set git some configurations - these must be set to these values for gitea to work correctly // Set git some configurations - these must be set to these values for forgejo to work correctly
if err := configSet("core.quotePath", "false"); err != nil { if err := configSet("core.quotePath", "false"); err != nil {
return err return err
} }
if CheckGitVersionAtLeast("2.10") == nil {
if err := configSet("receive.advertisePushOptions", "true"); err != nil { if err := configSet("receive.advertisePushOptions", "true"); err != nil {
return err return err
} }
}
if CheckGitVersionAtLeast("2.18") == nil {
if err := configSet("core.commitGraph", "true"); err != nil { if err := configSet("core.commitGraph", "true"); err != nil {
return err return err
} }
@ -255,18 +245,11 @@ func syncGitConfig() (err error) {
if err := configSet("fetch.writeCommitGraph", "true"); err != nil { if err := configSet("fetch.writeCommitGraph", "true"); err != nil {
return err return err
} }
}
if SupportProcReceive {
// set support for AGit flow // set support for AGit flow
if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil { if err := configAddNonExist("receive.procReceiveRefs", "refs/for"); err != nil {
return err return err
} }
} else {
if err := configUnsetAll("receive.procReceiveRefs", "refs/for"); err != nil {
return err
}
}
// Due to CVE-2022-24765, git now denies access to git directories which are not owned by current user // Due to CVE-2022-24765, git now denies access to git directories which are not owned by current user
// however, some docker users and samba users find it difficult to configure their systems so that Gitea's git repositories are owned by the Gitea user. (Possibly Windows Service users - but ownership in this case should really be set correctly on the filesystem.) // however, some docker users and samba users find it difficult to configure their systems so that Gitea's git repositories are owned by the Gitea user. (Possibly Windows Service users - but ownership in this case should really be set correctly on the filesystem.)
@ -284,11 +267,6 @@ func syncGitConfig() (err error) {
switch setting.Repository.Signing.Format { switch setting.Repository.Signing.Format {
case "ssh": case "ssh":
// First do a git version check.
if CheckGitVersionAtLeast("2.34.0") != nil {
return errors.New("ssh signing requires Git >= 2.34.0")
}
// Get the ssh-keygen binary that Git will use. // Get the ssh-keygen binary that Git will use.
// This can be overridden in app.ini in [git.config] section, so we must // This can be overridden in app.ini in [git.config] section, so we must
// query this information. // query this information.
@ -325,8 +303,7 @@ func syncGitConfig() (err error) {
} }
} }
// By default partial clones are disabled, enable them from git v2.22 if !setting.Git.DisablePartialClone {
if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
if err = configSet("uploadpack.allowfilter", "true"); err != nil { if err = configSet("uploadpack.allowfilter", "true"); err != nil {
return err return err
} }

View file

@ -14,7 +14,6 @@ import (
"forgejo.org/modules/test" "forgejo.org/modules/test"
"forgejo.org/modules/util" "forgejo.org/modules/util"
"github.com/hashicorp/go-version"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -105,10 +104,6 @@ func TestSyncConfigGPGFormat(t *testing.T) {
}) })
t.Run("SSH format", func(t *testing.T) { t.Run("SSH format", func(t *testing.T) {
if CheckGitVersionAtLeast("2.34.0") != nil {
t.SkipNow()
}
r, err := os.OpenRoot(t.TempDir()) r, err := os.OpenRoot(t.TempDir())
require.NoError(t, err) require.NoError(t, err)
f, err := r.OpenFile("ssh-keygen", os.O_CREATE|os.O_TRUNC, 0o700) f, err := r.OpenFile("ssh-keygen", os.O_CREATE|os.O_TRUNC, 0o700)
@ -121,13 +116,6 @@ func TestSyncConfigGPGFormat(t *testing.T) {
assert.True(t, gitConfigContains("[gpg]")) assert.True(t, gitConfigContains("[gpg]"))
assert.True(t, gitConfigContains("format = ssh")) assert.True(t, gitConfigContains("format = ssh"))
t.Run("Old version", func(t *testing.T) {
oldVersion, err := version.NewVersion("2.33.0")
require.NoError(t, err)
defer test.MockVariableValue(&GitVersion, oldVersion)()
require.ErrorContains(t, syncGitConfig(), "ssh signing requires Git >= 2.34.0")
})
t.Run("No ssh-keygen binary", func(t *testing.T) { t.Run("No ssh-keygen binary", func(t *testing.T) {
require.NoError(t, r.Remove("ssh-keygen")) require.NoError(t, r.Remove("ssh-keygen"))
require.ErrorContains(t, syncGitConfig(), "git signing requires a ssh-keygen binary") require.ErrorContains(t, syncGitConfig(), "git signing requires a ssh-keygen binary")

View file

@ -16,26 +16,6 @@ import (
"forgejo.org/modules/log" "forgejo.org/modules/log"
) )
// RevListAllObjects runs rev-list --objects --all and writes to a pipewriter
func RevListAllObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.WaitGroup, basePath string, errChan chan<- error) {
defer wg.Done()
defer revListWriter.Close()
stderr := new(bytes.Buffer)
var errbuf strings.Builder
cmd := git.NewCommand(ctx, "rev-list", "--objects", "--all")
if err := cmd.Run(&git.RunOpts{
Dir: basePath,
Stdout: revListWriter,
Stderr: stderr,
}); err != nil {
log.Error("git rev-list --objects --all [%s]: %v - %s", basePath, err, errbuf.String())
err = fmt.Errorf("git rev-list --objects --all [%s]: %w - %s", basePath, err, errbuf.String())
_ = revListWriter.CloseWithError(err)
errChan <- err
}
}
// RevListObjects run rev-list --objects from headSHA to baseSHA // RevListObjects run rev-list --objects from headSHA to baseSHA
func RevListObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.WaitGroup, tmpBasePath, headSHA, baseSHA string, errChan chan<- error) { func RevListObjects(ctx context.Context, revListWriter *io.PipeWriter, wg *sync.WaitGroup, tmpBasePath, headSHA, baseSHA string, errChan chan<- error) {
defer wg.Done() defer wg.Done()

View file

@ -12,14 +12,7 @@ import (
// GetRemoteAddress returns remote url of git repository in the repoPath with special remote name // GetRemoteAddress returns remote url of git repository in the repoPath with special remote name
func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) { func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) {
var cmd *Command result, _, err := NewCommand(ctx, "remote", "get-url").AddDynamicArguments(remoteName).RunStdString(&RunOpts{Dir: repoPath})
if CheckGitVersionAtLeast("2.7") == nil {
cmd = NewCommand(ctx, "remote", "get-url").AddDynamicArguments(remoteName)
} else {
cmd = NewCommand(ctx, "config", "--get").AddDynamicArguments("remote." + remoteName + ".url")
}
result, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
if err != nil { if err != nil {
return "", err return "", err
} }

View file

@ -5,7 +5,6 @@ package git
import ( import (
"context" "context"
"fmt"
"io" "io"
"io/fs" "io/fs"
"os" "os"
@ -197,7 +196,7 @@ func TestGitAttributeCheckerError(t *testing.T) {
path := t.TempDir() path := t.TempDir()
// we can't use unittest.CopyDir because of an import cycle (git.Init in unittest) // we can't use unittest.CopyDir because of an import cycle (git.Init in unittest)
require.NoError(t, CopyFS(path, os.DirFS(filepath.Join(testReposDir, "language_stats_repo")))) require.NoError(t, os.CopyFS(path, os.DirFS(filepath.Join(testReposDir, "language_stats_repo"))))
gitRepo, err := openRepositoryWithDefaultContext(path) gitRepo, err := openRepositoryWithDefaultContext(path)
require.NoError(t, err) require.NoError(t, err)
@ -324,32 +323,3 @@ func TestGitAttributeCheckerError(t *testing.T) {
require.ErrorIs(t, err, fs.ErrClosed) require.ErrorIs(t, err, fs.ErrClosed)
}) })
} }
// CopyFS is adapted from https://github.com/golang/go/issues/62484
// which should be available with go1.23
func CopyFS(dir string, fsys fs.FS) error {
return fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, _ error) error {
targ := filepath.Join(dir, filepath.FromSlash(path))
if d.IsDir() {
return os.MkdirAll(targ, 0o777)
}
r, err := fsys.Open(path)
if err != nil {
return err
}
defer r.Close()
info, err := r.Stat()
if err != nil {
return err
}
w, err := os.OpenFile(targ, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o666|info.Mode()&0o777)
if err != nil {
return err
}
if _, err := io.Copy(w, r); err != nil {
w.Close()
return fmt.Errorf("copying %s: %v", path, err)
}
return w.Close()
})
}

View file

@ -443,7 +443,6 @@ func (repo *Repository) getCommitsBeforeLimit(id ObjectID, num int) ([]*Commit,
} }
func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) { func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error) {
if CheckGitVersionAtLeast("2.7.0") == nil {
command := NewCommand(repo.Ctx, "for-each-ref", "--format=%(refname:strip=2)").AddOptionValues("--contains", commit.ID.String(), BranchPrefix) command := NewCommand(repo.Ctx, "for-each-ref", "--format=%(refname:strip=2)").AddOptionValues("--contains", commit.ID.String(), BranchPrefix)
if limit != -1 { if limit != -1 {
@ -457,29 +456,6 @@ func (repo *Repository) getBranches(commit *Commit, limit int) ([]string, error)
branches := strings.Fields(stdout) branches := strings.Fields(stdout)
return branches, nil return branches, nil
}
stdout, _, err := NewCommand(repo.Ctx, "branch").AddOptionValues("--contains", commit.ID.String()).RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
refs := strings.Split(stdout, "\n")
var max int
if len(refs) > limit {
max = limit
} else {
max = len(refs) - 1
}
branches := make([]string, max)
for i, ref := range refs[:max] {
parts := strings.Fields(ref)
branches[i] = parts[len(parts)-1]
}
return branches, nil
} }
// GetCommitsFromIDs get commits from commit IDs // GetCommitsFromIDs get commits from commit IDs

View file

@ -11,10 +11,8 @@ import (
// WriteCommitGraph write commit graph to speed up repo access // WriteCommitGraph write commit graph to speed up repo access
// this requires git v2.18 to be installed // this requires git v2.18 to be installed
func WriteCommitGraph(ctx context.Context, repoPath string) error { func WriteCommitGraph(ctx context.Context, repoPath string) error {
if CheckGitVersionAtLeast("2.18") == nil {
if _, _, err := NewCommand(ctx, "commit-graph", "write").RunStdString(&RunOpts{Dir: repoPath}); err != nil { if _, _, err := NewCommand(ctx, "commit-graph", "write").RunStdString(&RunOpts{Dir: repoPath}); err != nil {
return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err) return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err)
} }
}
return nil return nil
} }

View file

@ -116,32 +116,37 @@ func (te *TreeEntry) Type() string {
} }
} }
// LinkTarget returns the target of the symlink as string.
func (te *TreeEntry) LinkTarget() (string, error) {
if !te.IsLink() {
return "", ErrBadLink{te.Name(), "not a symlink"}
}
const symlinkLimit = 4096 // according to git config core.longpaths https://stackoverflow.com/a/22575737
blob := te.Blob()
if blob.Size() > symlinkLimit {
return "", ErrBadLink{te.Name(), "symlink too large"}
}
rc, size, err := blob.NewTruncatedReader(symlinkLimit)
if err != nil {
return "", err
}
defer rc.Close()
buf := make([]byte, int(size))
_, err = io.ReadFull(rc, buf)
return string(buf), err
}
// FollowLink returns the entry pointed to by a symlink // FollowLink returns the entry pointed to by a symlink
func (te *TreeEntry) FollowLink() (*TreeEntry, string, error) { func (te *TreeEntry) FollowLink() (*TreeEntry, string, error) {
if !te.IsLink() {
return nil, "", ErrBadLink{te.Name(), "not a symlink"}
}
// read the link // read the link
r, err := te.Blob().DataAsync() lnk, err := te.LinkTarget()
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
closed := false
defer func() {
if !closed {
_ = r.Close()
}
}()
buf := make([]byte, te.Size())
_, err = io.ReadFull(r, buf)
if err != nil {
return nil, "", err
}
_ = r.Close()
closed = true
lnk := string(buf)
t := te.ptree t := te.ptree
// traverse up directories // traverse up directories

View file

@ -99,7 +99,7 @@ func setServeHeadersByFile(r *http.Request, w http.ResponseWriter, filePath stri
Filename: path.Base(filePath), Filename: path.Base(filePath),
} }
sniffedType := typesniffer.DetectContentType(mineBuf) sniffedType := typesniffer.DetectContentType(mineBuf, opts.Filename)
// the "render" parameter came from year 2016: 638dd24c, it doesn't have clear meaning, so I think it could be removed later // the "render" parameter came from year 2016: 638dd24c, it doesn't have clear meaning, so I think it could be removed later
isPlain := sniffedType.IsText() || r.FormValue("render") != "" isPlain := sniffedType.IsText() || r.FormValue("render") != ""

View file

@ -177,7 +177,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro
fileContents, err := io.ReadAll(io.LimitReader(batchReader, size)) fileContents, err := io.ReadAll(io.LimitReader(batchReader, size))
if err != nil { if err != nil {
return err return err
} else if !typesniffer.DetectContentType(fileContents).IsText() { } else if !typesniffer.DetectContentType(fileContents, update.Filename).IsText() {
// FIXME: UTF-16 files will probably fail here // FIXME: UTF-16 files will probably fail here
// Even if the file is not recognized as a "text file", we could still put its name into the indexers to make the filename become searchable, while leave the content to empty. // Even if the file is not recognized as a "text file", we could still put its name into the indexers to make the filename become searchable, while leave the content to empty.
fileContents = nil fileContents = nil

View file

@ -144,7 +144,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro
fileContents, err := io.ReadAll(io.LimitReader(batchReader, size)) fileContents, err := io.ReadAll(io.LimitReader(batchReader, size))
if err != nil { if err != nil {
return nil, err return nil, err
} else if !typesniffer.DetectContentType(fileContents).IsText() { } else if !typesniffer.DetectContentType(fileContents, update.Filename).IsText() {
// FIXME: UTF-16 files will probably fail here // FIXME: UTF-16 files will probably fail here
return nil, nil return nil, nil
} }

View file

@ -170,7 +170,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
if issueID, err := token.ParseIssueReference(); err == nil { if issueID, err := token.ParseIssueReference(); err == nil {
idQuery := inner_bleve.NumericEqualityQuery(issueID, "index") idQuery := inner_bleve.NumericEqualityQuery(issueID, "index")
idQuery.SetBoost(5.0) idQuery.SetBoost(20.0)
innerQ.AddQuery(idQuery) innerQ.AddQuery(idQuery)
} }
@ -197,6 +197,15 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
queries = append(queries, bleve.NewDisjunctionQuery(repoQueries...)) queries = append(queries, bleve.NewDisjunctionQuery(repoQueries...))
} }
if options.PriorityRepoID.Has() {
eq := inner_bleve.NumericEqualityQuery(options.PriorityRepoID.Value(), "repo_id")
eq.SetBoost(10.0)
meh := bleve.NewMatchAllQuery()
meh.SetBoost(0)
should := bleve.NewDisjunctionQuery(eq, meh)
queries = append(queries, should)
}
if options.IsPull.Has() { if options.IsPull.Has() {
queries = append(queries, inner_bleve.BoolFieldQuery(options.IsPull.Value(), "is_pull")) queries = append(queries, inner_bleve.BoolFieldQuery(options.IsPull.Value(), "is_pull"))
} }

View file

@ -53,6 +53,7 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
cond := builder.NewCond() cond := builder.NewCond()
var priorityIssueIndex int64
if options.Keyword != "" { if options.Keyword != "" {
repoCond := builder.In("repo_id", options.RepoIDs) repoCond := builder.In("repo_id", options.RepoIDs)
if len(options.RepoIDs) == 1 { if len(options.RepoIDs) == 1 {
@ -82,6 +83,7 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
builder.Eq{"`index`": issueID}, builder.Eq{"`index`": issueID},
cond, cond,
) )
priorityIssueIndex = issueID
} }
} }
@ -89,6 +91,7 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
if err != nil { if err != nil {
return nil, err return nil, err
} }
opt.PriorityIssueIndex = priorityIssueIndex
// If pagesize == 0, return total count only. It's a special case for search count. // If pagesize == 0, return total count only. It's a special case for search count.
if options.Paginator != nil && options.Paginator.PageSize == 0 { if options.Paginator != nil && options.Paginator.PageSize == 0 {

View file

@ -78,6 +78,11 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m
User: nil, User: nil,
} }
if options.PriorityRepoID.Has() {
opts.SortType = "priorityrepo"
opts.PriorityRepoID = options.PriorityRepoID.Value()
}
if len(options.MilestoneIDs) == 1 && options.MilestoneIDs[0] == 0 { if len(options.MilestoneIDs) == 1 && options.MilestoneIDs[0] == 0 {
opts.MilestoneIDs = []int64{db.NoConditionID} opts.MilestoneIDs = []int64{db.NoConditionID}
} else { } else {

View file

@ -165,7 +165,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
} }
var eitherQ elastic.Query = innerQ var eitherQ elastic.Query = innerQ
if issueID, err := token.ParseIssueReference(); err == nil { if issueID, err := token.ParseIssueReference(); err == nil {
indexQ := elastic.NewTermQuery("index", issueID).Boost(15.0) indexQ := elastic.NewTermQuery("index", issueID).Boost(20)
eitherQ = elastic.NewDisMaxQuery().Query(indexQ).Query(innerQ).TieBreaker(0.5) eitherQ = elastic.NewDisMaxQuery().Query(indexQ).Query(innerQ).TieBreaker(0.5)
} }
switch token.Kind { switch token.Kind {
@ -188,6 +188,10 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
} }
query.Must(q) query.Must(q)
} }
if options.PriorityRepoID.Has() {
q := elastic.NewTermQuery("repo_id", options.PriorityRepoID.Value()).Boost(10)
query.Should(q)
}
if options.IsPull.Has() { if options.IsPull.Has() {
query.Must(elastic.NewTermQuery("is_pull", options.IsPull.Value())) query.Must(elastic.NewTermQuery("is_pull", options.IsPull.Value()))

View file

@ -77,6 +77,7 @@ type SearchOptions struct {
RepoIDs []int64 // repository IDs which the issues belong to RepoIDs []int64 // repository IDs which the issues belong to
AllPublic bool // if include all public repositories AllPublic bool // if include all public repositories
PriorityRepoID optional.Option[int64] // issues from this repository will be prioritized when SortByScore
IsPull optional.Option[bool] // if the issues is a pull request IsPull optional.Option[bool] // if the issues is a pull request
IsClosed optional.Option[bool] // if the issues is closed IsClosed optional.Option[bool] // if the issues is closed

View file

@ -742,6 +742,25 @@ var cases = []*testIndexerCase{
} }
}, },
}, },
{
Name: "PriorityRepoID",
SearchOptions: &internal.SearchOptions{
IsPull: optional.Some(false),
IsClosed: optional.Some(false),
PriorityRepoID: optional.Some(int64(3)),
Paginator: &db.ListOptionsAll,
SortBy: internal.SortByScore,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
for i, v := range result.Hits {
if i < 7 {
assert.Equal(t, int64(3), data[v.ID].RepoID)
} else {
assert.NotEqual(t, int64(3), data[v.ID].RepoID)
}
}
},
},
} }
type testIndexerCase struct { type testIndexerCase struct {

View file

@ -39,16 +39,7 @@ func SearchPointerBlobs(ctx context.Context, repo *git.Repository, pointerChan c
go pipeline.BlobsLessThan1024FromCatFileBatchCheck(catFileCheckReader, shasToBatchWriter, &wg) go pipeline.BlobsLessThan1024FromCatFileBatchCheck(catFileCheckReader, shasToBatchWriter, &wg)
// 1. Run batch-check on all objects in the repository // 1. Run batch-check on all objects in the repository
if git.CheckGitVersionAtLeast("2.6.0") != nil {
revListReader, revListWriter := io.Pipe()
shasToCheckReader, shasToCheckWriter := io.Pipe()
wg.Add(2)
go pipeline.CatFileBatchCheck(ctx, shasToCheckReader, catFileCheckWriter, &wg, basePath)
go pipeline.BlobsFromRevListObjects(revListReader, shasToCheckWriter, &wg)
go pipeline.RevListAllObjects(ctx, revListWriter, &wg, basePath, errChan)
} else {
go pipeline.CatFileBatchCheckAllObjects(ctx, catFileCheckWriter, &wg, basePath, errChan) go pipeline.CatFileBatchCheckAllObjects(ctx, catFileCheckWriter, &wg, basePath, errChan)
}
wg.Wait() wg.Wait()
close(pointerChan) close(pointerChan)

View file

@ -104,7 +104,7 @@ func TestRender_Images(t *testing.T) {
test( test(
"!["+title+"]("+url+")", "!["+title+"]("+url+")",
`<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`"/></a></p>`) `<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`" loading="lazy"/></a></p>`)
test( test(
"[["+title+"|"+url+"]]", "[["+title+"|"+url+"]]",
@ -115,7 +115,7 @@ func TestRender_Images(t *testing.T) {
test( test(
"!["+title+"]("+url+")", "!["+title+"]("+url+")",
`<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`"/></a></p>`) `<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`" loading="lazy"/></a></p>`)
test( test(
"[["+title+"|"+url+"]]", "[["+title+"|"+url+"]]",
@ -412,8 +412,8 @@ func TestRenderSiblingImages_Issue12925(t *testing.T) {
testcase := `![image1](/image1) testcase := `![image1](/image1)
![image2](/image2) ![image2](/image2)
` `
expected := `<p><a href="/image1" target="_blank" rel="nofollow noopener"><img src="/image1" alt="image1"></a><br> expected := `<p><a href="/image1" target="_blank" rel="nofollow noopener"><img src="/image1" alt="image1" loading="lazy"></a><br>
<a href="/image2" target="_blank" rel="nofollow noopener"><img src="/image2" alt="image2"></a></p> <a href="/image2" target="_blank" rel="nofollow noopener"><img src="/image2" alt="image2" loading="lazy"></a></p>
` `
res, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase) res, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase)
require.NoError(t, err) require.NoError(t, err)
@ -845,10 +845,10 @@ mail@domain.com
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/src/file.bin" rel="nofollow">local link</a><br/> <a href="/src/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image"/></a><br/> <a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/path/file" target="_blank" rel="nofollow noopener"><img src="/path/file" alt="local image"/></a><br/> <a href="/path/file" target="_blank" rel="nofollow noopener"><img src="/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/path/file" target="_blank" rel="nofollow noopener"><img src="/path/file" alt="local image"/></a><br/> <a href="/path/file" target="_blank" rel="nofollow noopener"><img src="/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/> <a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/image.jpg" rel="nofollow"><img src="/image.jpg" title="local image" alt=""/></a><br/> <a href="/image.jpg" rel="nofollow"><img src="/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/> <a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/> <a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -872,10 +872,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/wiki/file.bin" rel="nofollow">local link</a><br/> <a href="/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/image.jpg" alt="local image"/></a><br/> <a href="/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/path/file" alt="local image"/></a><br/> <a href="/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/path/file" alt="local image"/></a><br/> <a href="/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/> <a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/wiki/raw/image.jpg" rel="nofollow"><img src="/wiki/raw/image.jpg" title="local image" alt=""/></a><br/> <a href="/wiki/raw/image.jpg" rel="nofollow"><img src="/wiki/raw/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/> <a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/> <a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -901,10 +901,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="https://gitea.io/src/file.bin" rel="nofollow">local link</a><br/> <a href="https://gitea.io/src/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="https://gitea.io/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/image.jpg" alt="local image"/></a><br/> <a href="https://gitea.io/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="https://gitea.io/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/path/file" alt="local image"/></a><br/> <a href="https://gitea.io/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://gitea.io/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/path/file" alt="local image"/></a><br/> <a href="https://gitea.io/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/> <a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="https://gitea.io/image.jpg" rel="nofollow"><img src="https://gitea.io/image.jpg" title="local image" alt=""/></a><br/> <a href="https://gitea.io/image.jpg" rel="nofollow"><img src="https://gitea.io/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/> <a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/> <a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -930,10 +930,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/> <a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="https://gitea.io/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/image.jpg" alt="local image"/></a><br/> <a href="https://gitea.io/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="https://gitea.io/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/path/file" alt="local image"/></a><br/> <a href="https://gitea.io/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://gitea.io/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/path/file" alt="local image"/></a><br/> <a href="https://gitea.io/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/> <a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="https://gitea.io/wiki/raw/image.jpg" rel="nofollow"><img src="https://gitea.io/wiki/raw/image.jpg" title="local image" alt=""/></a><br/> <a href="https://gitea.io/wiki/raw/image.jpg" rel="nofollow"><img src="https://gitea.io/wiki/raw/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/> <a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/> <a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -959,10 +959,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/src/file.bin" rel="nofollow">local link</a><br/> <a href="/relative/path/src/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/image.jpg" alt="local image"/></a><br/> <a href="/relative/path/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/path/file" alt="local image"/></a><br/> <a href="/relative/path/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/path/file" alt="local image"/></a><br/> <a href="/relative/path/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/> <a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/relative/path/image.jpg" rel="nofollow"><img src="/relative/path/image.jpg" title="local image" alt=""/></a><br/> <a href="/relative/path/image.jpg" rel="nofollow"><img src="/relative/path/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/> <a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/> <a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -988,10 +988,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/> <a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/> <a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/> <a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/> <a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/> <a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt=""/></a><br/> <a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/> <a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/> <a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -1018,10 +1018,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/user/repo/src/branch/main/file.bin" rel="nofollow">local link</a><br/> <a href="/user/repo/src/branch/main/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/user/repo/media/branch/main/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/image.jpg" alt="local image"/></a><br/> <a href="/user/repo/media/branch/main/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/user/repo/media/branch/main/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/path/file" alt="local image"/></a><br/> <a href="/user/repo/media/branch/main/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/user/repo/media/branch/main/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/path/file" alt="local image"/></a><br/> <a href="/user/repo/media/branch/main/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/> <a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/user/repo/media/branch/main/image.jpg" rel="nofollow"><img src="/user/repo/media/branch/main/image.jpg" title="local image" alt=""/></a><br/> <a href="/user/repo/media/branch/main/image.jpg" rel="nofollow"><img src="/user/repo/media/branch/main/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/> <a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/> <a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -1048,10 +1048,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/> <a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/> <a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/> <a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/> <a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/> <a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt=""/></a><br/> <a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/> <a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/> <a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -1078,10 +1078,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/user/repo/src/sub/folder/file.bin" rel="nofollow">local link</a><br/> <a href="/user/repo/src/sub/folder/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/user/repo/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/image.jpg" alt="local image"/></a><br/> <a href="/user/repo/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/user/repo/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/path/file" alt="local image"/></a><br/> <a href="/user/repo/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/user/repo/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/path/file" alt="local image"/></a><br/> <a href="/user/repo/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/> <a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/user/repo/image.jpg" rel="nofollow"><img src="/user/repo/image.jpg" title="local image" alt=""/></a><br/> <a href="/user/repo/image.jpg" rel="nofollow"><img src="/user/repo/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/> <a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/> <a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -1108,10 +1108,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/> <a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/> <a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/> <a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/> <a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/> <a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt=""/></a><br/> <a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/> <a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/> <a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -1139,10 +1139,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/user/repo/src/branch/main/sub/folder/file.bin" rel="nofollow">local link</a><br/> <a href="/user/repo/src/branch/main/sub/folder/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/user/repo/media/branch/main/sub/folder/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/sub/folder/image.jpg" alt="local image"/></a><br/> <a href="/user/repo/media/branch/main/sub/folder/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/sub/folder/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/user/repo/media/branch/main/sub/folder/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/sub/folder/path/file" alt="local image"/></a><br/> <a href="/user/repo/media/branch/main/sub/folder/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/sub/folder/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/user/repo/media/branch/main/sub/folder/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/sub/folder/path/file" alt="local image"/></a><br/> <a href="/user/repo/media/branch/main/sub/folder/path/file" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/sub/folder/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/> <a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/user/repo/media/branch/main/sub/folder/image.jpg" rel="nofollow"><img src="/user/repo/media/branch/main/sub/folder/image.jpg" title="local image" alt=""/></a><br/> <a href="/user/repo/media/branch/main/sub/folder/image.jpg" rel="nofollow"><img src="/user/repo/media/branch/main/sub/folder/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/> <a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/> <a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
@ -1170,10 +1170,10 @@ space</p>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/> <a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
<a href="https://example.com" rel="nofollow">remote link</a><br/> <a href="https://example.com" rel="nofollow">remote link</a><br/>
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/> <a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/> <a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image"/></a><br/> <a href="/relative/path/wiki/raw/path/file" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/path/file" alt="local image" loading="lazy"/></a><br/>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/> <a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a><br/>
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt=""/></a><br/> <a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt=""/></a><br/>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/> <a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a><br/>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/> <a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>

View file

@ -44,6 +44,7 @@ func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image)
for _, attr := range v.Attributes() { for _, attr := range v.Attributes() {
image.SetAttribute(attr.Name, attr.Value) image.SetAttribute(attr.Name, attr.Value)
} }
image.SetAttributeString("loading", []byte("lazy"))
for child := v.FirstChild(); child != nil; { for child := v.FirstChild(); child != nil; {
next := child.NextSibling() next := child.NextSibling()
image.AppendChild(image, child) image.AppendChild(image, child)

View file

@ -108,6 +108,9 @@ func createDefaultPolicy() *bluemonday.Policy {
// Allow classes for emojis // Allow classes for emojis
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img") policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img")
// Allow attributes for images
policy.AllowAttrs("loading").Matching(regexp.MustCompile(`^lazy$`)).OnElements("img")
// Allow icons, emojis, chroma syntax and keyword markup on span // Allow icons, emojis, chroma syntax and keyword markup on span
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji)|(language-math display)|(language-math inline))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span") policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji)|(language-math display)|(language-math inline))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span")
policy.AllowAttrs("data-alias").Matching(regexp.MustCompile(`^[a-zA-Z0-9-_+]+$`)).OnElements("span") policy.AllowAttrs("data-alias").Matching(regexp.MustCompile(`^[a-zA-Z0-9-_+]+$`)).OnElements("span")

View file

@ -75,6 +75,10 @@ func Test_Sanitizer(t *testing.T) {
// Emoji // Emoji
`<span class="emoji" aria-label="thumbs up" data-alias="+1">THUMBS UP</span>`, `<span class="emoji" aria-label="thumbs up" data-alias="+1">THUMBS UP</span>`, `<span class="emoji" aria-label="thumbs up" data-alias="+1">THUMBS UP</span>`, `<span class="emoji" aria-label="thumbs up" data-alias="+1">THUMBS UP</span>`,
`<span class="emoji" aria-label="thumbs up" data-alias="(+!)">THUMBS UP</span>`, `<span class="emoji" aria-label="thumbs up">THUMBS UP</span>`, `<span class="emoji" aria-label="thumbs up" data-alias="(+!)">THUMBS UP</span>`, `<span class="emoji" aria-label="thumbs up">THUMBS UP</span>`,
// Images lazy loading
`<img src="/image1" alt="image1" loading="lazy">`, `<img src="/image1" alt="image1" loading="lazy">`,
`<img src="/image1" alt="image1" loading="eager">`, `<img src="/image1" alt="image1">`,
} }
for i := 0; i < len(testCases); i += 2 { for i := 0; i < len(testCases); i += 2 {

View file

@ -1,4 +1,5 @@
// Copyright 2022 The Gitea Authors. All rights reserved. // Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2024 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
package structs package structs
@ -7,3 +8,15 @@ package structs
type ActivityPub struct { type ActivityPub struct {
Context string `json:"@context"` Context string `json:"@context"`
} }
type APRemoteFollowOption struct {
Target string `json:"target"`
}
type APPersonFollowItem struct {
ActorID string `json:"actor_id"`
Note string `json:"note"`
OriginalURL string `json:"original_url"`
OriginalItem string `json:"original_item"`
}

View file

@ -22,6 +22,12 @@ type Attachment struct {
Type string `json:"type"` Type string `json:"type"`
} }
// WebAttachment the generic attachment with mime type
type WebAttachment struct {
*Attachment
MimeType string `json:"mime_type"`
}
// EditAttachmentOptions options for editing attachments // EditAttachmentOptions options for editing attachments
// swagger:model // swagger:model
type EditAttachmentOptions struct { type EditAttachmentOptions struct {

View file

@ -54,7 +54,6 @@ type CreateHookOption struct {
AuthorizationHeader string `json:"authorization_header"` AuthorizationHeader string `json:"authorization_header"`
// default: false // default: false
Active bool `json:"active"` Active bool `json:"active"`
IsSystemWebhook bool `json:"is_system_webhook"`
} }
// EditHookOption options when modify one hook // EditHookOption options when modify one hook

View file

@ -192,8 +192,8 @@ func TestRenderMarkdownToHtml(t *testing.T) {
<a href="https://example.com" rel="nofollow">remote link</a> <a href="https://example.com" rel="nofollow">remote link</a>
<a href="/src/file.bin" rel="nofollow">local link</a> <a href="/src/file.bin" rel="nofollow">local link</a>
<a href="https://example.com" rel="nofollow">remote link</a> <a href="https://example.com" rel="nofollow">remote link</a>
<a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image"/></a> <a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image" loading="lazy"/></a>
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a> <a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image" loading="lazy"/></a>
<a href="/image.jpg" rel="nofollow"><img src="/image.jpg" title="local image" alt=""/></a> <a href="/image.jpg" rel="nofollow"><img src="/image.jpg" title="local image" alt=""/></a>
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a> <a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt=""/></a>
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow"><code>88fc37a3c0...12fc37a3c0 (hash)</code></a> <a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow"><code>88fc37a3c0...12fc37a3c0 (hash)</code></a>

View file

@ -10,56 +10,79 @@ import (
"net/http/httptest" "net/http/httptest"
"strings" "strings"
"testing" "testing"
"forgejo.org/modules/util"
) )
type FederationServerMockPerson struct { type FederationServerMockPerson struct {
ID int64 ID int64
Name string Name string
PubKey string PubKey string
PrivKey string
} }
type FederationServerMockRepository struct { type FederationServerMockRepository struct {
ID int64 ID int64
} }
type ApActorMock struct {
PrivKey string
PubKey string
}
type FederationServerMock struct { type FederationServerMock struct {
ApActor ApActorMock
Persons []FederationServerMockPerson Persons []FederationServerMockPerson
Repositories []FederationServerMockRepository Repositories []FederationServerMockRepository
LastPost string LastPost string
} }
func NewFederationServerMockPerson(id int64, name string) FederationServerMockPerson { func NewFederationServerMockPerson(id int64, name string) FederationServerMockPerson {
priv, pub, _ := util.GenerateKeyPair(3072)
return FederationServerMockPerson{ return FederationServerMockPerson{
ID: id, ID: id,
Name: name, Name: name,
PubKey: `"-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA18H5s7N6ItZUAh9tneII\nIuZdTTa3cZlLa/9ejWAHTkcp3WLW+/zbsumlMrWYfBy2/yTm56qasWt38iY4D6ul\n` + PubKey: pub,
`CPiwhAqX3REvVq8tM79a2CEqZn9ka6vuXoDgBg/sBf/BUWqf7orkjUXwk/U0Egjf\nk5jcurF4vqf1u+rlAHH37dvSBaDjNj6Qnj4OP12bjfaY/yvs7+jue/eNXFHjzN4E\n` + PrivKey: priv,
`T2H4B/yeKTJ4UuAwTlLaNbZJul2baLlHelJPAsxiYaziVuV5P+IGWckY6RSerRaZ\nAkc4mmGGtjAyfN9aewe+lNVfwS7ElFx546PlLgdQgjmeSwLX8FWxbPE5A/PmaXCs\n` +
`nx+nou+3dD7NluULLtdd7K+2x02trObKXCAzmi5/Dc+yKTzpFqEz+hLNCz7TImP/\ncK//NV9Q+X67J9O27baH9R9ZF4zMw8rv2Pg0WLSw1z7lLXwlgIsDapeMCsrxkVO4\n` +
`LXX5AQ1xQNtlssnVoUBqBrvZsX2jUUKUocvZqMGuE4hfAgMBAAE=\n-----END PUBLIC KEY-----\n"`,
} }
} }
func (p *FederationServerMockPerson) KeyID(host string) string {
return fmt.Sprintf("%[1]v/api/v1/activitypub/user-id/%[2]v#main-key", host, p.ID)
}
func NewFederationServerMockRepository(id int64) FederationServerMockRepository { func NewFederationServerMockRepository(id int64) FederationServerMockRepository {
return FederationServerMockRepository{ return FederationServerMockRepository{
ID: id, ID: id,
} }
} }
func NewApActorMock() ApActorMock {
priv, pub, _ := util.GenerateKeyPair(1024)
return ApActorMock{
PrivKey: priv,
PubKey: pub,
}
}
func (u *ApActorMock) KeyID(host string) string {
return fmt.Sprintf("%[1]v/api/v1/activitypub/actor#main-key", host)
}
func (p FederationServerMockPerson) marshal(host string) string { func (p FederationServerMockPerson) marshal(host string) string {
return fmt.Sprintf(`{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],`+ return fmt.Sprintf(`{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],`+
`"id":"http://%[1]v/api/activitypub/user-id/%[2]v",`+ `"id":"http://%[1]v/api/v1/activitypub/user-id/%[2]v",`+
`"type":"Person",`+ `"type":"Person",`+
`"icon":{"type":"Image","mediaType":"image/png","url":"http://%[1]v/avatars/1bb05d9a5f6675ed0272af9ea193063c"},`+ `"icon":{"type":"Image","mediaType":"image/png","url":"http://%[1]v/avatars/1bb05d9a5f6675ed0272af9ea193063c"},`+
`"url":"http://%[1]v/%[2]v",`+ `"url":"http://%[1]v/%[2]v",`+
`"inbox":"http://%[1]v/api/activitypub/user-id/%[2]v/inbox",`+ `"inbox":"http://%[1]v/api/v1/activitypub/user-id/%[2]v/inbox",`+
`"outbox":"http://%[1]v/api/activitypub/user-id/%[2]v/outbox",`+ `"outbox":"http://%[1]v/api/v1/activitypub/user-id/%[2]v/outbox",`+
`"preferredUsername":"%[3]v",`+ `"preferredUsername":"%[3]v",`+
`"publicKey":{"id":"http://%[1]v/api/activitypub/user-id/%[2]v#main-key",`+ `"publicKey":{"id":"http://%[1]v/api/v1/activitypub/user-id/%[2]v#main-key",`+
`"owner":"http://%[1]v/api/activitypub/user-id/%[2]v",`+ `"owner":"http://%[1]v/api/v1/activitypub/user-id/%[2]v",`+
`"publicKeyPem":%[4]v}}`, host, p.ID, p.Name, p.PubKey) `"publicKeyPem":%[4]q}}`, host, p.ID, p.Name, p.PubKey)
} }
func NewFederationServerMock() *FederationServerMock { func NewFederationServerMock() *FederationServerMock {
return &FederationServerMock{ return &FederationServerMock{
ApActor: NewApActorMock(),
Persons: []FederationServerMockPerson{ Persons: []FederationServerMockPerson{
NewFederationServerMockPerson(15, "stargoose1"), NewFederationServerMockPerson(15, "stargoose1"),
NewFederationServerMockPerson(30, "stargoose2"), NewFederationServerMockPerson(30, "stargoose2"),
@ -71,8 +94,18 @@ func NewFederationServerMock() *FederationServerMock {
} }
} }
func (mock *FederationServerMock) recordLastPost(t *testing.T, req *http.Request) {
buf := new(strings.Builder)
_, err := io.Copy(buf, req.Body)
if err != nil {
t.Errorf("Error reading body: %q", err)
}
mock.LastPost = strings.ReplaceAll(buf.String(), req.Host, "DISTANT_FEDERATION_HOST")
}
func (mock *FederationServerMock) DistantServer(t *testing.T) *httptest.Server { func (mock *FederationServerMock) DistantServer(t *testing.T) *httptest.Server {
federatedRoutes := http.NewServeMux() federatedRoutes := http.NewServeMux()
federatedRoutes.HandleFunc("/.well-known/nodeinfo", federatedRoutes.HandleFunc("/.well-known/nodeinfo",
func(res http.ResponseWriter, req *http.Request) { func(res http.ResponseWriter, req *http.Request) {
// curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/.well-known/nodeinfo // curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/.well-known/nodeinfo
@ -87,30 +120,28 @@ func (mock *FederationServerMock) DistantServer(t *testing.T) *httptest.Server {
`"protocols":["activitypub"],"services":{"inbound":[],"outbound":["rss2.0"]},`+ `"protocols":["activitypub"],"services":{"inbound":[],"outbound":["rss2.0"]},`+
`"openRegistrations":true,"usage":{"users":{"total":14,"activeHalfyear":2}},"metadata":{}}`) `"openRegistrations":true,"usage":{"users":{"total":14,"activeHalfyear":2}},"metadata":{}}`)
}) })
for _, person := range mock.Persons { for _, person := range mock.Persons {
federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/user-id/%v", person.ID), federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/user-id/%v", person.ID),
func(res http.ResponseWriter, req *http.Request) { func(res http.ResponseWriter, req *http.Request) {
// curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/2 // curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/2
fmt.Fprint(res, person.marshal(req.Host)) fmt.Fprint(res, person.marshal(req.Host))
}) })
} federatedRoutes.HandleFunc(fmt.Sprintf("POST /api/v1/activitypub/user-id/%v/inbox", person.ID),
for _, repository := range mock.Repositories {
federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/repository-id/%v/inbox", repository.ID),
func(res http.ResponseWriter, req *http.Request) { func(res http.ResponseWriter, req *http.Request) {
if req.Method != "POST" { mock.recordLastPost(t, req)
t.Errorf("POST expected at: %q", req.URL.EscapedPath()) })
} }
buf := new(strings.Builder)
_, err := io.Copy(buf, req.Body) for _, repository := range mock.Repositories {
if err != nil { federatedRoutes.HandleFunc(fmt.Sprintf("POST /api/v1/activitypub/repository-id/%v/inbox", repository.ID),
t.Errorf("Error reading body: %q", err) func(res http.ResponseWriter, req *http.Request) {
} mock.recordLastPost(t, req)
mock.LastPost = buf.String()
}) })
} }
federatedRoutes.HandleFunc("/", federatedRoutes.HandleFunc("/",
func(res http.ResponseWriter, req *http.Request) { func(res http.ResponseWriter, req *http.Request) {
t.Errorf("Unhandled request: %q", req.URL.EscapedPath()) t.Errorf("Unhandled %v request: %q", req.Method, req.URL.EscapedPath())
}) })
federatedSrv := httptest.NewServer(federatedRoutes) federatedSrv := httptest.NewServer(federatedRoutes)
return federatedSrv return federatedSrv

View file

@ -124,7 +124,7 @@ func (ct SniffedType) GetMimeType() string {
} }
// DetectContentType extends http.DetectContentType with more content types. Defaults to text/unknown if input is empty. // DetectContentType extends http.DetectContentType with more content types. Defaults to text/unknown if input is empty.
func DetectContentType(data []byte) SniffedType { func DetectContentType(data []byte, filename string) SniffedType {
if len(data) == 0 { if len(data) == 0 {
return SniffedType{"text/unknown"} return SniffedType{"text/unknown"}
} }
@ -176,6 +176,13 @@ func DetectContentType(data []byte) SniffedType {
} }
} }
if ct == "application/octet-stream" &&
filename != "" &&
!strings.HasSuffix(strings.ToUpper(filename), ".LCOM") &&
bytes.Contains(data, []byte("(DEFINE-FILE-INFO ")) {
ct = "text/vnd.interlisp"
}
// GLTF is unsupported by http.DetectContentType // GLTF is unsupported by http.DetectContentType
// hexdump -n 4 -C glTF.glb // hexdump -n 4 -C glTF.glb
if bytes.HasPrefix(data, []byte("glTF")) { if bytes.HasPrefix(data, []byte("glTF")) {
@ -186,7 +193,7 @@ func DetectContentType(data []byte) SniffedType {
} }
// DetectContentTypeFromReader guesses the content type contained in the reader. // DetectContentTypeFromReader guesses the content type contained in the reader.
func DetectContentTypeFromReader(r io.Reader) (SniffedType, error) { func DetectContentTypeFromReader(r io.Reader, filename string) (SniffedType, error) {
buf := make([]byte, sniffLen) buf := make([]byte, sniffLen)
n, err := util.ReadAtMost(r, buf) n, err := util.ReadAtMost(r, buf)
if err != nil { if err != nil {
@ -194,5 +201,5 @@ func DetectContentTypeFromReader(r io.Reader) (SniffedType, error) {
} }
buf = buf[:n] buf = buf[:n]
return DetectContentType(buf), nil return DetectContentType(buf, filename), nil
} }

View file

@ -16,63 +16,63 @@ import (
func TestDetectContentTypeLongerThanSniffLen(t *testing.T) { func TestDetectContentTypeLongerThanSniffLen(t *testing.T) {
// Pre-condition: Shorter than sniffLen detects SVG. // Pre-condition: Shorter than sniffLen detects SVG.
assert.Equal(t, "image/svg+xml", DetectContentType([]byte(`<!-- Comment --><svg></svg>`)).contentType) assert.Equal(t, "image/svg+xml", DetectContentType([]byte(`<!-- Comment --><svg></svg>`), "").contentType)
// Longer than sniffLen detects something else. // Longer than sniffLen detects something else.
assert.NotEqual(t, "image/svg+xml", DetectContentType([]byte(`<!-- `+strings.Repeat("x", sniffLen)+` --><svg></svg>`)).contentType) assert.NotEqual(t, "image/svg+xml", DetectContentType([]byte(`<!-- `+strings.Repeat("x", sniffLen)+` --><svg></svg>`), "").contentType)
} }
func TestIsTextFile(t *testing.T) { func TestIsTextFile(t *testing.T) {
assert.True(t, DetectContentType([]byte{}).IsText()) assert.True(t, DetectContentType([]byte{}, "").IsText())
assert.True(t, DetectContentType([]byte("lorem ipsum")).IsText()) assert.True(t, DetectContentType([]byte("lorem ipsum"), "").IsText())
} }
func TestIsSvgImage(t *testing.T) { func TestIsSvgImage(t *testing.T) {
assert.True(t, DetectContentType([]byte("<svg></svg>")).IsSvgImage()) assert.True(t, DetectContentType([]byte("<svg></svg>"), "").IsSvgImage())
assert.True(t, DetectContentType([]byte(" <svg></svg>")).IsSvgImage()) assert.True(t, DetectContentType([]byte(" <svg></svg>"), "").IsSvgImage())
assert.True(t, DetectContentType([]byte(`<svg width="100"></svg>`)).IsSvgImage()) assert.True(t, DetectContentType([]byte(`<svg width="100"></svg>`), "").IsSvgImage())
assert.True(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?><svg></svg>`)).IsSvgImage()) assert.True(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?><svg></svg>`), "").IsSvgImage())
assert.True(t, DetectContentType([]byte(`<!-- Comment --> assert.True(t, DetectContentType([]byte(`<!-- Comment -->
<svg></svg>`)).IsSvgImage()) <svg></svg>`), "").IsSvgImage())
assert.True(t, DetectContentType([]byte(`<!-- Multiple --> assert.True(t, DetectContentType([]byte(`<!-- Multiple -->
<!-- Comments --> <!-- Comments -->
<svg></svg>`)).IsSvgImage()) <svg></svg>`), "").IsSvgImage())
assert.True(t, DetectContentType([]byte(`<!-- Multiline assert.True(t, DetectContentType([]byte(`<!-- Multiline
Comment --> Comment -->
<svg></svg>`)).IsSvgImage()) <svg></svg>`), "").IsSvgImage())
assert.True(t, DetectContentType([]byte(`<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Basic//EN" assert.True(t, DetectContentType([]byte(`<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Basic//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd"> "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd">
<svg></svg>`)).IsSvgImage()) <svg></svg>`), "").IsSvgImage())
assert.True(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?> assert.True(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?>
<!-- Comment --> <!-- Comment -->
<svg></svg>`)).IsSvgImage()) <svg></svg>`), "").IsSvgImage())
assert.True(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?> assert.True(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?>
<!-- Multiple --> <!-- Multiple -->
<!-- Comments --> <!-- Comments -->
<svg></svg>`)).IsSvgImage()) <svg></svg>`), "").IsSvgImage())
assert.True(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?> assert.True(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?>
<!-- Multiline <!-- Multiline
Comment --> Comment -->
<svg></svg>`)).IsSvgImage()) <svg></svg>`), "").IsSvgImage())
assert.True(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?> assert.True(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Multiline <!-- Multiline
Comment --> Comment -->
<svg></svg>`)).IsSvgImage()) <svg></svg>`), "").IsSvgImage())
// the DetectContentType should work for incomplete data, because only beginning bytes are used for detection // the DetectContentType should work for incomplete data, because only beginning bytes are used for detection
assert.True(t, DetectContentType([]byte(`<svg>....`)).IsSvgImage()) assert.True(t, DetectContentType([]byte(`<svg>....`), "").IsSvgImage())
assert.False(t, DetectContentType([]byte{}).IsSvgImage()) assert.False(t, DetectContentType([]byte{}, "").IsSvgImage())
assert.False(t, DetectContentType([]byte("svg")).IsSvgImage()) assert.False(t, DetectContentType([]byte("svg"), "").IsSvgImage())
assert.False(t, DetectContentType([]byte("<svgfoo></svgfoo>")).IsSvgImage()) assert.False(t, DetectContentType([]byte("<svgfoo></svgfoo>"), "").IsSvgImage())
assert.False(t, DetectContentType([]byte("text<svg></svg>")).IsSvgImage()) assert.False(t, DetectContentType([]byte("text<svg></svg>"), "").IsSvgImage())
assert.False(t, DetectContentType([]byte("<html><body><svg></svg></body></html>")).IsSvgImage()) assert.False(t, DetectContentType([]byte("<html><body><svg></svg></body></html>"), "").IsSvgImage())
assert.False(t, DetectContentType([]byte(`<script>"<svg></svg>"</script>`)).IsSvgImage()) assert.False(t, DetectContentType([]byte(`<script>"<svg></svg>"</script>`), "").IsSvgImage())
assert.False(t, DetectContentType([]byte(`<!-- <svg></svg> inside comment --> assert.False(t, DetectContentType([]byte(`<!-- <svg></svg> inside comment -->
<foo></foo>`)).IsSvgImage()) <foo></foo>`), "").IsSvgImage())
assert.False(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?> assert.False(t, DetectContentType([]byte(`<?xml version="1.0" encoding="UTF-8"?>
<!-- <svg></svg> inside comment --> <!-- <svg></svg> inside comment -->
<foo></foo>`)).IsSvgImage()) <foo></foo>`), "").IsSvgImage())
assert.False(t, DetectContentType([]byte(` assert.False(t, DetectContentType([]byte(`
<!-- comment1 --> <!-- comment1 -->
@ -80,7 +80,7 @@ func TestIsSvgImage(t *testing.T) {
<!-- comment2 --> <!-- comment2 -->
<svg></svg> <svg></svg>
</div> </div>
`)).IsSvgImage()) `), "").IsSvgImage())
assert.False(t, DetectContentType([]byte(` assert.False(t, DetectContentType([]byte(`
<!-- comment1 <!-- comment1
@ -90,56 +90,56 @@ func TestIsSvgImage(t *testing.T) {
--> -->
<svg></svg> <svg></svg>
</div> </div>
`)).IsSvgImage()) `), "").IsSvgImage())
assert.False(t, DetectContentType([]byte(`<html><body><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg></svg></body></html>`)).IsSvgImage()) assert.False(t, DetectContentType([]byte(`<html><body><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg></svg></body></html>`), "").IsSvgImage())
assert.False(t, DetectContentType([]byte(`<html><body><?xml version="1.0" encoding="UTF-8"?><svg></svg></body></html>`)).IsSvgImage()) assert.False(t, DetectContentType([]byte(`<html><body><?xml version="1.0" encoding="UTF-8"?><svg></svg></body></html>`), "").IsSvgImage())
} }
func TestIsPDF(t *testing.T) { func TestIsPDF(t *testing.T) {
pdf, _ := base64.StdEncoding.DecodeString("JVBERi0xLjYKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp4nF3NPwsCMQwF8D2f4s2CNYk1baF0EHRwOwg4iJt/NsFb/PpevUE4Mjwe") pdf, _ := base64.StdEncoding.DecodeString("JVBERi0xLjYKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp4nF3NPwsCMQwF8D2f4s2CNYk1baF0EHRwOwg4iJt/NsFb/PpevUE4Mjwe")
assert.True(t, DetectContentType(pdf).IsPDF()) assert.True(t, DetectContentType(pdf, "").IsPDF())
assert.False(t, DetectContentType([]byte("plain text")).IsPDF()) assert.False(t, DetectContentType([]byte("plain text"), "").IsPDF())
} }
func TestIsVideo(t *testing.T) { func TestIsVideo(t *testing.T) {
mp4, _ := base64.StdEncoding.DecodeString("AAAAGGZ0eXBtcDQyAAAAAGlzb21tcDQyAAEI721vb3YAAABsbXZoZAAAAADaBlwX2gZcFwAAA+gA") mp4, _ := base64.StdEncoding.DecodeString("AAAAGGZ0eXBtcDQyAAAAAGlzb21tcDQyAAEI721vb3YAAABsbXZoZAAAAADaBlwX2gZcFwAAA+gA")
assert.True(t, DetectContentType(mp4).IsVideo()) assert.True(t, DetectContentType(mp4, "").IsVideo())
assert.False(t, DetectContentType([]byte("plain text")).IsVideo()) assert.False(t, DetectContentType([]byte("plain text"), "").IsVideo())
} }
func TestIsAudio(t *testing.T) { func TestIsAudio(t *testing.T) {
mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl") mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl")
assert.True(t, DetectContentType(mp3).IsAudio()) assert.True(t, DetectContentType(mp3, "").IsAudio())
assert.False(t, DetectContentType([]byte("plain text")).IsAudio()) assert.False(t, DetectContentType([]byte("plain text"), "").IsAudio())
assert.True(t, DetectContentType([]byte("ID3Toy\000")).IsAudio()) assert.True(t, DetectContentType([]byte("ID3Toy\000"), "").IsAudio())
assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ...")).IsText()) // test ID3 tag for plain text assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ..."), "").IsText()) // test ID3 tag for plain text
assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ..."+"🌛"[0:2])).IsText()) // test ID3 tag with incomplete UTF8 char assert.True(t, DetectContentType([]byte("ID3Toy\n====\t* hi 🌞, ..."+"🌛"[0:2]), "").IsText()) // test ID3 tag with incomplete UTF8 char
} }
func TestIsGLB(t *testing.T) { func TestIsGLB(t *testing.T) {
glb, _ := hex.DecodeString("676c5446") glb, _ := hex.DecodeString("676c5446")
assert.True(t, DetectContentType(glb).IsGLB()) assert.True(t, DetectContentType(glb, "").IsGLB())
assert.True(t, DetectContentType(glb).Is3DModel()) assert.True(t, DetectContentType(glb, "").Is3DModel())
assert.False(t, DetectContentType([]byte("plain text")).IsGLB()) assert.False(t, DetectContentType([]byte("plain text"), "").IsGLB())
assert.False(t, DetectContentType([]byte("plain text")).Is3DModel()) assert.False(t, DetectContentType([]byte("plain text"), "").Is3DModel())
} }
func TestDetectContentTypeFromReader(t *testing.T) { func TestDetectContentTypeFromReader(t *testing.T) {
mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl") mp3, _ := base64.StdEncoding.DecodeString("SUQzBAAAAAABAFRYWFgAAAASAAADbWFqb3JfYnJhbmQAbXA0MgBUWFhYAAAAEQAAA21pbm9yX3Zl")
st, err := DetectContentTypeFromReader(bytes.NewReader(mp3)) st, err := DetectContentTypeFromReader(bytes.NewReader(mp3), "")
require.NoError(t, err) require.NoError(t, err)
assert.True(t, st.IsAudio()) assert.True(t, st.IsAudio())
} }
func TestDetectContentTypeOgg(t *testing.T) { func TestDetectContentTypeOgg(t *testing.T) {
oggAudio, _ := hex.DecodeString("4f67675300020000000000000000352f0000000000007dc39163011e01766f72626973000000000244ac0000000000000071020000000000b8014f6767530000") oggAudio, _ := hex.DecodeString("4f67675300020000000000000000352f0000000000007dc39163011e01766f72626973000000000244ac0000000000000071020000000000b8014f6767530000")
st, err := DetectContentTypeFromReader(bytes.NewReader(oggAudio)) st, err := DetectContentTypeFromReader(bytes.NewReader(oggAudio), "")
require.NoError(t, err) require.NoError(t, err)
assert.True(t, st.IsAudio()) assert.True(t, st.IsAudio())
oggVideo, _ := hex.DecodeString("4f676753000200000000000000007d9747ef000000009b59daf3012a807468656f7261030201001e00110001e000010e00020000001e00000001000001000001") oggVideo, _ := hex.DecodeString("4f676753000200000000000000007d9747ef000000009b59daf3012a807468656f7261030201001e00110001e000010e00020000001e00000001000001000001")
st, err = DetectContentTypeFromReader(bytes.NewReader(oggVideo)) st, err = DetectContentTypeFromReader(bytes.NewReader(oggVideo), "")
require.NoError(t, err) require.NoError(t, err)
assert.True(t, st.IsVideo()) assert.True(t, st.IsVideo())
} }
@ -148,7 +148,7 @@ func TestDetectContentTypeAvif(t *testing.T) {
avifImage, err := hex.DecodeString("000000206674797061766966") avifImage, err := hex.DecodeString("000000206674797061766966")
require.NoError(t, err) require.NoError(t, err)
st, err := DetectContentTypeFromReader(bytes.NewReader(avifImage)) st, err := DetectContentTypeFromReader(bytes.NewReader(avifImage), "")
require.NoError(t, err) require.NoError(t, err)
assert.True(t, st.IsImage()) assert.True(t, st.IsImage())
@ -158,10 +158,24 @@ func TestDetectContentTypeModelGLB(t *testing.T) {
glb, err := hex.DecodeString("676c5446") glb, err := hex.DecodeString("676c5446")
require.NoError(t, err) require.NoError(t, err)
st, err := DetectContentTypeFromReader(bytes.NewReader(glb)) st, err := DetectContentTypeFromReader(bytes.NewReader(glb), "")
require.NoError(t, err) require.NoError(t, err)
// print st for debugging // print st for debugging
assert.Equal(t, "model/gltf-binary", st.GetMimeType()) assert.Equal(t, "model/gltf-binary", st.GetMimeType())
assert.True(t, st.IsGLB()) assert.True(t, st.IsGLB())
} }
func TestDetectInterlisp(t *testing.T) {
interlisp, err := base64.StdEncoding.DecodeString("ICAKKERFRklORS1GSUxFLUlORk8gHlBBQ0tBR0UgIklOVEVSTElTUCIgHlJFQURUQUJMRSAiSU5URVJMSVNQIiAeQkFTRSAxMCkKCgYB")
require.NoError(t, err)
st, err := DetectContentTypeFromReader(bytes.NewReader(interlisp), "test")
require.NoError(t, err)
assert.True(t, st.IsText())
st, err = DetectContentTypeFromReader(bytes.NewReader(interlisp), "")
require.NoError(t, err)
assert.False(t, st.IsText())
st, err = DetectContentTypeFromReader(bytes.NewReader(interlisp), "test.lcom")
require.NoError(t, err)
assert.False(t, st.IsText())
}

View file

@ -699,7 +699,7 @@ issues.filter_milestone_all = كل الأهداف
issues.unlock.notice_2 = - يمكنك دوما إقفال هذه المسألة من جديد في المستقبل. issues.unlock.notice_2 = - يمكنك دوما إقفال هذه المسألة من جديد في المستقبل.
issues.num_participants_few = %d متحاور issues.num_participants_few = %d متحاور
release.title = عنوان الإصدار release.title = عنوان الإصدار
issues.closed_at = `أغلق هذه المسألة <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.closed_at = `أغلق هذه المسألة %s`
issues.lock.title = إقفال التحاور في هذه المسألة. issues.lock.title = إقفال التحاور في هذه المسألة.
issues.new.no_label = بلا تصنيف issues.new.no_label = بلا تصنيف
issues.filter_sort.mostforks = الأعلى اشتقاقا issues.filter_sort.mostforks = الأعلى اشتقاقا
@ -759,7 +759,7 @@ branch.renamed = غُيّر اسم الفرع %s إلى %s.
delete_preexisting = احذف الملفات الموجودة سابقا delete_preexisting = احذف الملفات الموجودة سابقا
branch.included_desc = هذا الفرع جزء من الفرع المبدئي branch.included_desc = هذا الفرع جزء من الفرع المبدئي
trust_model_helper_collaborator_committer = مشترك+مودع: ثق بتوقيعات المشتركين التي تطابق المودع trust_model_helper_collaborator_committer = مشترك+مودع: ثق بتوقيعات المشتركين التي تطابق المودع
issues.reopened_at = `أعاد فتح هذه المسألة <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at = `أعاد فتح هذه المسألة %s`
issues.action_milestone = هدف issues.action_milestone = هدف
issues.new.assignees = المكلَّفون issues.new.assignees = المكلَّفون
release.tag_name_protected = اسم الوسم محمي. release.tag_name_protected = اسم الوسم محمي.
@ -1166,7 +1166,7 @@ pulls.status_checking = في انتظار بعض الفحوص
pulls.status_checks_failure = بعض الفحوص فشلت pulls.status_checks_failure = بعض الفحوص فشلت
pulls.status_checks_success = جميع الفحوص ناجحة pulls.status_checks_success = جميع الفحوص ناجحة
pulls.status_checks_warning = بعض الفحوص تعطي تحذيرات pulls.status_checks_warning = بعض الفحوص تعطي تحذيرات
pulls.commit_ref_at = `أشار إلى طلب الدمج من إيداع <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.commit_ref_at = `أشار إلى طلب الدمج من إيداع %s`
pulls.cmd_instruction_hint = `أظهر شرح استخدام سطر الأوامر.` pulls.cmd_instruction_hint = `أظهر شرح استخدام سطر الأوامر.`
pulls.cmd_instruction_checkout_title = اسحب pulls.cmd_instruction_checkout_title = اسحب
pulls.cmd_instruction_checkout_desc = من مستودع مشروعك، اسحب (check out) فرعا جديدا واختبر التغييرات. pulls.cmd_instruction_checkout_desc = من مستودع مشروعك، اسحب (check out) فرعا جديدا واختبر التغييرات.
@ -1257,8 +1257,8 @@ pulls.status_checks_details = تفاصيل
pulls.status_checks_hide_all = أخفِ كل الفحوص pulls.status_checks_hide_all = أخفِ كل الفحوص
pulls.status_checks_show_all = أظهر كل الفحوص pulls.status_checks_show_all = أظهر كل الفحوص
pulls.close = أغلق طلب الدمج pulls.close = أغلق طلب الدمج
pulls.closed_at = `أغلق طلب الدمج <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.closed_at = `أغلق طلب الدمج %s`
pulls.reopened_at = `أعاد فتح طلب الدمج <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.reopened_at = `أعاد فتح طلب الدمج %s`
milestones.title = العنوان milestones.title = العنوان
milestones.desc = الوصف milestones.desc = الوصف
milestones.edit = عدّل الهدف milestones.edit = عدّل الهدف
@ -1302,11 +1302,11 @@ issues.closed_by_fake = من %[2]s أُغلقت %[1]s
issues.num_comments_1 = %d تعليق issues.num_comments_1 = %d تعليق
issues.num_comments = %d تعليقا issues.num_comments = %d تعليقا
issues.commented_at = `علّق <a href="#%s">%s</a>` issues.commented_at = `علّق <a href="#%s">%s</a>`
issues.commit_ref_at = `أشار إلى هذه المسألة من إيداع <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at = `أشار إلى هذه المسألة من إيداع %s`
issues.ref_issue_from = `<a href="%[3]s">أشار إلى هذه المسألة %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_issue_from = `<a href="%[2]s">أشار إلى هذه المسألة %[3]s</a> %[1]s`
issues.ref_pull_from = `<a href="%[3]s">أشار إلى هذا الطلب %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_pull_from = `<a href="%[2]s">أشار إلى هذا الطلب %[3]s</a> %[1]s`
issues.ref_closing_from = `<a href="%[3]s">أشار إلى طلب دمج %[4]s سيغلق هذه المسألة</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closing_from = `<a href="%[2]s">أشار إلى طلب دمج %[3]s سيغلق هذه المسألة</a> %[1]s`
issues.ref_reopening_from = `<a href="%[3]s">أشار إلى طلب دمج %[4]s سيعيد فتح هذه المسألة</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopening_from = `<a href="%[2]s">أشار إلى طلب دمج %[3]s سيعيد فتح هذه المسألة</a> %[1]s`
issues.ref_closed_from = `<a href="%[3]s">أغلق هذه المسألة %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closed_from = `<a href="%[3]s">أغلق هذه المسألة %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from = `<a href="%[3]s">أعاد فتح هذه المسألة %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopened_from = `<a href="%[3]s">أعاد فتح هذه المسألة %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reference_issue.body = المحتوى issues.reference_issue.body = المحتوى

View file

@ -749,7 +749,7 @@ settings.admin_settings = Администраторски настройки
issues.role.owner = Притежател issues.role.owner = Притежател
settings.transfer.title = Прехвърляне на притежанието settings.transfer.title = Прехвърляне на притежанието
issues.author = Автор issues.author = Автор
issues.closed_at = `затвори тази задача <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.closed_at = `затвори тази задача %s`
settings.collaborator_deletion_desc = Премахването на сътрудник ще отнеме достъпа му до това хранилище. Продължаване? settings.collaborator_deletion_desc = Премахването на сътрудник ще отнеме достъпа му до това хранилище. Продължаване?
commits.message = Съобщение commits.message = Съобщение
issues.due_date_not_set = Няма зададен краен срок. issues.due_date_not_set = Няма зададен краен срок.
@ -773,9 +773,9 @@ issues.filter_type.all_issues = Всички задачи
issues.filter_poster_no_select = Всички автори issues.filter_poster_no_select = Всички автори
issues.opened_by = отворена %[1]s от <a href="%[2]s">%[3]s</a> issues.opened_by = отворена %[1]s от <a href="%[2]s">%[3]s</a>
issues.action_open = Отваряне issues.action_open = Отваряне
pulls.closed_at = `затвори тази заявка за сливане <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.closed_at = `затвори тази заявка за сливане %s`
pulls.reopened_at = `отвори наново тази заявка за сливане <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.reopened_at = `отвори наново тази заявка за сливане %s`
issues.reopened_at = `отвори наново тази задача <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at = `отвори наново тази задача %s`
projects.column.edit = Редактиране на колоната projects.column.edit = Редактиране на колоната
issues.close = Затваряне на задачата issues.close = Затваряне на задачата
issues.ref_reopened_from = `<a href="%[3]s">отвори наново тази задача %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopened_from = `<a href="%[3]s">отвори наново тази задача %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
@ -1205,7 +1205,7 @@ issues.dependency.cancel = Отказ
issues.dependency.add_error_dep_exists = Зависимостта вече съществува. issues.dependency.add_error_dep_exists = Зависимостта вече съществува.
issues.dependency.add_error_dep_not_exist = Зависимостта не съществува. issues.dependency.add_error_dep_not_exist = Зависимостта не съществува.
issues.remove_ref_at = `премахна препратката <b>%s</b> %s` issues.remove_ref_at = `премахна препратката <b>%s</b> %s`
issues.ref_pull_from = `<a href="%[3]s">спомена тази заявка за сливане %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_pull_from = `<a href="%[2]s">спомена тази заявка за сливане %[3]s</a> %[1]s`
issues.dependency.pr_no_dependencies = Няма зададени зависимости. issues.dependency.pr_no_dependencies = Няма зададени зависимости.
issues.dependency.remove_info = Премахване на тази зависимост issues.dependency.remove_info = Премахване на тази зависимост
issues.dependency.removed_dependency = `премахна зависимостта %s` issues.dependency.removed_dependency = `премахна зависимостта %s`
@ -1230,11 +1230,11 @@ issues.dependency.title = Зависимости
issues.dependency.issue_no_dependencies = Няма зададени зависимости. issues.dependency.issue_no_dependencies = Няма зададени зависимости.
issues.dependency.pr_close_blocked = Трябва да затворите всички задачи, блокиращи тази заявка за сливане, преди да можете да я слеете. issues.dependency.pr_close_blocked = Трябва да затворите всички задачи, блокиращи тази заявка за сливане, преди да можете да я слеете.
issues.dependency.pr_close_blocks = Тази заявка за сливане блокира затварянето на следните задачи issues.dependency.pr_close_blocks = Тази заявка за сливане блокира затварянето на следните задачи
issues.ref_issue_from = `<a href="%[3]s">спомена тази задача %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_issue_from = `<a href="%[2]s">спомена тази задача %[3]s</a> %[1]s`
issues.commit_ref_at = `спомена тази задача в подаване <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at = `спомена тази задача в подаване %s`
issues.add_ref_at = `добави препратка <b>%s</b> %s` issues.add_ref_at = `добави препратка <b>%s</b> %s`
pulls.merged_info_text = Клонът %s вече може да бъде изтрит. pulls.merged_info_text = Клонът %s вече може да бъде изтрит.
pulls.commit_ref_at = `спомена тази заявка за сливане в подаване <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.commit_ref_at = `спомена тази заявка за сливане в подаване %s`
issues.change_ref_at = `промени препратката от <b><strike>%s</strike></b> на <b>%s</b> %s` issues.change_ref_at = `промени препратката от <b><strike>%s</strike></b> на <b>%s</b> %s`
diff.review.reject = Поискване на промени diff.review.reject = Поискване на промени
diff.bin_not_shown = Двоичният файл не е показан. diff.bin_not_shown = Двоичният файл не е показан.
@ -1299,9 +1299,9 @@ branch.create_new_branch = Създаване на клон от клон:
pulls.status_checks_show_all = Показване на всички проверки pulls.status_checks_show_all = Показване на всички проверки
size_format = %[1]s: %[2]s; %[3]s: %[4]s size_format = %[1]s: %[2]s; %[3]s: %[4]s
pulls.filter_changes_by_commit = Филтриране по подаване pulls.filter_changes_by_commit = Филтриране по подаване
issues.ref_closing_from = `<a href="%[3]s">спомена тази задача в заявка за сливане %[4]s, която ще я затвори</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closing_from = `<a href="%[2]s">спомена тази задача в заявка за сливане %[3]s, която ще я затвори</a>, %[1]s`
issues.ref_from = `от %[1]s` issues.ref_from = `от %[1]s`
issues.ref_reopening_from = `<a href="%[3]s">спомена тази задача в заявка за сливане %[4]s, която ще я отвори наново </a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopening_from = `<a href="%[2]s">спомена тази задача в заявка за сливане %[3]s, която ще я отвори наново </a>, %[1]s`
issues.draft_title = Чернова issues.draft_title = Чернова
pulls.reopen_to_merge = Моля, отворете наново тази заявка за сливане, за да извършите сливане. pulls.reopen_to_merge = Моля, отворете наново тази заявка за сливане, за да извършите сливане.
pulls.cant_reopen_deleted_branch = Тази заявка за сливане не може да бъде отворена наново, защото клонът е изтрит. pulls.cant_reopen_deleted_branch = Тази заявка за сливане не може да бъде отворена наново, защото клонът е изтрит.

View file

@ -1633,13 +1633,13 @@ issues.opened_by_fake=otevřeno %[1]s uživatelem %[2]s
issues.closed_by_fake=od %[2]s byl uzavřen %[1]s issues.closed_by_fake=od %[2]s byl uzavřen %[1]s
issues.previous=Předchozí issues.previous=Předchozí
issues.next=Další issues.next=Další
issues.open_title=Otevřeno issues.open_title=Otevřené
issues.closed_title=Uzavřeno issues.closed_title=Uzavřené
issues.draft_title=Koncept issues.draft_title=Koncept
issues.num_comments_1=%d komentář issues.num_comments_1=%d komentář
issues.num_comments=%d komentářů issues.num_comments=%d komentářů
issues.commented_at=`okomentoval/a <a href="#%s">%s</a>` issues.commented_at=`okomentoval/a <a href="#%s">%s</a>`
issues.delete_comment_confirm=Jste si jist, že chcete smazat tento komentář? issues.delete_comment_confirm=Opravdu chcete smazat tento komentář?
issues.context.copy_link=Kopírovat odkaz issues.context.copy_link=Kopírovat odkaz
issues.context.quote_reply=Citovat odpověď issues.context.quote_reply=Citovat odpověď
issues.context.reference_issue=Odkázat v novém problému issues.context.reference_issue=Odkázat v novém problému
@ -1653,13 +1653,13 @@ issues.close_comment_issue=Zavřít s komentářem
issues.reopen_issue=Znovu otevřít issues.reopen_issue=Znovu otevřít
issues.reopen_comment_issue=Znovu otevřít s komentářem issues.reopen_comment_issue=Znovu otevřít s komentářem
issues.create_comment=Komentovat issues.create_comment=Komentovat
issues.closed_at=`uzavřel/a tento problém <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.closed_at=`uzavřel/a tento problém %s`
issues.reopened_at=`znovu otevřel/a tento problém <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`znovu otevřel/a tento problém %s`
issues.commit_ref_at=`odkázal/a na tento problém z revize <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`odkázal/a na tento problém z revize %s`
issues.ref_issue_from=`<a href="%[3]s">odkázal/a na tento problém %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_issue_from=`<a href="%[2]s">odkázal/a na tento problém %[3]s</a> %[1]s`
issues.ref_pull_from=`<a href="%[3]s">odkázal/a na tuto žádost o sloučení %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_pull_from=`<a href="%[2]s">odkázal/a na tuto žádost o sloučení %[3]s</a> %[1]s`
issues.ref_closing_from=`<a href="%[3]s">odkazoval/a na tento problém ze žádosti o sloučení %[4]s, která jej uzavře</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closing_from=`<a href="%[2]s">odkázal/a na tento problém ze žádosti o sloučení %[3]s, která jej uzavře</a>, %[1]s`
issues.ref_reopening_from=`<a href="%[3]s">odkazoval/a na tento problém ze žádosti o sloučení %[4]s, která jej znovu otevře</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopening_from=`<a href="%[2]s">odkázal/a na tento problém ze žádosti o sloučení %[3]s, která jej znovu otevře</a>, %[1]s`
issues.ref_closed_from=`<a href="%[3]s">uzavřel/a tento problém %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closed_from=`<a href="%[3]s">uzavřel/a tento problém %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">znovu otevřel/a tento problém %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopened_from=`<a href="%[3]s">znovu otevřel/a tento problém %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_from=`z %[1]s` issues.ref_from=`z %[1]s`
@ -1966,8 +1966,8 @@ pulls.update_branch_success=Aktualizace větve byla úspěšná
pulls.update_not_allowed=Nemáte oprávnění aktualizovat větev pulls.update_not_allowed=Nemáte oprávnění aktualizovat větev
pulls.outdated_with_base_branch=Tato větev je zastaralá oproti základní větvi pulls.outdated_with_base_branch=Tato větev je zastaralá oproti základní větvi
pulls.close=Zavřít žádost o sloučení pulls.close=Zavřít žádost o sloučení
pulls.closed_at=`uzavřel/a tuto žádost o sloučení <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.closed_at=`uzavřel/a tuto žádost o sloučení %s`
pulls.reopened_at=`znovu otevřel/a tuto žádost o sloučení <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.reopened_at=`znovu otevřel/a tuto žádost o sloučení %s`
pulls.cmd_instruction_hint=Zobrazit instrukce příkazové řádky pulls.cmd_instruction_hint=Zobrazit instrukce příkazové řádky
pulls.cmd_instruction_checkout_desc=Z vašeho repositáře projektu se podívejte na novou větev a vyzkoušejte změny. pulls.cmd_instruction_checkout_desc=Z vašeho repositáře projektu se podívejte na novou větev a vyzkoušejte změny.
pulls.cmd_instruction_merge_title=Sloučit pulls.cmd_instruction_merge_title=Sloučit
@ -2758,7 +2758,7 @@ settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning = Tuto ak
settings.new_owner_blocked_doer = Nový majitel vás zablokoval. settings.new_owner_blocked_doer = Nový majitel vás zablokoval.
settings.mirror_settings.pushed_repository = Odeslaný repozitář settings.mirror_settings.pushed_repository = Odeslaný repozitář
settings.add_collaborator_blocked_our = Nepodařilo se přidat spolupracovníka, jelikož byl zablokován majitelem repozitáře. settings.add_collaborator_blocked_our = Nepodařilo se přidat spolupracovníka, jelikož byl zablokován majitelem repozitáře.
pulls.commit_ref_at = `se odkázal/a na tuto žádost o sloučení z revize <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.commit_ref_at = `odkázal/a na tuto žádost o sloučení z revize %s`
settings.wiki_rename_branch_main = Normalizovat název větve wiki settings.wiki_rename_branch_main = Normalizovat název větve wiki
settings.wiki_rename_branch_main_desc = Přejmenovat větev interně používanou pro wiki na „%s“. Tato změna je trvalá a nelze ji vrátit. settings.wiki_rename_branch_main_desc = Přejmenovat větev interně používanou pro wiki na „%s“. Tato změna je trvalá a nelze ji vrátit.
pulls.fast_forward_only_merge_pull_request = Pouze zrychlené pulls.fast_forward_only_merge_pull_request = Pouze zrychlené

View file

@ -1520,15 +1520,15 @@ issues.add_labels = tilføjede %s etiketterne %s
issues.add_remove_labels = tilføjede %s og fjernede %s etiketter %s issues.add_remove_labels = tilføjede %s og fjernede %s etiketter %s
issues.add_milestone_at = `føjede dette til <b>%s</b> milepælen %s` issues.add_milestone_at = `føjede dette til <b>%s</b> milepælen %s`
issues.add_project_at = `føjede dette til <b>%s</b>- projektet %s` issues.add_project_at = `føjede dette til <b>%s</b>- projektet %s`
issues.ref_reopening_from = `<a href="%[3]s">henviste til dette problem fra en pull-anmodning %[4]s, der vil genåbne den</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopening_from = `<a href="%[2]s">henviste til dette problem fra en pull-anmodning %[3]s, der vil genåbne den</a>, %[1]s`
issues.ref_closed_from = `<a href="%[3]s">lukkede dette problem %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2 ]s</a>` issues.ref_closed_from = `<a href="%[3]s">lukkede dette problem %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2 ]s</a>`
issues.ref_reopened_from = `<a href="%[3]s">genåbnede dette problem %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2 ]s</a>` issues.ref_reopened_from = `<a href="%[3]s">genåbnede dette problem %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2 ]s</a>`
issues.ref_from = `fra %[1]s` issues.ref_from = `fra %[1]s`
issues.author = Forfatter issues.author = Forfatter
issues.commit_ref_at = `henviste til dette problem fra en commit <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at = `henviste til dette problem fra en commit %s`
issues.ref_issue_from = `<a href="%[3]s">henviste til dette problem %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2 ]s</a>` issues.ref_issue_from = `<a href="%[2]s">henviste til dette problem %[3]s</a> <a id="%[1]s" href="#%[1]s">%[2 ]s</a>`
issues.ref_pull_from = `<a href="%[3]s">henviste til denne pull-anmodning %[4]s</a> <a id="%[1]s" href="#%[1]s">%[ 2]s</a>` issues.ref_pull_from = `<a href="%[2]s">henviste til denne pull-anmodning %[3]s</a> %[1]s`
issues.ref_closing_from = `<a href="%[3]s">henviste til dette problem fra en pull-anmodning %[4]s, der vil lukke det</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closing_from = `<a href="%[2]s">henviste til dette problem fra en pull-anmodning %[3]s, der vil lukke det</a>, %[1]s`
issues.author.tooltip.issue = Denne bruger er forfatteren til dette problem. issues.author.tooltip.issue = Denne bruger er forfatteren til dette problem.
issues.author.tooltip.pr = Denne bruger er forfatteren af denne pull-anmodning. issues.author.tooltip.pr = Denne bruger er forfatteren af denne pull-anmodning.
issues.role.owner = Ejer issues.role.owner = Ejer
@ -1564,8 +1564,8 @@ issues.reaction.alt_add = Tilføj %[1]s reaktion til kommentar.
issues.context.menu = Kommentar menu issues.context.menu = Kommentar menu
issues.reopen_comment_issue = Genåbner med kommentar issues.reopen_comment_issue = Genåbner med kommentar
issues.create_comment = Kommentar issues.create_comment = Kommentar
issues.closed_at = `lukkede dette problem <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.closed_at = `lukkede dette problem %s`
issues.reopened_at = `genåbnede dette problem <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at = `genåbnede dette problem %s`
issues.remove_label = fjernede %s etiketten %s issues.remove_label = fjernede %s etiketten %s
issues.remove_labels = fjernede %s etiketterne %s issues.remove_labels = fjernede %s etiketterne %s
issues.change_project_at = `modificerede projektet fra <b>%s</b> til <b>%s</b> %s` issues.change_project_at = `modificerede projektet fra <b>%s</b> til <b>%s</b> %s`
@ -1911,10 +1911,10 @@ pulls.editable_explanation = Denne pull-anmodning tillader redigeringer fra vedl
pulls.auto_merge_button_when_succeed = (Når kontroller lykkes) pulls.auto_merge_button_when_succeed = (Når kontroller lykkes)
pulls.status_checks_requested = Påkrævet pulls.status_checks_requested = Påkrævet
pulls.close = Luk pull anmodning pulls.close = Luk pull anmodning
pulls.commit_ref_at = `henviste til denne pull-anmodning fra en commit <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.commit_ref_at = `henviste til denne pull-anmodning fra en commit %s`
pulls.cmd_instruction_hint = Se instruktionerne på kommandolinjen pulls.cmd_instruction_hint = Se instruktionerne på kommandolinjen
pulls.reopened_at = `genåbnede denne pull-anmodning <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.reopened_at = `genåbnede denne pull-anmodning %s`
pulls.closed_at = `lukkede denne pull-anmodning <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.closed_at = `lukkede denne pull-anmodning %s`
pulls.cmd_instruction_checkout_desc = Fra dit projektdepot, tjek en ny gren og test ændringerne. pulls.cmd_instruction_checkout_desc = Fra dit projektdepot, tjek en ny gren og test ændringerne.
pulls.editable = Redigerbar pulls.editable = Redigerbar
pulls.made_using_agit = AGit pulls.made_using_agit = AGit

View file

@ -1577,7 +1577,7 @@ issues.remove_ref_at=`hat die Referenz <b>%s</b> %s entfernt`
issues.add_ref_at=`hat die Referenz <b>%s</b> %s hinzugefügt` issues.add_ref_at=`hat die Referenz <b>%s</b> %s hinzugefügt`
issues.delete_branch_at=`löschte den Branch <b>%s</b> %s` issues.delete_branch_at=`löschte den Branch <b>%s</b> %s`
issues.filter_label=Label issues.filter_label=Label
issues.filter_label_exclude=`<code>Alt</code> + <code>Klick/Enter</code> verwenden, um Labels auszuschließen` issues.filter_label_exclude=`Verwende <kbd>Alt</kbd> + <kbd>Klick/Enter</kbd>, um Labels auszuschließen`
issues.filter_label_no_select=Alle Labels issues.filter_label_no_select=Alle Labels
issues.filter_label_select_no_label=Kein Label issues.filter_label_select_no_label=Kein Label
issues.filter_milestone=Meilenstein issues.filter_milestone=Meilenstein
@ -1651,13 +1651,13 @@ issues.close_comment_issue=Mit Kommentar schließen
issues.reopen_issue=Wieder öffnen issues.reopen_issue=Wieder öffnen
issues.reopen_comment_issue=Mit Kommentar wieder öffnen issues.reopen_comment_issue=Mit Kommentar wieder öffnen
issues.create_comment=Kommentieren issues.create_comment=Kommentieren
issues.closed_at=`hat diesen Issue <a id="%[1]s" href="#%[1]s">%[2]s</a> geschlossen` issues.closed_at=`hat dieses Issue %s geschlossen`
issues.reopened_at=`hat dieses Issue <a id="%[1]s" href="#%[1]s">%[2]s</a> wieder geöffnet` issues.reopened_at=`hat dieses Issue %s wieder geöffnet`
issues.commit_ref_at=`hat dieses Issue <a id="%[1]s" href="#%[1]s">%[2]s</a> aus einem Commit referenziert` issues.commit_ref_at=`hat dieses Issue %s aus einem Commit referenziert`
issues.ref_issue_from=`<a href="%[3]s">hat</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> <a href="%[3]s">auf dieses Issue verwiesen %[4]s</a>` issues.ref_issue_from=`<a href="%[2]s">hat</a> %[1]s <a href="%[2]s">auf dieses Issue verwiesen %[3]s</a>`
issues.ref_pull_from=`<a href="%[3]s">hat</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> <a href="%[3]s">auf diesen Pull-Request verwiesen %[4]s</a>` issues.ref_pull_from=`<a href="%[2]s">referenzierte diesen Pull-Request %[3]s</a> %[1]s`
issues.ref_closing_from=`<a href="%[3]s">hat</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> <a href="%[3]s">in einem Pull-Request %[4]s auf dieses Issue verwiesen, welcher es schließen wird</a>` issues.ref_closing_from=`<a href="%[2]s">referenzierte dieses Issue aus einem Pull-Request %[3]s der es schließen wird</a>, %[1]s`
issues.ref_reopening_from=`<a href="%[3]s">hat</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> <a href="%[3]s"> in einem Pull-Request %[4]s auf dieses Issue verwiesen, welcher es erneut öffnen wird</a>` issues.ref_reopening_from=`<a href="%[2]s">referenzierte dieses Issue aus einem Pull-Request %[3]s der es wieder öffnen wird</a>, %[1]s`
issues.ref_closed_from=`<a href="%[3]s">hat dieses Issue %[4]s geschlossen</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closed_from=`<a href="%[3]s">hat dieses Issue %[4]s geschlossen</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">hat dieses Issue %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> wieder geöffnet` issues.ref_reopened_from=`<a href="%[3]s">hat dieses Issue %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a> wieder geöffnet`
issues.ref_from=`von %[1]s` issues.ref_from=`von %[1]s`
@ -1962,8 +1962,8 @@ pulls.update_branch_success=Branch-Aktualisierung erfolgreich
pulls.update_not_allowed=Du hast keine Berechtigung, den Branch zu updaten pulls.update_not_allowed=Du hast keine Berechtigung, den Branch zu updaten
pulls.outdated_with_base_branch=Dieser Branch enthält nicht die neusten Commits des Basis-Branches pulls.outdated_with_base_branch=Dieser Branch enthält nicht die neusten Commits des Basis-Branches
pulls.close=Pull-Request schließen pulls.close=Pull-Request schließen
pulls.closed_at=`hat diesen Pull-Request <a id="%[1]s" href="#%[1]s">%[2]s</a> geschlossen` pulls.closed_at=`hat diesen Pull-Request %s geschlossen`
pulls.reopened_at=`hat diesen Pull-Request <a id="%[1]s" href="#%[1]s">%[2]s</a> wieder geöffnet` pulls.reopened_at=`hat diesen Pull-Request %s wieder geöffnet`
pulls.clear_merge_message=Merge-Nachricht löschen pulls.clear_merge_message=Merge-Nachricht löschen
pulls.clear_merge_message_hint=Das Löschen der Merge-Nachricht wird nur den Inhalt der Commit-Nachricht entfernen und generierte Git-Trailer wie „Co-Authored-By …“ erhalten. pulls.clear_merge_message_hint=Das Löschen der Merge-Nachricht wird nur den Inhalt der Commit-Nachricht entfernen und generierte Git-Trailer wie „Co-Authored-By …“ erhalten.
@ -2767,7 +2767,7 @@ 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.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 = 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 Reviews), nicht in die Gesamtzahl der Genehmigung des PRs mitzählen. Irrelevant, falls abgestandene Reviews bereits verworfen werden.
pulls.commit_ref_at = `hat sich auf diesen Pull-Request von einem Commit <a id="%[1]s" href="#%[1]s">%[2]s</a> bezogen` pulls.commit_ref_at = `referenzierte diesen Pull-Request aus einem Commit %s`
pulls.fast_forward_only_merge_pull_request = Nur Fast-forward 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. pulls.cmd_instruction_checkout_desc = Checke einen neuen Branch aus deinem Projekt-Repository aus und teste die Änderungen.
pulls.cmd_instruction_merge_title = Zusammenführen pulls.cmd_instruction_merge_title = Zusammenführen
@ -3061,8 +3061,8 @@ teams.invite.by=Von %s eingeladen
teams.invite.description=Bitte klicke auf die folgende Schaltfläche, um dem Team beizutreten. teams.invite.description=Bitte klicke auf die folgende Schaltfläche, um dem Team beizutreten.
follow_blocked_user = Du kannst dieser Organisation nicht folgen, weil diese Organisation dich blockiert hat. follow_blocked_user = Du kannst dieser Organisation nicht folgen, weil diese Organisation dich blockiert hat.
open_dashboard = Übersicht öffnen open_dashboard = Übersicht öffnen
settings.change_orgname_redirect_prompt.with_cooldown.one = Der alte Organisationsname ist nach einer Abkühldauer von einem Tag wieder für alle verfügbar. Du kannst den alten Namen während dieser Abkühldauer erneut beanspruchen. settings.change_orgname_redirect_prompt.with_cooldown.one = Der alte Organisationsname ist nach einer Schutzzeit von einem Tag wieder für alle verfügbar. Du kannst den alten Namen während dieser Schutzzeit erneut beanspruchen.
settings.change_orgname_redirect_prompt.with_cooldown.few = Der alte Organisationsname ist nach einer Abkühldauer von %[1]d Tagen wieder für alle verfügbar. Du kannst den alten Namen während dieser Abkühldauer erneut beanspruchen. settings.change_orgname_redirect_prompt.with_cooldown.few = Der alte Organisationsname ist nach einer Schutzzeit von %[1]d Tagen wieder für alle verfügbar. Du kannst den alten Namen während dieser Schutzzeit erneut beanspruchen.
[admin] [admin]
dashboard=Übersicht dashboard=Übersicht

View file

@ -1626,13 +1626,13 @@ issues.close_comment_issue=Αποστολή σχολίου και κλείσιμ
issues.reopen_issue=Ανοίξτε ξανά issues.reopen_issue=Ανοίξτε ξανά
issues.reopen_comment_issue=Αποστολή σχολίου και επανάνοιγμα ζητήματος issues.reopen_comment_issue=Αποστολή σχολίου και επανάνοιγμα ζητήματος
issues.create_comment=Προσθήκη Σχολίου issues.create_comment=Προσθήκη Σχολίου
issues.closed_at=`αυτό το ζήτημα έκλεισε <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.closed_at=`αυτό το ζήτημα έκλεισε %s`
issues.reopened_at=`ξανά άνοιξε αυτό το ζήτημα <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`ξανά άνοιξε αυτό το ζήτημα %s`
issues.commit_ref_at=`αναφορά σε αυτό το ζήτημα από την παραπομπή <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`αναφορά σε αυτό το ζήτημα από την παραπομπή %s`
issues.ref_issue_from=`<a href="%[3]s">αναφέρθηκε σε αυτό το ζήτημα %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_issue_from=`<a href="%[2]s">αναφέρθηκε σε αυτό το ζήτημα %[3]s</a> %[1]s`
issues.ref_pull_from=`<a href="%[3]s">αναφέρθηκε σε αυτό το pull request %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_pull_from=`<a href="%[2]s">αναφέρθηκε σε αυτό το pull request %[3]s</a> %[1]s`
issues.ref_closing_from=`<a href="%[3]s">ανέφερε αυτό το ζήτημα σε ένα pull request %[4]s που στοχεύει να κλείσει το ζήτημα</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closing_from=`<a href="%[2]s">ανέφερε αυτό το ζήτημα σε ένα pull request %[3]s που στοχεύει να κλείσει το ζήτημα</a> %[1]s`
issues.ref_reopening_from=`<a href="%[3]s">αναφέρθηκε σε αυτό το ζήτημα σε ένα pull request %[4]s που θα ξαναανοίξει αυτό το ζήτημα</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopening_from=`<a href="%[2]s">αναφέρθηκε σε αυτό το ζήτημα σε ένα pull request %[3]s που θα ξαναανοίξει αυτό το ζήτημα</a> %[1]s`
issues.ref_closed_from=`<a href="%[3]s">έκλεισε αυτό το ζήτημα %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closed_from=`<a href="%[3]s">έκλεισε αυτό το ζήτημα %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">άνοιξε ξανά αυτό το ζήτημα %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopened_from=`<a href="%[3]s">άνοιξε ξανά αυτό το ζήτημα %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_from=`από %[1]s` issues.ref_from=`από %[1]s`
@ -1939,8 +1939,8 @@ pulls.update_branch_success=Η ενημέρωση του κλάδου ήταν
pulls.update_not_allowed=Δεν επιτρέπεται να ενημερώσετε τον κλάδο pulls.update_not_allowed=Δεν επιτρέπεται να ενημερώσετε τον κλάδο
pulls.outdated_with_base_branch=Αυτός ο κλάδος δεν είναι ενημερωμένος με τον βασικό κλάδο pulls.outdated_with_base_branch=Αυτός ο κλάδος δεν είναι ενημερωμένος με τον βασικό κλάδο
pulls.close=Κλείσιμο pull request pulls.close=Κλείσιμο pull request
pulls.closed_at=`έκλεισε αυτό το pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.closed_at=`έκλεισε αυτό το pull request %s`
pulls.reopened_at=`άνοιξε ξανά αυτό το pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.reopened_at=`άνοιξε ξανά αυτό το pull request %s`
pulls.cmd_instruction_hint=Προβολή οδηγιών γραμμής εντολών pulls.cmd_instruction_hint=Προβολή οδηγιών γραμμής εντολών
pulls.cmd_instruction_checkout_title=Έλεγχος pulls.cmd_instruction_checkout_title=Έλεγχος
pulls.cmd_instruction_checkout_desc=Από το repository του έργου σας, ελέγξτε έναν νέο κλάδο και δοκιμάστε τις αλλαγές. pulls.cmd_instruction_checkout_desc=Από το repository του έργου σας, ελέγξτε έναν νέο κλάδο και δοκιμάστε τις αλλαγές.
@ -2720,7 +2720,7 @@ settings.new_owner_blocked_doer = Ο νέος κάτοχος του αποθετ
settings.enter_repo_name = Γράψτε το όνομα του κατόχου και του αποθετηρίου ακριβώς όπως το βλέπετε: settings.enter_repo_name = Γράψτε το όνομα του κατόχου και του αποθετηρίου ακριβώς όπως το βλέπετε:
settings.confirmation_string = Κείμενο επιβεβαίωσης settings.confirmation_string = Κείμενο επιβεβαίωσης
settings.units.overview = Επισκόπηση settings.units.overview = Επισκόπηση
pulls.commit_ref_at = `ανέφερε το pull request στο commit <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.commit_ref_at = `ανέφερε το pull request στο commit %s`
contributors.contribution_type.filter_label = Είδος συνεισφοράς: contributors.contribution_type.filter_label = Είδος συνεισφοράς:
settings.wiki_rename_branch_main_notices_1 = Αυτή η ενέργεια <strong>ΔΕΝ</strong> αναιρείται. settings.wiki_rename_branch_main_notices_1 = Αυτή η ενέργεια <strong>ΔΕΝ</strong> αναιρείται.
activity.navbar.contributors = Συνεισφέροντες activity.navbar.contributors = Συνεισφέροντες

View file

@ -1648,13 +1648,13 @@ issues.close_comment_issue=Cerrar con comentario
issues.reopen_issue=Reabrir issues.reopen_issue=Reabrir
issues.reopen_comment_issue=Reabrir con comentario issues.reopen_comment_issue=Reabrir con comentario
issues.create_comment=Comentar issues.create_comment=Comentar
issues.closed_at=`cerró esta incidencia <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.closed_at=`cerró esta incidencia %s`
issues.reopened_at=`reabrió esta incidencia <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`reabrió esta incidencia %s`
issues.commit_ref_at=`referenció esta incidencia en un commit <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`referenció esta incidencia en un commit %s`
issues.ref_issue_from=`<a href="%[3]s">referenció esta incidencia %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_issue_from=`<a href="%[2]s">referenció esta incidencia %[3]s</a> %[1]s`
issues.ref_pull_from=`<a href="%[3]s">referenció este pull request %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_pull_from=`<a href="%[2]s">referenció este pull request %[3]s</a> %[1]s`
issues.ref_closing_from=`<a href="%[3]s">hizo referencia a esta incidencia desde un pull request %[4]s que lo cerrará</a> , <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closing_from=`<a href="%[2]s">hizo referencia a esta incidencia desde un pull request %[3]s que lo cerrará</a> , %[1]s`
issues.ref_reopening_from=`<a href="%[3]s">hizo referencia a esta incidencia desde un pull request %[4]s que lo reabrirá</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopening_from=`<a href="%[2]s">hizo referencia a esta incidencia desde un pull request %[3]s que lo reabrirá</a>, %[1]s`
issues.ref_closed_from=`<a href="%[3]s">cerró esta incidencia %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closed_from=`<a href="%[3]s">cerró esta incidencia %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">reabrió esta incidencia %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopened_from=`<a href="%[3]s">reabrió esta incidencia %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_from=`de %[1]s` issues.ref_from=`de %[1]s`
@ -1959,8 +1959,8 @@ pulls.update_branch_success=La actualización de la rama ha finalizado correctam
pulls.update_not_allowed=No tiene permisos para actualizar esta rama pulls.update_not_allowed=No tiene permisos para actualizar esta rama
pulls.outdated_with_base_branch=Esta rama está desactualizada con la rama base pulls.outdated_with_base_branch=Esta rama está desactualizada con la rama base
pulls.close=Cerrar pull request pulls.close=Cerrar pull request
pulls.closed_at=`cerró este pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.closed_at=`cerró este pull request %s`
pulls.reopened_at=`reabrió este pull request <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.reopened_at=`reabrió este pull request %s`
pulls.clear_merge_message=Borrar mensaje de fusión pulls.clear_merge_message=Borrar mensaje de fusión
pulls.clear_merge_message_hint=Limpiar el mensaje de fusión solo eliminará el contenido del mensaje de commit y mantendrá frases generadas como "Co-Autorizado por …". pulls.clear_merge_message_hint=Limpiar el mensaje de fusión solo eliminará el contenido del mensaje de commit y mantendrá frases generadas como "Co-Autorizado por …".
@ -2789,7 +2789,7 @@ pulls.status_checks_hide_all = Ocultar todas las verificaciones
settings.federation_not_enabled = La federación no está habilitada en tu instancia. settings.federation_not_enabled = La federación no está habilitada en tu instancia.
wiki.search = Buscar en wiki wiki.search = Buscar en wiki
pulls.status_checks_show_all = Mostrar todas las verificaciones pulls.status_checks_show_all = Mostrar todas las verificaciones
pulls.commit_ref_at = `hizo referencia a este pull request desde un commit <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.commit_ref_at = `hizo referencia a este pull request desde un commit %s`
pulls.cmd_instruction_merge_title = Fusionar pulls.cmd_instruction_merge_title = Fusionar
contributors.contribution_type.deletions = Eliminaciones contributors.contribution_type.deletions = Eliminaciones
contributors.contribution_type.filter_label = Tipo de contribución: contributors.contribution_type.filter_label = Tipo de contribución:

View file

@ -1250,13 +1250,13 @@ issues.close_comment_issue=ثبت دیدگاه و بستن
issues.reopen_issue=بازگشایی issues.reopen_issue=بازگشایی
issues.reopen_comment_issue=ثبت دیدگاه و بازگشایی issues.reopen_comment_issue=ثبت دیدگاه و بازگشایی
issues.create_comment=دیدگاه issues.create_comment=دیدگاه
issues.closed_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> این موضوع را بست` issues.closed_at=`%s این موضوع را بست`
issues.reopened_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> این موضوع را دوباره باز کرد` issues.reopened_at=`%s این موضوع را دوباره باز کرد`
issues.commit_ref_at=`ارجاع این مسئله به کامیت <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`ارجاع این مسئله به کامیت %s`
issues.ref_issue_from=`<a href="%[3]s"> ارجاعات این مسائله %[4]</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_issue_from=`<a href="%[2]s"> ارجاعات این مسائله %[3]</a> %[1]s`
issues.ref_pull_from=`<a href="%[3]s"> ارجاعات این تقاضای ادغام %[4]</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_pull_from=`<a href="%[2]s"> ارجاعات این تقاضای ادغام %[4]</a> %[1]s`
issues.ref_closing_from=`<a href="%[3]s"> ارجاعات این تقاضای واکشی %[4]</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closing_from=`<a href="%[2]s"> ارجاعات این تقاضای واکشی %[4]</a> %[1]s`
issues.ref_reopening_from=`<a href="%[3]s"> تقاضای واکشی ارجاع شده %[4] که مسائله بازگشایی خواهد کرد</a> <a id="%[1]s" href="#%[1]s">%[2] </a>` issues.ref_reopening_from=`<a href="%[2]s"> تقاضای واکشی ارجاع شده %[3]sکه مسائله بازگشایی خواهد کرد</a> <a id="%[1]s" href="#%[1]s">%[2] </a>`
issues.ref_closed_from=`<a href="%[3]s"> بسته شده این مسائله %[4]</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closed_from=`<a href="%[3]s"> بسته شده این مسائله %[4]</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s"> بازگشایی این مسائله %[4]</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopened_from=`<a href="%[3]s"> بازگشایی این مسائله %[4]</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_from=`از %[1]` issues.ref_from=`از %[1]`
@ -1493,8 +1493,8 @@ pulls.update_branch_rebase=بروزآوری شاخه با بازسازی مجد
pulls.update_branch_success=شاخه به موفقیت بروز شد pulls.update_branch_success=شاخه به موفقیت بروز شد
pulls.update_not_allowed=شما اجازه بروزرسانی شاخه را ندارید pulls.update_not_allowed=شما اجازه بروزرسانی شاخه را ندارید
pulls.outdated_with_base_branch=این شاخه با شاخه پایه منسوخ شده است pulls.outdated_with_base_branch=این شاخه با شاخه پایه منسوخ شده است
pulls.closed_at=`این درخواست pull بسته شده <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.closed_at=`این درخواست pull بسته شده %s`
pulls.reopened_at=`این درخواست pull را بازگشایی کرد <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.reopened_at=`این درخواست pull را بازگشایی کرد %s`

View file

@ -1293,9 +1293,9 @@ issues.close_comment_issue=Kommentoi ja sulje
issues.reopen_issue=Avaa uudelleen issues.reopen_issue=Avaa uudelleen
issues.reopen_comment_issue=Kommentoi ja avaa uudelleen issues.reopen_comment_issue=Kommentoi ja avaa uudelleen
issues.create_comment=Kommentoi issues.create_comment=Kommentoi
issues.closed_at=`sulki tämän ongelman <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.closed_at=`sulki tämän ongelman %s`
issues.reopened_at=`uudelleenavasi tämän ongelman <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`uudelleenavasi tämän ongelman %s`
issues.commit_ref_at=`viittasi tähän ongelmaan kommitissa <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`viittasi tähän ongelmaan kommitissa %s`
issues.author=Tekijä issues.author=Tekijä
issues.role.owner=Omistaja issues.role.owner=Omistaja
issues.role.member=Jäsen issues.role.member=Jäsen
@ -2185,7 +2185,7 @@ settings.confirmation_string = Vahvistusteksti
settings.delete_notices_2 = - Tämä toiminto poistaa pysyvästi tietovaraston <strong>%s</strong> mukaan lukien koodin, ongelmat, kommentit, wikidatan ja avustaja-asetukset. settings.delete_notices_2 = - Tämä toiminto poistaa pysyvästi tietovaraston <strong>%s</strong> mukaan lukien koodin, ongelmat, kommentit, wikidatan ja avustaja-asetukset.
issues.filter_assginee_no_select = Kaikki käsittelijät issues.filter_assginee_no_select = Kaikki käsittelijät
issues.new.assign_to_me = Osoita itselle issues.new.assign_to_me = Osoita itselle
pulls.closed_at = `sulki tämän vetopyynnön <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.closed_at = `sulki tämän vetopyynnön %s`
tree_path_not_found_branch = Polkua %[1]s ei ole olemassa haarassa %[2]s tree_path_not_found_branch = Polkua %[1]s ei ole olemassa haarassa %[2]s
transfer.no_permission_to_reject = Sinulla ei ole oikeutta hylätä tätä siirtoa. transfer.no_permission_to_reject = Sinulla ei ole oikeutta hylätä tätä siirtoa.
generate_repo = Luo tietovarasto generate_repo = Luo tietovarasto
@ -2199,8 +2199,8 @@ issues.new.no_reviewers = Ei katselmoijia
issues.add_label = lisäsi nimilapun %s %s issues.add_label = lisäsi nimilapun %s %s
issues.due_date_added = lisäsi eräpäivän %s %s issues.due_date_added = lisäsi eräpäivän %s %s
issues.review.add_review_request = pyysi katselmointia käyttäjältä %[1]s %[2]s issues.review.add_review_request = pyysi katselmointia käyttäjältä %[1]s %[2]s
issues.ref_pull_from = `<a href="%[3]s">viittasi tähän vetopyyntöön %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_pull_from = `<a href="%[2]s">viittasi tähän vetopyyntöön %[3]s</a> %[1]s`
pulls.commit_ref_at = `viittasi tähän vetopyyntöön kommitista <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.commit_ref_at = `viittasi tähän vetopyyntöön kommitista %s`
issues.review.comment = katselmoi %s issues.review.comment = katselmoi %s
issues.add_labels = lisäsi nimilaput %s %s issues.add_labels = lisäsi nimilaput %s %s
issues.review.add_review_requests = pyysi katselmointeja käyttäjiltä %[1]s %[2]s issues.review.add_review_requests = pyysi katselmointeja käyttäjiltä %[1]s %[2]s
@ -2381,7 +2381,7 @@ wiki.page_name_desc = Kirjoita tämän wikisivun nimi. Joitain erikoisnimiä ova
pulls.blocked_by_changed_protected_files_1 = Tämä vetopyyntö sisältää suojatun tiedoston ja on siksi estetty: pulls.blocked_by_changed_protected_files_1 = Tämä vetopyyntö sisältää suojatun tiedoston ja on siksi estetty:
pulls.status_checks_warning = Jotkin tarkistukset raportoivat varoituksia pulls.status_checks_warning = Jotkin tarkistukset raportoivat varoituksia
pulls.status_checks_error = Jotkin tarkistukset raportoivat virheitä pulls.status_checks_error = Jotkin tarkistukset raportoivat virheitä
pulls.reopened_at = `avasi uudelleen tämän vetopyynnön <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.reopened_at = `avasi uudelleen tämän vetopyynnön %s`
pulls.auto_merge_when_succeed = Yhdistä automaatisesti kun kaikki tarkistukset onnistuvat pulls.auto_merge_when_succeed = Yhdistä automaatisesti kun kaikki tarkistukset onnistuvat
signing.wont_sign.error = Tapahtui virhe tarkistaessa voiko kommitin allekirjoittaa. signing.wont_sign.error = Tapahtui virhe tarkistaessa voiko kommitin allekirjoittaa.
signing.wont_sign.twofa = Sinulla tulee olla kaksivaiheinen todennus käytössä, jotta kommitit voi allekirjoittaa. signing.wont_sign.twofa = Sinulla tulee olla kaksivaiheinen todennus käytössä, jotta kommitit voi allekirjoittaa.

View file

@ -365,7 +365,7 @@ table_modal.label.columns = Mga Column
link_modal.header = Magdagdag ng link link_modal.header = Magdagdag ng link
link_modal.url = Url link_modal.url = Url
link_modal.description = Deskripsyon link_modal.description = Deskripsyon
link_modal.paste_reminder = Pahiwatig: Kapag may URL sa clipboard, maari mong direktang i-paste sa editor para gumawa ng link. link_modal.paste_reminder = Pahiwatig: Kapag may URL sa clipboard, maaari mong direktang i-paste sa editor para gumawa ng link.
[filter] [filter]
string.asc = A - Z string.asc = A - Z
@ -432,7 +432,7 @@ openid_connect_desc = Ang piniling OpenID URI ay hindi alam. Iugnay iyan sa bago
invalid_code = Ang iyong confirmation code ay hindi wasto o nag-expire na. invalid_code = Ang iyong confirmation code ay hindi wasto o nag-expire na.
oauth_signin_title = Mag-sign in para pahintulutan ang naka-link na account oauth_signin_title = Mag-sign in para pahintulutan ang naka-link na account
invalid_code_forgot_password = Ang iyong confirmation code ay hindi wasto o nag-expire na. Mag-click <a href="%s">dito</a> para magsimula ng bagong session. invalid_code_forgot_password = Ang iyong confirmation code ay hindi wasto o nag-expire na. Mag-click <a href="%s">dito</a> para magsimula ng bagong session.
confirmation_mail_sent_prompt = Ang isang bagong email na pang-kumpirma ay ipinadala sa <b>%s</b>. Para kumpletuhin ang proseso ng pagrehistro, pakisuri ang iyong inbox at sundan ang ibinigay na link sa loob ng %s. Kung mali ang email, maari kang mag-log in, at humingi ng isa pang email pang-kumpirma na ipapadala sa ibang address. confirmation_mail_sent_prompt = Ang isang bagong email na pang-kumpirma ay ipinadala sa <b>%s</b>. Para kumpletuhin ang proseso ng pagrehistro, pakisuri ang iyong inbox at sundan ang ibinigay na link sa loob ng %s. Kung mali ang email, maaari kang mag-log in, at humingi ng isa pang email pang-kumpirma na ipapadala sa ibang address.
invalid_password = Ang iyong password ay hindi tugma sa password na ginamit para gawin ang account. invalid_password = Ang iyong password ay hindi tugma sa password na ginamit para gawin ang account.
twofa_scratch_used = Ginamit mo na ang scratch code. Na-redirect ka sa two-factor settings page para tanggalin ang device enrollment o mag-generate ng bagong scratch code. twofa_scratch_used = Ginamit mo na ang scratch code. Na-redirect ka sa two-factor settings page para tanggalin ang device enrollment o mag-generate ng bagong scratch code.
manual_activation_only = Makipag-ugnayan sa tagapangangasiwa ng site para kumpletuhin ang pagrehistro. manual_activation_only = Makipag-ugnayan sa tagapangangasiwa ng site para kumpletuhin ang pagrehistro.
@ -484,7 +484,7 @@ admin.new_user.text = Mangyaring <a href="%s">mag-click dito</a> para ipamahala
register_notify = Maligayang Pagdating sa %s register_notify = Maligayang Pagdating sa %s
register_notify.title = %[1]s, maligayang pagdating sa %[2]s register_notify.title = %[1]s, maligayang pagdating sa %[2]s
register_notify.text_1 = ito ang iyong registration confirmation email para sa %s! register_notify.text_1 = ito ang iyong registration confirmation email para sa %s!
register_notify.text_2 = Maari kang mag-sign in sa iyong account gamit ng iyong username: %s register_notify.text_2 = Maaari kang mag-sign in sa iyong account gamit ng iyong username: %s
reset_password = I-recover ang iyong account reset_password = I-recover ang iyong account
reset_password.title = %s, nagkaroon kami ng hiling para i-recover ang iyong account reset_password.title = %s, nagkaroon kami ng hiling para i-recover ang iyong account
reset_password.text = Kung ikaw ito, paki-click ang sumusunod na link para i-recover ang iyong account sa loob ng <b>%s</b>: reset_password.text = Kung ikaw ito, paki-click ang sumusunod na link para i-recover ang iyong account sa loob ng <b>%s</b>:
@ -535,7 +535,7 @@ totp_disabled.text_1 = Ngayon lang na-disable ang Time-based one-time password (
totp_disabled.no_2fa = Wala nang mga ibang paraan ng 2FA ang naka-configure, nangangahulugan na hindi na kailangang mag-log in sa iyong account gamit ang 2FA. totp_disabled.no_2fa = Wala nang mga ibang paraan ng 2FA ang naka-configure, nangangahulugan na hindi na kailangang mag-log in sa iyong account gamit ang 2FA.
removed_security_key.subject = May tinanggal na security key removed_security_key.subject = May tinanggal na security key
removed_security_key.text_1 = Tinanggal ngayon lang ang security key na "%[1]s" sa iyong account. removed_security_key.text_1 = Tinanggal ngayon lang ang security key na "%[1]s" sa iyong account.
account_security_caution.text_1 = Kung ikaw ito, maari mong ligtas na huwag pansinin ang mail na ito. account_security_caution.text_1 = Kung ikaw ito, maaari mong ligtas na huwag pansinin ang mail na ito.
account_security_caution.text_2 = Kung hindi ito ikaw, nakompromiso ang iyong account. Mangyaring makipag-ugnayan sa mga tagapangasiwa ng site na ito. account_security_caution.text_2 = Kung hindi ito ikaw, nakompromiso ang iyong account. Mangyaring makipag-ugnayan sa mga tagapangasiwa ng site na ito.
totp_enrolled.subject = Nag-activate ka ng TOTP bilang paraan ng 2FA totp_enrolled.subject = Nag-activate ka ng TOTP bilang paraan ng 2FA
totp_enrolled.text_1.has_webauthn = Na-enable mo lang ang TOTP para sa iyong account. Nangangahulugan ito na para sa lahat ng mga hinaharap na pag-login sa iyong account, kailangan mong gumamit ng TOTP bilang paraan ng 2FA o gamitin ang iyong mga security key. totp_enrolled.text_1.has_webauthn = Na-enable mo lang ang TOTP para sa iyong account. Nangangahulugan ito na para sa lahat ng mga hinaharap na pag-login sa iyong account, kailangan mong gumamit ng TOTP bilang paraan ng 2FA o gamitin ang iyong mga security key.
@ -644,7 +644,7 @@ AccessToken = Token ng pag-access
Biography = Byograpya Biography = Byograpya
Location = Lokasyon Location = Lokasyon
visit_rate_limit = Natugunan ang limitasyon sa rate ng malayuang pagbisita. visit_rate_limit = Natugunan ang limitasyon sa rate ng malayuang pagbisita.
username_claiming_cooldown = Hindi ma-claim ang username na ito, dahil hindi pa tapos ang panahon ng cooldown. Maari itong i-claim sa %[1]s. username_claiming_cooldown = Hindi ma-claim ang username na ito, dahil hindi pa tapos ang panahon ng cooldown. Maaari itong i-claim sa %[1]s.
email_domain_is_not_allowed = Sumasalungat ang domain ng email address ng user <b>%s</b> sa EMAIL_DOMAIN_ALLOWLIST o EMAIL_DOMAIN_BLOCKLIST. Siguraduhing natakda mo ang email address nang tama. email_domain_is_not_allowed = Sumasalungat ang domain ng email address ng user <b>%s</b> sa EMAIL_DOMAIN_ALLOWLIST o EMAIL_DOMAIN_BLOCKLIST. Siguraduhing natakda mo ang email address nang tama.
[user] [user]
@ -685,7 +685,7 @@ followers.title.few = Mga tagasunod
following.title.one = Sinusundan following.title.one = Sinusundan
followers.title.one = Tagasunod followers.title.one = Tagasunod
public_activity.visibility_hint.self_public = Nakikita ng lahat ang iyong aktibidad, maliban sa mga interaksyon sa pribadong espasyo. <a href="%s">I-configure</a>. public_activity.visibility_hint.self_public = Nakikita ng lahat ang iyong aktibidad, maliban sa mga interaksyon sa pribadong espasyo. <a href="%s">I-configure</a>.
public_activity.visibility_hint.admin_public = Nakikita ng lahat ang aktibidad na ito, ngunit bilang tagapangasiwa maari mo ring makita ang mga interaksyon sa mga pribadong espasyo. public_activity.visibility_hint.admin_public = Nakikita ng lahat ang aktibidad na ito, ngunit bilang tagapangasiwa maaari mo ring makita ang mga interaksyon sa mga pribadong espasyo.
public_activity.visibility_hint.self_private = Nakikita mo lang at mga tagapangasiwa ng instansya ang iyong aktibidad. <a href="%s">I-configure</a>. public_activity.visibility_hint.self_private = Nakikita mo lang at mga tagapangasiwa ng instansya ang iyong aktibidad. <a href="%s">I-configure</a>.
public_activity.visibility_hint.admin_private = Nakikita mo ang aktibidad na ito dahil isa kang tagapangasiwa, ngunit gusto ng user na panatilihin itong pribado. public_activity.visibility_hint.admin_private = Nakikita mo ang aktibidad na ito dahil isa kang tagapangasiwa, ngunit gusto ng user na panatilihin itong pribado.
public_activity.visibility_hint.self_private_profile = Ikaw lang at ang mga tagapangasiwa ng instansya ang makakakita ng iyong aktibidad dahil pribado ang iyong profile. <a href="%s">I-configure</a>. public_activity.visibility_hint.self_private_profile = Ikaw lang at ang mga tagapangasiwa ng instansya ang makakakita ng iyong aktibidad dahil pribado ang iyong profile. <a href="%s">I-configure</a>.
@ -842,7 +842,7 @@ gpg_key_verify = I-verify
gpg_invalid_token_signature = Ang ibinigay na GPG key, signature, at token ay hindi tumutugma o luma. gpg_invalid_token_signature = Ang ibinigay na GPG key, signature, at token ay hindi tumutugma o luma.
gpg_token_required = Kailangan mong magbigay ng signature para sa token sa ibaba gpg_token_required = Kailangan mong magbigay ng signature para sa token sa ibaba
gpg_token = Token gpg_token = Token
gpg_token_help = Maari kang mag-generate ng signature gamit ng: gpg_token_help = Maaari kang mag-generate ng signature gamit ng:
gpg_token_signature = Naka-armor na GPG signature gpg_token_signature = Naka-armor na GPG signature
key_signature_gpg_placeholder = Nagsisimula sa "-----BEGIN PGP SIGNATURE-----" key_signature_gpg_placeholder = Nagsisimula sa "-----BEGIN PGP SIGNATURE-----"
verify_gpg_key_success = Na-verify na ang GPG key na "%s". verify_gpg_key_success = Na-verify na ang GPG key na "%s".
@ -851,7 +851,7 @@ ssh_key_verify = I-verify
ssh_invalid_token_signature = Ang ibinigay na SSH key, signature, o token ay hindi tumutugma o luma. ssh_invalid_token_signature = Ang ibinigay na SSH key, signature, o token ay hindi tumutugma o luma.
ssh_token_required = Kailangan mong magbigay ng signature para sa token sa ibaba ssh_token_required = Kailangan mong magbigay ng signature para sa token sa ibaba
ssh_token = Token ssh_token = Token
ssh_token_help = Maari kang mag-generate ng signature gamit ng: ssh_token_help = Maaari kang mag-generate ng signature gamit ng:
ssh_token_signature = Naka-armor na SSH signature ssh_token_signature = Naka-armor na SSH signature
key_signature_ssh_placeholder = Nagsisimula sa "-----BEGIN SSH SIGNATURE-----" key_signature_ssh_placeholder = Nagsisimula sa "-----BEGIN SSH SIGNATURE-----"
verify_ssh_key_success = Na-verify na ang SSH key na "%s". verify_ssh_key_success = Na-verify na ang SSH key na "%s".
@ -912,10 +912,10 @@ create_oauth2_application_success = Matagumpay kang gumawa ang bagong OAuth2 app
oauth2_confidential_client = Kumpidensyal na kliyente. Piliin para sa mga app na pinapatilihing kumpidensyal ang sikreto, tulad ng mga web app. Huwag piliin para sa mga web app kasama ang mga desktop at mobile app. oauth2_confidential_client = Kumpidensyal na kliyente. Piliin para sa mga app na pinapatilihing kumpidensyal ang sikreto, tulad ng mga web app. Huwag piliin para sa mga web app kasama ang mga desktop at mobile app.
twofa_desc = Para protektahin ang iyong account laban sa pagnanakaw ng password, pwede mo gamitin ang iyong smartphone o ibang device para sa pagtanggap ng time-based one-time password ("TOTP"). twofa_desc = Para protektahin ang iyong account laban sa pagnanakaw ng password, pwede mo gamitin ang iyong smartphone o ibang device para sa pagtanggap ng time-based one-time password ("TOTP").
twofa_scratch_token_regenerated = Ang iyong isang-beses na paggamit na recovery key ngayon ay %s. Ilagay ito sa ligtas na lugar, dahil hindi na ito ipapakita muli. twofa_scratch_token_regenerated = Ang iyong isang-beses na paggamit na recovery key ngayon ay %s. Ilagay ito sa ligtas na lugar, dahil hindi na ito ipapakita muli.
regenerate_scratch_token_desc = Kapag nawala mo ang iyong recovery key o ginamit mo na oara mag-sign in, maari mong i-reset dito. regenerate_scratch_token_desc = Kapag nawala mo ang iyong recovery key o ginamit mo na oara mag-sign in, maaari mong i-reset dito.
twofa_disable_desc = Ang pag-disable ng authentikasyong two-factor ay gagawing hindi gaanong ligtas ang iyong account. Magpatuloy? twofa_disable_desc = Ang pag-disable ng authentikasyong two-factor ay gagawing hindi gaanong ligtas ang iyong account. Magpatuloy?
twofa_enrolled = Matagumpay na na-enroll ang iyong account. Ilagay ang iyong isang-beses na paggamit na recovery key (%s) sa isang ligtas na lugar, dahil hindi na ito ipapakita muli. twofa_enrolled = Matagumpay na na-enroll ang iyong account. Ilagay ang iyong isang-beses na paggamit na recovery key (%s) sa isang ligtas na lugar, dahil hindi na ito ipapakita muli.
webauthn_desc = Ang mga security key ay isang hardware device na naglalaman ng mga cryptographic key. Maari silang gamitin para sa authentikasyong two-factor. Ang mga security key ay dapat suportahan ang <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a> na standard. webauthn_desc = Ang mga security key ay isang hardware device na naglalaman ng mga cryptographic key. Maaari silang gamitin para sa authentikasyong two-factor. Ang mga security key ay dapat suportahan ang <a rel="noreferrer" target="_blank" href="%s">WebAuthn Authenticator</a> na standard.
remove_oauth2_application = Tanggalin ang OAuth2 Application remove_oauth2_application = Tanggalin ang OAuth2 Application
remove_oauth2_application_desc = Ang pagtanggal ng OAuth2 application ay babawiin ang access sa lahat ng mga naka-sign na access token. Magpatuloy? remove_oauth2_application_desc = Ang pagtanggal ng OAuth2 application ay babawiin ang access sa lahat ng mga naka-sign na access token. Magpatuloy?
remove_oauth2_application_success = Binura na ang application. remove_oauth2_application_success = Binura na ang application.
@ -931,13 +931,13 @@ oauth2_regenerate_secret = I-regenerate ang sikreto
oauth2_regenerate_secret_hint = Nawala mo ang iyong sikreto? oauth2_regenerate_secret_hint = Nawala mo ang iyong sikreto?
oauth2_client_secret_hint = Ang sikreto ay hindi ipapakita muli pagkatapos umalis ka o i-refresh ang page na ito. Mangyaring siguraduhin na na-save mo iyan. oauth2_client_secret_hint = Ang sikreto ay hindi ipapakita muli pagkatapos umalis ka o i-refresh ang page na ito. Mangyaring siguraduhin na na-save mo iyan.
oauth2_application_edit = I-edit oauth2_application_edit = I-edit
twofa_recovery_tip = Kapag mawala mo ang iyong device, maari kang gumamit ng isang isang-beses na paggamit na recovery key para makakuha muli ng access sa iyong account. twofa_recovery_tip = Kapag mawala mo ang iyong device, maaari kang gumamit ng isang isang-beses na paggamit na recovery key para makakuha muli ng access sa iyong account.
twofa_is_enrolled = Ang iyong account ay kasalukuyang <strong>naka-enroll</strong> sa autentikasyong two-factor. twofa_is_enrolled = Ang iyong account ay kasalukuyang <strong>naka-enroll</strong> sa autentikasyong two-factor.
twofa_not_enrolled = Kasalukuyang hindi naka-enroll ang iyong account sa authentikasyong two-factor. twofa_not_enrolled = Kasalukuyang hindi naka-enroll ang iyong account sa authentikasyong two-factor.
twofa_disable = I-disable ang authentikasyong two-factor twofa_disable = I-disable ang authentikasyong two-factor
twofa_scratch_token_regenerate = I-regenerate ang isang-beses na paggamit na recovery key twofa_scratch_token_regenerate = I-regenerate ang isang-beses na paggamit na recovery key
twofa_enroll = Mag-enroll sa authentikasyong two-factor twofa_enroll = Mag-enroll sa authentikasyong two-factor
twofa_disable_note = Maari mong i-disable ang authentikasyong two-factor kapag kinakailangan. twofa_disable_note = Maaari mong i-disable ang authentikasyong two-factor kapag kinakailangan.
twofa_disabled = Na-disable na ang authentikasyong two-factor. twofa_disabled = Na-disable na ang authentikasyong two-factor.
scan_this_image = I-scan ang image na ito gamit ng iyong aplikasyong pang-authentikasyon: scan_this_image = I-scan ang image na ito gamit ng iyong aplikasyong pang-authentikasyon:
or_enter_secret = O ilagay ang sikreto: %s or_enter_secret = O ilagay ang sikreto: %s
@ -1005,8 +1005,8 @@ language.description = Mase-save ang wika sa iyong account at gagamitin bilang d
language.localization_project = Tulungan kaming isalin ang Forgejo sa iyong wika! <a href="%s">Matuto pa</a>. language.localization_project = Tulungan kaming isalin ang Forgejo sa iyong wika! <a href="%s">Matuto pa</a>.
pronouns_custom_label = Mga pasadyang pronoun pronouns_custom_label = Mga pasadyang pronoun
user_block_yourself = Hindi mo maaaring harangan ang sarili mo. user_block_yourself = Hindi mo maaaring harangan ang sarili mo.
change_username_redirect_prompt.with_cooldown.one = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. change_username_redirect_prompt.with_cooldown.one = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown.
change_username_redirect_prompt.with_cooldown.few = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. change_username_redirect_prompt.with_cooldown.few = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown.
keep_pronouns_private = Ipakita lang ang mga panghalip sa mga naka-authenticate na user keep_pronouns_private = Ipakita lang ang mga panghalip sa mga naka-authenticate na user
keep_pronouns_private.description = Itatago nito ang iyong mga panghalip mula sa mga bisita na hindi naka-log in. keep_pronouns_private.description = Itatago nito ang iyong mga panghalip mula sa mga bisita na hindi naka-log in.
quota.applies_to_user = Nag-aapply ang mga sumusunod na panuntunan ng quota sa iyong account quota.applies_to_user = Nag-aapply ang mga sumusunod na panuntunan ng quota sa iyong account
@ -1071,7 +1071,7 @@ readme_helper_desc = Ito ang lugar kung saan makakasulat ka ng kumpletong deskri
trust_model_helper_collaborator_committer = Katulong+Committer: I-trust ang mga signature batay sa mga katulong na tumutugma sa committer trust_model_helper_collaborator_committer = Katulong+Committer: I-trust ang mga signature batay sa mga katulong na tumutugma sa committer
mirror_interval = Interval ng mirror (ang mga wastong unit ng oras ay "h", "m", "s"). 0 para i-disable ang periodic sync. (Pinakamababang interval: %s) mirror_interval = Interval ng mirror (ang mga wastong unit ng oras ay "h", "m", "s"). 0 para i-disable ang periodic sync. (Pinakamababang interval: %s)
transfer.reject_desc = Kanselahin ang pag-transfer mula sa "%s" transfer.reject_desc = Kanselahin ang pag-transfer mula sa "%s"
mirror_lfs_endpoint_desc = Ang sync ay susubukang gamitin ang clone url upang <a target="_blank" rel="noopener noreferrer" href="%s">matukoy ang LFS server</a>. Maari ka rin tumukoy ng isang custom na endpoint kapag ang LFS data ng repositoryo ay nilalagay sa ibang lugar. mirror_lfs_endpoint_desc = Ang sync ay susubukang gamitin ang clone url upang <a target="_blank" rel="noopener noreferrer" href="%s">matukoy ang LFS server</a>. Maaari ka rin tumukoy ng isang custom na endpoint kapag ang LFS data ng repositoryo ay nilalagay sa ibang lugar.
adopt_search = Ilagay ang username para maghanap ng mga unadopted na repositoryo… (iwanang walang laman para hanapin lahat) adopt_search = Ilagay ang username para maghanap ng mga unadopted na repositoryo… (iwanang walang laman para hanapin lahat)
object_format = Format ng object object_format = Format ng object
readme_helper = Pumili ng README file template readme_helper = Pumili ng README file template
@ -1164,8 +1164,8 @@ tree_path_not_found_commit = Hindi umiiral ang path na %[1]s sa commit %[2]s
tree_path_not_found_branch = Hindi umiiral ang daanang %[1]s sa branch %[2]s tree_path_not_found_branch = Hindi umiiral ang daanang %[1]s sa branch %[2]s
migrate_items_pullrequests = Mga hiling sa paghila migrate_items_pullrequests = Mga hiling sa paghila
archive.pull.nocomment = Naka-archive ang repositoryong ito. Hindi ka makakakomento sa mga pull request. archive.pull.nocomment = Naka-archive ang repositoryong ito. Hindi ka makakakomento sa mga pull request.
archive.title = Naka-archive ang repositoryong ito. Maari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado ito, tulad ng pagtulak at paggawa ng mga isyu, pull request o mga komento. archive.title = Naka-archive ang repositoryong ito. Maaari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado ito, tulad ng pagtulak at paggawa ng mga isyu, pull request o mga komento.
archive.title_date = Naka-archive ang repositoryo na ito noong %s. Maari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado nito, tulad ng pagtulak o paggawa ng mga bagong isyu, mga pull request, o komento. archive.title_date = Naka-archive ang repositoryo na ito noong %s. Maaari mong itignan ang mga file at i-clone ito, pero hindi ka makakagawa ng anumang pagbabago sa estado nito, tulad ng pagtulak o paggawa ng mga bagong isyu, mga pull request, o komento.
pulls = Mga hiling sa paghila pulls = Mga hiling sa paghila
activity.merged_prs_count_n = Mga naisamang hiling sa paghila activity.merged_prs_count_n = Mga naisamang hiling sa paghila
wiki.last_updated = Huling binago %s wiki.last_updated = Huling binago %s
@ -1183,7 +1183,7 @@ issues.action_open = Buksan
issues.closed_title = Sarado issues.closed_title = Sarado
issues.reopen_issue = Buksang muli issues.reopen_issue = Buksang muli
pulls.merged = Naisama na pulls.merged = Naisama na
pulls.merged_info_text = Maari nang burahin ang branch %s. pulls.merged_info_text = Maaari nang burahin ang branch %s.
milestones.update_ago = Binago %s milestones.update_ago = Binago %s
activity.closed_issue_label = Sarado activity.closed_issue_label = Sarado
activity.merged_prs_label = Naisama activity.merged_prs_label = Naisama
@ -1205,7 +1205,7 @@ migrate.clone_address_desc = Ang HTTP(S) o Git "clone" URL ng umiiral na reposit
need_auth = Awtorisasyon need_auth = Awtorisasyon
migrate.github_token_desc = Maaari kang maglagay ng isa o higit pang mga token na hinihiwalay ng kuwit dito upang gawing mas-mabilis ang pagmigrate dahil sa rate limit ng GitHub API. BABALA: Ang pagabuso ng feature na ito ay maaaring maglabag sa patakaran ng tagapagbigay ng serbisyo at maaaring magdulot ng pag-block ng account. migrate.github_token_desc = Maaari kang maglagay ng isa o higit pang mga token na hinihiwalay ng kuwit dito upang gawing mas-mabilis ang pagmigrate dahil sa rate limit ng GitHub API. BABALA: Ang pagabuso ng feature na ito ay maaaring maglabag sa patakaran ng tagapagbigay ng serbisyo at maaaring magdulot ng pag-block ng account.
template.invalid = Kailangang pumili ng kahit isang template na repositoryo template.invalid = Kailangang pumili ng kahit isang template na repositoryo
migrate_options_lfs_endpoint.description = Susubukan ng migration na gamitin ang iyong Git remote upang <a target="_blank" rel="noopener noreferrer" href="%s">matukoy ang LFS server</a>. Maari mong magtiyak ng custom na endpoint kapag ang LFS data ng repositoryo ay nakalagay sa ibang lugar. migrate_options_lfs_endpoint.description = Susubukan ng migration na gamitin ang iyong Git remote upang <a target="_blank" rel="noopener noreferrer" href="%s">matukoy ang LFS server</a>. Maaari mong magtiyak ng custom na endpoint kapag ang LFS data ng repositoryo ay nakalagay sa ibang lugar.
blame.ignore_revs.failed = Nabigong hindi pansinin ang mga rebisyon sa <a href="%s">.git-blame-ignore-revs</a>. blame.ignore_revs.failed = Nabigong hindi pansinin ang mga rebisyon sa <a href="%s">.git-blame-ignore-revs</a>.
tree_path_not_found_tag = Hindi umiiral ang path na %[1]s sa tag %[2]s tree_path_not_found_tag = Hindi umiiral ang path na %[1]s sa tag %[2]s
form.reach_limit_of_creation_n = Naabot na ng may-ari ang limitasyon na %d mga repositoryo. form.reach_limit_of_creation_n = Naabot na ng may-ari ang limitasyon na %d mga repositoryo.
@ -1471,10 +1471,10 @@ activity.new_issue_label = Nabuksan
activity.merged_prs_count_1 = Naisamang hiling sa paghila activity.merged_prs_count_1 = Naisamang hiling sa paghila
activity.opened_prs_count_1 = Inimungkahing hiling sa paghila activity.opened_prs_count_1 = Inimungkahing hiling sa paghila
activity.opened_prs_label = Inimungkahi activity.opened_prs_label = Inimungkahi
pulls.reopened_at = `nabuksang muli ang hiling sa paghatak na <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.reopened_at = `binuksan muli ang hiling sa paghila %s`
issues.opened_by_fake = binuksan ang %[1]s ni/ng %[2]s issues.opened_by_fake = binuksan ang %[1]s ni/ng %[2]s
pulls.reopen_failed.base_branch = Hindi mabuksang muli ang hiling sa paghatak na ito dahil hindi na umiiral ang base branch. pulls.reopen_failed.base_branch = Hindi mabuksang muli ang hiling sa paghatak na ito dahil hindi na umiiral ang base branch.
issues.reopened_at = `binuksang muli ang isyung ito <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at = `binuksang muli ang isyung ito %s`
pulls.reopen_failed.head_branch = Hindi mabubuksan muli ang hiling sa paghila, dahil hindi na umiiral ang head branch. pulls.reopen_failed.head_branch = Hindi mabubuksan muli ang hiling sa paghila, dahil hindi na umiiral ang head branch.
settings.event_pull_request_desc = Binuksan, sinara, muling binuksan, o binago ang hiling sa paghatak. settings.event_pull_request_desc = Binuksan, sinara, muling binuksan, o binago ang hiling sa paghatak.
activity.opened_prs_count_n = Mga inimungkahing hiling sa paghila activity.opened_prs_count_n = Mga inimungkahing hiling sa paghila
@ -1500,7 +1500,7 @@ issues.content_history.created = ginawa
editor.patching = Pina-patch: editor.patching = Pina-patch:
editor.fail_to_apply_patch = Hindi malapat ang patch na "%s" editor.fail_to_apply_patch = Hindi malapat ang patch na "%s"
settings.danger_zone = Mapanganib na lugar settings.danger_zone = Mapanganib na lugar
issues.closed_at = `isinara ang isyung ito <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.closed_at = `isinara ang isyung ito %s`
settings.collaboration.admin = Tagapangasiwa settings.collaboration.admin = Tagapangasiwa
settings.admin_settings = Mga setting ng tagapangasiwa settings.admin_settings = Mga setting ng tagapangasiwa
issues.start_tracking_history = `sinimulan ang trabaho %s` issues.start_tracking_history = `sinimulan ang trabaho %s`
@ -1627,7 +1627,7 @@ projects.column.edit_title = Pangalan
projects.column.new_title = Pangalan projects.column.new_title = Pangalan
projects.card_type.desc = Mga preview ng card projects.card_type.desc = Mga preview ng card
commits.desc = I-browse ang history ng pagbabago ng source code. commits.desc = I-browse ang history ng pagbabago ng source code.
commits.search.tooltip = Maari kang mag-prefix ng mga keyword gamit ang "author:", "committer:", "after:", o "before:", hal. "revert author:Nijika before:2022-10-09". commits.search.tooltip = Maaari kang mag-prefix ng mga keyword gamit ang "author:", "committer:", "after:", o "before:", hal. "revert author:Nijika before:2022-10-09".
issues.force_push_codes = `puwersahang itinulak ang %[1]s mula <a class="%[7]s" href="%[3]s"><code>%[2]s</code></a> sa <a class="%[7]s" href="%[5]s"><code>%[4]s</code></a> %[6]s` issues.force_push_codes = `puwersahang itinulak ang %[1]s mula <a class="%[7]s" href="%[3]s"><code>%[2]s</code></a> sa <a class="%[7]s" href="%[5]s"><code>%[4]s</code></a> %[6]s`
issues.push_commit_1 = idinagdag ang %d commit %s issues.push_commit_1 = idinagdag ang %d commit %s
issues.push_commits_n = idinagdag ang %d mga commit %s issues.push_commits_n = idinagdag ang %d mga commit %s
@ -1707,7 +1707,7 @@ issues.action_milestone = Milestone
issues.action_milestone_no_select = Walang milestone issues.action_milestone_no_select = Walang milestone
issues.delete_branch_at = `binura ang branch na <b>%s</b> %s` issues.delete_branch_at = `binura ang branch na <b>%s</b> %s`
issues.filter_label = Label issues.filter_label = Label
issues.filter_label_exclude = `Gamitin ang <code>alt</code> + <code>click/enter</code> para hindi isama ang mga label` issues.filter_label_exclude = `Gamitin ang <kbd>Alt</kbd> + <kbd>Click</kbd> para hindi isama ang mga label`
issues.filter_label_no_select = Lahat ng mga label issues.filter_label_no_select = Lahat ng mga label
issues.filter_milestone_closed = Mga nakasarang milestone issues.filter_milestone_closed = Mga nakasarang milestone
issues.filter_assignee = Mangangasiwa issues.filter_assignee = Mangangasiwa
@ -1771,7 +1771,7 @@ issues.lock = I-lock ang usapan
issues.unlock = I-unlock ang usapan issues.unlock = I-unlock ang usapan
issues.unlock_comment = na-unlock ang usapang ito %s issues.unlock_comment = na-unlock ang usapang ito %s
issues.unlock.notice_1 = - Makakakomento muli ang lahat ng mga tao sa isyung ito. issues.unlock.notice_1 = - Makakakomento muli ang lahat ng mga tao sa isyung ito.
issues.unlock.notice_2 = - Maari mong i-lock muli ang isyung ito sa hinaharap. issues.unlock.notice_2 = - Maaari mong i-lock muli ang isyung ito sa hinaharap.
issues.comment_on_locked = Hindi ka makakakomento sa naka-lock na isyu. issues.comment_on_locked = Hindi ka makakakomento sa naka-lock na isyu.
issues.closed_by_fake = ni/ng %[2]s ay isinara %[1]s issues.closed_by_fake = ni/ng %[2]s ay isinara %[1]s
issues.comment_manually_pull_merged_at = manwal na isinama ang commit %[1]s sa %[2]s %[3]s issues.comment_manually_pull_merged_at = manwal na isinama ang commit %[1]s sa %[2]s %[3]s
@ -1787,10 +1787,10 @@ issues.label_archive_tooltip = Ang mga naka-archive na label ay hindi isasama bi
issues.is_stale = May mga pagbabago sa PR na ito mula sa pagsuri na ito issues.is_stale = May mga pagbabago sa PR na ito mula sa pagsuri na ito
issues.role.first_time_contributor = Unang-beses na contributor issues.role.first_time_contributor = Unang-beses na contributor
issues.lock.notice_1 = - Hindi makakadagdag ng mga bagong komento ang mga ibang user sa isyu na ito. issues.lock.notice_1 = - Hindi makakadagdag ng mga bagong komento ang mga ibang user sa isyu na ito.
issues.lock.notice_3 = - Maari mong i-unlock muli ang isyung ito sa hinaharap. issues.lock.notice_3 = - Maaari mong i-unlock muli ang isyung ito sa hinaharap.
issues.label_deletion_desc = Ang pagbura ng label ay tatanggalin ito sa lahat ng mga isyu. Magpatuloy? issues.label_deletion_desc = Ang pagbura ng label ay tatanggalin ito sa lahat ng mga isyu. Magpatuloy?
issues.commit_ref_at = `isinangguni ang isyu na ito mula sa commit <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at = `isinangguni ang isyu na ito mula sa commit %s`
issues.ref_issue_from = `<a href="%[3]s">isinangguni ang isyu na ito sa %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_issue_from = `<a href="%[2]s">isinangguni ang isyu na ito sa %[3]s</a> %[1]s`
issues.num_participants_one = %d kasali issues.num_participants_one = %d kasali
issues.attachment.download = `I-click para i-download ang "%s" ` issues.attachment.download = `I-click para i-download ang "%s" `
issues.num_participants_few = %d mga kasali issues.num_participants_few = %d mga kasali
@ -1815,10 +1815,10 @@ issues.sign_in_require_desc = <a href="%s">Mag-sign in</a> upang sumali sa usapa
issues.num_comments = %d mga komento issues.num_comments = %d mga komento
issues.role.contributor_helper = Nakaraang nag-commit ang user na ito sa repositoryo na ito. issues.role.contributor_helper = Nakaraang nag-commit ang user na ito sa repositoryo na ito.
issues.comment_pull_merged_at = isinama ang commit %[1]s sa %[2]s %[3]s issues.comment_pull_merged_at = isinama ang commit %[1]s sa %[2]s %[3]s
pulls.commit_ref_at = `isinangguni ang hiling sa paghila mula sa isang commit <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.commit_ref_at = `isinangguni ang hiling sa paghila mula sa isang commit %s`
wiki.last_commit_info = Binago ni %s ang pahinang ito %s wiki.last_commit_info = Binago ni %s ang pahinang ito %s
issues.content_history.edited = binago issues.content_history.edited = binago
issues.ref_pull_from = `<a href="%[3]s">isinangguni ang hiling sa paghila na ito %[4]s </a><a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_pull_from = `<a href="%[2]s">isinangguni ang hiling sa paghila na ito %[3]s</a> %[1]s`
pulls.merged_title_desc_few = isinali ang %[1]d mga commit mula sa <code>%[2]s</code> patungong <code>%[3]s</code> %[4]s pulls.merged_title_desc_few = isinali ang %[1]d mga commit mula sa <code>%[2]s</code> patungong <code>%[3]s</code> %[4]s
settings.org_not_allowed_to_be_collaborator = Hindi maaaring idagdag ang mga organisasyon bilang tagatulong. settings.org_not_allowed_to_be_collaborator = Hindi maaaring idagdag ang mga organisasyon bilang tagatulong.
settings.add_collaborator_success = Naidagdag ang tagatulong. settings.add_collaborator_success = Naidagdag ang tagatulong.
@ -1828,7 +1828,7 @@ pulls.create = Gumawa ng hiling sa paghila
issues.dependency.pr_close_blocked = Kailangan mong isara ang lahat ng mga isyu na humaharang sa hiling sa paghila na ito bago mo ito isama. issues.dependency.pr_close_blocked = Kailangan mong isara ang lahat ng mga isyu na humaharang sa hiling sa paghila na ito bago mo ito isama.
pulls.delete.title = Burahin ang hiling sa paghila na ito? pulls.delete.title = Burahin ang hiling sa paghila na ito?
issues.dependency.pr_closing_blockedby = Hinarang ng mga sumusunod na isyu mula sa pagsara ng hiling sa paghila na ito issues.dependency.pr_closing_blockedby = Hinarang ng mga sumusunod na isyu mula sa pagsara ng hiling sa paghila na ito
pulls.closed_at = `isinara ang hiling sa paghila na <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.closed_at = `isinara ang hiling sa paghila na ito %s`
pulls.close = Isara ang hiling sa paghila pulls.close = Isara ang hiling sa paghila
pulls.cmd_instruction_hint = Tingnan ang mga panuto para sa command line pulls.cmd_instruction_hint = Tingnan ang mga panuto para sa command line
project = Mga proyekto project = Mga proyekto
@ -1836,8 +1836,8 @@ issues.content_history.deleted = binura
pulls.no_results = Walang mga nahanap na resulta. pulls.no_results = Walang mga nahanap na resulta.
pulls.closed = Sarado ang hiling sa paghila pulls.closed = Sarado ang hiling sa paghila
pulls.is_closed = Naisara na ang hiling sa paghila. pulls.is_closed = Naisara na ang hiling sa paghila.
issues.ref_closing_from = `<a href="%[3]s">nagsangguni ang isyu mula sa hiling sa paghila %[4]s na magsasara sa isyu</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closing_from = `<a href="%[2]s">nagsangguni ang isyu mula sa hiling sa paghila %[3]s na magsasara sa isyu</a>, %[1]s`
issues.ref_reopening_from = `<a href="%[3]s">nagsangguni ang isyu na ito mula sa hiling sa paghila %[4]s na muling bubukas</a>, <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopening_from = `<a href="%[2]s">nagsangguni ang isyu na ito mula sa hiling sa paghila %[3]s na muling bubukas nito</a>, %[1]s`
issues.ref_closed_from = `<a href="%[3]s">isinara ang isyung ito %[4]s</a><a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closed_from = `<a href="%[3]s">isinara ang isyung ito %[4]s</a><a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.review.wait = hiniling sa pagsuri %s issues.review.wait = hiniling sa pagsuri %s
issues.review.reject = hinihiling ang mga pagbago %s issues.review.reject = hinihiling ang mga pagbago %s
@ -2015,14 +2015,14 @@ wiki.cancel = Kanselahin
settings.collaboration.undefined = Hindi Natukoy settings.collaboration.undefined = Hindi Natukoy
settings.federation_settings = Mga Setting ng Federation settings.federation_settings = Mga Setting ng Federation
settings = Mga Setting settings = Mga Setting
settings.desc = Ang mga setting ang lugar kung saan maari mong ipamahala ang mga setting para sa repositoryo settings.desc = Ang mga setting ang lugar kung saan maaari mong ipamahala ang mga setting para sa repositoryo
pulls.collapse_files = I-collapse ang lahat ng mga file pulls.collapse_files = I-collapse ang lahat ng mga file
pulls.add_prefix = Magdagdag ng <strong>%s</strong> na prefix pulls.add_prefix = Magdagdag ng <strong>%s</strong> na prefix
pulls.still_in_progress = Ginagawa pa? pulls.still_in_progress = Ginagawa pa?
activity.title.prs_1 = %d hiling sa paghila activity.title.prs_1 = %d hiling sa paghila
activity.active_issues_count_n = <strong>%d</strong> mga aktibong isyu activity.active_issues_count_n = <strong>%d</strong> mga aktibong isyu
pulls.required_status_check_missing = Nawawala ang ilang mga kinakailangang pagsusuri. pulls.required_status_check_missing = Nawawala ang ilang mga kinakailangang pagsusuri.
pulls.required_status_check_administrator = Bilang tagapangasiwa, maari mo pa ring isama ang hiling sa paghila na ito. pulls.required_status_check_administrator = Bilang tagapangasiwa, maaari mo pa ring isama ang hiling sa paghila na ito.
pulls.blocked_by_approvals = Wala pang sapat na pag-apruba ang hiling sa paghila na ito. %d ng %d na pag-apruba ang ibinigay. pulls.blocked_by_approvals = Wala pang sapat na pag-apruba ang hiling sa paghila na ito. %d ng %d na pag-apruba ang ibinigay.
settings.options = Repositoryo settings.options = Repositoryo
wiki.back_to_wiki = Bumalik sa pahina ng wiki wiki.back_to_wiki = Bumalik sa pahina ng wiki
@ -2110,7 +2110,7 @@ settings.actions_desc = I-enable ang mga kasamang CI/CD pipeline gamit ang Forge
settings.admin_indexer_commit_sha = Huling na-index na commit settings.admin_indexer_commit_sha = Huling na-index na commit
settings.admin_indexer_unindexed = Hindi naka-index settings.admin_indexer_unindexed = Hindi naka-index
settings.transfer_notices_3 = - Kung pribado ang repositoryo at ilipat sa isang indibidwal na user, ang aksyon na ito ay sinisigurado na ang user ay may pahintulot na basahin (at palitan ang mga pahintulot kung kailangan). settings.transfer_notices_3 = - Kung pribado ang repositoryo at ilipat sa isang indibidwal na user, ang aksyon na ito ay sinisigurado na ang user ay may pahintulot na basahin (at palitan ang mga pahintulot kung kailangan).
settings.convert_desc = Maari mong i-convert ang repositoryo na ito sa regular na repositoryo. Hindi ito mababawi. settings.convert_desc = Maaari mong i-convert ang repositoryo na ito sa regular na repositoryo. Hindi ito mababawi.
settings.transfer.button = Ilipat ang pagmamay-ari settings.transfer.button = Ilipat ang pagmamay-ari
settings.signing_settings = Mga setting sa pagpapatunay ng pag-sign settings.signing_settings = Mga setting sa pagpapatunay ng pag-sign
settings.admin_enable_close_issues_via_commit_in_any_branch = Isara ang isyu sa pamamagitan ng commit na ginawa sa hindi default na branch settings.admin_enable_close_issues_via_commit_in_any_branch = Isara ang isyu sa pamamagitan ng commit na ginawa sa hindi default na branch
@ -2137,7 +2137,7 @@ settings.deploy_key_deletion = Tanggalin ang deploy key
settings.protect_enable_push = I-enable ang pagtulak settings.protect_enable_push = I-enable ang pagtulak
settings.discord_icon_url.exceeds_max_length = Kailangang bababa o equal sa 2048 characters ang URL ng icon settings.discord_icon_url.exceeds_max_length = Kailangang bababa o equal sa 2048 characters ang URL ng icon
settings.protected_branch.save_rule = I-save ang rule settings.protected_branch.save_rule = I-save ang rule
settings.mirror_settings.docs.can_still_use = Bagama't na hindi ka makakabago ng mga umiiral na mirror o gumawa ng bago, maari mo pa rin gamitin ang iyong umiiral na mirror. settings.mirror_settings.docs.can_still_use = Bagama't na hindi ka makakabago ng mga umiiral na mirror o gumawa ng bago, maaari mo pa rin gamitin ang iyong umiiral na mirror.
settings.slack_color = Kulay settings.slack_color = Kulay
settings.discord_icon_url = URL ng icon settings.discord_icon_url = URL ng icon
settings.convert_fork_confirm = I-convert ang repositoryo settings.convert_fork_confirm = I-convert ang repositoryo
@ -2254,7 +2254,7 @@ settings.pulls.allow_rebase_update = I-enable ang pag-update ng hiling sa paghil
settings.admin_enable_health_check = I-enable ang pagsusuri ng kalusugan ng repositoryo (git fsck) settings.admin_enable_health_check = I-enable ang pagsusuri ng kalusugan ng repositoryo (git fsck)
settings.new_owner_has_same_repo = Ang bagong may-ari ay may repositoryo na may katulad na pangalan. Mangyaring pumili ng ibang pangalan. settings.new_owner_has_same_repo = Ang bagong may-ari ay may repositoryo na may katulad na pangalan. Mangyaring pumili ng ibang pangalan.
settings.convert = I-convert sa regular na repositoryo settings.convert = I-convert sa regular na repositoryo
settings.convert_fork_desc = Maari mong i-convert ang fork na ito bilang regular na repositoryo. Hindi ito mababawi. settings.convert_fork_desc = Maaari mong i-convert ang fork na ito bilang regular na repositoryo. Hindi ito mababawi.
settings.convert_fork_notices_1 = Ang operasyon na ito ay ico-convert ang fork bilang regular na repositoryo at hindi mababawi. settings.convert_fork_notices_1 = Ang operasyon na ito ay ico-convert ang fork bilang regular na repositoryo at hindi mababawi.
settings.transfer_abort_invalid = Hindi mo makakansela ang isang hindi umiiral na paglipat ng repositoryo. settings.transfer_abort_invalid = Hindi mo makakansela ang isang hindi umiiral na paglipat ng repositoryo.
settings.transfer_quota_exceeded = Ang bagong may-ari (%s) ay lumalagpas sa quota. Hindi nailipat ang repositoryo. settings.transfer_quota_exceeded = Ang bagong may-ari (%s) ay lumalagpas sa quota. Hindi nailipat ang repositoryo.
@ -2290,8 +2290,8 @@ settings.webhook.headers = Mga header
settings.webhook.payload = Nilalaman settings.webhook.payload = Nilalaman
settings.webhook.body = Katawan settings.webhook.body = Katawan
settings.webhook.replay.description = I-replay ang webhook na ito. settings.webhook.replay.description = I-replay ang webhook na ito.
settings.webhook.delivery.success = May nadagdag na event sa delivery queue. Maari magtagal ng ilang segundo bago makita sa delivery history. settings.webhook.delivery.success = May nadagdag na event sa delivery queue. Maaari magtagal ng ilang segundo bago makita sa delivery history.
settings.githooks_desc = Pinapagana ng Git ang mga Git hook. Maari mong baguhin ang mga hook file sa ibaba para mag-set up ng mga custom na operasyon. settings.githooks_desc = Pinapagana ng Git ang mga Git hook. Maaari mong baguhin ang mga hook file sa ibaba para mag-set up ng mga custom na operasyon.
settings.githook_name = Pangalan ng hook settings.githook_name = Pangalan ng hook
settings.githook_content = Nilalaman ng hook settings.githook_content = Nilalaman ng hook
settings.update_githook = I-update ang hook settings.update_githook = I-update ang hook
@ -2362,7 +2362,7 @@ settings.mirror_settings.docs.pull_mirror_instructions = Para mag-set up ng pull
milestones.invalid_due_date_format = Kailangang "yyyy-mm-dd" na format ang takdang petsa. milestones.invalid_due_date_format = Kailangang "yyyy-mm-dd" na format ang takdang petsa.
signing.wont_sign.nokey = Walang key ang instansya na ito para i-sign ang commit na ito. signing.wont_sign.nokey = Walang key ang instansya na ito para i-sign ang commit na ito.
activity.title.releases_1 = %d paglabas activity.title.releases_1 = %d paglabas
settings.mirror_settings.docs.more_information_if_disabled = Maari kang matuto pa tungkol sa mga push at pull na mirror dito: settings.mirror_settings.docs.more_information_if_disabled = Maaari kang matuto pa tungkol sa mga push at pull na mirror dito:
settings.branches.switch_default_branch = Magpalit ng default branch settings.branches.switch_default_branch = Magpalit ng default branch
settings.convert_notices_1 = Ang operasyon na ito ay ico-covert ang mirror sa regular na repositoryo at hindi mababawi. settings.convert_notices_1 = Ang operasyon na ito ay ico-covert ang mirror sa regular na repositoryo at hindi mababawi.
settings.convert_fork_succeed = Na-convert na ang fork sa regular na repositoryo. settings.convert_fork_succeed = Na-convert na ang fork sa regular na repositoryo.
@ -2732,7 +2732,7 @@ settings.protect_protected_file_patterns = Mga pattern ng nakaprotektang file (h
settings.update_protect_branch_success = Binago na ang branch protection rule na "%s". settings.update_protect_branch_success = Binago na ang branch protection rule na "%s".
settings.remove_protected_branch_success = Tinanggal ang branch protection rule na "%s". settings.remove_protected_branch_success = Tinanggal ang branch protection rule na "%s".
settings.tags.protection.pattern = Pattern ng tag settings.tags.protection.pattern = Pattern ng tag
settings.tags.protection.pattern.description = Maari kang gumamit ng iisang pangalan o glob pattern o regular expression para magtugma ng maraming tag. Magbasa pa sa <a target="_blank" rel="noopener" href="%s">guide ng mga nakaprotektang tag</a>. settings.tags.protection.pattern.description = Maaari kang gumamit ng iisang pangalan o glob pattern o regular expression para magtugma ng maraming tag. Magbasa pa sa <a target="_blank" rel="noopener" href="%s">guide ng mga nakaprotektang tag</a>.
settings.thread_id = ID ng thread settings.thread_id = ID ng thread
settings.matrix.room_id = ID ng room settings.matrix.room_id = ID ng room
diff.has_escaped = May mga nakatagong Unicode character ang linya na ito diff.has_escaped = May mga nakatagong Unicode character ang linya na ito
@ -2746,7 +2746,7 @@ diff.bin = BIN
settings.default_update_style_desc = Ang default na istilio na gagamitin sa pag-update ng mga hiling sa paghila na nalilipas sa base branch. settings.default_update_style_desc = Ang default na istilio na gagamitin sa pag-update ng mga hiling sa paghila na nalilipas sa base branch.
pulls.sign_in_require = <a href="%s">Mag-sign in</a> para gumawa ng bagong hiling sa paghila. pulls.sign_in_require = <a href="%s">Mag-sign in</a> para gumawa ng bagong hiling sa paghila.
new_from_template = Gumamit ng template new_from_template = Gumamit ng template
new_from_template_description = Maari kang pumili ng umiiral na repository template sa instansya na ito at i-apply ang mga setting nito. new_from_template_description = Maaari kang pumili ng umiiral na repository template sa instansya na ito at i-apply ang mga setting nito.
new_advanced = Mga advanced na setting new_advanced = Mga advanced na setting
new_advanced_expand = I-click para i-expand new_advanced_expand = I-click para i-expand
auto_init_description = Simulan ang kasaysayan ng Git gamit ang README at opsyonal na magdagdag ng mga lisensya at .gitignore na file. auto_init_description = Simulan ang kasaysayan ng Git gamit ang README at opsyonal na magdagdag ng mga lisensya at .gitignore na file.
@ -2780,6 +2780,7 @@ settings.event_action_recover = I-recover
settings.event_action_success = Matagumpay settings.event_action_success = Matagumpay
settings.event_action_success_desc = Matagumpay na natapos ang Action Run. settings.event_action_success_desc = Matagumpay na natapos ang Action Run.
settings.event_action_recover_desc = Matagumpay na natapos ang Action Run pagkatapos na nabigo ang huling Action Run sa katulad na workflow. settings.event_action_recover_desc = Matagumpay na natapos ang Action Run pagkatapos na nabigo ang huling Action Run sa katulad na workflow.
issues.filter_type.all_pull_requests = Lahat ng mga hiling sa paghila
[search] [search]
commit_kind = Maghanap ng mga commit… commit_kind = Maghanap ng mga commit…
@ -3205,7 +3206,7 @@ self_check.database_collation_mismatch = Inaasahan ang database na gamitin ang c
auths.oauth2_admin_group = Group claim value para sa mga tagapangasiwa. (Opsyonal - kinakailangan ang claim name sa itaas) auths.oauth2_admin_group = Group claim value para sa mga tagapangasiwa. (Opsyonal - kinakailangan ang claim name sa itaas)
auths.tip.facebook = Magrehistro ng bagong application sa %s at idagdag ang produktong "Facebook Login" auths.tip.facebook = Magrehistro ng bagong application sa %s at idagdag ang produktong "Facebook Login"
users.restricted.description = Payagan lamang ang interaksyon sa mga repositoryo at organisasyon kung saan ang user ay dinagdag bilang tagatulong. Iniiwasan nito ang pag-access sa publikong repositoryo sa instansya na ito. users.restricted.description = Payagan lamang ang interaksyon sa mga repositoryo at organisasyon kung saan ang user ay dinagdag bilang tagatulong. Iniiwasan nito ang pag-access sa publikong repositoryo sa instansya na ito.
users.local_import.description = Payagan ang pag-import ng mga repositoryo mula sa local file system ng user. Maari itong maging isyu sa seguridad. users.local_import.description = Payagan ang pag-import ng mga repositoryo mula sa local file system ng user. Maaari itong maging isyu sa seguridad.
emails.delete = Burahin ang Email emails.delete = Burahin ang Email
emails.deletion_success = Binura na ang email address. emails.deletion_success = Binura na ang email address.
auths.oauth2_required_claim_value = Kinakailangan na claim value auths.oauth2_required_claim_value = Kinakailangan na claim value
@ -3450,8 +3451,8 @@ teams.owners_permission_desc = Ang mga owner ay may punong access sa <strong>lah
teams.add_nonexistent_repo = Hindi pa umiiral ang repositoryo na sinusubukan mong idagdag. Mangyaring gawin iyan muna. teams.add_nonexistent_repo = Hindi pa umiiral ang repositoryo na sinusubukan mong idagdag. Mangyaring gawin iyan muna.
teams.all_repositories = Lahat ng mga repositoryo teams.all_repositories = Lahat ng mga repositoryo
teams.all_repositories_helper = Ang koponan ay may access sa lahat ng mga repositoryo. Ang pagpili nito ay <strong>idadagdag ang lahat ng mga umiiral</strong> na repositoryo sa koponan. teams.all_repositories_helper = Ang koponan ay may access sa lahat ng mga repositoryo. Ang pagpili nito ay <strong>idadagdag ang lahat ng mga umiiral</strong> na repositoryo sa koponan.
settings.change_orgname_redirect_prompt.with_cooldown.few = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang pangalan sa panahon ng cooldown. settings.change_orgname_redirect_prompt.with_cooldown.few = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang pangalan sa panahon ng cooldown.
settings.change_orgname_redirect_prompt.with_cooldown.one = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang pangalan ng panahon ng cooldown. settings.change_orgname_redirect_prompt.with_cooldown.one = Magiging available ang lumang pangalan ng organisasyon sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw. Maaari mo pa ring ma-claim muli ang lumang pangalan ng panahon ng cooldown.
[packages] [packages]

View file

@ -1062,8 +1062,8 @@ language.localization_project = Aidez-nous à traduire Forgejo dans votre langue
language.description = Cette langue sera enregistrée dans votre compte et utilisée comme langue par défaut après votre connexion. language.description = Cette langue sera enregistrée dans votre compte et utilisée comme langue par défaut après votre connexion.
user_block_yourself = Vous ne pouvez pas vous bloquer vous même. user_block_yourself = Vous ne pouvez pas vous bloquer vous même.
pronouns_custom_label = Pronoms personnalisés pronouns_custom_label = Pronoms personnalisés
change_username_redirect_prompt.with_cooldown.one = L'ancien pseudonyme sera disponible pour n'importe qui après une période d'%[1]d jour, vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période. change_username_redirect_prompt.with_cooldown.one = L'ancien pseudonyme sera disponible pour n'importe qui après une période d'%[1]d jour. Vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période.
change_username_redirect_prompt.with_cooldown.few = L'ancien pseudonyme sera disponible pour n'importe qui après une période de %[1]d jours, vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période. change_username_redirect_prompt.with_cooldown.few = L'ancien pseudonyme sera disponible pour n'importe qui après une période de %[1]d jours. Vous pouvez toujours réclamer votre ancien pseudonyme pendant cette période.
quota.rule.exceeded = Dépassé quota.rule.exceeded = Dépassé
regenerate_token = Régénérer regenerate_token = Régénérer
access_token_regeneration = Régénérer le token d'accès access_token_regeneration = Régénérer le token d'accès
@ -1653,13 +1653,13 @@ issues.close_comment_issue=Fermer avec le commentaire
issues.reopen_issue=Rouvrir issues.reopen_issue=Rouvrir
issues.reopen_comment_issue=Réouvrir avec le commentaire issues.reopen_comment_issue=Réouvrir avec le commentaire
issues.create_comment=Commenter issues.create_comment=Commenter
issues.closed_at=`a fermé ce ticket <a id="%[1]s" href="#%[1]s">%[2]s</a>.` issues.closed_at=`a fermé ce ticket %s`
issues.reopened_at=`a rouvert ce ticket <a id="%[1]s" href="#%[1]s">%[2]s</a>.` issues.reopened_at=`a rouvert ce ticket %s`
issues.commit_ref_at=`a référencé ce ticket depuis une révision <a id="%[1]s" href="#%[1]s"> %[2]s</a>.` issues.commit_ref_at=`a référencé ce ticket depuis une révision %s`
issues.ref_issue_from=`<a href="%[3]s">a fait référence à %[4]s</a> ce ticket <a id="%[1]s" href="#%[1]s">%[2]s</a>.` issues.ref_issue_from=`<a href="%[2]s">a fait référence à ce ticket %[3]s</a> %[1]s`
issues.ref_pull_from=`<a href="%[3]s">a fait référence</a> à cette demande d'ajout %[4]s <a id="%[1]s" href="#%[1]s">%[2]s</a>.` issues.ref_pull_from=`<a href="%[2]s">a fait référence à cette demande d'ajout %[3]s</a> %[1]s`
issues.ref_closing_from=`<a href="%[3]s">a fait référence</a> à une demande d'ajout %[4]s qui clora ce ticket, <a id="%[1]s" href="#%[1]s">%[2]s</a>.` issues.ref_closing_from=`<a href="%[2]s">a fait référence à une demande d'ajout %[3]s qui clora ce ticket</a>, %[1]s`
issues.ref_reopening_from=`<a href="%[3]s">a référencé une pull request %[4]s qui va ré-ouvrir ce ticket</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopening_from=`<a href="%[2]s">a référencé ce ticket dans une pull request %[3]s qui va ré-ouvrir ce ticket</a>, %[1]s`
issues.ref_closed_from=`<a href="%[3]s">a fermé ce ticket %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closed_from=`<a href="%[3]s">a fermé ce ticket %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">a rouvert</a> ce ticket %[4]s <a id="%[1]s" href="#%[1]s">%[2]s</a>.` issues.ref_reopened_from=`<a href="%[3]s">a rouvert</a> ce ticket %[4]s <a id="%[1]s" href="#%[1]s">%[2]s</a>.`
issues.ref_from=`de %[1]s` issues.ref_from=`de %[1]s`
@ -1967,8 +1967,8 @@ pulls.update_branch_success=La mise à jour de la branche a réussi
pulls.update_not_allowed=Vous n'êtes pas autorisé à mettre à jour la branche pulls.update_not_allowed=Vous n'êtes pas autorisé à mettre à jour la branche
pulls.outdated_with_base_branch=Cette branche est désynchronisée avec la branche de base pulls.outdated_with_base_branch=Cette branche est désynchronisée avec la branche de base
pulls.close=Fermer la demande dajout pulls.close=Fermer la demande dajout
pulls.closed_at=`a fermé cette demande d'ajout <a id="%[1]s" href="#%[1]s">%[2]s</a>.` pulls.closed_at=`a fermé cette demande d'ajout %s`
pulls.reopened_at=`a rouvert cette demande d'ajout <a id="%[1]s" href="#%[1]s">%[2]s</a>.` pulls.reopened_at=`a rouvert cette demande d'ajout %s`
pulls.cmd_instruction_hint=Voir les instructions en ligne de commande pulls.cmd_instruction_hint=Voir les instructions en ligne de commande
pulls.cmd_instruction_checkout_title=Basculer pulls.cmd_instruction_checkout_title=Basculer
pulls.cmd_instruction_checkout_desc=Depuis votre dépôt, basculer sur une nouvelle branche et tester des modifications. pulls.cmd_instruction_checkout_desc=Depuis votre dépôt, basculer sur une nouvelle branche et tester des modifications.
@ -2762,7 +2762,7 @@ issues.blocked_by_user = Vous ne pouvez pas créer de tickets sur ce dépôt car
pulls.blocked_by_user = Vous ne pouvez pas créer une pull request sur ce dépôt car vous êtes bloqué par son propriétaire. pulls.blocked_by_user = Vous ne pouvez pas créer une pull request sur ce dépôt car vous êtes bloqué par son propriétaire.
wiki.cancel = Annuler wiki.cancel = Annuler
settings.wiki_globally_editable = Permettre l'édition du wiki a tout le monde settings.wiki_globally_editable = Permettre l'édition du wiki a tout le monde
pulls.commit_ref_at = `a référencé cette pull request depuis le commit <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.commit_ref_at = `a référencé cette pull request depuis un commit %s`
settings.new_owner_blocked_doer = Le nouveau propriétaire vous a bloqué. settings.new_owner_blocked_doer = Le nouveau propriétaire vous a bloqué.
settings.enter_repo_name = Confirmez en entrant le propriétaire et le nom du dépôt exactement comme affiché : settings.enter_repo_name = Confirmez en entrant le propriétaire et le nom du dépôt exactement comme affiché :
settings.wiki_rename_branch_main = Normalise le nom de la branche du Wiki settings.wiki_rename_branch_main = Normalise le nom de la branche du Wiki
@ -3058,8 +3058,8 @@ teams.invite.by=Invité par %s
teams.invite.description=Veuillez cliquer sur le bouton ci-dessous pour rejoindre léquipe. teams.invite.description=Veuillez cliquer sur le bouton ci-dessous pour rejoindre léquipe.
follow_blocked_user = Vous ne pouvez pas suivre cette organisation car elle vous a bloqué. follow_blocked_user = Vous ne pouvez pas suivre cette organisation car elle vous a bloqué.
open_dashboard = Ouvrir le tableau de bord open_dashboard = Ouvrir le tableau de bord
settings.change_orgname_redirect_prompt.with_cooldown.few = L'ancien nom d'organisation sera disponible pour n'importe qui après une période de %[1]d jours, vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période. settings.change_orgname_redirect_prompt.with_cooldown.few = L'ancien nom d'organisation sera disponible pour n'importe qui après une période de %[1]d jours. Vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période.
settings.change_orgname_redirect_prompt.with_cooldown.one = L'ancien nom d'organisation sera disponible pour n'importe qui après une période d'%[1]d jour, vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période. settings.change_orgname_redirect_prompt.with_cooldown.one = L'ancien nom d'organisation sera disponible pour n'importe qui après une période d'%[1]d jour. Vous pouvez toujours réclamer votre ancien nom d'organisation pendant cette période.
[admin] [admin]
dashboard=Tableau de bord dashboard=Tableau de bord

View file

@ -1219,11 +1219,11 @@ issues.close_comment_issue = Dún le trácht
issues.reopen_issue = Athoscail issues.reopen_issue = Athoscail
issues.reopen_comment_issue = Athoscail le trácht issues.reopen_comment_issue = Athoscail le trácht
issues.create_comment = Trácht issues.create_comment = Trácht
issues.closed_at = `dhún an cheist seo <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.closed_at = `dhún an cheist seo %s`
issues.reopened_at = `athoscail an t-eagrán seo <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at = `athoscail an t-eagrán seo %s`
issues.commit_ref_at = `rinne tagairt don cheist seo ó ghealltanas <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at = `rinne tagairt don cheist seo ó ghealltanas %s`
issues.ref_issue_from = `<a href="%[3]s">rinne dagairt don cheist seo %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_issue_from = `<a href="%[2]s">rinne dagairt don cheist seo %[3]s</a> %[1]s`
issues.ref_pull_from = `<a href="%[3]s">rinne dagairt don iarratas tarraingthe seo %[4]s</a> <a id="%[1]s" href="#%[1]s">%[ 2]s</a>` issues.ref_pull_from = `<a href="%[2]s">rinne dagairt don iarratas tarraingthe seo %[3]s</a> %[1]s`
issues.ref_closed_from = `<a href="%[3]s">dhún an cheist seo %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closed_from = `<a href="%[3]s">dhún an cheist seo %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from = `<a href="%[3]s">d'athoscail an eagrán seo %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopened_from = `<a href="%[3]s">d'athoscail an eagrán seo %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_from = `ó %[1]s` issues.ref_from = `ó %[1]s`
@ -1456,8 +1456,8 @@ pulls.update_branch_success = Bhí nuashonrú brainse rathúil
pulls.update_not_allowed = Ní cheadaítear duit brainse a nuashonrú pulls.update_not_allowed = Ní cheadaítear duit brainse a nuashonrú
pulls.outdated_with_base_branch = Tá an brainse seo as dáta leis an mbunbhrainse pulls.outdated_with_base_branch = Tá an brainse seo as dáta leis an mbunbhrainse
pulls.close = Dún Iarratas Tarraing pulls.close = Dún Iarratas Tarraing
pulls.closed_at = `dhún an t-iarratas tarraingthe seo <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.closed_at = `dhún an t-iarratas tarraingthe seo %s`
pulls.reopened_at = `athoscail an t-iarratas tarraingthe seo <a id="%[1]s" href="#%[1]s">%[2]s</a>` pulls.reopened_at = `athoscail an t-iarratas tarraingthe seo %s`
pulls.cmd_instruction_checkout_title = Seiceáil pulls.cmd_instruction_checkout_title = Seiceáil
pulls.cmd_instruction_checkout_desc = Ó stór tionscadail, seiceáil brainse nua agus déan tástáil ar na hathruithe. pulls.cmd_instruction_checkout_desc = Ó stór tionscadail, seiceáil brainse nua agus déan tástáil ar na hathruithe.
pulls.cmd_instruction_merge_title = Cumaisc pulls.cmd_instruction_merge_title = Cumaisc

View file

@ -932,7 +932,7 @@ issues.close_comment_issue=Hozzászólás és lezárás
issues.reopen_issue=Újranyitás issues.reopen_issue=Újranyitás
issues.reopen_comment_issue=Hozzászólás és újranyitás issues.reopen_comment_issue=Hozzászólás és újranyitás
issues.create_comment=Hozzászólás issues.create_comment=Hozzászólás
issues.commit_ref_at=`hivatkozott erre a hibajegyre egy commit-ból <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`hivatkozott erre a hibajegyre egy commit-ból %s`
issues.role.owner=Tulajdonos issues.role.owner=Tulajdonos
issues.role.member=Tag issues.role.member=Tag
issues.re_request_review=Véleményezés újrakérése issues.re_request_review=Véleményezés újrakérése

View file

@ -796,7 +796,7 @@ issues.close_comment_issue=Komentar dan Tutup
issues.reopen_issue=Buka kembali issues.reopen_issue=Buka kembali
issues.reopen_comment_issue=Komentar dan Buka Kembali issues.reopen_comment_issue=Komentar dan Buka Kembali
issues.create_comment=Komentar issues.create_comment=Komentar
issues.commit_ref_at=`merujuk masalah dari komit <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`merujuk masalah dari komit %s`
issues.role.owner=Pemilik issues.role.owner=Pemilik
issues.role.member=Anggota issues.role.member=Anggota
issues.sign_in_require_desc=<a href="%s">Masuk</a> untuk bergabung dengan percakapan ini. issues.sign_in_require_desc=<a href="%s">Masuk</a> untuk bergabung dengan percakapan ini.

View file

@ -805,8 +805,8 @@ issues.close_comment_issue=Senda ummæli og Loka
issues.reopen_issue=Enduropna issues.reopen_issue=Enduropna
issues.reopen_comment_issue=Senda ummæli og Enduropna issues.reopen_comment_issue=Senda ummæli og Enduropna
issues.create_comment=Senda Ummæli issues.create_comment=Senda Ummæli
issues.closed_at=`lokaði þessu vandamáli <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.closed_at=`lokaði þessu vandamáli %s`
issues.reopened_at=`enduropnaði þetta vandamál <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.reopened_at=`enduropnaði þetta vandamál %s`
issues.ref_reopened_from=`<a href="%[3]s">enduropnaði þetta vandamál %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopened_from=`<a href="%[3]s">enduropnaði þetta vandamál %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.author=Höfundur issues.author=Höfundur
issues.role.owner=Eigandi issues.role.owner=Eigandi

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