1
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-01-02 01:17:43 +03:00

Merge branch 'main' into lunny/use_db_getbranch

This commit is contained in:
Lunny Xiao 2024-12-29 12:01:21 -08:00
commit f8c5ae43a3
33 changed files with 488 additions and 164 deletions

View File

@ -64,11 +64,9 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
for _, tc := range testCases {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.userID})
doer := &user_model.User{ID: tc.doerID}
_, err := unittest.LoadBeanIfExists(doer)
assert.NoError(t, err)
if tc.doerID == 0 {
doer = nil
var doer *user_model.User
if tc.doerID != 0 {
doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.doerID})
}
// get the action for comparison

View File

@ -44,7 +44,7 @@ func TestWebAuthnCredential_UpdateSignCount(t *testing.T) {
cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1})
cred.SignCount = 1
assert.NoError(t, cred.UpdateSignCount(db.DefaultContext))
unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{ID: 1, SignCount: 1})
unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1, SignCount: 1})
}
func TestWebAuthnCredential_UpdateLargeCounter(t *testing.T) {
@ -52,7 +52,7 @@ func TestWebAuthnCredential_UpdateLargeCounter(t *testing.T) {
cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1})
cred.SignCount = 0xffffffff
assert.NoError(t, cred.UpdateSignCount(db.DefaultContext))
unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{ID: 1, SignCount: 0xffffffff})
unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1, SignCount: 0xffffffff})
}
func TestCreateCredential(t *testing.T) {
@ -63,5 +63,5 @@ func TestCreateCredential(t *testing.T) {
assert.Equal(t, "WebAuthn Created Credential", res.Name)
assert.Equal(t, []byte("Test"), res.CredentialID)
unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
}

View File

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_NewIssueUsers(t *testing.T) {
@ -27,9 +28,8 @@ func Test_NewIssueUsers(t *testing.T) {
}
// artificially insert new issue
unittest.AssertSuccessfulInsert(t, newIssue)
assert.NoError(t, issues_model.NewIssueUsers(db.DefaultContext, repo, newIssue))
require.NoError(t, db.Insert(db.DefaultContext, newIssue))
require.NoError(t, issues_model.NewIssueUsers(db.DefaultContext, repo, newIssue))
// issue_user table should now have entries for new issue
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: newIssue.ID, UID: newIssue.PosterID})

View File

@ -387,7 +387,7 @@ func TestDeleteIssueLabel(t *testing.T) {
expectedNumIssues := label.NumIssues
expectedNumClosedIssues := label.NumClosedIssues
if unittest.BeanExists(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID}) {
if unittest.GetBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID}) != nil {
expectedNumIssues--
if issue.IsClosed {
expectedNumClosedIssues--

View File

@ -131,7 +131,7 @@ func TestAddOrgUser(t *testing.T) {
testSuccess := func(orgID, userID int64, isPublic bool) {
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID})
expectedNumMembers := org.NumMembers
if !unittest.BeanExists(t, &organization.OrgUser{OrgID: orgID, UID: userID}) {
if unittest.GetBean(t, &organization.OrgUser{OrgID: orgID, UID: userID}) == nil {
expectedNumMembers++
}
assert.NoError(t, organization.AddOrgUser(db.DefaultContext, orgID, userID))

View File

@ -1,12 +1,10 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//nolint:forbidigo
package unittest
import (
"fmt"
"os"
"time"
"code.gitea.io/gitea/models/db"
@ -37,7 +35,7 @@ func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) {
} else {
fixtureOptionFiles = testfixtures.Files(opts.Files...)
}
dialect := "unknown"
var dialect string
switch e.Dialect().URI().DBType {
case schemas.POSTGRES:
dialect = "postgres"
@ -48,8 +46,7 @@ func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) {
case schemas.SQLITE:
dialect = "sqlite3"
default:
fmt.Println("Unsupported RDBMS for integration tests")
os.Exit(1)
return fmt.Errorf("unsupported RDBMS for integration tests: %q", e.Dialect().URI().DBType)
}
loaderOptions := []func(loader *testfixtures.Loader) error{
testfixtures.Database(e.DB().DB),
@ -69,9 +66,7 @@ func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) {
// register the dummy hash algorithm function used in the test fixtures
_ = hash.Register("dummy", hash.NewDummyHasher)
setting.PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
return err
}
@ -87,7 +82,7 @@ func LoadFixtures(engine ...*xorm.Engine) error {
time.Sleep(200 * time.Millisecond)
}
if err != nil {
fmt.Printf("LoadFixtures failed after retries: %v\n", err)
return fmt.Errorf("LoadFixtures failed after retries: %w", err)
}
// Now if we're running postgres we need to tell it to update the sequences
if e.Dialect().URI().DBType == schemas.POSTGRES {
@ -108,21 +103,18 @@ func LoadFixtures(engine ...*xorm.Engine) error {
AND T.relname = PGT.tablename
ORDER BY S.relname;`)
if err != nil {
fmt.Printf("Failed to generate sequence update: %v\n", err)
return err
return fmt.Errorf("failed to generate sequence update: %w", err)
}
for _, r := range results {
for _, value := range r {
_, err = e.Exec(value)
if err != nil {
fmt.Printf("Failed to update sequence: %s Error: %v\n", value, err)
return err
return fmt.Errorf("failed to update sequence: %s, error: %w", value, err)
}
}
}
}
_ = hash.Register("dummy", hash.NewDummyHasher)
setting.PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
return err
return nil
}

View File

@ -4,7 +4,7 @@
package unittest
import (
"log"
"fmt"
"reflect"
)
@ -14,7 +14,7 @@ func fieldByName(v reflect.Value, field string) reflect.Value {
}
f := v.FieldByName(field)
if !f.IsValid() {
log.Panicf("can not read %s for %v", field, v)
panic(fmt.Errorf("can not read %s for %v", field, v))
}
return f
}

View File

@ -34,11 +34,6 @@ var (
fixturesDir string
)
// FixturesDir returns the fixture directory
func FixturesDir() string {
return fixturesDir
}
func fatalTestError(fmtStr string, args ...any) {
_, _ = fmt.Fprintf(os.Stderr, fmtStr, args...)
os.Exit(1)

View File

@ -4,13 +4,17 @@
package unittest
import (
"fmt"
"math"
"os"
"strings"
"code.gitea.io/gitea/models/db"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"xorm.io/builder"
"xorm.io/xorm"
)
// Code in this file is mainly used by unittest.CheckConsistencyFor, which is not in the unit test for various reasons.
@ -51,22 +55,23 @@ func whereOrderConditions(e db.Engine, conditions []any) db.Engine {
return e.OrderBy(orderBy)
}
// LoadBeanIfExists loads beans from fixture database if exist
func LoadBeanIfExists(bean any, conditions ...any) (bool, error) {
func getBeanIfExists(bean any, conditions ...any) (bool, error) {
e := db.GetEngine(db.DefaultContext)
return whereOrderConditions(e, conditions).Get(bean)
}
// BeanExists for testing, check if a bean exists
func BeanExists(t assert.TestingT, bean any, conditions ...any) bool {
exists, err := LoadBeanIfExists(bean, conditions...)
assert.NoError(t, err)
return exists
func GetBean[T any](t require.TestingT, bean T, conditions ...any) (ret T) {
exists, err := getBeanIfExists(bean, conditions...)
require.NoError(t, err)
if exists {
return bean
}
return ret
}
// AssertExistsAndLoadBean assert that a bean exists and load it from the test database
func AssertExistsAndLoadBean[T any](t require.TestingT, bean T, conditions ...any) T {
exists, err := LoadBeanIfExists(bean, conditions...)
exists, err := getBeanIfExists(bean, conditions...)
require.NoError(t, err)
require.True(t, exists,
"Expected to find %+v (of type %T, with conditions %+v), but did not",
@ -112,25 +117,11 @@ func GetCount(t assert.TestingT, bean any, conditions ...any) int {
// AssertNotExistsBean assert that a bean does not exist in the test database
func AssertNotExistsBean(t assert.TestingT, bean any, conditions ...any) {
exists, err := LoadBeanIfExists(bean, conditions...)
exists, err := getBeanIfExists(bean, conditions...)
assert.NoError(t, err)
assert.False(t, exists)
}
// AssertExistsIf asserts that a bean exists or does not exist, depending on
// what is expected.
func AssertExistsIf(t assert.TestingT, expected bool, bean any, conditions ...any) {
exists, err := LoadBeanIfExists(bean, conditions...)
assert.NoError(t, err)
assert.Equal(t, expected, exists)
}
// AssertSuccessfulInsert assert that beans is successfully inserted
func AssertSuccessfulInsert(t assert.TestingT, beans ...any) {
err := db.Insert(db.DefaultContext, beans...)
assert.NoError(t, err)
}
// AssertCount assert the count of a bean
func AssertCount(t assert.TestingT, bean, expected any) bool {
return assert.EqualValues(t, expected, GetCount(t, bean))
@ -155,3 +146,39 @@ func AssertCountByCond(t assert.TestingT, tableName string, cond builder.Cond, e
return assert.EqualValues(t, expected, GetCountByCond(t, tableName, cond),
"Failed consistency test, the counted bean (of table %s) was %+v", tableName, cond)
}
// DumpQueryResult dumps the result of a query for debugging purpose
func DumpQueryResult(t require.TestingT, sqlOrBean any, sqlArgs ...any) {
x := db.GetEngine(db.DefaultContext).(*xorm.Engine)
goDB := x.DB().DB
sql, ok := sqlOrBean.(string)
if !ok {
sql = fmt.Sprintf("SELECT * FROM %s", db.TableName(sqlOrBean))
} else if !strings.Contains(sql, " ") {
sql = fmt.Sprintf("SELECT * FROM %s", sql)
}
rows, err := goDB.Query(sql, sqlArgs...)
require.NoError(t, err)
defer rows.Close()
columns, err := rows.Columns()
require.NoError(t, err)
_, _ = fmt.Fprintf(os.Stdout, "====== DumpQueryResult: %s ======\n", sql)
idx := 0
for rows.Next() {
row := make([]any, len(columns))
rowPointers := make([]any, len(columns))
for i := range row {
rowPointers[i] = &row[i]
}
require.NoError(t, rows.Scan(rowPointers...))
_, _ = fmt.Fprintf(os.Stdout, "- # row[%d]\n", idx)
for i, col := range columns {
_, _ = fmt.Fprintf(os.Stdout, " %s: %v\n", col, row[i])
}
idx++
}
if idx == 0 {
_, _ = fmt.Fprintf(os.Stdout, "(no result, columns: %s)\n", strings.Join(columns, ", "))
}
}

View File

@ -93,6 +93,7 @@ remove_all=Alle entfernen
remove_label_str=Element "%s " entfernen
edit=Bearbeiten
view=Anzeigen
test=Test
enabled=Aktiviert
disabled=Deaktiviert
@ -103,6 +104,7 @@ copy_url=URL kopieren
copy_hash=Hash kopieren
copy_content=Inhalt kopieren
copy_branch=Branchnamen kopieren
copy_path=Pfad kopieren
copy_success=Kopiert!
copy_error=Kopieren fehlgeschlagen
copy_type_unsupported=Dieser Dateityp kann nicht kopiert werden
@ -143,6 +145,7 @@ confirm_delete_selected=Alle ausgewählten Elemente löschen?
name=Name
value=Wert
readme=Readme
filter=Filter
filter.clear=Filter leeren
@ -158,12 +161,15 @@ filter.public=Öffentlich
filter.private=Privat
no_results_found=Es wurden keine Ergebnisse gefunden.
internal_error_skipped=Ein interner Fehler ist aufgetreten, wurde aber übersprungen: %s
[search]
search=Suche ...
type_tooltip=Suchmodus
fuzzy=Ähnlich
fuzzy_tooltip=Ergebnisse einbeziehen, die dem Suchbegriff ähnlich sind
exact=Exakt
exact_tooltip=Nur Suchbegriffe einbeziehen, die dem exakten Suchbegriff entsprechen
repo_kind=Repositories durchsuchen ...
user_kind=Benutzer durchsuchen ...
org_kind=Organisationen durchsuchen ...
@ -174,9 +180,13 @@ code_search_by_git_grep=Aktuelle Code-Suchergebnisse werden von "git grep" berei
package_kind=Pakete durchsuchen ...
project_kind=Projekte durchsuchen ...
branch_kind=Branches durchsuchen ...
tag_kind=Tags durchsuchen...
tag_tooltip=Suche nach passenden Tags. Benutze '%', um jede Sequenz von Zahlen zu treffen.
commit_kind=Commits durchsuchen ...
runner_kind=Runner durchsuchen ...
no_results=Es wurden keine passenden Ergebnisse gefunden.
issue_kind=Issues durchsuchen ...
pull_kind=Pull-Requests durchsuchen...
keyword_search_unavailable=Zurzeit ist die Stichwort-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator.
[aria]
@ -201,7 +211,10 @@ buttons.link.tooltip=Link hinzufügen
buttons.list.unordered.tooltip=Liste hinzufügen
buttons.list.ordered.tooltip=Nummerierte Liste hinzufügen
buttons.list.task.tooltip=Aufgabenliste hinzufügen
buttons.table.add.tooltip=Tabelle hinzufügen
buttons.table.add.insert=Hinzufügen
buttons.table.rows=Zeilen
buttons.table.cols=Spalten
buttons.mention.tooltip=Benutzer oder Team erwähnen
buttons.ref.tooltip=Issue oder Pull-Request referenzieren
buttons.switch_to_legacy.tooltip=Legacy-Editor verwenden
@ -214,16 +227,20 @@ string.desc=ZA
[error]
occurred=Ein Fehler ist aufgetreten
report_message=Wenn du glaubst, dass dies ein Fehler von Gitea ist, suche bitte auf <a href="%s" target="_blank">GitHub</a> nach diesem Fehler und erstelle gegebenenfalls einen neuen Bugreport.
not_found=Das Ziel konnte nicht gefunden werden.
network_error=Netzwerkfehler
[startpage]
app_desc=Ein einfacher, selbst gehosteter Git-Service
install=Einfach zu installieren
install_desc=Starte einfach <a target="_blank" rel="noopener noreferrer" href="%[1]s">die Anwendung</a> für deine Plattform oder nutze <a target="_blank" rel="noopener noreferrer" href="%[2]s">Docker</a>. Es existieren auch <a target="_blank" rel="noopener noreferrer" href="%[3]s">paketierte Versionen</a>.
platform=Plattformübergreifend
platform_desc=Gitea läuft überall, wo <a target="_blank" rel="noopener noreferrer" href="%s">Go</a> kompiliert: Windows, macOS, Linux, ARM, etc. Wähle das System, das dir am meisten gefällt!
lightweight=Leichtgewicht
lightweight_desc=Gitea hat minimale Systemanforderungen und kann selbst auf einem günstigen und stromsparenden Raspberry Pi betrieben werden!
license=Quelloffen
license_desc=Hol dir den Code unter <a target="_blank" rel="noopener noreferrer" href="%[1]s">%[2]s</a>! Leiste deinen <a target="_blank" rel="noopener noreferrer" href="%[3]s">Beitrag</a> bei der Verbesserung dieses Projekts. Trau dich!
[install]
install=Installation
@ -337,6 +354,7 @@ enable_update_checker=Aktualisierungsprüfung aktivieren
enable_update_checker_helper=Stellt regelmäßig eine Verbindung zu gitea.io her, um nach neuen Versionen zu prüfen.
env_config_keys=Umgebungskonfiguration
env_config_keys_prompt=Die folgenden Umgebungsvariablen werden auch auf Ihre Konfigurationsdatei angewendet:
config_write_file_prompt=Diese Konfigurationsoptionen werden in %s geschrieben
[home]
nav_menu=Navigationsmenü
@ -377,6 +395,8 @@ relevant_repositories=Es werden nur relevante Repositories angezeigt, <a href="%
[auth]
create_new_account=Konto anlegen
already_have_account=Du hast bereits ein Konto?
sign_in_now=Jetzt anmelden!
disable_register_prompt=Die Registrierung ist deaktiviert. Bitte wende dich an den Administrator.
disable_register_mail=E-Mail-Bestätigung bei der Registrierung ist deaktiviert.
manual_activation_only=Kontaktiere den Website-Administrator, um die Aktivierung abzuschließen.
@ -384,6 +404,8 @@ remember_me=Dieses Gerät speichern
remember_me.compromised=Das Login-Token ist nicht mehr gültig, was auf ein kompromittiertes Konto hindeuten kann. Bitte überprüfe dein Konto auf ungewöhnliche Aktivitäten.
forgot_password_title=Passwort vergessen
forgot_password=Passwort vergessen?
need_account=Noch kein Konto?
sign_up_now=Jetzt registrieren.
sign_up_successful=Konto wurde erfolgreich erstellt. Willkommen!
confirmation_mail_sent_prompt_ex=Eine neue Bestätigungs-E-Mail wurde an <b>%s</b>gesendet. Bitte überprüfe deinen Posteingang innerhalb der nächsten %s, um den Registrierungsprozess abzuschließen. Wenn deine Registrierungs-E-Mail-Adresse falsch ist, kannst du dich erneut anmelden und diese ändern.
must_change_password=Aktualisiere dein Passwort
@ -424,6 +446,7 @@ oauth_signin_submit=Konto verbinden
oauth.signin.error=Beim Verarbeiten der Autorisierungsanfrage ist ein Fehler aufgetreten. Wenn dieser Fehler weiterhin besteht, wende dich bitte an deinen Administrator.
oauth.signin.error.access_denied=Die Autorisierungsanfrage wurde abgelehnt.
oauth.signin.error.temporarily_unavailable=Autorisierung fehlgeschlagen, da der Authentifizierungsserver vorübergehend nicht verfügbar ist. Bitte versuch es später erneut.
oauth_callback_unable_auto_reg=Automatische Registrierung ist aktiviert, aber der OAuth2-Provider %[1]s hat fehlende Felder zurückgegeben: %[2]s, kann den Account nicht automatisch erstellen. Bitte erstelle oder verbinde einen Account oder kontaktieren den Administrator.
openid_connect_submit=Verbinden
openid_connect_title=Mit bestehendem Konto verbinden
openid_connect_desc=Die gewählte OpenID-URI ist unbekannt. Ordne sie hier einem neuen Account zu.
@ -437,12 +460,16 @@ authorize_application=Anwendung autorisieren
authorize_redirect_notice=Du wirst zu %s weitergeleitet, wenn du diese Anwendung autorisierst.
authorize_application_created_by=Diese Anwendung wurde von %s erstellt.
authorize_application_description=Wenn du diese Anwendung autorisierst, wird sie die Berechtigung erhalten, alle Informationen zu deinem Account zu bearbeiten oder zu lesen. Dies beinhaltet auch private Repositories und Organisationen.
authorize_application_with_scopes=Mit Bereichen: %s
authorize_title=`"%s" den Zugriff auf deinen Account gestatten?`
authorization_failed=Autorisierung fehlgeschlagen
authorization_failed_desc=Die Autorisierung ist fehlgeschlagen, da wir eine ungültige Anfrage erkannt haben. Bitte kontaktiere den Betreuer der App, die du zu autorisieren versucht hast.
sspi_auth_failed=SSPI-Authentifizierung fehlgeschlagen
password_pwned=Das von dir gewählte Passwort befindet sich auf einer <a target="_blank" rel="noopener noreferrer" href="%s">Liste gestohlener Passwörter</a>, die öffentlich verfügbar sind. Bitte versuche es erneut mit einem anderen Passwort und ziehe in Erwägung, auch anderswo deine Passwörter zu ändern.
password_pwned_err=Anfrage an HaveIBeenPwned konnte nicht abgeschlossen werden
last_admin=Du kannst den letzten Admin nicht entfernen. Es muss mindestens einen Administrator geben.
signin_passkey=Mit einem Passkey anmelden
back_to_sign_in=Zurück zum Anmelden
[mail]
view_it_on=Auf %s ansehen
@ -459,6 +486,7 @@ activate_email=Bestätige deine E-Mail-Adresse
activate_email.title=%s, bitte verifiziere deine E-Mail-Adresse
activate_email.text=Bitte klicke innerhalb von <b>%s</b> auf folgenden Link, um dein Konto zu aktivieren:
register_notify=Willkommen bei %s
register_notify.title=%[1]s, willkommen bei %[2]s
register_notify.text_1=dies ist deine Bestätigungs-E-Mail für %s!
register_notify.text_2=Du kannst dich jetzt mit dem Benutzernamen "%s" anmelden.
@ -560,6 +588,8 @@ lang_select_error=Wähle eine Sprache aus der Liste aus.
username_been_taken=Der Benutzername ist bereits vergeben.
username_change_not_local_user=Nicht-lokale Benutzer dürfen ihren Nutzernamen nicht ändern.
change_username_disabled=Ändern des Benutzernamens ist deaktiviert.
change_full_name_disabled=Ändern des vollständigen Namens ist deaktiviert.
username_has_not_been_changed=Benutzername wurde nicht geändert
repo_name_been_taken=Der Repository-Name wird schon verwendet.
repository_force_private=Privat erzwingen ist aktiviert: Private Repositories können nicht veröffentlicht werden.
@ -609,6 +639,7 @@ org_still_own_repo=Diese Organisation besitzt noch ein oder mehrere Repositories
org_still_own_packages=Diese Organisation besitzt noch ein oder mehrere Pakete, lösche diese zuerst.
target_branch_not_exist=Der Ziel-Branch existiert nicht.
target_ref_not_exist=Zielreferenz existiert nicht %s
admin_cannot_delete_self=Du kannst dich nicht selbst löschen, wenn du ein Administrator bist. Bitte entferne zuerst deine Administratorrechte.
@ -685,6 +716,8 @@ public_profile=Öffentliches Profil
biography_placeholder=Erzähle uns ein wenig über Dich selbst! (Du kannst Markdown verwenden)
location_placeholder=Teile Deinen ungefähren Standort mit anderen
profile_desc=Lege fest, wie dein Profil anderen Benutzern angezeigt wird. Deine primäre E-Mail-Adresse wird für Benachrichtigungen, Passwort-Wiederherstellung und webbasierte Git-Operationen verwendet.
password_username_disabled=Du bist nicht berechtigt, den Benutzernamen zu ändern. Bitte kontaktiere Deinen Seitenadministrator für weitere Details.
password_full_name_disabled=Du bist nicht berechtigt, den vollständigen Namen zu ändern. Bitte kontaktiere Deinen Seitenadministrator für weitere Details.
full_name=Vollständiger Name
website=Webseite
location=Standort
@ -702,6 +735,7 @@ cancel=Abbrechen
language=Sprache
ui=Theme
hidden_comment_types=Ausgeblendeter Kommentartypen
hidden_comment_types_description=Die hier markierten Kommentartypen werden nicht innerhalb der Issue-Seiten angezeigt. Beispielsweise entfernt das Markieren von "Label" alle "{user} hat {label} hinzugefügt/entfernt"-Kommentare.
hidden_comment_types.ref_tooltip=Kommentare, in denen dieses Issue von einem anderen Issue/Commit referenziert wurde
hidden_comment_types.issue_ref_tooltip=Kommentare, bei denen der Benutzer den Branch/Tag des Issues ändert
comment_type_group_reference=Verweis auf Mitglieder
@ -733,6 +767,7 @@ uploaded_avatar_not_a_image=Die hochgeladene Datei ist kein Bild.
uploaded_avatar_is_too_big=Die hochgeladene Dateigröße (%d KiB) überschreitet die maximale Größe (%d KiB).
update_avatar_success=Dein Profilbild wurde geändert.
update_user_avatar_success=Der Avatar des Benutzers wurde aktualisiert.
cropper_prompt=Sie können das Bild vor dem Speichern bearbeiten. Das bearbeitete Bild wird als PNG-Datei gespeichert.
change_password=Passwort aktualisieren
old_password=Aktuelles Passwort
@ -748,6 +783,8 @@ manage_themes=Standard-Theme auswählen
manage_openid=OpenID-Adressen verwalten
email_desc=Deine primäre E-Mail-Adresse wird für Benachrichtigungen, Passwort-Wiederherstellung und, sofern sie nicht versteckt ist, web-basierte Git-Operationen verwendet.
theme_desc=Dies wird dein Standard-Theme auf der Seite sein.
theme_colorblindness_help=Hilfe zum Theme für Farbenblinde
theme_colorblindness_prompt=Gitea erhält aktuell einfache Unterstützung für Farbenblinde durch einige Themes, die nur wenige Farben definiert haben. Die Arbeit ist noch im Gange. Weitere Verbesserungen können durch die Definition von mehr Farben in den CSS-Theme-Dateien vorgenommen werden.
primary=Primär
activated=Aktiviert
requires_activation=Erfordert Aktivierung
@ -872,8 +909,9 @@ repo_and_org_access=Repository- und Organisationszugriff
permissions_public_only=Nur öffentlich
permissions_access_all=Alle (öffentlich, privat und begrenzt)
select_permissions=Berechtigungen auswählen
permission_not_set=Nicht festgelegt
permission_no_access=Kein Zugriff
permission_read=Gelesen
permission_read=Lesen
permission_write=Lesen und Schreiben
access_token_desc=Ausgewählte Token-Berechtigungen beschränken die Authentifizierung auf die entsprechenden <a %s>API</a>-Routen. Lies die <a %s>Dokumentation</a> für mehr Informationen.
at_least_one_permission=Du musst mindestens eine Berechtigung auswählen, um ein Token zu erstellen
@ -891,6 +929,7 @@ create_oauth2_application_success=Du hast erfolgreich eine neue OAuth2-Anwendung
update_oauth2_application_success=Du hast die OAuth2-Anwendung erfolgreich aktualisiert.
oauth2_application_name=Name der Anwendung
oauth2_confidential_client=Vertraulicher Client. Für Anwendungen aktivieren, die das Geheimnis sicher speichern, z. B. Webanwendungen. Wähle diese Option nicht für native Anwendungen für PCs und Mobilgeräte.
oauth2_skip_secondary_authorization=Autorisierung für öffentliche Clients nach einmaliger Gewährung des Zugriffs überspringen. <strong>Dies kann ein Sicherheitsrisiko darstellen.</strong>
oauth2_redirect_uris=URIs für die Weiterleitung. Bitte verwende eine neue Zeile für jede URI.
save_application=Speichern
oauth2_client_id=Client-ID
@ -901,6 +940,7 @@ oauth2_client_secret_hint=Das Secret wird nach dem Verlassen oder Aktualisieren
oauth2_application_edit=Bearbeiten
oauth2_application_create_description=OAuth2 Anwendungen geben deiner Drittanwendung Zugriff auf Benutzeraccounts dieser Gitea-Instanz.
oauth2_application_remove_description=Das Entfernen einer OAuth2-Anwendung hat zur Folge, dass diese nicht mehr auf autorisierte Benutzeraccounts auf dieser Instanz zugreifen kann. Möchtest Du fortfahren?
oauth2_application_locked=Wenn es in der Konfiguration aktiviert ist, registriert Gitea einige OAuth2-Anwendungen beim Starten vor. Um unerwartetes Verhalten zu verhindern, können diese weder bearbeitet noch entfernt werden. Weitere Informationen findest Du in der OAuth2-Dokumentation.
authorized_oauth2_applications=Autorisierte OAuth2-Anwendungen
authorized_oauth2_applications_description=Den folgenden Drittanbieter-Apps hast Du Zugriff auf Deinen persönlichen Gitea-Account gewährt. Bitte widerrufe die Autorisierung für Apps, die Du nicht mehr nutzt.
@ -909,20 +949,26 @@ revoke_oauth2_grant=Autorisierung widerrufen
revoke_oauth2_grant_description=Wenn du die Autorisierung widerrufst, kann die Anwendung nicht mehr auf deine Daten zugreifen. Bist du dir sicher?
revoke_oauth2_grant_success=Zugriff erfolgreich widerrufen.
twofa_desc=Um dein Konto vor Passwortdiebstahl zu schützen, kannst du ein Smartphone oder ein anderes Gerät verwenden, um zeitbasierte Einmalpasswörter ("TOTP") zu erhalten.
twofa_recovery_tip=Wenn du dein Gerät verlierst, kannst du einen einmalig verwendbaren Wiederherstellungsschlüssel nutzen, um den Zugriff auf dein Konto wiederherzustellen.
twofa_is_enrolled=Für dein Konto ist die Zwei-Faktor-Authentifizierung <strong>eingeschaltet</strong>.
twofa_not_enrolled=Für dein Konto ist die Zwei-Faktor-Authentifizierung momentan nicht eingeschaltet.
twofa_disable=Zwei-Faktor-Authentifizierung deaktivieren
twofa_scratch_token_regenerate=Einweg-Wiederherstellungsschlüssel neu generieren
twofa_scratch_token_regenerated=Dein Einweg-Wiederherstellungsschlüssel ist jetzt %s. Speichere ihn an einem sicheren Ort, er wird nie wieder angezeigt.
twofa_enroll=Zwei-Faktor-Authentifizierung aktivieren
twofa_disable_note=Du kannst die Zwei-Faktor-Authentifizierung auch wieder deaktivieren.
twofa_disable_desc=Wenn du die Zwei-Faktor-Authentifizierung deaktivierst, wird die Sicherheit deines Kontos verringert. Fortfahren?
regenerate_scratch_token_desc=Wenn du deinen Wiederherstellungsschlüssel verlegt oder bereits benutzt hast, kannst du ihn hier zurücksetzen.
twofa_disabled=Zwei-Faktor-Authentifizierung wurde deaktiviert.
scan_this_image=Scanne diese Grafik mit deiner Authentifizierungs-App:
or_enter_secret=Oder gib das Secret ein: %s
then_enter_passcode=Und gebe dann die angezeigte PIN der Anwendung ein:
passcode_invalid=Die PIN ist falsch. Probiere es erneut.
twofa_enrolled=Die Zwei-Faktor-Authentifizierung wurde für dein Konto aktiviert. Bewahre deinen Einweg-Wiederherstellungsschlüssel (%s) an einem sicheren Ort auf, da er nicht wieder angezeigt werden wird.
twofa_failed_get_secret=Fehler beim Abrufen des Secrets.
webauthn_desc=Sicherheitsschlüssel sind Geräte, die kryptografische Schlüssel beeinhalten. Diese können für die Zwei-Faktor-Authentifizierung verwendet werden. Der Sicherheitsschlüssel muss den Standard "<a rel="noreferrer" target="_blank" href="%s">WebAuthn</a>" unterstützen.
webauthn_register_key=Sicherheitsschlüssel hinzufügen
webauthn_nickname=Nickname
webauthn_delete_key=Sicherheitsschlüssel entfernen
@ -988,6 +1034,8 @@ fork_to_different_account=Fork in ein anderes Konto erstellen
fork_visibility_helper=Die Sichtbarkeit eines geforkten Repositories kann nicht geändert werden.
fork_branch=Branch, der zum Fork geklont werden soll
all_branches=Alle Branches
view_all_branches=Alle Branches anzeigen
view_all_tags=Alle Tags anzeigen
fork_no_valid_owners=Dieses Repository kann nicht geforkt werden, da keine gültigen Besitzer vorhanden sind.
fork.blocked_user=Das Repository kann nicht geforkt werden, da du vom Repository-Eigentümer blockiert wurdest.
use_template=Dieses Template verwenden
@ -999,6 +1047,8 @@ generate_repo=Repository erstellen
generate_from=Erstelle aus
repo_desc=Beschreibung
repo_desc_helper=Gib eine kurze Beschreibung an (optional)
repo_no_desc=Keine Beschreibung vorhanden
repo_lang=Sprachen
repo_gitignore_helper=Wähle eine .gitignore-Vorlage aus.
repo_gitignore_helper_desc=Wähle aus einer Liste an Vorlagen für bekannte Sprachen, welche Dateien ignoriert werden sollen. Typische Artefakte, die durch die Build Tools der gewählten Sprache generiert werden, sind standardmäßig Bestandteil der .gitignore.
issue_labels=Issue Label
@ -1006,6 +1056,7 @@ issue_labels_helper=Wähle ein Issue-Label-Set.
license=Lizenz
license_helper=Wähle eine Lizenz aus.
license_helper_desc=Eine Lizenz regelt, was Andere mit deinem Code (nicht) tun können. Unsicher, welches für dein Projekt die Richtige ist? Siehe <a target="_blank" rel="noopener noreferrer" href="%s">eine Lizenz wählen</a>.
multiple_licenses=Mehrere Lizenzen
object_format=Objektformat
object_format_helper=Objektformat des Repositories. Es kann später nicht geändert werden. SHA1 ist am meisten kompatibel.
readme=README
@ -1059,13 +1110,16 @@ delete_preexisting_success=Nicht übernommene Dateien in %s gelöscht
blame_prior=Blame vor dieser Änderung anzeigen
blame.ignore_revs=Revisionen in <a href="%s">.git-blame-ignore-revs</a> werden ignoriert. Klicke <a href="%s">hier, um das zu umgehen</a> und die normale Blame-Ansicht zu sehen.
blame.ignore_revs.failed=Fehler beim Ignorieren der Revisionen in <a href="%s">.git-blame-ignore-revs</a>.
user_search_tooltip=Zeigt maximal 30 Benutzer
tree_path_not_found_commit=Pfad %[1]s existiert nicht in Commit%[2]s
tree_path_not_found_branch=Pfad %[1]s existiert nicht in Branch %[2]s
tree_path_not_found_tag=Pfad %[1]s existiert nicht in Tag %[2]s
transfer.accept=Übertragung Akzeptieren
transfer.accept_desc=`Übertragung nach "%s"`
transfer.reject=Übertragung Ablehnen
transfer.reject_desc=Übertragung nach "%s " abbrechen
transfer.no_permission_to_accept=Du hast keine Berechtigung, diesen Transfer anzunehmen.
transfer.no_permission_to_reject=Du hast keine Berechtigung, diesen Transfer abzulehnen.
@ -1140,6 +1194,11 @@ migrate.gogs.description=Daten von notabug.org oder anderen Gogs Instanzen migri
migrate.onedev.description=Daten von code.onedev.io oder anderen OneDev Instanzen migrieren.
migrate.codebase.description=Daten von codebasehq.com migrieren.
migrate.gitbucket.description=Daten von GitBucket Instanzen migrieren.
migrate.codecommit.description=Daten von AWS CodeCommit migrieren.
migrate.codecommit.aws_access_key_id=AWS Access Key ID
migrate.codecommit.aws_secret_access_key=AWS Secret Access Key
migrate.codecommit.https_git_credentials_username=HTTPS-Git-Nutzername
migrate.codecommit.https_git_credentials_password=HTTPS-Git-Passwort
migrate.migrating_git=Git-Daten werden migriert
migrate.migrating_topics=Themen werden migriert
migrate.migrating_milestones=Meilensteine werden migriert
@ -1200,6 +1259,7 @@ releases=Releases
tag=Tag
released_this=hat released
tagged_this=hat getaggt
file.title=%s in %s
file_raw=Originalformat
file_history=Verlauf
file_view_source=Quelltext anzeigen
@ -1207,12 +1267,16 @@ file_view_rendered=Ansicht rendern
file_view_raw=Originalformat anzeigen
file_permalink=Permalink
file_too_large=Die Datei ist zu groß zum Anzeigen.
file_is_empty=Die Datei ist leer.
code_preview_line_from_to=Zeilen %[1]d bis %[2]d in %[3]s
code_preview_line_in=Zeile %[1]d in %[2]s
invisible_runes_header=`Diese Datei enthält unsichtbare Unicode-Zeichen`
invisible_runes_description=`Diese Datei enthält unsichtbare Unicode-Zeichen, die für Menschen nicht unterscheidbar sind, aber von einem Computer unterschiedlich verarbeitet werden können. Wenn du glaubst, dass das absichtlich so ist, kannst du diese Warnung ignorieren. Benutze den „Escape“-Button, um versteckte Zeichen anzuzeigen.`
ambiguous_runes_header=`Diese Datei enthält mehrdeutige Unicode-Zeichen`
ambiguous_runes_description=`Diese Datei enthält Unicode-Zeichen, die mit anderen Zeichen verwechselt werden können. Wenn du glaubst, dass das absichtlich so ist, kannst du diese Warnung ignorieren. Benutze den „Escape“-Button, um versteckte Zeichen anzuzeigen.`
invisible_runes_line=`Diese Zeile enthält unsichtbare Unicode-Zeichen`
ambiguous_runes_line=`Diese Zeile enthält mehrdeutige Unicode-Zeichen`
ambiguous_character=`%[1]c [U+%04[1]X] kann mit %[2]c [U+%04[2]X] verwechselt werden`
escape_control_characters=Escapen
unescape_control_characters=Unescapen
@ -1260,6 +1324,7 @@ editor.or=oder
editor.cancel_lower=Abbrechen
editor.commit_signed_changes=Committe signierte Änderungen
editor.commit_changes=Änderungen committen
editor.add_tmpl='{filename}' hinzufügen
editor.add=%s hinzugefügt
editor.update=%s aktualisiert
editor.delete=%s gelöscht
@ -1343,6 +1408,7 @@ commitstatus.success=Erfolg
ext_issues=Zugriff auf Externe Issues
ext_issues.desc=Link zu externem Issuetracker.
projects.desc=Verwalte Issues und Pull-Requests in Projekten.
projects.description=Beschreibung (optional)
projects.description_placeholder=Beschreibung
projects.create=Projekt erstellen
@ -1402,7 +1468,9 @@ issues.new.clear_milestone=Meilenstein entfernen
issues.new.assignees=Zuständig
issues.new.clear_assignees=Zuständige entfernen
issues.new.no_assignees=Niemand zuständig
issues.new.no_reviewers=Keine Reviewer
issues.new.blocked_user=Das Issue kann nicht erstellt werden, da du vom Repository-Eigentümer blockiert wurdest.
issues.edit.already_changed=Änderungen zum Issue konnten nicht gespeichert werden. Es scheint, dass der Inhalt bereits von einem anderen Benutzer geändert wurde. Bitte aktualisiere die Seite und bearbeite diese erneut, um zu verhindern, dass die Änderungen des anderen Benutzers überschrieben werden
issues.edit.blocked_user=Der Inhalt kann nicht bearbeitet werden, da du vom Repository-Eigentümer blockiert wurdest.
issues.choose.get_started=Los geht's
issues.choose.open_external_link=Öffnen
@ -1429,6 +1497,7 @@ issues.remove_labels=hat die Labels %s %s entfernt
issues.add_remove_labels=hat %s hinzugefügt, und %s %s entfernt
issues.add_milestone_at=`hat diesen Issue %[2]s zum <b>%[1]s</b> Meilenstein hinzugefügt`
issues.add_project_at=`hat dieses zum <b>%s</b> projekt %s hinzugefügt`
issues.move_to_column_of_project=`hat dies zu %s in %s %s verschoben`
issues.change_milestone_at=`hat den Meilenstein %[3]s von <b>%[1]s</b> zu <b>%[2]s</b> geändert`
issues.change_project_at=`hat das Projekt %[3]s von <b>%[1]s</b> zu <b>%[2]s</b> geändert`
issues.remove_milestone_at=`hat dieses Issue %[2]s vom <b>%[1]s</b> Meilenstein entfernt`
@ -1460,6 +1529,8 @@ issues.filter_assignee=Zuständig
issues.filter_assginee_no_select=Alle Zuständigen
issues.filter_assginee_no_assignee=Niemand zuständig
issues.filter_poster=Autor
issues.filter_user_placeholder=Benutzer suchen
issues.filter_user_no_select=Alle Benutzer
issues.filter_type=Typ
issues.filter_type.all_issues=Alle Issues
issues.filter_type.assigned_to_you=Dir zugewiesen
@ -1513,7 +1584,9 @@ issues.no_content=Keine Beschreibung angegeben.
issues.close=Issue schließen
issues.comment_pull_merged_at=hat Commit %[1]s in %[2]s %[3]s gemerged
issues.comment_manually_pull_merged_at=hat Commit %[1]s in %[2]s %[3]s manuell gemerged
issues.close_comment_issue=Kommentieren und schließen
issues.reopen_issue=Wieder öffnen
issues.reopen_comment_issue=Kommentieren und wieder öffnen
issues.create_comment=Kommentieren
issues.comment.blocked_user=Der Kommentar kann nicht erstellt oder bearbeitet werden, da du vom Repository-Eigentümer blockiert wurdest.
issues.closed_at=`hat diesen Issue <a id="%[1]s" href="#%[1]s">%[2]s</a> geschlossen`
@ -1602,12 +1675,25 @@ issues.delete.title=Dieses Issue löschen?
issues.delete.text=Möchtest du dieses Issue wirklich löschen? (Dadurch wird der Inhalt dauerhaft gelöscht. Denke daran, es stattdessen zu schließen, wenn du es archivieren willst)
issues.tracker=Zeiterfassung
issues.timetracker_timer_start=Timer starten
issues.timetracker_timer_stop=Timer stoppen
issues.timetracker_timer_discard=Timer verwerfen
issues.timetracker_timer_manually_add=Zeit hinzufügen
issues.time_estimate_set=Geschätzte Zeit festlegen
issues.time_estimate_display=Schätzung: %s
issues.change_time_estimate_at=Zeitschätzung geändert zu <b>%s</b> %s
issues.remove_time_estimate_at=Zeitschätzung %s entfernt
issues.time_estimate_invalid=Format der Zeitschätzung ist ungültig
issues.start_tracking_history=hat die Zeiterfassung %s gestartet
issues.tracker_auto_close=Der Timer wird automatisch gestoppt, wenn dieser Issue geschlossen wird
issues.tracking_already_started=`Du hast die Zeiterfassung bereits in <a href="%s">diesem Issue</a> gestartet!`
issues.stop_tracking_history=hat für <b>%s</b> gearbeitet %s
issues.cancel_tracking_history=`hat die Zeiterfassung %s abgebrochen`
issues.del_time=Diese Zeiterfassung löschen
issues.add_time_history=hat <b>%s</b> gearbeitete Zeit hinzugefügt %s
issues.del_time_history=`hat %s gearbeitete Zeit gelöscht`
issues.add_time_manually=Zeit manuell hinzufügen
issues.add_time_hours=Stunden
issues.add_time_minutes=Minuten
issues.add_time_sum_to_small=Es wurde keine Zeit eingegeben.
@ -1667,6 +1753,7 @@ issues.dependency.add_error_dep_not_same_repo=Beide Issues müssen sich im selbe
issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen.
issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen.
issues.review.approve=hat die Änderungen %s genehmigt
issues.review.comment=hat %s überprüft
issues.review.dismissed=verwarf %ss Review %s
issues.review.dismissed_label=Verworfen
issues.review.left_comment=hat einen Kommentar hinterlassen
@ -1692,6 +1779,11 @@ issues.review.resolve_conversation=Diskussion als "erledigt" markieren
issues.review.un_resolve_conversation=Diskussion als "nicht-erledigt" markieren
issues.review.resolved_by=markierte diese Unterhaltung als gelöst
issues.review.commented=Kommentieren
issues.review.official=Genehmigt
issues.review.requested=Prüfung ausstehend
issues.review.rejected=Änderungen angefordert
issues.review.stale=Aktualisiert seit der Genehmigung
issues.review.unofficial=Ungezählte Genehmigung
issues.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Beauftragten hinzugefügt werden.
issues.reference_issue.body=Beschreibung
issues.content_history.deleted=gelöscht
@ -1708,6 +1800,8 @@ compare.compare_head=vergleichen
pulls.desc=Pull-Requests und Code-Reviews aktivieren.
pulls.new=Neuer Pull-Request
pulls.new.blocked_user=Der Pull Request kann nicht erstellt werden, da du vom Repository-Eigentümer blockiert wurdest.
pulls.new.must_collaborator=Du musst Mitarbeiter sein, um Pull-Requests zu erstellen.
pulls.edit.already_changed=Änderungen zum Pull-Request konnten nicht gespeichert werden. Es scheint, dass der Inhalt bereits von einem anderen Benutzer geändert wurde. Bitte aktualisieren die Seite und bearbeite diesen erneut, um zu verhindern, dass die Änderungen des anderen Benutzers überschrieben werden
pulls.view=Pull-Request ansehen
pulls.compare_changes=Neuer Pull-Request
pulls.allow_edits_from_maintainers=Änderungen von Maintainern erlauben
@ -1763,6 +1857,8 @@ pulls.is_empty=Die Änderungen an diesem Branch sind bereits auf dem Zielbranch.
pulls.required_status_check_failed=Einige erforderliche Prüfungen waren nicht erfolgreich.
pulls.required_status_check_missing=Einige erforderliche Prüfungen fehlen.
pulls.required_status_check_administrator=Als Administrator kannst du diesen Pull-Request weiterhin mergen.
pulls.blocked_by_approvals=Dieser Pull-Request hat noch nicht genügend Genehmigungen. %d von %d Genehmigungen erteilt.
pulls.blocked_by_approvals_whitelisted=Dieser Pull-Request hat noch nicht genug erforderliche Genehmigungen. %d von %d Genehmigungen von Benutzern oder Teams auf der Berechtigungsliste.
pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Reviewer angefragt wurden.
pulls.blocked_by_official_review_requests=Dieser Pull Request hat offizielle Review-Anfragen.
pulls.blocked_by_outdated_branch=Dieser Pull Request ist blockiert, da er veraltet ist.
@ -1804,7 +1900,9 @@ pulls.unrelated_histories=Merge fehlgeschlagen: Der Head des Merges und die Basi
pulls.merge_out_of_date=Merge fehlgeschlagen: Während des Mergens wurde die Basis aktualisiert. Hinweis: Versuche es erneut.
pulls.head_out_of_date=Mergen fehlgeschlagen: Der Head wurde aktualisiert während der Merge erstellt wurde. Tipp: Versuche es erneut.
pulls.has_merged=Fehler: Der Pull-Request wurde gemerged, du kannst den Zielbranch nicht wieder mergen oder ändern.
pulls.push_rejected=Push fehlgeschlagen: Der Push wurde abgelehnt. Überprüfe die Git Hooks für dieses Repository.
pulls.push_rejected_summary=Vollständige Ablehnungsmeldung
pulls.push_rejected_no_message=Push fehlgeschlagen: Der Push wurde abgelehnt, aber es gab keine Fehlermeldung. Überprüfe die Git Hooks für dieses Repository
pulls.open_unmerged_pull_exists=`Du kannst diesen Pull-Request nicht erneut öffnen, da noch ein anderer (#%d) mit identischen Eigenschaften offen ist.`
pulls.status_checking=Einige Prüfungen sind noch ausstehend
pulls.status_checks_success=Alle Prüfungen waren erfolgreich
@ -1828,6 +1926,7 @@ pulls.cmd_instruction_checkout_title=Checkout
pulls.cmd_instruction_checkout_desc=Wechsle auf einen neuen Branch in deinem lokalen Repository und teste die Änderungen.
pulls.cmd_instruction_merge_title=Mergen
pulls.cmd_instruction_merge_desc=Die Änderungen mergen und auf Gitea aktualisieren.
pulls.cmd_instruction_merge_warning=Warnung: Dieser Vorgang kann den Pull-Request nicht mergen, da "manueller Merge" nicht aktiviert wurde
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.
@ -1847,9 +1946,15 @@ pulls.delete.title=Diesen Pull-Request löschen?
pulls.delete.text=Willst du diesen Pull-Request wirklich löschen? (Dies wird den Inhalt unwiderruflich löschen. Überlege, ob du ihn nicht lieber schließen willst, um ihn zu archivieren)
pulls.recently_pushed_new_branches=Du hast auf den Branch <strong>%[1]s</strong> %[2]s gepusht
pulls.upstream_diverging_prompt_behind_1=Dieser Branch ist %[1]d Commit hinter %[2]s
pulls.upstream_diverging_prompt_behind_n=Dieser Branch ist %[1]d Commits hinter %[2]s
pulls.upstream_diverging_prompt_base_newer=Der Basis-Branch %s hat neue Änderungen
pulls.upstream_diverging_merge=Fork synchronisieren
pull.deleted_branch=(gelöscht):%s
pull.agit_documentation=Dokumentation zu AGit durchschauen
comments.edit.already_changed=Änderungen zum Kommentar konnten nicht gespeichert werden. Es scheint, dass der Inhalt bereits von einem anderen Benutzer geändert wurde. Bitte aktualisiere die Seite und bearbeite diesen erneut, um zu verhindern, dass die Änderungen des anderen Benutzers überschrieben werden
milestones.new=Neuer Meilenstein
milestones.closed=Geschlossen %s
@ -1858,6 +1963,7 @@ milestones.no_due_date=Kein Fälligkeitsdatum
milestones.open=Öffnen
milestones.close=Schließen
milestones.new_subheader=Benutze Meilensteine, um Issues zu organisieren und den Fortschritt darzustellen.
milestones.completeness=<strong>%d%%</strong> abgeschlossen
milestones.create=Meilenstein erstellen
milestones.title=Titel
milestones.desc=Beschreibung
@ -2042,12 +2148,14 @@ settings.push_mirror_sync_in_progress=Aktuell werden Änderungen auf %s gepusht.
settings.site=Webseite
settings.update_settings=Einstellungen speichern
settings.update_mirror_settings=Mirror-Einstellungen aktualisieren
settings.branches.switch_default_branch=Standardbranch wechseln
settings.branches.update_default_branch=Standardbranch aktualisieren
settings.branches.add_new_rule=Neue Regel hinzufügen
settings.advanced_settings=Erweiterte Einstellungen
settings.wiki_desc=Repository-Wiki aktivieren
settings.use_internal_wiki=Eingebautes Wiki verwenden
settings.default_wiki_branch_name=Standardbezeichnung für Wiki-Branch
settings.default_wiki_everyone_access=Standard-Zugriffsberechtigung für angemeldete Benutzer:
settings.failed_to_change_default_wiki_branch=Das Ändern des Standard-Wiki-Branches ist fehlgeschlagen.
settings.use_external_wiki=Externes Wiki verwenden
settings.external_wiki_url=Externe Wiki-URL
@ -2078,6 +2186,7 @@ settings.pulls.default_delete_branch_after_merge=Standardmäßig bei Pull-Reques
settings.pulls.default_allow_edits_from_maintainers=Änderungen von Maintainern standardmäßig erlauben
settings.releases_desc=Repository-Releases aktivieren
settings.packages_desc=Repository Packages Registry aktivieren
settings.projects_desc=Projekte aktivieren
settings.projects_mode_desc=Projekte-Modus (welche Art Projekte angezeigt werden sollen)
settings.projects_mode_repo=Nur Repo-Projekte
settings.projects_mode_owner=Nur Benutzer- oder Organisations-Projekte
@ -2117,6 +2226,7 @@ settings.transfer_in_progress=Es gibt derzeit eine laufende Übertragung. Bitte
settings.transfer_notices_1= Du wirst keinen Zugriff mehr haben, wenn der neue Besitzer ein individueller Benutzer ist.
settings.transfer_notices_2= Du wirst weiterhin Zugriff haben, wenn der neue Besitzer eine Organisation ist und du einer der Besitzer bist.
settings.transfer_notices_3=- Wenn das Repository privat ist und an einen einzelnen Benutzer übertragen wird, wird sichergestellt, dass der Benutzer mindestens Leserechte hat (und die Berechtigungen werden gegebenenfalls ändert).
settings.transfer_notices_4=- Wenn das Repository einer Organisation gehört und du es an eine andere Organisation oder eine andere Person überträgst, verlierst du die Verlinkungen zwischen den Issues des Repositorys und dem Projektboard der Organisation.
settings.transfer_owner=Neuer Besitzer
settings.transfer_perform=Übertragung durchführen
settings.transfer_started=`Für dieses Repository wurde eine Übertragung eingeleitet und wartet nun auf die Bestätigung von "%s"`
@ -2216,6 +2326,7 @@ settings.event_wiki_desc=Wiki-Seite erstellt, umbenannt, bearbeitet oder gelösc
settings.event_release=Release
settings.event_release_desc=Release in einem Repository veröffentlicht, aktualisiert oder gelöscht.
settings.event_push=Push
settings.event_force_push=Force Push
settings.event_push_desc=Git push in ein Repository.
settings.event_repository=Repository
settings.event_repository_desc=Repository erstellt oder gelöscht.
@ -2252,6 +2363,7 @@ settings.event_pull_request_merge=Pull-Request-Merge
settings.event_package=Paket
settings.event_package_desc=Paket wurde in einem Repository erstellt oder gelöscht.
settings.branch_filter=Branch-Filter
settings.branch_filter_desc=Whitelist für Branches für Push-, Erzeugungs- und Löschevents, als Glob-Pattern beschrieben. Es werden Events für alle Branches gemeldet, falls das Pattern <code>*</code> ist, oder falls es leer ist. Siehe die <a href="%[1]s">%[2]s</a> Dokumentation für die Syntax (Englisch). Beispiele: <code>master</code>, <code>{master,release*}</code>.
settings.authorization_header=Authorization-Header
settings.authorization_header_desc=Wird, falls vorhanden, als Authorization-Header mitgesendet. Beispiele: %s.
settings.active=Aktiv
@ -2300,22 +2412,50 @@ settings.branches=Branches
settings.protected_branch=Branch-Schutz
settings.protected_branch.save_rule=Regel speichern
settings.protected_branch.delete_rule=Regel löschen
settings.protected_branch_can_push=Push erlauben?
settings.protected_branch_can_push_yes=Du kannst pushen
settings.protected_branch_can_push_no=Du kannst nicht pushen
settings.branch_protection=Branch-Schutz für Branch '<b>%s</b>'
settings.protect_this_branch=Branch-Schutz aktivieren
settings.protect_this_branch_desc=Verhindert das Löschen und schränkt Git auf Push- und Merge-Änderungen auf dem Branch ein.
settings.protect_disable_push=Push deaktivieren
settings.protect_disable_push_desc=Kein Push auf diesen Branch erlauben.
settings.protect_disable_force_push=Force-Push deaktivieren
settings.protect_disable_force_push_desc=Force-Push auf diesen Branch nicht erlauben.
settings.protect_enable_push=Push aktivieren
settings.protect_enable_push_desc=Jeder, der Schreibzugriff hat, darf in diesen Branch Pushen (aber kein Force-Push).
settings.protect_enable_force_push_all=Force-Push aktivieren
settings.protect_enable_force_push_all_desc=Jeder mit Push-Zugriff wird in diesen Branch force-pushen können.
settings.protect_enable_force_push_allowlist=Force-Push beschränkt auf Genehmigungsliste
settings.protect_enable_force_push_allowlist_desc=Nur Benutzer oder Teams auf der Genehmigungsliste mit Push-Zugriff werden in diesen Branch force-pushen können.
settings.protect_enable_merge=Merge aktivieren
settings.protect_enable_merge_desc=Jeder mit Schreibzugriff darf die Pull-Requests in diesen Branch mergen.
settings.protect_whitelist_committers=Genehmigungsliste für eingeschränkten Push
settings.protect_whitelist_committers_desc=Jeder, der auf der Genehmigungsliste steht, darf in diesen Branch pushen (aber kein Force-Push).
settings.protect_whitelist_deploy_keys=Genehmigungsliste für Deploy-Schlüssel mit Schreibzugriff zum Pushen.
settings.protect_whitelist_users=Nutzer, die pushen dürfen:
settings.protect_whitelist_teams=Teams, die pushen dürfen:
settings.protect_force_push_allowlist_users=Erlaubte Benutzer für Force-Push:
settings.protect_force_push_allowlist_teams=Erlaubte Teams für Force-Push:
settings.protect_force_push_allowlist_deploy_keys=Genehmigungsliste für Deploy-Schlüssel mit Schreibzugriff zum Force-Push.
settings.protect_merge_whitelist_committers=Merge-Genehmigungsliste aktivieren
settings.protect_merge_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Genehmigungsliste Pull-Requests in diesen Branch zu mergen.
settings.protect_merge_whitelist_users=Nutzer, die mergen dürfen:
settings.protect_merge_whitelist_teams=Teams, die mergen dürfen:
settings.protect_check_status_contexts=Statusprüfungen aktivieren
settings.protect_status_check_patterns=Statuscheck-Muster:
settings.protect_status_check_patterns_desc=Gib Muster ein, um festzulegen, welche Statusüberprüfungen durchgeführt werden müssen, bevor Branches in einen Branch, der dieser Regel entspricht, gemerged werden können. Jede Zeile gibt ein Muster an. Muster dürfen nicht leer sein.
settings.protect_check_status_contexts_desc=Vor dem Mergen müssen Statusprüfungen bestanden werden. Wähle aus, welche Statusprüfungen erfolgreich durchgeführt werden müssen, bevor Branches in einen anderen gemergt werden können, der dieser Regel entspricht. Wenn aktiviert, müssen Commits zuerst auf einen anderen Branch gepusht werden, dann nach bestandener Statusprüfung gemergt oder direkt auf einen Branch gepusht werden, der dieser Regel entspricht. Wenn kein Kontext ausgewählt ist, muss der letzte Commit unabhängig vom Kontext erfolgreich sein.
settings.protect_check_status_contexts_list=Statusprüfungen, die in der letzten Woche für dieses Repository gefunden wurden
settings.protect_status_check_matched=Übereinstimmung
settings.protect_invalid_status_check_pattern=Ungültiges Muster: "%s".
settings.protect_no_valid_status_check_patterns=Keine gültigen Statuscheck-Muster.
settings.protect_required_approvals=Erforderliche Zustimmungen:
settings.protect_required_approvals_desc=Erlaube das Mergen des Pull-Requests nur mit genügend Genehmigungen.
settings.protect_approvals_whitelist_enabled=Genehmigungen auf Benutzer oder Teams auf der Genehmigungsliste beschränken
settings.protect_approvals_whitelist_enabled_desc=Nur Bewertungen von Benutzern auf der Genehmigungsliste oder Teams zählen zu den erforderlichen Genehmigungen. Gibt es keine Genehmigungsliste, so zählen Reviews von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen.
settings.protect_approvals_whitelist_users=Freigeschaltete Reviewer:
settings.protect_approvals_whitelist_teams=Freigeschaltete Teams:
settings.dismiss_stale_approvals=Entferne alte Genehmigungen
settings.dismiss_stale_approvals_desc=Wenn neue Commits gepusht werden, die den Inhalt des Pull-Requests ändern, werden alte Genehmigungen entfernt.
settings.ignore_stale_approvals=Veraltete Genehmigungen ignorieren
@ -2323,12 +2463,18 @@ settings.ignore_stale_approvals_desc=Genehmigungen, die für ältere Commits ert
settings.require_signed_commits=Signierte Commits erforderlich
settings.require_signed_commits_desc=Pushes auf diesen Branch ablehnen, wenn Commits nicht signiert oder nicht überprüfbar sind.
settings.protect_branch_name_pattern=Muster für geschützte Branchnamen
settings.protect_branch_name_pattern_desc=Geschützte Branch-Namensmuster. Siehe <a href="%s">die Dokumentation</a> für die Pattern-Syntax. Beispiele: main, release/**
settings.protect_patterns=Muster
settings.protect_protected_file_patterns=Geschützte Dateimuster (durch Semikolon ';' getrennt):
settings.protect_protected_file_patterns_desc=Geschützte Dateien dürfen nicht direkt geändert werden, auch wenn der Benutzer Rechte hat, Dateien in diesem Branch hinzuzufügen, zu bearbeiten oder zu löschen. Mehrere Muster können mit Semikolon (';') getrennt werden. Siehe <a href='%[1]s'>%[2]s</a> Dokumentation zur Pattern-Syntax. Beispiele: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.protect_unprotected_file_patterns=Ungeschützte Dateimuster (durch Semikolon ';' getrennt):
settings.protect_unprotected_file_patterns_desc=Ungeschützte Dateien, die direkt geändert werden dürfen, wenn der Benutzer Schreibzugriff hat, können die Push-Beschränkung umgehen. Mehrere Muster können mit Semikolon (';') getrennt werden. Siehe <a href='%[1]s'>%[2]s</a> Dokumentation zur Mustersyntax. Beispiele: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.add_protected_branch=Schutz aktivieren
settings.delete_protected_branch=Schutz deaktivieren
settings.update_protect_branch_success=Branchschutzregel "%s" wurde geändert.
settings.remove_protected_branch_success=Branchschutzregel "%s" wurde deaktiviert.
settings.remove_protected_branch_failed=Entfernen der Branchschutzregel "%s" fehlgeschlagen.
settings.protected_branch_deletion=Branch-Schutz deaktivieren
settings.protected_branch_deletion_desc=Wenn du den Branch-Schutz deaktivierst, können alle Nutzer mit Schreibrechten auf den Branch pushen. Fortfahren?
settings.block_rejected_reviews=Merge bei abgelehnten Reviews blockieren
settings.block_rejected_reviews_desc=Mergen ist nicht möglich, wenn Änderungen durch offizielle Reviewer angefragt werden, auch wenn es genügend Zustimmungen gibt.
@ -2336,8 +2482,11 @@ settings.block_on_official_review_requests=Mergen bei offiziellen Review-Anfrage
settings.block_on_official_review_requests_desc=Mergen ist nicht möglich wenn offizielle Review-Anfrangen vorliegen, selbst wenn es genügend Zustimmungen gibt.
settings.block_outdated_branch=Merge blockieren, wenn der Pull-Request veraltet ist
settings.block_outdated_branch_desc=Mergen ist nicht möglich, wenn der Head-Branch hinter dem Basis-Branch ist.
settings.block_admin_merge_override=Administratoren müssen die Schutzregeln für Branches befolgen
settings.block_admin_merge_override_desc=Administratoren müssen die Schutzregeln für Branches befolgen und können sie nicht umgehen.
settings.default_branch_desc=Wähle einen Standardbranch für Pull-Requests und Code-Commits:
settings.merge_style_desc=Merge-Styles
settings.default_merge_style_desc=Standard-Mergeverhalten für Pull-Requests
settings.choose_branch=Branch wählen…
settings.no_protected_branch=Es gibt keine geschützten Branches.
settings.edit_protected_branch=Bearbeiten
@ -2353,12 +2502,25 @@ settings.tags.protection.allowed.teams=Erlaubte Teams
settings.tags.protection.allowed.noone=Niemand
settings.tags.protection.create=Tag schützen
settings.tags.protection.none=Es gibt keine geschützten Tags.
settings.tags.protection.pattern.description=Du kannst einen einzigen Namen oder ein globales Schema oder einen regulären Ausdruck verwenden, um mehrere Tags zu schützen. Mehr dazu im <a target="_blank" rel="noopener" href="%s">Guide für geschützte Tags (Englisch)</a>.
settings.bot_token=Bot-Token
settings.chat_id=Chat-ID
settings.thread_id=Thread-ID
settings.matrix.homeserver_url=Homeserver-URL
settings.matrix.room_id=Raum-ID
settings.matrix.message_type=Nachrichtentyp
settings.visibility.private.button=Auf privat setzen
settings.visibility.private.text=Das Ändern der Sichtbarkeit auf privat wird das Repository nicht nur für erlaubte Mitglieder sichtbar machen, sondern kann auch die Beziehung zwischen ihm und Forks, Beobachtern und Sternen entfernen.
settings.visibility.private.bullet_title=<strong>Das Ändern der Sichtbarkeit auf privat wird:</strong>
settings.visibility.private.bullet_one=Das Repository nur für zugelassene Mitglieder sichtbar machen.
settings.visibility.private.bullet_two=Kann die Beziehung zwischen ihm und <strong>Forks</strong>, <strong>Beobachtern</strong>und <strong>Sternen</strong> entfernen.
settings.visibility.public.button=Auf öffentlich setzen
settings.visibility.public.text=Das Ändern der Sichtbarkeit auf öffentlich macht das Repository für jeden sichtbar.
settings.visibility.public.bullet_title=<strong>Das Ändern der Sichtbarkeit auf öffentlich wird:</strong>
settings.visibility.public.bullet_one=Das Repository für jeden sichtbar machen.
settings.visibility.success=Die Sichtbarkeit des Repositorys wurde geändert.
settings.visibility.error=Beim Versuch, die Sichtbarkeit des Repositorys zu ändern, ist ein Fehler aufgetreten.
settings.visibility.fork_error=Die Sichtbarkeit von geforkten Repositories ist nicht veränderbar.
settings.archive.button=Repo archivieren
settings.archive.header=Dieses Repo archivieren
settings.archive.text=Durch das Archivieren wird ein Repo vollständig schreibgeschützt. Es wird vom Dashboard versteckt. Niemand (nicht einmal du!) wird in der Lage sein, neue Commits zu erstellen oder Issues oder Pull-Requests zu öffnen.
@ -2470,6 +2632,7 @@ release.new_release=Neues Release
release.draft=Entwurf
release.prerelease=Pre-Release
release.stable=Stabil
release.latest=Aktuell
release.compare=Vergleichen
release.edit=bearbeiten
release.ahead.commits=<strong>%d</strong> Commits
@ -2554,6 +2717,7 @@ tag.create_success=Tag "%s" wurde erstellt.
topic.manage_topics=Themen verwalten
topic.done=Fertig
topic.count_prompt=Du kannst nicht mehr als 25 Themen auswählen
topic.format_prompt=Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) und Punkte ('.') enthalten und bis zu 35 Zeichen lang sein. Nur Kleinbuchstaben sind zulässig.
find_file.go_to_file=Datei suchen
@ -2651,6 +2815,7 @@ teams.leave.detail=%s verlassen?
teams.can_create_org_repo=Repositories erstellen
teams.can_create_org_repo_helper=Mitglieder können neue Repositories in der Organisation erstellen. Der Ersteller erhält Administrator-Zugriff auf das neue Repository.
teams.none_access=Kein Zugriff
teams.none_access_helper=Mitglieder können keine anderen Aktionen für diese Einheit anzeigen oder durchführen. Dies hat keine Wirkung auf öffentliche Repositories.
teams.general_access=Allgemeiner Zugriff
teams.general_access_helper=Mitgliederberechtigungen werden durch folgende Berechtigungstabelle festgelegt.
teams.read_access=Lesen
@ -2697,6 +2862,7 @@ teams.invite.by=Von %s eingeladen
teams.invite.description=Bitte klicke auf die folgende Schaltfläche, um dem Team beizutreten.
[admin]
maintenance=Wartung
dashboard=Dashboard
self_check=Selbstprüfung
identity_access=Identität & Zugriff
@ -2718,7 +2884,9 @@ last_page=Letzte
total=Gesamt: %d
settings=Administratoreinstellungen
dashboard.new_version_hint=Gitea %s ist jetzt verfügbar, deine derzeitige Version ist %s. Weitere Details findest du im <a target="_blank" rel="noreferrer" href="%s">Blog</a>.
dashboard.statistic=Übersicht
dashboard.maintenance_operations=Wartungsoperationen
dashboard.system_status=System-Status
dashboard.operation_name=Name der Operation
dashboard.operation_switch=Wechseln
@ -2759,6 +2927,7 @@ dashboard.reinit_missing_repos=Alle Git-Repositories neu einlesen, für die Eint
dashboard.sync_external_users=Externe Benutzerdaten synchronisieren
dashboard.cleanup_hook_task_table=Hook-Task-Tabelle bereinigen
dashboard.cleanup_packages=Veraltete Pakete löschen
dashboard.cleanup_actions=Abgelaufene Ressourcen von Actions bereinigen
dashboard.server_uptime=Server-Uptime
dashboard.current_goroutine=Aktuelle Goroutinen
dashboard.current_memory_usage=Aktuelle Speichernutzung
@ -2788,12 +2957,19 @@ dashboard.total_gc_time=Gesamte GC-Pause
dashboard.total_gc_pause=Gesamte GC-Pause
dashboard.last_gc_pause=Letzte GC-Pause
dashboard.gc_times=Anzahl GC
dashboard.delete_old_actions=Alle alten Aktionen aus der Datenbank löschen
dashboard.delete_old_actions.started=Löschen aller alten Aktionen in der Datenbank gestartet.
dashboard.update_checker=Update-Checker
dashboard.delete_old_system_notices=Alle alten Systemmeldungen aus der Datenbank löschen
dashboard.gc_lfs=Garbage-Collection für LFS Meta-Objekte ausführen
dashboard.stop_zombie_tasks=Zombie-Aufgaben stoppen
dashboard.stop_endless_tasks=Endlose Aktionen stoppen
dashboard.cancel_abandoned_jobs=Aufgegebene Jobs abbrechen
dashboard.start_schedule_tasks=Terminierte Aufgaben starten
dashboard.sync_branch.started=Synchronisierung der Branches gestartet
dashboard.sync_tag.started=Tag-Synchronisierung gestartet
dashboard.rebuild_issue_indexer=Issue-Indexer neu bauen
dashboard.sync_repo_licenses=Repo-Lizenzen synchronisieren
users.user_manage_panel=Benutzerkontenverwaltung
users.new_account=Benutzerkonto erstellen
@ -2865,6 +3041,10 @@ emails.not_updated=Fehler beim Aktualisieren der angeforderten E-Mail-Adresse: %
emails.duplicate_active=Diese E-Mail-Adresse wird bereits von einem Nutzer verwendet.
emails.change_email_header=E-Mail-Eigenschaften aktualisieren
emails.change_email_text=Bist du dir sicher, dass du diese E-Mail-Adresse aktualisieren möchtest?
emails.delete=E-Mail löschen
emails.delete_desc=Willst du diese E-Mail-Adresse wirklich löschen?
emails.deletion_success=Die E-Mail-Adresse wurde gelöscht.
emails.delete_primary_email_error=Du kannst die primäre E-Mail-Adresse nicht löschen.
orgs.org_manage_panel=Organisationsverwaltung
orgs.name=Name
@ -2897,10 +3077,12 @@ packages.size=Größe
packages.published=Veröffentlicht
defaulthooks=Standard-Webhooks
defaulthooks.desc=Webhooks senden automatisch eine HTTP-POST-Anfrage an einen Server, wenn bestimmte Gitea-Events ausgelöst werden. Hier definierte Webhooks sind die Standardwerte, die in alle neuen Repositories kopiert werden. Mehr Infos findest du in der <a target="_blank" rel="noopener" href="%s">Webhooks-Anleitung</a> (auf Englisch).
defaulthooks.add_webhook=Standard-Webhook hinzufügen
defaulthooks.update_webhook=Standard-Webhook aktualisieren
systemhooks=System-Webhooks
systemhooks.desc=Webhooks senden automatisch HTTP-POST-Anfragen an einen Server, wenn bestimmte Gitea-Events ausgelöst werden. Hier definierte Webhooks werden auf alle Repositories des Systems übertragen, beachte daher mögliche Performance-Einbrüche. Mehr Infos findest du in der <a target="_blank" rel="noopener" href="%s">Webhooks-Anleitung</a> (auf Englisch).
systemhooks.add_webhook=System-Webhook hinzufügen
systemhooks.update_webhook=System-Webhook aktualisieren
@ -2995,7 +3177,18 @@ auths.tips=Tipps
auths.tips.oauth2.general=OAuth2-Authentifizierung
auths.tips.oauth2.general.tip=Beim Registrieren einer OAuth2-Anwendung sollte die Callback-URL folgendermaßen lauten:
auths.tip.oauth2_provider=OAuth2-Anbieter
auths.tip.bitbucket=Registriere einen neuen OAuth-Consumer unter %s und füge die Berechtigung 'Account' - 'Read' hinzu
auths.tip.nextcloud=Registriere über das "Settings -> Security -> OAuth 2.0 client"-Menü einen neuen "OAuth consumer" auf der Nextcloud-Instanz
auths.tip.dropbox=Erstelle eine neue App auf %s
auths.tip.facebook=Erstelle eine neue Anwendung auf %s und füge das Produkt "Facebook Login“ hinzu
auths.tip.github=Erstelle unter %s eine neue OAuth-Anwendung
auths.tip.gitlab_new=Erstelle eine neue Anwendung unter %s
auths.tip.google_plus=Du erhältst die OAuth2-Client-Zugangsdaten in der Google-API-Konsole unter %s
auths.tip.openid_connect=Benutze die OpenID-Connect-Discovery-URL "https://{server}/.well-known/openid-configuration", um die Endpunkte zu spezifizieren
auths.tip.twitter=Gehe zu %s, erstelle eine Anwendung und stelle sicher, dass die Option „Allow this application to be used to Sign in with Twitter“ aktiviert ist
auths.tip.discord=Erstelle unter %s eine neue Anwendung
auths.tip.gitea=Registriere eine neue OAuth2-Anwendung. Eine Anleitung findest du unter %s
auths.tip.yandex=`Erstelle eine neue Anwendung auf %s. Wähle folgende Berechtigungen aus dem "Yandex.Passport API" Bereich: "Zugriff auf E-Mail-Adresse", "Zugriff auf Benutzeravatar" und "Zugriff auf Benutzername, Vor- und Nachname, Geschlecht"`
auths.tip.mastodon=Gebe eine benutzerdefinierte URL für die Mastodon-Instanz ein, mit der du dich authentifizieren möchtest (oder benutze die standardmäßige)
auths.edit=Authentifikationsquelle bearbeiten
auths.activated=Diese Authentifikationsquelle ist aktiviert
@ -3111,6 +3304,10 @@ config.cache_adapter=Cache-Adapter
config.cache_interval=Cache-Intervall
config.cache_conn=Cache-Anbindung
config.cache_item_ttl=Cache Item-TTL
config.cache_test=Cache testen
config.cache_test_failed=Fehler beim Prüfen des Caches: %v.
config.cache_test_slow=Cache-Test erfolgreich, aber die Antwortzeit ist langsam: %s.
config.cache_test_succeeded=Cache-Test erfolgreich, Antwort in %s erhalten.
config.session_config=Session-Konfiguration
config.session_provider=Session-Provider
@ -3157,6 +3354,7 @@ monitor.next=Nächste Ausführung
monitor.previous=Letzte Ausführung
monitor.execute_times=Ausführungen
monitor.process=Laufende Prozesse
monitor.stacktrace=Stacktraces
monitor.processes_count=%d Prozesse
monitor.download_diagnosis_report=Diagnosebericht herunterladen
monitor.desc=Beschreibung
@ -3164,6 +3362,8 @@ monitor.start=Startzeit
monitor.execute_time=Ausführungszeit
monitor.last_execution_result=Ergebnis
monitor.process.cancel=Prozess abbrechen
monitor.process.cancel_desc=Abbrechen eines Prozesses kann Datenverlust verursachen
monitor.process.cancel_notices=Abbrechen: <strong>%s</strong>?
monitor.process.children=Subprozesse
monitor.queues=Warteschlangen
@ -3202,11 +3402,13 @@ notices.op=Aktion
notices.delete_success=Diese Systemmeldung wurde gelöscht.
self_check.no_problem_found=Bisher wurde kein Problem festgestellt.
self_check.startup_warnings=Warnungen beim Start:
self_check.database_collation_mismatch=Erwarte Datenbank-Kollation: %s
self_check.database_collation_case_insensitive=Die Datenbank verwendet die Kollation %s, was eine unsensible Kollation ist. Obwohl Gitea damit arbeiten könnte, gibt es vielleicht einige seltene Fälle, die nicht wie erwartet funktionieren.
self_check.database_inconsistent_collation_columns=Die Datenbank verwendet die Kollation %s, aber diese Spalten verwenden unzutreffende Kollationen. Dies könnte zu unerwarteten Problemen führen.
self_check.database_fix_mysql=Für MySQL/MariaDB-Benutzer kann man den Befehl "gitea doctor convert" oder manuell auch "ALTER ... COLLATE ..."-SQLs verwenden, um die Sortierprobleme zu beheben.
self_check.database_fix_mssql=Für MSSQL-Benutzer kann das Problem im Moment nur durch "ALTER ... COLLATE ..." SQLs manuell behoben werden.
self_check.location_origin_mismatch=Aktuelle URL (%[1]s) stimmt nicht mit der URL überein, die Gitea (%[2]s) sieht. Wenn du einen Reverse-Proxy verwendest, stelle bitte sicher, dass die Header "Host" und "X-Forwarded-Proto" korrekt gesetzt sind.
[action]
create_repo=hat das Repository <a href="%s">%s</a> erstellt
@ -3234,6 +3436,7 @@ mirror_sync_create=neue Referenz <a href="%[2]s">%[3]s</a> bei <a href="%[1]s">%
mirror_sync_delete=hat die Referenz des Mirrors <code>%[2]s</code> in <a href="%[1]s">%[3]s</a> synchronisiert und gelöscht
approve_pull_request=`hat <a href="%[1]s">%[3]s#%[2]s</a> approved`
reject_pull_request=`schlug Änderungen für <a href="%[1]s">%[3]s#%[2]s</a> vor`
publish_release=`veröffentlichte Release <a href="%[2]s"> "%[4]s" </a> in <a href="%[1]s">%[3]s</a>`
review_dismissed=`verwarf das Review von <b>%[4]s</b> in <a href="%[1]s">%[3]s#%[2]s</a>`
review_dismissed_reason=Grund:
create_branch=legte den Branch <a href="%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a> an
@ -3262,6 +3465,8 @@ raw_minutes=Minuten
[dropzone]
default_message=Zum Hochladen hier klicken oder Datei ablegen.
invalid_input_type=Dateien dieses Dateityps können nicht hochgeladen werden.
file_too_big=Dateigröße ({{filesize}} MB) überschreitet die Maximalgröße ({{maxFilesize}} MB).
remove_file=Datei entfernen
[notification]
@ -3298,6 +3503,7 @@ error.unit_not_allowed=Du hast keine Berechtigung, um auf diesen Repository-Bere
title=Pakete
desc=Repository-Pakete verwalten.
empty=Noch keine Pakete vorhanden.
no_metadata=Keine Metadaten.
empty.documentation=Weitere Informationen zur Paket-Registry findest Du in der <a target="_blank" rel="noopener noreferrer" href="%s">Dokumentation</a>.
empty.repo=Hast du ein Paket hochgeladen, das hier nicht angezeigt wird? Gehe zu den <a href="%[1]s">Paketeinstellungen</a> und verlinke es mit diesem Repo.
registry.documentation=Für weitere Informationen zur %s-Registry, schaue in der <a target="_blank" rel="noopener noreferrer" href="%s">Dokumentation</a> nach.
@ -3332,6 +3538,8 @@ alpine.repository=Repository-Informationen
alpine.repository.branches=Branches
alpine.repository.repositories=Repositories
alpine.repository.architectures=Architekturen
arch.registry=Server mit gebrauchtem Repository und Architektur zu <code>/etc/pacman.conf</code> hinzufügen:
arch.install=Paket mit pacman synchronisieren:
arch.repository=Repository-Informationen
arch.repository.repositories=Repositories
arch.repository.architectures=Architekturen
@ -3382,6 +3590,7 @@ npm.install=Um das Paket mit npm zu installieren, führe den folgenden Befehl au
npm.install2=oder füge es zur package.json-Datei hinzu:
npm.dependencies=Abhängigkeiten
npm.dependencies.development=Entwicklungsabhängigkeiten
npm.dependencies.bundle=Gebündelte Abhängigkeiten
npm.dependencies.peer=Peer Abhängigkeiten
npm.dependencies.optional=Optionale Abhängigkeiten
npm.details.tag=Tag
@ -3513,6 +3722,7 @@ runners.status.active=Aktiv
runners.status.offline=Offline
runners.version=Version
runners.reset_registration_token=Registrierungs-Token zurücksetzen
runners.reset_registration_token_confirm=Möchtest du den aktuellen Token invalidieren und einen neuen generieren?
runners.reset_registration_token_success=Runner-Registrierungstoken erfolgreich zurückgesetzt
runs.all_workflows=Alle Workflows
@ -3522,6 +3732,7 @@ runs.pushed_by=gepusht von
runs.invalid_workflow_helper=Die Workflow-Konfigurationsdatei ist ungültig. Bitte überprüfe Deine Konfigurationsdatei: %s
runs.no_matching_online_runner_helper=Kein passender Runner online mit Label: %s
runs.no_job_without_needs=Der Workflow muss mindestens einen Job ohne Abhängigkeiten enthalten.
runs.no_job=Der Workflow muss mindestens einen Job enthalten
runs.actor=Initiator
runs.status=Status
runs.actors_no_select=Alle Initiatoren
@ -3532,12 +3743,18 @@ runs.no_workflows.quick_start=Du weißt nicht, wie du mit Gitea Actions loslegst
runs.no_workflows.documentation=Weitere Informationen zu Gitea Actions findest du in der <a target="_blank" rel="noopener noreferrer" href="%s"> Dokumentation</a>.
runs.no_runs=Der Workflow hat noch keine Ausführungen.
runs.empty_commit_message=(leere Commit-Nachricht)
runs.expire_log_message=Protokolle wurden geleert, weil sie zu alt waren.
workflow.disable=Workflow deaktivieren
workflow.disable_success=Workflow '%s' erfolgreich deaktiviert.
workflow.enable=Workflow aktivieren
workflow.enable_success=Workflow '%s' erfolgreich aktiviert.
workflow.disabled=Workflow ist deaktiviert.
workflow.run=Workflow ausführen
workflow.not_found=Workflow '%s' wurde nicht gefunden.
workflow.run_success=Workflow '%s' erfolgreich ausgeführt.
workflow.from_ref=Nutze Workflow von
workflow.has_workflow_dispatch=Dieser Workflow hat einen workflow_dispatch Event-Trigger.
need_approval_desc=Um Workflows für den Pull-Request eines Forks auszuführen, ist eine Genehmigung erforderlich.
@ -3557,8 +3774,11 @@ variables.creation.success=Die Variable „%s“ wurde hinzugefügt.
variables.update.failed=Fehler beim Bearbeiten der Variable.
variables.update.success=Die Variable wurde bearbeitet.
logs.always_auto_scroll=Autoscroll für Logs immer aktivieren
logs.always_expand_running=Laufende Logs immer erweitern
[projects]
deleted.display_name=Gelöschtes Projekt
type-1.display_name=Individuelles Projekt
type-2.display_name=Repository-Projekt
type-3.display_name=Organisationsprojekt

View File

@ -162,10 +162,11 @@ func TestUpdateIssueLabel_Toggle(t *testing.T) {
UpdateIssueLabel(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
for _, issueID := range testCase.IssueIDs {
unittest.AssertExistsIf(t, testCase.ExpectedAdd, &issues_model.IssueLabel{
IssueID: issueID,
LabelID: testCase.LabelID,
})
if testCase.ExpectedAdd {
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: testCase.LabelID})
} else {
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: testCase.LabelID})
}
}
unittest.CheckConsistencyFor(t, &issues_model.Label{})
}

View File

@ -47,7 +47,7 @@ func TestRemoveOrgUser(t *testing.T) {
testSuccess := func(org *organization.Organization, user *user_model.User) {
expectedNumMembers := org.NumMembers
if unittest.BeanExists(t, &organization.OrgUser{OrgID: org.ID, UID: user.ID}) {
if unittest.GetBean(t, &organization.OrgUser{OrgID: org.ID, UID: user.ID}) != nil {
expectedNumMembers--
}
assert.NoError(t, RemoveOrgUser(db.DefaultContext, org, user))

View File

@ -9,11 +9,15 @@ import (
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
webhook_model "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
webhook_module "code.gitea.io/gitea/modules/webhook"
"code.gitea.io/gitea/services/convert"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestWebhook_GetSlackHook(t *testing.T) {
@ -77,3 +81,11 @@ func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) {
unittest.AssertNotExistsBean(t, hookTask)
}
}
func TestWebhookUserMail(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
setting.Service.NoReplyAddress = "no-reply.com"
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.Equal(t, user.GetPlaceholderEmail(), convert.ToUser(db.DefaultContext, user, nil).Email)
assert.Equal(t, user.Email, convert.ToUser(db.DefaultContext, user, user).Email)
}

View File

@ -1,7 +1,11 @@
{{.Message}}
{{if .Details}}
<details>
<summary>{{.Summary}}</summary>
<code>
{{.Details | SanitizeHTML}}
</code>
<code>{{.Details | SanitizeHTML}}</code>
</details>
{{else}}
<div>
{{.Summary}}
</div>
{{end}}

View File

@ -29,7 +29,8 @@
</div>
<p class="info tw-flex tw-items-center tw-my-1">{{svg "octicon-git-commit" 16 "tw-mr-1"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{ctx.RenderUtils.RenderCommitMessage .DefaultBranchBranch.DBBranch.CommitMessage (.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{DateUtils.TimeSince .DefaultBranchBranch.DBBranch.CommitTime}}{{if .DefaultBranchBranch.DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
</td>
<td class="right aligned middle aligned overflow-visible">
{{/* FIXME: here and below, the tw-overflow-visible is not quite right but it is still needed the moment: to show the important buttons when the width is narrow */}}
<td class="right aligned middle aligned tw-overflow-visible">
{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
<button class="btn interact-bg show-create-branch-modal tw-p-2"
data-modal="#create-branch-modal"
@ -148,7 +149,8 @@
{{end}}
{{end}}
</td>
<td class="three wide right aligned overflow-visible">
{{/* FIXME: here and above, the tw-overflow-visible is not quite right */}}
<td class="three wide right aligned tw-overflow-visible">
{{if and $.IsWriter (not $.Repository.IsArchived) (not .DBBranch.IsDeleted)}}
<button class="btn interact-bg tw-p-2 show-modal show-create-branch-modal"
data-branch-from="{{.DBBranch.Name}}"

View File

@ -1,10 +1,10 @@
{{if .Statuses}}
{{if and (eq (len .Statuses) 1) .Status.TargetURL}}
<a class="tw-align-middle {{.AdditionalClasses}} tw-no-underline" data-tippy="commit-statuses" href="{{.Status.TargetURL}}">
<a class="flex-text-inline tw-no-underline {{.AdditionalClasses}}" data-tippy="commit-statuses" href="{{.Status.TargetURL}}">
{{template "repo/commit_status" .Status}}
</a>
{{else}}
<span class="tw-align-middle {{.AdditionalClasses}}" data-tippy="commit-statuses" tabindex="0">
<span class="flex-text-inline {{.AdditionalClasses}}" data-tippy="commit-statuses" tabindex="0">
{{template "repo/commit_status" .Status}}
</span>
{{end}}

View File

@ -235,7 +235,7 @@
{{if and (not $.Repository.IsArchived) (not .DiffNotAvailable)}}
<template id="issue-comment-editor-template">
<div class="ui form comment">
<form class="ui form comment">
{{template "shared/combomarkdowneditor" (dict
"CustomInit" true
"MarkdownPreviewInRepo" $.Repository
@ -252,7 +252,7 @@
<button class="ui cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
<button class="ui primary button">{{ctx.Locale.Tr "repo.issues.save"}}</button>
</div>
</div>
</form>
</template>
{{end}}
{{if (not .DiffNotAvailable)}}

View File

@ -365,8 +365,9 @@
{{if .Review}}{{$reviewType = .Review.Type}}{{end}}
{{if not .OriginalAuthor}}
{{/* Some timeline avatars need a offset to correctly align with their speech bubble.
The condition depends on whether the comment has contents/attachments or reviews */}}
<a class="timeline-avatar{{if or .Content .Attachments (and .Review .Review.CodeComments)}} timeline-avatar-offset{{end}}"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}>
The condition depends on whether the comment has contents/attachments,
review's comment is also controlled/rendered by issue comment's Content field */}}
<a class="timeline-avatar{{if or .Content .Attachments}} timeline-avatar-offset{{end}}"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}>
{{ctx.AvatarUtils.Avatar .Poster 40}}
</a>
{{end}}

View File

@ -3,7 +3,7 @@
{{else}}
{{if .LatestCommitUser}}
{{ctx.AvatarUtils.Avatar .LatestCommitUser 24 "tw-mr-1"}}
{{ctx.AvatarUtils.Avatar .LatestCommitUser 24}}
{{if and .LatestCommitUser.FullName DefaultShowFullName}}
<a class="muted author-wrapper" title="{{.LatestCommitUser.FullName}}" href="{{.LatestCommitUser.HomeLink}}"><strong>{{.LatestCommitUser.FullName}}</strong></a>
{{else}}
@ -11,7 +11,7 @@
{{end}}
{{else}}
{{if .LatestCommit.Author}}
{{ctx.AvatarUtils.AvatarByEmail .LatestCommit.Author.Email .LatestCommit.Author.Name 24 "tw-mr-1"}}
{{ctx.AvatarUtils.AvatarByEmail .LatestCommit.Author.Email .LatestCommit.Author.Name 24}}
<span class="author-wrapper" title="{{.LatestCommit.Author.Name}}"><strong>{{.LatestCommit.Author.Name}}</strong></span>
{{end}}
{{end}}

View File

@ -733,7 +733,7 @@
<span class="field">
{{if .CodeIndexerStatus}}
<a rel="nofollow" class="ui sha label" href="{{.RepoLink}}/commit/{{.CodeIndexerStatus.CommitSha}}">
<span class="shortsha">{{ShortSha .CodeIndexerStatus.CommitSha}}</span>
{{ShortSha .CodeIndexerStatus.CommitSha}}
</a>
{{else}}
<span>{{ctx.Locale.Tr "repo.settings.admin_indexer_unindexed"}}</span>
@ -752,7 +752,7 @@
<span class="field">
{{if and .StatsIndexerStatus .StatsIndexerStatus.CommitSha}}
<a rel="nofollow" class="ui sha label" href="{{.RepoLink}}/commit/{{.StatsIndexerStatus.CommitSha}}">
<span class="shortsha">{{ShortSha .StatsIndexerStatus.CommitSha}}</span>
{{ShortSha .StatsIndexerStatus.CommitSha}}
</a>
{{else}}
<span>{{ctx.Locale.Tr "repo.settings.admin_indexer_unindexed"}}</span>

View File

@ -25,30 +25,10 @@
{{end}}
</span>
</div>
{{if or .TotalTrackedTime .Assignees .NumComments}}
<div class="flex-item-trailing">
{{if .TotalTrackedTime}}
<div class="text grey flex-text-block">
{{svg "octicon-clock" 16}}
{{.TotalTrackedTime | Sec2Time}}
</div>
{{end}}
{{if .Assignees}}
<div class="text grey">
{{range .Assignees}}
<a class="ui assignee tw-no-underline" href="{{.HomeLink}}" data-tooltip-content="{{.GetDisplayName}}">
{{ctx.AvatarUtils.Avatar . 20}}
</a>
{{end}}
</div>
{{end}}
{{if .NumComments}}
<div class="text grey">
<a class="tw-no-underline muted flex-text-block" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
{{svg "octicon-comment" 16}}{{.NumComments}}
</a>
</div>
{{end}}
{{if .TotalTrackedTime}}
<div class="text grey flex-text-block">
{{svg "octicon-clock" 16}}
{{.TotalTrackedTime | Sec2Time}}
</div>
{{end}}
</div>
@ -152,6 +132,26 @@
{{end}}
</div>
</div>
{{if or .Assignees .NumComments}}
<div class="flex-item-trailing">
{{if .Assignees}}
<div class="text grey">
{{range .Assignees}}
<a class="ui assignee tw-no-underline" href="{{.HomeLink}}" data-tooltip-content="{{.GetDisplayName}}">
{{ctx.AvatarUtils.Avatar . 20}}
</a>
{{end}}
</div>
{{end}}
{{if .NumComments}}
<div class="text grey">
<a class="tw-no-underline muted flex-text-block" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
{{svg "octicon-comment" 16}}{{.NumComments}}
</a>
</div>
{{end}}
</div>
{{end}}
</div>
{{end}}
{{if .IssueIndexerUnavailable}}

View File

@ -332,7 +332,7 @@ func TestLDAPUserSyncWithGroupFilter(t *testing.T) {
// Assert members of LDAP group "cn=git" are added
for _, gitLDAPUser := range te.gitLDAPUsers {
unittest.BeanExists(t, &user_model.User{
unittest.AssertExistsAndLoadBean(t, &user_model.User{
Name: gitLDAPUser.UserName,
})
}

View File

@ -12,6 +12,7 @@ import (
"strings"
"testing"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/tests"
@ -83,6 +84,30 @@ func testPullCreateDirectly(t *testing.T, session *TestSession, baseRepoOwner, b
return resp
}
func testPullCreateFailure(t *testing.T, session *TestSession, baseRepoOwner, baseRepoName, baseBranch, headRepoOwner, headRepoName, headBranch, title string) *httptest.ResponseRecorder {
headCompare := headBranch
if headRepoOwner != "" {
if headRepoName != "" {
headCompare = fmt.Sprintf("%s/%s:%s", headRepoOwner, headRepoName, headBranch)
} else {
headCompare = fmt.Sprintf("%s:%s", headRepoOwner, headBranch)
}
}
req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/compare/%s...%s", baseRepoOwner, baseRepoName, baseBranch, headCompare))
resp := session.MakeRequest(t, req, http.StatusOK)
// Submit the form for creating the pull
htmlDoc := NewHTMLParser(t, resp.Body)
link, exists := htmlDoc.doc.Find("form.ui.form").Attr("action")
assert.True(t, exists, "The template has changed")
req = NewRequestWithValues(t, "POST", link, map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"title": title,
})
resp = session.MakeRequest(t, req, http.StatusBadRequest)
return resp
}
func TestPullCreate(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user1")
@ -245,3 +270,46 @@ func TestCreateAgitPullWithReadPermission(t *testing.T) {
})
})
}
/*
Setup: user2 has repository, user1 forks it
---
1. User2 blocks User1
2. User1 adds changes to fork
3. User1 attempts to create a pull request
4. User1 sees alert that the action is not allowed because of the block
*/
func TestCreatePullWhenBlocked(t *testing.T) {
RepoOwner := "user2"
ForkOwner := "user16"
onGiteaRun(t, func(t *testing.T, u *url.URL) {
// Setup
// User1 forks repo1 from User2
sessionFork := loginUser(t, ForkOwner)
testRepoFork(t, sessionFork, RepoOwner, "repo1", ForkOwner, "forkrepo1", "")
// 1. User2 blocks user1
// sessionBase := loginUser(t, "user2")
token := getUserToken(t, RepoOwner, auth_model.AccessTokenScopeWriteUser)
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/blocks/%s", ForkOwner)).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNotFound)
req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/blocks/%s", ForkOwner)).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)
// 2. User1 adds changes to fork
testEditFile(t, sessionFork, ForkOwner, "forkrepo1", "master", "README.md", "Hello, World (Edited)\n")
// 3. User1 attempts to create a pull request
testPullCreateFailure(t, sessionFork, RepoOwner, "repo1", "master", ForkOwner, "forkrepo1", "master", "This is a pull title")
// Teardown
// Unblock user
req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/blocks/%s", ForkOwner)).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)
})
}

View File

@ -92,7 +92,7 @@ func TestPullView_CodeOwner(t *testing.T) {
testPullCreate(t, session, "user2", "test_codeowner", false, repo.DefaultBranch, "codeowner-basebranch", "Test Pull Request")
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: repo.ID, HeadBranch: "codeowner-basebranch"})
unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5})
unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5})
assert.NoError(t, pr.LoadIssue(db.DefaultContext))
err := issue_service.ChangeTitle(db.DefaultContext, pr.Issue, user2, "[WIP] Test Pull Request")
@ -139,7 +139,7 @@ func TestPullView_CodeOwner(t *testing.T) {
testPullCreate(t, session, "user2", "test_codeowner", false, repo.DefaultBranch, "codeowner-basebranch2", "Test Pull Request2")
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "codeowner-basebranch2"})
unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
})
t.Run("Forked Repo Pull Request", func(t *testing.T) {
@ -169,13 +169,13 @@ func TestPullView_CodeOwner(t *testing.T) {
testPullCreateDirectly(t, session, "user5", "test_codeowner", forkedRepo.DefaultBranch, "", "", "codeowner-basebranch-forked", "Test Pull Request on Forked Repository")
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"})
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
unittest.AssertNotExistsBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
// create a pull request to base repository, code reviewers should be mentioned
testPullCreateDirectly(t, session, repo.OwnerName, repo.Name, repo.DefaultBranch, forkedRepo.OwnerName, forkedRepo.Name, "codeowner-basebranch-forked", "Test Pull Request3")
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"})
unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
})
})
}

View File

@ -9,6 +9,7 @@ import (
"strings"
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
@ -17,6 +18,7 @@ import (
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func testLoginFailed(t *testing.T, username, password, message string) {
@ -42,7 +44,7 @@ func TestSignin(t *testing.T) {
user.Name = "testuser"
user.LowerName = strings.ToLower(user.Name)
user.ID = 0
unittest.AssertSuccessfulInsert(t, user)
require.NoError(t, db.Insert(db.DefaultContext, user))
samples := []struct {
username string

View File

@ -928,7 +928,8 @@ strong.attention-caution, svg.attention-caution {
color: var(--color-red-dark-1);
}
.center:not(.popup) {
/* FIXME: this is a longstanding dirty patch since 2015, it only makes the pages more messy and shouldn't be used */
.center {
text-align: center;
}

View File

@ -1,13 +1,4 @@
.explore .secondary-nav {
border-width: 1px !important;
}
.explore .secondary-nav .svg {
width: 16px;
text-align: center;
margin-right: 5px;
}
/* FIXME: need to refactor the repo branches list page and move these styles to proper place */
.ui.repository.branches .info {
font-size: 12px;
color: var(--color-text-light);
@ -20,12 +11,3 @@
overflow: hidden;
text-overflow: ellipsis;
}
.ui.repository.branches .overflow-visible {
overflow: visible;
}
/* fix alignment of PR popup in branches table */
.ui.repository.branches table .ui.popup {
text-align: left;
}

View File

@ -82,5 +82,6 @@
@import "./review.css";
@import "./actions.css";
@tailwind utilities;
@import "./helpers.css";
@tailwind utilities;

View File

@ -103,11 +103,11 @@
#navbar .ui.dropdown .navbar-profile-admin {
display: block;
position: absolute;
font-size: 10px;
font-size: 9px;
font-weight: var(--font-weight-bold);
color: var(--color-nav-bg);
background: var(--color-primary);
padding: 2px 4px;
padding: 2px 3px;
border-radius: 10px;
top: -1px;
left: 18px;

View File

@ -123,6 +123,12 @@ td .commit-summary {
gap: 0.25em;
}
@media (max-width: 767.98px) {
.latest-commit .commit-id-short {
display: none;
}
}
.repo-path {
display: flex;
overflow-wrap: anywhere;
@ -1670,6 +1676,10 @@ tbody.commit-list {
white-space: nowrap;
}
.latest-commit .message-wrapper {
max-width: calc(100% - 2.5rem);
}
/* in the commit list, messages can wrap so we can use inline */
.commit-list .message-wrapper {
display: inline;

View File

@ -38,6 +38,8 @@ function initRepoDiffFileViewToggle() {
}
function initRepoDiffConversationForm() {
// FIXME: there could be various different form in a conversation-holder (for example: reply form, edit form).
// This listener is for "reply form" only, it should clearly distinguish different forms in the future.
addDelegatedEventListener<HTMLFormElement, SubmitEvent>(document, 'submit', '.conversation-holder form', async (form, e) => {
e.preventDefault();
const textArea = form.querySelector<HTMLTextAreaElement>('textarea');

View File

@ -1,20 +1,17 @@
import $ from 'jquery';
import {svg} from '../svg.ts';
import {showErrorToast} from '../modules/toast.ts';
import {GET, POST} from '../modules/fetch.ts';
import {showElem} from '../utils/dom.ts';
import {createElementFromHTML, showElem} from '../utils/dom.ts';
import {parseIssuePageInfo} from '../utils.ts';
import {fomanticQuery} from '../modules/fomantic/base.ts';
let i18nTextEdited;
let i18nTextOptions;
let i18nTextDeleteFromHistory;
let i18nTextDeleteFromHistoryConfirm;
let i18nTextEdited: string;
let i18nTextOptions: string;
let i18nTextDeleteFromHistory: string;
let i18nTextDeleteFromHistoryConfirm: string;
function showContentHistoryDetail(issueBaseUrl, commentId, historyId, itemTitleHtml) {
let $dialog = $('.content-history-detail-dialog');
if ($dialog.length) return;
$dialog = $(`
function showContentHistoryDetail(issueBaseUrl: string, commentId: string, historyId: string, itemTitleHtml: string) {
const elDetailDialog = createElementFromHTML(`
<div class="ui modal content-history-detail-dialog">
${svg('octicon-x', 16, 'close icon inside')}
<div class="header tw-flex tw-items-center tw-justify-between">
@ -29,8 +26,11 @@ function showContentHistoryDetail(issueBaseUrl, commentId, historyId, itemTitleH
</div>
<div class="comment-diff-data is-loading"></div>
</div>`);
$dialog.appendTo($('body'));
$dialog.find('.dialog-header-options').dropdown({
document.body.append(elDetailDialog);
const elOptionsDropdown = elDetailDialog.querySelector('.ui.dropdown.dialog-header-options');
const $fomanticDialog = fomanticQuery(elDetailDialog);
const $fomanticDropdownOptions = fomanticQuery(elOptionsDropdown);
$fomanticDropdownOptions.dropdown({
showOnFocus: false,
allowReselection: true,
async onChange(_value, _text, $item) {
@ -46,7 +46,7 @@ function showContentHistoryDetail(issueBaseUrl, commentId, historyId, itemTitleH
const resp = await response.json();
if (resp.ok) {
$dialog.modal('hide');
$fomanticDialog.modal('hide');
} else {
showErrorToast(resp.message);
}
@ -60,10 +60,10 @@ function showContentHistoryDetail(issueBaseUrl, commentId, historyId, itemTitleH
}
},
onHide() {
$(this).dropdown('clear', true);
$fomanticDropdownOptions.dropdown('clear', true);
},
});
$dialog.modal({
$fomanticDialog.modal({
async onShow() {
try {
const params = new URLSearchParams();
@ -74,25 +74,25 @@ function showContentHistoryDetail(issueBaseUrl, commentId, historyId, itemTitleH
const response = await GET(url);
const resp = await response.json();
const commentDiffData = $dialog.find('.comment-diff-data')[0];
commentDiffData?.classList.remove('is-loading');
const commentDiffData = elDetailDialog.querySelector('.comment-diff-data');
commentDiffData.classList.remove('is-loading');
commentDiffData.innerHTML = resp.diffHtml;
// there is only one option "item[data-option-item=delete]", so the dropdown can be entirely shown/hidden.
if (resp.canSoftDelete) {
showElem($dialog.find('.dialog-header-options'));
showElem(elOptionsDropdown);
}
} catch (error) {
console.error('Error:', error);
}
},
onHidden() {
$dialog.remove();
$fomanticDialog.remove();
},
}).modal('show');
}
function showContentHistoryMenu(issueBaseUrl, $item, commentId) {
const $headerLeft = $item.find('.comment-header-left');
function showContentHistoryMenu(issueBaseUrl: string, elCommentItem: Element, commentId: string) {
const elHeaderLeft = elCommentItem.querySelector('.comment-header-left');
const menuHtml = `
<div class="ui dropdown interact-fg content-history-menu" data-comment-id="${commentId}">
&bull; ${i18nTextEdited}${svg('octicon-triangle-down', 14, 'dropdown icon')}
@ -100,9 +100,12 @@ function showContentHistoryMenu(issueBaseUrl, $item, commentId) {
</div>
</div>`;
$headerLeft.find(`.content-history-menu`).remove();
$headerLeft.append($(menuHtml));
$headerLeft.find('.dropdown').dropdown({
elHeaderLeft.querySelector(`.ui.dropdown.content-history-menu`)?.remove(); // remove the old one if exists
elHeaderLeft.append(createElementFromHTML(menuHtml));
const elDropdown = elHeaderLeft.querySelector('.ui.dropdown.content-history-menu');
const $fomanticDropdown = fomanticQuery(elDropdown);
$fomanticDropdown.dropdown({
action: 'hide',
apiSettings: {
cache: false,
@ -110,7 +113,7 @@ function showContentHistoryMenu(issueBaseUrl, $item, commentId) {
},
saveRemoteData: false,
onHide() {
$(this).dropdown('change values', null);
$fomanticDropdown.dropdown('change values', null);
},
onChange(value, itemHtml, $item) {
if (value && !$item.find('[data-history-is-deleted=1]').length) {
@ -124,9 +127,9 @@ export async function initRepoIssueContentHistory() {
const issuePageInfo = parseIssuePageInfo();
if (!issuePageInfo.issueNumber) return;
const $itemIssue = $('.repository.issue .timeline-item.comment.first'); // issue(PR) main content
const $comments = $('.repository.issue .comment-list .comment'); // includes: issue(PR) comments, review comments, code comments
if (!$itemIssue.length && !$comments.length) return;
const elIssueDescription = document.querySelector('.repository.issue .timeline-item.comment.first'); // issue(PR) main content
const elComments = document.querySelectorAll('.repository.issue .comment-list .comment'); // includes: issue(PR) comments, review comments, code comments
if (!elIssueDescription && !elComments.length) return;
const issueBaseUrl = `${issuePageInfo.repoLink}/issues/${issuePageInfo.issueNumber}`;
@ -139,13 +142,13 @@ export async function initRepoIssueContentHistory() {
i18nTextDeleteFromHistoryConfirm = resp.i18n.textDeleteFromHistoryConfirm;
i18nTextOptions = resp.i18n.textOptions;
if (resp.editedHistoryCountMap[0] && $itemIssue.length) {
showContentHistoryMenu(issueBaseUrl, $itemIssue, '0');
if (resp.editedHistoryCountMap[0] && elIssueDescription) {
showContentHistoryMenu(issueBaseUrl, elIssueDescription, '0');
}
for (const [commentId, _editedCount] of Object.entries(resp.editedHistoryCountMap)) {
if (commentId === '0') continue;
const $itemComment = $(`#issuecomment-${commentId}`);
showContentHistoryMenu(issueBaseUrl, $itemComment, commentId);
const elIssueComment = document.querySelector(`#issuecomment-${commentId}`);
if (elIssueComment) showContentHistoryMenu(issueBaseUrl, elIssueComment, commentId);
}
} catch (error) {
console.error('Error:', error);

View File

@ -30,6 +30,9 @@ async function tryOnEditContent(e) {
const saveAndRefresh = async (e) => {
e.preventDefault();
// we are already in a form, do not bubble up to the document otherwise there will be other "form submit handlers"
// at the moment, the form submit event conflicts with initRepoDiffConversationForm (global '.conversation-holder form' event handler)
e.stopPropagation();
renderContent.classList.add('is-loading');
showElem(renderContent);
hideElem(editContentZone);