Compare commits

...

29 Commits

Author SHA1 Message Date
Tyrone Yeh e39a78889a
Merge 0b78ae4161 into 29b28002aa 2025-06-23 03:16:56 +02:00
GiteaBot 29b28002aa [skip ci] Updated translations via Crowdin 2025-06-23 00:40:49 +00:00
Zettat123 618e2d8106
Fix required contexts and commit status matching bug (#34815)
Fix #34504

Since one required context can match more than one commit statuses, we
should not directly compare the lengths of `requiredCommitStatuses` and
`requiredContexts`

---------

Signed-off-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-22 23:31:46 +00:00
NorthRealm 485d8f1121
Add "Cancel workflow run" button to Actions list page (#34817) 2025-06-22 19:05:16 -04:00
Tyrone Yeh 0b78ae4161
Fix LoadProjects function 2025-05-26 09:04:53 +08:00
Tyrone Yeh 57957ecb64
Fix creating new issue redirect link from project 2025-05-26 08:59:30 +08:00
Tyrone Yeh 5c1bc2defc
Revert "Rename variable projectIDs to ids"
This reverts commit 7e6750e998.
2025-05-12 10:29:14 +08:00
Tyrone Yeh 7e6750e998
Rename variable projectIDs to ids 2025-05-12 08:22:09 +08:00
Tyrone Yeh 967a233cbe
Rename LoadProject to LoadProjects 2025-05-11 15:10:44 +08:00
Tyrone Yeh d132f84323
Change db.Exec to Update method 2025-05-09 15:59:24 +08:00
Tyrone Yeh 388533f97a
Fix list project item has icon issue 2025-05-09 15:20:55 +08:00
Tyrone Yeh ec78073456
Fix on issue list clear project issue 2025-05-09 14:45:45 +08:00
Tyrone Yeh 4c72c32f9b
Improve string join code for projects id 2025-05-09 14:29:18 +08:00
Tyrone Yeh e912b7e3db
Improve adjust A to B project bug 2025-05-09 08:45:37 +08:00
Tyrone Yeh cce417c3a4
Adjuect from Cols to Select 2025-05-08 15:20:24 +08:00
Tyrone Yeh 41be926458
Fix unit test on TestIssueLoadAttributes 2025-05-08 13:27:21 +08:00
Tyrone Yeh f5717d045c
Fix unit test on Test_Projects fail 2025-05-08 13:19:58 +08:00
Tyrone Yeh ef8fa684b1
Fix unit test on TestIssueList_LoadAttributes 2025-05-08 11:56:49 +08:00
Tyrone Yeh 0365eecd36
Fix no project filter list issue 2025-05-08 10:57:42 +08:00
Tyrone Yeh be48c8bada
Try fix unit test issue 2025-05-07 16:32:34 +08:00
Tyrone Yeh 6a272eb0f4
Try fix unit test fail 2025-05-07 15:31:23 +08:00
Tyrone Yeh 4f2532de42
Improject filter search method 2025-05-07 15:24:40 +08:00
Tyrone Yeh 1f43f8a566
Fix search empty issue 2025-05-07 14:55:42 +08:00
Tyrone Yeh e75c510cb2
Remove lessuse code 2025-05-07 13:27:14 +08:00
Tyrone Yeh f839864ee5
Fix add issue to multiple project issue 2025-05-07 13:24:24 +08:00
Tyrone Yeh 18f2d469d9
Fix lint issue 2025-05-07 13:15:35 +08:00
Tyrone Yeh a82a39d322
Try fixing your unit tests 2025-05-07 12:25:12 +08:00
Tyrone Yeh 3a2ffcbc97
Fix board column move issue 2025-05-07 10:41:18 +08:00
Tyrone Yeh d48061bb19
Added multi-project feature 2025-05-07 09:02:35 +08:00
62 changed files with 433 additions and 177 deletions

View File

@ -74,17 +74,17 @@ type Issue struct {
PosterID int64 `xorm:"INDEX"`
Poster *user_model.User `xorm:"-"`
OriginalAuthor string
OriginalAuthorID int64 `xorm:"index"`
Title string `xorm:"name"`
Content string `xorm:"LONGTEXT"`
RenderedContent template.HTML `xorm:"-"`
ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
Labels []*Label `xorm:"-"`
isLabelsLoaded bool `xorm:"-"`
MilestoneID int64 `xorm:"INDEX"`
Milestone *Milestone `xorm:"-"`
isMilestoneLoaded bool `xorm:"-"`
Project *project_model.Project `xorm:"-"`
OriginalAuthorID int64 `xorm:"index"`
Title string `xorm:"name"`
Content string `xorm:"LONGTEXT"`
RenderedContent template.HTML `xorm:"-"`
ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
Labels []*Label `xorm:"-"`
isLabelsLoaded bool `xorm:"-"`
MilestoneID int64 `xorm:"INDEX"`
Milestone *Milestone `xorm:"-"`
isMilestoneLoaded bool `xorm:"-"`
Projects []*project_model.Project `xorm:"-"`
Priority int
AssigneeID int64 `xorm:"-"`
Assignee *user_model.User `xorm:"-"`
@ -327,7 +327,7 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
return err
}
if err = issue.LoadProject(ctx); err != nil {
if err = issue.LoadProjects(ctx); err != nil {
return err
}

View File

@ -207,14 +207,19 @@ func (issues IssueList) LoadProjects(ctx context.Context) error {
return err
}
for _, project := range projects {
projectMaps[project.IssueID] = project.Project
projectMaps[project.ID] = project.Project
}
left -= limit
issueIDs = issueIDs[limit:]
}
for _, issue := range issues {
issue.Project = projectMaps[issue.ID]
projectIDs := issue.projectIDs(ctx)
for _, i := range projectIDs {
if projectMaps[i] != nil {
issue.Projects = append(issue.Projects, projectMaps[i])
}
}
}
return nil
}

View File

@ -66,10 +66,10 @@ func TestIssueList_LoadAttributes(t *testing.T) {
}
if issue.ID == int64(1) {
assert.Equal(t, int64(400), issue.TotalTrackedTime)
assert.NotNil(t, issue.Project)
assert.Equal(t, int64(1), issue.Project.ID)
assert.NotNil(t, issue.Projects[0])
assert.Equal(t, int64(1), issue.Projects[0].ID)
} else {
assert.Nil(t, issue.Project)
assert.Nil(t, issue.Projects)
}
}
}

View File

@ -13,28 +13,22 @@ import (
)
// LoadProject load the project the issue was assigned to
func (issue *Issue) LoadProject(ctx context.Context) (err error) {
if issue.Project == nil {
var p project_model.Project
has, err := db.GetEngine(ctx).Table("project").
func (issue *Issue) LoadProjects(ctx context.Context) (err error) {
if issue.Projects == nil {
err = db.GetEngine(ctx).Table("project").
Join("INNER", "project_issue", "project.id=project_issue.project_id").
Where("project_issue.issue_id = ?", issue.ID).Get(&p)
if err != nil {
return err
} else if has {
issue.Project = &p
}
Where("project_issue.issue_id = ?", issue.ID).Find(&issue.Projects)
}
return err
}
func (issue *Issue) projectID(ctx context.Context) int64 {
var ip project_model.ProjectIssue
has, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Get(&ip)
if err != nil || !has {
return 0
func (issue *Issue) projectIDs(ctx context.Context) []int64 {
var ids []int64
if err := db.GetEngine(ctx).Table("project_issue").Where("issue_id=?", issue.ID).Select("project_id").Find(&ids); err != nil {
return nil
}
return ip.ProjectID
return ids
}
// ProjectColumnID return project column id if issue was assigned to one
@ -68,7 +62,7 @@ func LoadProjectIssueColumnMap(ctx context.Context, projectID, defaultColumnID i
func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column, opts *IssuesOptions) (IssueList, error) {
issueList, err := Issues(ctx, opts.Copy(func(o *IssuesOptions) {
o.ProjectColumnID = b.ID
o.ProjectID = b.ProjectID
o.ProjectIDs = []int64{b.ProjectID}
o.SortType = "project-column-sorting"
}))
if err != nil {
@ -78,7 +72,7 @@ func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column, opts *Is
if b.Default {
issues, err := Issues(ctx, opts.Copy(func(o *IssuesOptions) {
o.ProjectColumnID = db.NoConditionID
o.ProjectID = b.ProjectID
o.ProjectIDs = []int64{b.ProjectID}
o.SortType = "project-column-sorting"
}))
if err != nil {
@ -96,71 +90,88 @@ func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column, opts *Is
// IssueAssignOrRemoveProject changes the project associated with an issue
// If newProjectID is 0, the issue is removed from the project
func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID, newColumnID int64) error {
func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectIDs []int64, newColumnID int64) error {
return db.WithTx(ctx, func(ctx context.Context) error {
oldProjectID := issue.projectID(ctx)
oldProjectIDs := issue.projectIDs(ctx)
if err := issue.LoadRepo(ctx); err != nil {
return err
}
// Only check if we add a new project and not remove it.
if newProjectID > 0 {
newProject, err := project_model.GetProjectByID(ctx, newProjectID)
if err != nil {
projectDB := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID)
newProjectIDs, oldProjectIDs := util.DiffSlice(oldProjectIDs, newProjectIDs)
if len(oldProjectIDs) > 0 {
if _, err := projectDB.Where("issue_id=?", issue.ID).In("project_id", oldProjectIDs).Delete(&project_model.ProjectIssue{}); err != nil {
return err
}
if !newProject.CanBeAccessedByOwnerRepo(issue.Repo.OwnerID, issue.Repo) {
return util.NewPermissionDeniedErrorf("issue %d can't be accessed by project %d", issue.ID, newProject.ID)
}
if newColumnID == 0 {
newDefaultColumn, err := newProject.MustDefaultColumn(ctx)
if err != nil {
for _, pID := range oldProjectIDs {
if _, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeProject,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
OldProjectID: pID,
ProjectID: 0,
}); err != nil {
return err
}
newColumnID = newDefaultColumn.ID
}
}
if _, err := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil {
return err
}
if oldProjectID > 0 || newProjectID > 0 {
if _, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeProject,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
OldProjectID: oldProjectID,
ProjectID: newProjectID,
}); err != nil {
return err
}
}
if newProjectID == 0 {
if len(newProjectIDs) == 0 {
return nil
}
if newColumnID == 0 {
panic("newColumnID must not be zero") // shouldn't happen
}
res := struct {
MaxSorting int64
IssueCount int64
}{}
if _, err := db.GetEngine(ctx).Select("max(sorting) as max_sorting, count(*) as issue_count").Table("project_issue").
Where("project_id=?", newProjectID).
if _, err := projectDB.Select("max(sorting) as max_sorting, count(*) as issue_count").Table("project_issue").
In("project_id", newProjectIDs).
And("project_board_id=?", newColumnID).
Get(&res); err != nil {
return err
}
newSorting := util.Iif(res.IssueCount > 0, res.MaxSorting+1, 0)
return db.Insert(ctx, &project_model.ProjectIssue{
IssueID: issue.ID,
ProjectID: newProjectID,
ProjectColumnID: newColumnID,
Sorting: newSorting,
})
pi := make([]*project_model.ProjectIssue, 0, len(newProjectIDs))
for _, pID := range newProjectIDs {
if pID == 0 {
continue
}
newProject, err := project_model.GetProjectByID(ctx, pID)
if err != nil {
return err
}
if !newProject.CanBeAccessedByOwnerRepo(issue.Repo.OwnerID, issue.Repo) {
return util.NewPermissionDeniedErrorf("issue %d can't be accessed by project %d", issue.ID, newProject.ID)
}
pi = append(pi, &project_model.ProjectIssue{
IssueID: issue.ID,
ProjectID: pID,
ProjectColumnID: newColumnID,
Sorting: newSorting,
})
if _, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeProject,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
OldProjectID: 0,
ProjectID: pID,
}); err != nil {
return err
}
}
if len(pi) > 0 {
return db.Insert(ctx, pi)
}
return nil
})
}

View File

@ -16,6 +16,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
"xorm.io/xorm"
@ -36,7 +37,7 @@ type IssuesOptions struct { //nolint
ReviewedID int64
SubscriberID int64
MilestoneIDs []int64
ProjectID int64
ProjectIDs []int64
ProjectColumnID int64
IsClosed optional.Option[bool]
IsPull optional.Option[bool]
@ -198,11 +199,12 @@ func applyMilestoneCondition(sess *xorm.Session, opts *IssuesOptions) {
}
func applyProjectCondition(sess *xorm.Session, opts *IssuesOptions) {
if opts.ProjectID > 0 { // specific project
opts.ProjectIDs = util.RemoveValue(opts.ProjectIDs, 0)
if len(opts.ProjectIDs) == 1 && opts.ProjectIDs[0] == db.NoConditionID { // show those that are in no project
sess.And(builder.NotIn("issue.id", builder.Select("issue_id").From("project_issue")))
} else if len(opts.ProjectIDs) > 0 { // specific project
sess.Join("INNER", "project_issue", "issue.id = project_issue.issue_id").
And("project_issue.project_id=?", opts.ProjectID)
} else if opts.ProjectID == db.NoConditionID { // show those that are in no project
sess.And(builder.NotIn("issue.id", builder.Select("issue_id").From("project_issue").And(builder.Neq{"project_id": 0})))
In("project_issue.project_id", opts.ProjectIDs)
}
// opts.ProjectID == 0 means all projects,
// do not need to apply any condition

View File

@ -418,10 +418,10 @@ func TestIssueLoadAttributes(t *testing.T) {
}
if issue.ID == int64(1) {
assert.Equal(t, int64(400), issue.TotalTrackedTime)
assert.NotNil(t, issue.Project)
assert.Equal(t, int64(1), issue.Project.ID)
assert.NotNil(t, issue.Projects[0])
assert.Equal(t, int64(1), issue.Projects[0].ID)
} else {
assert.Nil(t, issue.Project)
assert.Nil(t, issue.Projects)
}
}
}

View File

@ -241,9 +241,14 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
queries = append(queries, bleve.NewDisjunctionQuery(milestoneQueries...))
}
if options.ProjectID.Has() {
queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectID.Value(), "project_id"))
if len(options.ProjectIDs) > 0 {
var projectQueries []query.Query
for _, projectID := range options.ProjectIDs {
projectQueries = append(projectQueries, inner_bleve.NumericEqualityQuery(projectID, "project_id"))
}
queries = append(queries, bleve.NewDisjunctionQuery(projectQueries...))
}
if options.ProjectColumnID.Has() {
queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectColumnID.Value(), "project_board_id"))
}

View File

@ -65,7 +65,6 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m
ReviewRequestedID: convertID(options.ReviewRequestedID),
ReviewedID: convertID(options.ReviewedID),
SubscriberID: convertID(options.SubscriberID),
ProjectID: convertID(options.ProjectID),
ProjectColumnID: convertID(options.ProjectColumnID),
IsClosed: options.IsClosed,
IsPull: options.IsPull,
@ -88,6 +87,10 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m
opts.MilestoneIDs = options.MilestoneIDs
}
if len(options.ProjectIDs) > 0 {
opts.ProjectIDs = options.ProjectIDs
}
if options.NoLabelOnly {
opts.LabelIDs = []int64{0} // Be careful, it's zero, not db.NoConditionID
} else {

View File

@ -46,10 +46,8 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
searchOpt.MilestoneIDs = opts.MilestoneIDs
}
if opts.ProjectID > 0 {
searchOpt.ProjectID = optional.Some(opts.ProjectID)
} else if opts.ProjectID == db.NoConditionID { // FIXME: this is inconsistent from other places
searchOpt.ProjectID = optional.Some[int64](0) // Those issues with no project(projectid==0)
if len(opts.ProjectIDs) > 0 {
searchOpt.ProjectIDs = opts.ProjectIDs
}
searchOpt.AssigneeID = opts.AssigneeID

View File

@ -204,8 +204,8 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
query.Must(elastic.NewTermsQuery("milestone_id", toAnySlice(options.MilestoneIDs)...))
}
if options.ProjectID.Has() {
query.Must(elastic.NewTermQuery("project_id", options.ProjectID.Value()))
if len(options.ProjectIDs) > 0 {
query.Must(elastic.NewTermsQuery("project_id", toAnySlice(options.ProjectIDs)...))
}
if options.ProjectColumnID.Has() {
query.Must(elastic.NewTermQuery("project_board_id", options.ProjectColumnID.Value()))

View File

@ -416,7 +416,7 @@ func searchIssueInProject(t *testing.T) {
}{
{
SearchOptions{
ProjectID: optional.Some(int64(1)),
ProjectIDs: optional.Some(int64(1)),
},
[]int64{5, 3, 2, 1},
},

View File

@ -30,7 +30,7 @@ type IndexerData struct {
LabelIDs []int64 `json:"label_ids"`
NoLabel bool `json:"no_label"` // True if LabelIDs is empty
MilestoneID int64 `json:"milestone_id"`
ProjectID int64 `json:"project_id"`
ProjectIDs []int64 `json:"project_id"`
ProjectColumnID int64 `json:"project_board_id"` // the key should be kept as project_board_id to keep compatible
PosterID int64 `json:"poster_id"`
AssigneeID int64 `json:"assignee_id"`
@ -94,7 +94,7 @@ type SearchOptions struct {
MilestoneIDs []int64 // milestones the issues have
ProjectID optional.Option[int64] // project the issues belong to
ProjectIDs []int64 // project the issues belong to
ProjectColumnID optional.Option[int64] // project column the issues belong to
PosterID string // poster of the issues, "(none)" or "(any)" or a user ID

View File

@ -301,20 +301,25 @@ var cases = []*testIndexerCase{
},
},
{
Name: "ProjectID",
Name: "ProjectIDs",
SearchOptions: &internal.SearchOptions{
Paginator: &db.ListOptions{
PageSize: 5,
},
ProjectID: optional.Some(int64(1)),
ProjectIDs: []int64{1},
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
assert.Len(t, result.Hits, 5)
for _, v := range result.Hits {
assert.Equal(t, int64(1), data[v.ID].ProjectID)
if len(data[v.ID].ProjectIDs) > 0 {
assert.Equal(t, int64(1), data[v.ID].ProjectIDs[0])
}
}
assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool {
return v.ProjectID == 1
if len(data[v.ID].ProjectIDs) > 0 {
return v.ProjectIDs[0] == 1
}
return false
}), result.Total)
},
},
@ -324,15 +329,20 @@ var cases = []*testIndexerCase{
Paginator: &db.ListOptions{
PageSize: 5,
},
ProjectID: optional.Some(int64(0)),
ProjectIDs: []int64{0},
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
assert.Len(t, result.Hits, 5)
for _, v := range result.Hits {
assert.Equal(t, int64(0), data[v.ID].ProjectID)
if len(data[v.ID].ProjectIDs) > 0 {
assert.Equal(t, int64(0), data[v.ID].ProjectIDs[0])
}
}
assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool {
return v.ProjectID == 0
if len(data[v.ID].ProjectIDs) > 0 {
return v.ProjectIDs[0] == 1
}
return false
}), result.Total)
},
},
@ -719,7 +729,7 @@ func generateDefaultIndexerData() []*internal.IndexerData {
LabelIDs: labelIDs,
NoLabel: len(labelIDs) == 0,
MilestoneID: issueIndex % 4,
ProjectID: issueIndex % 5,
ProjectIDs: []int64{issueIndex % 5},
ProjectColumnID: issueIndex % 6,
PosterID: id%10 + 1, // PosterID should not be 0
AssigneeID: issueIndex % 10,

View File

@ -180,8 +180,8 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
query.And(inner_meilisearch.NewFilterIn("milestone_id", options.MilestoneIDs...))
}
if options.ProjectID.Has() {
query.And(inner_meilisearch.NewFilterEq("project_id", options.ProjectID.Value()))
if len(options.ProjectIDs) > 0 {
query.And(inner_meilisearch.NewFilterIn("project_id", options.ProjectIDs...))
}
if options.ProjectColumnID.Has() {
query.And(inner_meilisearch.NewFilterEq("project_board_id", options.ProjectColumnID.Value()))

View File

@ -87,9 +87,9 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD
return nil, false, err
}
var projectID int64
if issue.Project != nil {
projectID = issue.Project.ID
projectIDs := make([]int64, 0, len(issue.Projects))
for _, project := range issue.Projects {
projectIDs = append(projectIDs, project.ID)
}
projectColumnID, err := issue.ProjectColumnID(ctx)
@ -110,7 +110,7 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD
LabelIDs: labels,
NoLabel: len(labels) == 0,
MilestoneID: issue.MilestoneID,
ProjectID: projectID,
ProjectIDs: projectIDs,
ProjectColumnID: projectColumnID,
PosterID: issue.PosterID,
AssigneeID: issue.AssigneeID,

View File

@ -253,3 +253,49 @@ func ReserveLineBreakForTextarea(input string) string {
// Other than this, we should respect the original content, even leading or trailing spaces.
return strings.ReplaceAll(input, "\r\n", "\n")
}
func DiffSlice[T comparable](oldSlice, newSlice []T) (added, removed []T) {
oldSet := make(map[T]struct{}, len(oldSlice))
newSet := make(map[T]struct{}, len(newSlice))
for _, v := range oldSlice {
oldSet[v] = struct{}{}
}
for _, v := range newSlice {
newSet[v] = struct{}{}
}
for v := range newSet {
if _, found := oldSet[v]; !found {
added = append(added, v)
}
}
for v := range oldSet {
if _, found := newSet[v]; !found {
removed = append(removed, v)
}
}
return added, removed
}
func RemoveValue[T comparable](a []T, target T) []T {
n := 0
for _, v := range a {
if v != target {
a[n] = v
n++
}
}
return a[:n]
}
func JoinSlice[T any](items []T, toString func(T) string) string {
var b strings.Builder
sep := ""
for _, item := range items {
b.WriteString(sep)
b.WriteString(toString(item))
sep = ","
}
return b.String()
}

View File

@ -1368,6 +1368,7 @@ editor.require_signed_commit=Větev vyžaduje podepsaný commit
editor.cherry_pick=Cherry-pick %s na:
editor.revert=Vrátit %s na:
commits.desc=Procházet historii změn zdrojového kódu.
commits.commits=Commity
commits.no_commits=Žádné společné commity. „%s“ a „%s“ mají zcela odlišnou historii.

View File

@ -1392,6 +1392,7 @@ editor.require_signed_commit=Branch erfordert einen signierten Commit
editor.cherry_pick=Cherry-Picke %s von:
editor.revert=%s zurücksetzen auf:
commits.desc=Durchsuche die Quellcode-Änderungshistorie.
commits.commits=Commits
commits.no_commits=Keine gemeinsamen Commits. "%s" und "%s" haben vollständig unterschiedliche Historien.

View File

@ -1226,6 +1226,7 @@ editor.require_signed_commit=Ο κλάδος απαιτεί υπογεγραμμ
editor.cherry_pick=Ανθολόγηση (cherry-pic) του %s στο:
editor.revert=Απόσυρση του %s στο:
commits.desc=Δείτε το ιστορικό αλλαγών του πηγαίου κώδικα.
commits.commits=Υποβολές
commits.no_commits=Δεν υπάρχουν κοινές υποβολές. Τα "%s" και "%s" έχουν εντελώς διαφορετικές ιστορίες.

View File

@ -3839,6 +3839,7 @@ runs.no_runs = The workflow has no runs yet.
runs.empty_commit_message = (empty commit message)
runs.expire_log_message = Logs have been purged because they were too old.
runs.delete = Delete workflow run
runs.cancel = Cancel workflow run
runs.delete.description = Are you sure you want to permanently delete this workflow run? This action cannot be undone.
runs.not_done = This workflow run is not done.
runs.view_workflow_file = View workflow file

View File

@ -1216,6 +1216,7 @@ editor.require_signed_commit=Esta rama requiere un commit firmado
editor.cherry_pick=Hacer Cherry-pick %s en:
editor.revert=Revertir %s en:
commits.desc=Ver el historial de cambios de código fuente.
commits.commits=Commits
commits.no_commits=No hay commits en común. "%s" y "%s" tienen historias totalmente diferentes.

View File

@ -949,6 +949,7 @@ editor.no_commit_to_branch=نمی‌توان به طور مستقیم درمور
editor.user_no_push_to_branch=کاربر نمیتواند به شاخه ارسال کند
editor.require_signed_commit=شاخه یک کامیت امضا شده لازم دارد
commits.desc=تاریخچه تغییرات کد منبع را مرور کنید.
commits.commits=کامیت‌ها
commits.nothing_to_compare=این شاخه ها برابرند.

View File

@ -764,6 +764,7 @@ editor.no_changes_to_show=Ei muutoksia näytettäväksi.
editor.add_subdir=Lisää hakemisto…
editor.require_signed_commit=Haara vaatii vahvistetun commitin
commits.commits=Commitit
commits.nothing_to_compare=Nämä haarat vastaavat toisiaan.
commits.search_all=Kaikki haarat

View File

@ -1394,6 +1394,7 @@ editor.require_signed_commit=Cette branche nécessite une révision signée
editor.cherry_pick=Picorer %s vers:
editor.revert=Rétablir %s sur:
commits.desc=Naviguer dans l'historique des modifications.
commits.commits=Révisions
commits.no_commits=Pas de révisions en commun. "%s" et "%s" ont des historiques entièrement différents.

View File

@ -421,6 +421,7 @@ remember_me.compromised=Níl an comhartha logála isteach bailí níos mó a d'f
forgot_password_title=Dearmad ar an bPasfhocal
forgot_password=Dearmad ar an bPasfhocal?
need_account=An bhfuil cuntas ag teastáil uait?
sign_up_tip=Tá tú ag clárú an chéad chuntais sa chóras, a bhfuil pribhléidí riarthóra aige. Cuimhnigh go cúramach ar dainm úsáideora agus do phasfhocal. Má dhéanann tú dearmad ar an ainm úsáideora nó ar an pasfhocal, féach ar dhoiciméadacht Gitea le do thoil chun an cuntas a aisghabháil.
sign_up_now=Cláraigh anois.
sign_up_successful=Cruthaíodh cuntas go rathúil. Fáilte romhat!
confirmation_mail_sent_prompt_ex=Tá ríomhphost dearbhaithe nua seolta chuig <b>%s</b>. Seiceáil do bhosca isteach laistigh den chéad %s eile chun an próiseas clárúcháin a chur i gcrích. Má tá do sheoladh ríomhphoist clárúcháin mícheart, is féidir leat síniú isteach arís agus é a athrú.
@ -1373,6 +1374,7 @@ editor.branch_already_exists=Tá brainse "%s" ann cheana féin sa stóras seo.
editor.directory_is_a_file=Úsáidtear ainm eolaire "%s" cheana féin mar ainm comhaid sa stóras seo.
editor.file_is_a_symlink=Is nasc siombalach é "%s". Ní féidir naisc shiombalacha a chur in eagar san eagarthóir gréasáin
editor.filename_is_a_directory=Úsáidtear ainm comhaid "%s" cheana féin mar ainm eolaire sa stóras seo.
editor.file_modifying_no_longer_exists=Níl an comhad atá á mhodhnú, "%s", sa stóras seo a thuilleadh.
editor.file_changed_while_editing=Tá athrú tagtha ar ábhar an chomhad ó thosaigh tú ag eagarthóireacht <a target="_blank" rel="noopener noreferrer" href="%s">Cliceáil anseo</a> chun iad a fheiceáil nó Athru <strong>ithe a Tiomantas arís</strong> chun iad a fhorscríobh.
editor.file_already_exists=Tá comhad darb ainm "%s" ann cheana féin sa stóras seo.
editor.commit_id_not_matching=Ní mheaitseálann an ID Tiomanta leis an ID nuair a thosaigh tú ag eagarthóireacht. Tiomanta isteach i mbrainse paiste agus ansin cumaisc.
@ -1393,6 +1395,9 @@ editor.user_no_push_to_branch=Ní féidir leis an úsáideoir brúigh go dtí an
editor.require_signed_commit=Éilíonn an Brainse tiomantas sínithe
editor.cherry_pick=Roghnaigh silíní %s ar:
editor.revert=Fill %s ar:
editor.failed_to_commit=Theip ar athruithe a chur i bhfeidhm.
editor.failed_to_commit_summary=Teachtaireacht Earráide:
commits.desc=Brabhsáil stair athraithe cód foinse.
commits.commits=Tiomáintí
@ -2807,6 +2812,7 @@ team_permission_desc=Cead
team_unit_desc=Ceadaigh Rochtain ar Rannóga Stóras
team_unit_disabled=(Díchumasaithe)
form.name_been_taken=Tá ainm na heagraíochta "%s" tógtha cheana féin.
form.name_reserved=Tá an t-ainm eagraíochta "%s" curtha in áirithe.
form.name_pattern_not_allowed=Ní cheadaítear an patrún "%s" in ainm eagraíochta.
form.create_org_not_allowed=Níl cead agat eagraíocht a chruthú.
@ -2829,12 +2835,27 @@ settings.visibility.private_shortname=Príobháideach
settings.update_settings=Nuashonrú Socruithe
settings.update_setting_success=Nuashonraíodh socruithe eagraíochta.
settings.rename=Athainmnigh an Eagraíocht
settings.rename_desc=Má athraíonn tú ainm na heagraíochta, athrófar URL deagraíochta freisin agus saorfar an seanainm.
settings.rename_success=Athainmníodh an eagraíocht %[1]s go %[2]s go rathúil.
settings.rename_no_change=Níl aon athrú ar ainm na heagraíochta.
settings.rename_new_org_name=Ainm Nua na hEagraíochta
settings.rename_failed=Theip ar athainmniú na hEagraíochta mar gheall ar earráid inmheánach
settings.rename_notices_1=NÍ <strong>FÉIDIR</strong> an oibríocht seo a chealú.
settings.rename_notices_2=Déanfar an seanainm a atreorú go dtí go n-éileofar é.
settings.update_avatar_success=Nuashonraíodh avatar na heagraíochta.
settings.delete=Scrios Eagraíocht
settings.delete_account=Scrios an Eagraíocht seo
settings.delete_prompt=Bainfear an eagraíocht go buan. <strong>NÍ FÉIDIR</strong> é seo a chealú!
settings.name_confirm=Cuir isteach ainm na heagraíochta mar dheimhniú:
settings.delete_notices_1=NÍ <strong>FÉIDIR</strong> an oibríocht seo a chealú.
settings.delete_notices_2=Scriosfaidh an oibríocht seo go buan gach <strong>stórais</strong> de chuid <strong>%s</strong>, lena n-áirítear cód, saincheisteanna, tuairimí, sonraí vicí agus socruithe comhoibritheora.
settings.delete_notices_3=Scriosfaidh an oibríocht seo gach <strong>pacáiste</strong> de chuid <strong>%s</strong> go buan.
settings.delete_notices_4=Scriosfaidh an oibríocht seo gach <strong>tionscadal</strong> de chuid <strong>%s</strong> go buan.
settings.confirm_delete_account=Deimhnigh scriosadh
settings.delete_failed=Theip ar Scriosadh na hEagraíochta mar gheall ar earráid inmheánach
settings.delete_successful=Scriosadh an eagraíocht <b>%s</b> go rathúil.
settings.hooks_desc=Cuir crúcaí gréasán in leis a spreagfar do <strong>gach stóras</strong> faoin eagraíocht seo.
settings.labels_desc=Cuir lipéid leis ar féidir iad a úsáid ar shaincheisteanna do <strong>gach stóras</strong> faoin eagraíocht seo.

View File

@ -711,6 +711,7 @@ editor.commit_empty_file_header=Egy üres fájl commitolása
editor.no_changes_to_show=Nincsen megjeleníthető változás.
editor.add_subdir=Mappa hozzáadása…
commits.commits=Commit-ok
commits.search_all=Minden ág
commits.author=Szerző

View File

@ -717,6 +717,7 @@ editor.new_branch_name_desc=Nama branch baru…
editor.cancel=Membatalkan
editor.no_changes_to_show=Tidak ada perubahan untuk ditampilkan.
commits.commits=Melakukan
commits.author=Penulis
commits.message=Pesan

View File

@ -690,6 +690,7 @@ editor.create_new_branch_np=Búðu til <strong>nýja grein</strong> fyrir þetta
editor.new_branch_name_desc=Heiti nýjar greinar…
editor.cancel=Hætta við
commits.commits=Framlög
commits.author=Höfundur
commits.message=Skilaboð

View File

@ -1024,6 +1024,7 @@ editor.require_signed_commit=Il branch richiede un commit firmato
editor.cherry_pick=Cherry-pick %s suto:
editor.revert=Ripristina %s su:
commits.desc=Sfoglia la cronologia di modifiche del codice rogente.
commits.commits=Commit
commits.nothing_to_compare=Questi rami sono uguali.

View File

@ -421,6 +421,7 @@ remember_me.compromised=ログイントークンはもう有効ではなく、
forgot_password_title=パスワードを忘れた
forgot_password=パスワードをお忘れですか?
need_account=アカウントが必要ですか?
sign_up_tip=管理者権限を持つ、このシステムの最初のアカウントを登録しようとしています。 ユーザー名とパスワードをよく覚えておいてください。 ユーザー名またはパスワードを忘れた場合は、Giteaのドキュメントを参照してアカウントを復元してください。
sign_up_now=登録はこちら。
sign_up_successful=アカウントは無事に作成されました。ようこそ!
confirmation_mail_sent_prompt_ex=新しい確認メールを <b>%s</b> に送信しました。 %s以内にメールボックスを確認し、登録手続きを完了してください。 登録メールアドレスが間違っている場合は、もういちどサインインすると変更することができます。
@ -1332,7 +1333,9 @@ editor.upload_file=ファイルをアップロード
editor.edit_file=ファイルを編集
editor.preview_changes=変更をプレビュー
editor.cannot_edit_lfs_files=LFSのファイルはWebインターフェースで編集できません。
editor.cannot_edit_too_large_file=このファイルは大きすぎるため、編集できません。
editor.cannot_edit_non_text_files=バイナリファイルはWebインターフェースで編集できません。
editor.file_not_editable_hint=名前の変更や移動は可能です。
editor.edit_this_file=ファイルを編集
editor.this_file_locked=ファイルはロックされています
editor.must_be_on_a_branch=このファイルを変更したり変更の提案をするには、ブランチ上にいる必要があります。
@ -1352,6 +1355,7 @@ editor.update=%s を更新
editor.delete=%s を削除
editor.patch=パッチの適用
editor.patching=パッチ:
editor.fail_to_apply_patch=パッチを適用できません
editor.new_patch=新しいパッチ
editor.commit_message_desc=詳細な説明を追加…
editor.signoff_desc=コミットログメッセージの最後にコミッターの Signed-off-by 行を追加
@ -1371,6 +1375,7 @@ editor.branch_already_exists=ブランチ "%s" は、このリポジトリに既
editor.directory_is_a_file=ディレクトリ名 "%s" はすでにリポジトリ内のファイルで使用されています。
editor.file_is_a_symlink=`"%s" はシンボリックリンクです。 シンボリックリンクはWebエディターで編集できません。`
editor.filename_is_a_directory=ファイル名 "%s" は、このリポジトリ上でディレクトリ名としてすでに使用されています。
editor.file_modifying_no_longer_exists=修正中のファイル "%s" が、すでにリポジトリ内にありません。
editor.file_changed_while_editing=あなたが編集を開始したあと、ファイルの内容が変更されました。 <a target="_blank" rel="noopener noreferrer" href="%s">ここをクリック</a>して何が変更されたか確認するか、<strong>もう一度"変更をコミット"をクリック</strong>して上書きします。
editor.file_already_exists=ファイル "%s" は、このリポジトリに既に存在します。
editor.commit_id_not_matching=コミットIDが編集を開始したときのIDと一致しません。 パッチ用のブランチにコミットしたあとマージしてください。
@ -1391,6 +1396,9 @@ editor.user_no_push_to_branch=ユーザーはブランチにプッシュでき
editor.require_signed_commit=ブランチでは署名されたコミットが必須です
editor.cherry_pick=チェリーピック %s:
editor.revert=リバート %s:
editor.failed_to_commit=変更のコミットに失敗しました。
editor.failed_to_commit_summary=エラーメッセージ:
commits.desc=ソースコードの変更履歴を参照します。
commits.commits=コミット
@ -1555,6 +1563,7 @@ issues.filter_user_placeholder=ユーザーを検索
issues.filter_user_no_select=すべてのユーザー
issues.filter_type=タイプ
issues.filter_type.all_issues=すべてのイシュー
issues.filter_type.all_pull_requests=すべてのプルリクエスト
issues.filter_type.assigned_to_you=自分が担当
issues.filter_type.created_by_you=自分が作成
issues.filter_type.mentioning_you=自分が関係
@ -1646,6 +1655,7 @@ issues.save=保存
issues.label_title=名前
issues.label_description=説明
issues.label_color=カラー
issues.label_color_invalid=無効な色です
issues.label_exclusive=排他
issues.label_archive=アーカイブ ラベル
issues.label_archived_filter=アーカイブされたラベルを表示
@ -2394,6 +2404,8 @@ settings.event_pull_request_review_request_desc=プルリクエストのレビ
settings.event_pull_request_approvals=プルリクエストの承認
settings.event_pull_request_merge=プルリクエストのマージ
settings.event_header_workflow=ワークフローイベント
settings.event_workflow_run=ワークフロー実行
settings.event_workflow_run_desc=Gitea Actions のワークフロー実行が、キューに追加、待機中、実行中、完了になったとき。
settings.event_workflow_job=ワークフロージョブ
settings.event_workflow_job_desc=Gitea Actions のワークフロージョブが、キューに追加、待機中、実行中、完了になったとき。
settings.event_package=パッケージ
@ -2801,6 +2813,7 @@ team_permission_desc=権限
team_unit_desc=リポジトリのセクションへのアクセスを許可
team_unit_disabled=(無効)
form.name_been_taken=組織名 "%s" は既に使用されています。
form.name_reserved=組織名 "%s" は予約されています。
form.name_pattern_not_allowed=`"%s" の形式は組織名に使用できません。`
form.create_org_not_allowed=組織を作成する権限がありません。
@ -2823,12 +2836,27 @@ settings.visibility.private_shortname=プライベート
settings.update_settings=設定の更新
settings.update_setting_success=組織の設定を更新しました。
settings.rename=組織名の変更
settings.rename_desc=組織名を変更すると組織のURLも変更され、古い名前は解放されます。
settings.rename_success=組織 %[1]s の %[2]s への改名に成功しました。
settings.rename_no_change=組織名の変更はありません。
settings.rename_new_org_name=新しい組織名
settings.rename_failed=内部エラーのため組織名を変更できませんでした
settings.rename_notices_1=この操作は<strong>元に戻せません</strong> 。
settings.rename_notices_2=古い名前は、再使用されるまではリダイレクトします。
settings.update_avatar_success=組織のアバターを更新しました。
settings.delete=組織を削除
settings.delete_account=この組織を削除
settings.delete_prompt=組織は恒久的に削除されます。 元に戻すことは<strong>できません</strong>
settings.name_confirm=確認のため組織名を入力:
settings.delete_notices_1=この操作は<strong>元に戻せません</strong> 。
settings.delete_notices_2=この操作により、<strong>%s</strong>のすべての<strong>リポジトリ</strong>が恒久的に削除されます。 コード、イシュー、コメント、Wikiデータ、共同作業者の設定も含まれます。
settings.delete_notices_3=この操作により、<strong>%s</strong>のすべての<strong>パッケージ</strong>が恒久的に削除されます。
settings.delete_notices_4=この操作により、<strong>%s</strong>のすべての<strong>プロジェクト</strong>が恒久的に削除されます。
settings.confirm_delete_account=削除を確認
settings.delete_failed=内部エラーのため組織を削除できませんでした
settings.delete_successful=組織の<b>%s</b>の削除に成功しました。
settings.hooks_desc=この組織の<strong>すべてのリポジトリ</strong>でトリガーされるWebhookを追加します。
settings.labels_desc=この組織の<strong>すべてのリポジトリ</strong>で使用可能なイシューラベルを追加します。

View File

@ -648,6 +648,7 @@ editor.filename_cannot_be_empty=파일명이 빈칸입니다.
editor.no_changes_to_show=표시할 변경사항이 없습니다.
editor.add_subdir=경로 추가...
commits.desc=소스 코드 변경 내역 탐색
commits.commits=커밋
commits.search_all=모든 브랜치

View File

@ -1232,6 +1232,7 @@ editor.require_signed_commit=Atzarā var iesūtīt tikai parakstītas revīzijas
editor.cherry_pick=Izlasīt %s uz:
editor.revert=Atgriezt %s uz:
commits.desc=Pārlūkot pirmkoda izmaiņu vēsturi.
commits.commits=Revīzijas
commits.no_commits=Nav kopīgu revīziju. Atzariem "%s" un "%s" ir pilnībā atšķirīga izmaiņu vēsture.

View File

@ -1022,6 +1022,7 @@ editor.require_signed_commit=Branch vereist een ondertekende commit
editor.cherry_pick=Cherry-pick %s op:
editor.revert=%s ongedaan maken op:
commits.desc=Bekijk de broncode-wijzigingsgeschiedenis.
commits.commits=Commits
commits.nothing_to_compare=Deze branches zijn gelijk.

View File

@ -948,6 +948,7 @@ editor.no_commit_to_branch=Zatwierdzanie bezpośrednio do tej gałęzi nie jest
editor.user_no_push_to_branch=Użytkownik nie może wypychać do gałęzi
editor.require_signed_commit=Gałąź wymaga podpisanych commitów
commits.desc=Przeglądaj historię zmian kodu źródłowego.
commits.commits=Commity
commits.search_all=Wszystkie gałęzie

View File

@ -1227,6 +1227,7 @@ editor.require_signed_commit=Branch requer um commit assinado
editor.cherry_pick=Cherry-pick %s para:
editor.revert=Reverter %s para:
commits.desc=Veja o histórico de alterações do código de fonte.
commits.commits=Commits
commits.no_commits=Nenhum commit em comum. "%s" e "%s" tem históricos completamente diferentes.

View File

@ -1393,6 +1393,7 @@ editor.require_signed_commit=O ramo requer um cometimento assinado
editor.cherry_pick=Escolher a dedo %s para:
editor.revert=Reverter %s para:
commits.desc=Navegar pelo histórico de modificações no código fonte.
commits.commits=Cometimentos
commits.no_commits=Não há cometimentos em comum. "%s" e "%s" têm históricos completamente diferentes.

View File

@ -1205,6 +1205,7 @@ editor.require_signed_commit=Ветка ожидает подписанный к
editor.cherry_pick=Перенести изменения %s в:
editor.revert=Откатить %s к:
commits.desc=Просмотр истории изменений исходного кода.
commits.commits=Коммитов
commits.no_commits=Нет общих коммитов. «%s» и «%s» имеют совершенно разные истории.

View File

@ -923,6 +923,7 @@ editor.no_commit_to_branch=ශාඛාවට කෙලින්ම කැපව
editor.user_no_push_to_branch=පරිශීලකයාට ශාඛාවට තල්ලු කළ නොහැක
editor.require_signed_commit=ශාඛාවට අත්සන් කළ කැපවීමක් අවශ්ය වේ
commits.desc=මූලාශ්ර කේත වෙනස් කිරීමේ ඉතිහාසය පිරික්සන්න.
commits.commits=විවරයන්
commits.nothing_to_compare=මෙම ශාඛා සමාන වේ.

View File

@ -1007,6 +1007,7 @@ editor.commit_empty_file_text=Súbor, ktorý sa chystáte odoslať, je prázdny.
editor.no_commit_to_branch=Nedá sa odoslať priamo do vetvy, pretože:
editor.require_signed_commit=Vetva vyžaduje podpísaný commit
commits.commits=Commity
commits.search_all=Všetky vetvy
commits.author=Autor

View File

@ -782,6 +782,7 @@ editor.no_commit_to_branch=Det gick inte att committa direkt till branchen för:
editor.user_no_push_to_branch=Användaren kan inte pusha till branchen
editor.require_signed_commit=Branchen kräver en signerad commit
commits.desc=Bläddra i källkodens förändringshistorik.
commits.commits=Incheckningar
commits.search_all=Alla brancher

View File

@ -1385,6 +1385,7 @@ editor.require_signed_commit=Dal imzalı bir işleme gerektirir
editor.cherry_pick=%s şunun üzerine cımbızla:
editor.revert=%s şuna geri döndür:
commits.desc=Kaynak kodu değişiklik geçmişine göz atın.
commits.commits=İşleme
commits.no_commits=Ortak bir işleme yok. "%s" ve "%s" tamamen farklı geçmişlere sahip.

View File

@ -92,6 +92,7 @@ remove=Видалити
remove_all=Видалити все
remove_label_str=`Видалити елемент "%s"`
edit=Редагувати
view=Переглянути
test=Тест
enabled=Увімкнено
@ -414,6 +415,7 @@ remember_me.compromised=Токен для входу більше не дійс
forgot_password_title=Забув пароль
forgot_password=Забули пароль?
need_account=Потрібен обліковий запис?
sign_up_tip=Ви реєструєте перший обліковий запис у системі, з правами адміністратора. Будь ласка, уважно запам'ятайте своє ім'я користувача та пароль. Якщо ви їх забудете, зверніться до документації Gitea, щоб відновити обліковий запис.
sign_up_now=Зареєструватися.
sign_up_successful=Обліковий запис створено успішно. Вітаю!
confirmation_mail_sent_prompt_ex=Новий лист з підтвердженням було надіслано на <b>%s</b>. Будь ласка, перевірте свою поштову скриньку протягом наступних %s, щоб завершити процес реєстрації. Якщо ви вказали невірну адресу електронної пошти, ви можете увійти ще раз і змінити її.
@ -970,6 +972,7 @@ passcode_invalid=Некоректний пароль. Спробуй ще раз
twofa_failed_get_secret=Не вдалося отримати код.
webauthn_register_key=Додати ключ безпеки
webauthn_nickname=Псевдонім
webauthn_delete_key=Видалити ключ безпеки
webauthn_delete_key_desc=Якщо ви видалите ключ безпеки, ви більше не зможете ввійти за його допомогою. Продовжити?
webauthn_key_loss_warning=Якщо ви втратите ключі безпеки, ви втратите доступ до свого облікового запису.
@ -1336,6 +1339,7 @@ editor.commit_email=Електронна пошта коміту
editor.invalid_commit_email=Адреса електронної пошти для коміту недійсна.
editor.file_is_a_symlink=`"%s" - це символічне посилання. Символічні посилання не можна редагувати у веб-редакторі`
editor.filename_is_a_directory=Назва файлу '%s' вже використовується як назва каталогу у цьому сховищі.
editor.file_modifying_no_longer_exists=Редагований файл '%s' більше не існує в цьому сховищі.
editor.file_changed_while_editing=Зміст файлу змінився з моменту початку редагування. <a target="_blank" rel="noopener" href="%s"> Натисніть тут </a>, щоб переглянути що було змінено, або <strong>закомітьте зміни ще раз</strong>, щоб переписати їх.
editor.commit_empty_file_header=Закомітити порожній файл
editor.commit_empty_file_text=Файл, який ви збираєтеся закомітити, порожній. Продовжувати?
@ -1349,9 +1353,14 @@ editor.upload_file_is_locked=Файл "%s" заблоковано %s.
editor.upload_files_to_dir=`Завантажити файли до "%s"`
editor.no_commit_to_branch=Не вдалося внести коміт безпосередньо до гілки, тому що:
editor.require_signed_commit=Гілка вимагає підписаного коміту
editor.revert=Повернути %s до:
editor.failed_to_commit=Не вдалося зафіксувати зміни.
editor.failed_to_commit_summary=Помилка:
commits.desc=Переглянути історію зміни коду.
commits.commits=Коміти
commits.no_commits=Немає спільних комітів. '%s' та '%s' мають різну історію.
commits.nothing_to_compare=Ці гілки однакові.
commits.search_branch=Ця гілка
commits.search_all=Усі гілки
@ -1368,6 +1377,7 @@ commits.ssh_key_fingerprint=Відбиток ключа SSH
commits.view_path=Переглянути в історії
commits.view_file_diff=Переглянути зміни до цього файлу в цьому коміті
commit.operations=Дії
commit.revert=Повернути до попереднього стану
commit.revert-header=Повернути: %s
commit.revert-content=Виберіть гілку, до якої хочете повернутися:
@ -1442,6 +1452,7 @@ issues.new.clear_assignees=Прибрати виконавців
issues.new.no_assignees=Немає виконавців
issues.new.no_reviewers=Немає рецензентів
issues.new.blocked_user=Не вдалося створити задачу, тому що ви заблоковані власником сховища.
issues.edit.blocked_user=Неможливо редагувати вміст, оскільки вас заблоковано автором або власником сховища.
issues.choose.get_started=Розпочати
issues.choose.open_external_link=Відкрити
issues.choose.blank=Типово
@ -1459,6 +1470,7 @@ issues.label_templates.title=Завантажити визначений наб
issues.label_templates.info=Ще немає міток. Натисніть 'Нова мітка' або використовуйте попередньо визначений набір міток:
issues.label_templates.helper=Оберіть набір міток
issues.label_templates.use=Використати набір міток
issues.label_templates.fail_to_load_file=Не вдалося завантажити файл шаблона мітки '%s': %v
issues.add_label=додано %s з міткою %s
issues.add_labels=додано %s з мітками %s
issues.remove_label=видалено %s з міткою %s
@ -1494,6 +1506,7 @@ issues.filter_project=Проєкт
issues.filter_project_all=Всі проєкти
issues.filter_project_none=Проєкт відсутній
issues.filter_assignee=Виконавець
issues.filter_assignee_no_assignee=Нікому не присвоєно
issues.filter_assignee_any_assignee=Призначено будь-кому
issues.filter_poster=Автор
issues.filter_user_placeholder=Пошук користувачів
@ -1504,6 +1517,7 @@ issues.filter_type.all_pull_requests=Усі запити на злиття
issues.filter_type.assigned_to_you=Призначене вам
issues.filter_type.created_by_you=Створено вами
issues.filter_type.mentioning_you=Вас згадано
issues.filter_type.review_requested=Запит на рецензію
issues.filter_type.reviewed_by_you=Перевірено вами
issues.filter_sort=Сортувати
issues.filter_sort.latest=Найновіші
@ -1543,7 +1557,9 @@ issues.context.quote_reply=Цитувати відповідь
issues.context.reference_issue=Посилання в новій задачі
issues.context.edit=Редагувати
issues.context.delete=Видалити
issues.no_content=Немає опису.
issues.close=Закрити задачу
issues.comment_pull_merged_at=об'єднав(-ла) коміти %[1]s в %[2]s %[3]s
issues.comment_manually_pull_merged_at=вручну об'єднав(-ла) коміти %[1]s в %[2]s %[3]s
issues.close_comment_issue=Закрити з коментарем
issues.reopen_issue=Відкрити знову
@ -1586,6 +1602,7 @@ issues.label_title=Назва мітки
issues.label_description=Опис мітки
issues.label_color=Колір
issues.label_color_invalid=Недійсний колір
issues.label_exclusive=Ексклюзивно
issues.label_archive=Мітка архіву
issues.label_archived_filter=Показати архівовані мітки
issues.label_archive_tooltip=Архівовані мітки типово виключаються з пропозицій під час пошуку за мітками.
@ -1777,7 +1794,13 @@ pulls.switch_comparison_type=Перемкнути тип порівняння
pulls.switch_head_and_base=Поміняти місцями основну та базову гілку
pulls.filter_branch=Фільтр по гілці
pulls.show_all_commits=Показати всі коміти
pulls.show_changes_since_your_last_review=Показати зміни після вашого останнього відгуку
pulls.showing_only_single_commit=Відображаються лише зміни коміту %[1]s
pulls.showing_specified_commit_range=Відображаються лише зміни між %[1]s..%[2]s
pulls.select_commit_hold_shift_for_range=Виберіть коміт. Натисніть клавішу Shift + клацніть, щоб виділити діапазон
pulls.filter_changes_by_commit=Фільтр за комітом
pulls.nothing_to_compare=Ці гілки однакові. Немає необхідності створювати запитів на злиття.
pulls.nothing_to_compare_have_tag=Виділена гілка або мітка ідентичні.
pulls.nothing_to_compare_and_allow_empty_pr=Одинакові гілки. Цей PR буде порожнім.
pulls.has_pull_request=`Запит злиття для цих гілок вже існує: <a href="%[1]s">%[2]s#%[3]d</a>`
pulls.create=Створити запит на злиття
@ -1790,6 +1813,7 @@ pulls.tab_files=Змінені файли
pulls.reopen_to_merge=Будь ласка, заново відкрийте цей запит щоб виконати злиття.
pulls.cant_reopen_deleted_branch=Цей запит не можна повторно відкрити, оскільки гілку видалено.
pulls.merged=Злито
pulls.merged_success=Запит на злиття успішно об'єднано і закрито
pulls.closed=Запит на злиття закрито
pulls.manually_merged=Ручне злиття
pulls.merged_info_text=Гілку %s тепер можна видалити.
@ -1802,6 +1826,7 @@ pulls.remove_prefix=Видалити префікс <strong>%s</strong>
pulls.data_broken=Збій цього запиту на злиття через відсутність інформації про форк.
pulls.files_conflicted=Цей запит на злиття має зміни, що конфліктують з цільовою гілкою.
pulls.is_checking=Перевірка конфліктів об'єднання (merge) ...
pulls.is_ancestor=Цю гілку вже включено до цільової гілки. Нема чого об'єднувати.
pulls.required_status_check_failed=Деякі необхідні перевірки виконані з помилками.
pulls.required_status_check_missing=Декілька з необхідних перевірок відсутні.
pulls.required_status_check_administrator=Як адміністратор ви все одно можете об'єднати цей запит на злиття.
@ -1869,6 +1894,9 @@ pulls.auto_merge_not_scheduled=Цей запит на злиття не план
pulls.delete.title=Видалити цей запит на злиття?
pulls.upstream_diverging_prompt_behind_1=Ця гілка на %[1]d коміт позаду %[2]s
pulls.upstream_diverging_prompt_behind_n=Ця гілка на %[1]d комітів позаду %[2]s
pulls.upstream_diverging_prompt_base_newer=Базова гілка %s має нові зміни
pulls.upstream_diverging_merge_confirm=Хочете об’єднати "%[1]s" з "%[2]s"?
pull.deleted_branch=(видалена):%s
@ -2248,6 +2276,10 @@ settings.event_pull_request_review=Запит на злиття рецензов
settings.event_pull_request_review_desc=Запит на злиття підтверджено, відхилено або прокоментовано.
settings.event_pull_request_sync=Запит на злиття синхронізується
settings.event_pull_request_sync_desc=Запит до злиття синхронізовано.
settings.event_header_workflow=Події робочого процесу
settings.event_workflow_run=Запущений робочий процес
settings.event_workflow_run_desc=Запущений робочий процес Gitea в черзі, в очікуванні, в процесі виконання або завершений.
settings.event_workflow_job=Завдання робочого процесу
settings.event_package=Пакет
settings.branch_filter=Фільтр гілок
settings.authorization_header=Заголовок авторизації
@ -2586,6 +2618,7 @@ team_permission_desc=Права доступу
team_unit_desc=Дозволити доступ до розділів репозиторію
team_unit_disabled=(Вимкнено)
form.name_been_taken=Назва організації "%s" вже зайнята.
form.name_reserved=Назву організації "%s" зарезервовано.
form.name_pattern_not_allowed=Шаблон "%s" не допускається в назві організації.
form.create_org_not_allowed=Вам не дозволено створювати організації.
@ -2608,12 +2641,27 @@ settings.visibility.private_shortname=Приватний
settings.update_settings=Оновити налаштування
settings.update_setting_success=Налаштування організації оновлені.
settings.rename=Перейменувати організацію
settings.rename_desc=Зміна назви організації також змінить URL адресу вашої організації і звільнить стару назву.
settings.rename_success=Організацію %[1]s успішно перейменована на %[2].
settings.rename_no_change=Назва організації не змінилася.
settings.rename_new_org_name=Назва нової організації
settings.rename_failed=Не вдалося перейменувати організацію через внутрішню помилку
settings.rename_notices_1=Цю операцію <strong>НЕМОЖЛИВО</strong> скасувати.
settings.rename_notices_2=Стара назва буде перенаправлятися на нову, поки хтось не використає її.
settings.update_avatar_success=Аватар організації оновлений.
settings.delete=Видалити організацію
settings.delete_account=Видалити цю організацію
settings.delete_prompt=Організацію буде остаточно видалено. Це <strong>НЕМОЖЛИВО</strong> скасувати!
settings.name_confirm=Введіть назву організації для підтвердження:
settings.delete_notices_1=Цю операцію <strong>НЕМОЖЛИВО</strong> скасувати.
settings.delete_notices_2=Ця операція назавжди видалить <strong>сховища</strong> <strong>%s</strong>, включно з кодом, задачами, коментарями, даними вікі та налаштуваннями співавторів.
settings.delete_notices_3=Ця операція назавжди видалить всі <strong>пакети</strong> <strong>%s</strong>.
settings.delete_notices_4=Ця операція назавжди видалить всі <strong>проєкти</strong> <strong>%s</strong>.
settings.confirm_delete_account=Підтвердити видалення
settings.delete_failed=Не вдалося видалити організацію через внутрішню помилку
settings.delete_successful=Організацію <b>%s</b> успішно видалено.
settings.hooks_desc=Додайте веб-хуки, які спрацьовуватимуть для <strong>всіх сховищ</strong> у цій організації.
settings.labels_desc=Додайте мітки, які можна використовувати у задачах для <strong>усіх сховищ</strong> у цій організації.
@ -2639,6 +2687,7 @@ teams.leave.detail=Покинути %s?
teams.can_create_org_repo=Створити репозиторії
teams.can_create_org_repo_helper=Учасники можуть створювати нові репозиторії в організації. Автор отримає доступ адміністратора до нового репозиторію.
teams.none_access=Немає доступу
teams.none_access_helper=Учасники не можуть переглядати або виконувати будь-які інші дії з цією одиницею. Це не впливає на загальнодоступні сховища.
teams.general_access=Загальний доступ
teams.general_access_helper=Дозволи учасників будуть визначатися відповідно до наведеної нижче таблиці дозволів.
teams.read_access=Читання
@ -2667,6 +2716,7 @@ teams.remove_all_repos_title=Видалити всі репозиторії ко
teams.remove_all_repos_desc=Це видалить усі репозиторії команди.
teams.add_all_repos_title=Додати всі репозиторії
teams.add_all_repos_desc=Це додасть всі репозиторії організації до команди.
teams.add_nonexistent_repo=Сховище, яке ви намагаєтеся додати, не існує, будь ласка, створіть його спочатку.
teams.add_duplicate_users=Користувач уже є членом команди.
teams.repos.none=Для команди немає доступних репозиторіїв.
teams.members.none=Немає членів в цій команді.
@ -2758,6 +2808,7 @@ dashboard.resync_all_hooks=Заново синхронізувати хуки п
dashboard.reinit_missing_repos=Заново ініціалізувати всі відсутні сховища Git'а, для яких існують записи
dashboard.sync_external_users=Синхронізувати дані зовнішніх користувачів
dashboard.cleanup_hook_task_table=Очистити таблицю hook_task
dashboard.cleanup_packages=Очистити застарілі пакети
dashboard.cleanup_actions=Очищення ресурсів прострочених дій
dashboard.server_uptime=Час роботи сервера
dashboard.current_goroutine=Поточна кількість Goroutines
@ -2789,11 +2840,15 @@ dashboard.total_gc_pause=Загальна пауза збирача сміття
dashboard.last_gc_pause=Остання пауза збирача сміття (GC)
dashboard.gc_times=Кількість запусків збирача сміття (GC)
dashboard.delete_old_actions=Видалити всі старі дії з бази даних
dashboard.delete_old_actions.started=Видалення всіх старих дій з бази даних розпочато.
dashboard.update_checker=Перевірка оновлень
dashboard.delete_old_system_notices=Видалити всі старі системні повідомлення з бази даних
dashboard.gc_lfs=Збір сміття мета-об'єктів LFS
dashboard.stop_endless_tasks=Зупинити нескінченні завдання
dashboard.cancel_abandoned_jobs=Скасувати покинуті завдання
dashboard.start_schedule_tasks=Запуск запланованих завдань
dashboard.sync_branch.started=Розпочато синхронізацію гілок
dashboard.sync_tag.started=Розпочато синхронізацію міток
dashboard.rebuild_issue_indexer=Перебудувати індексатор задач
dashboard.sync_repo_licenses=Синхронізувати ліцензії сховища
@ -3187,6 +3242,7 @@ monitor.queue.settings.remove_all_items_done=Усі елементи черги
notices.system_notice_list=Сповіщення системи
notices.view_detail_header=Переглянути деталі повідомлення
notices.operations=Дії
notices.select_all=Вибрати все
notices.deselect_all=Скасувати виділення
notices.inverse_selection=Інвертувати виділене
@ -3254,6 +3310,7 @@ seconds=%d секунди
minutes=%d хвилини
hours=%d години
days=%d дні
weeks=%d тижні(в)
months=%d місяці
years=%d роки
raw_seconds=секунди
@ -3324,6 +3381,7 @@ details.license=Ліцензія
assets=Ресурси
versions=Версії
versions.view_all=Переглянути все
dependency.id=ID
dependency.version=Версія
search_in_external_registry=Шукати в %s
alpine.registry=Налаштуйте цей реєстр, додавши URL у ваш файл <code>/etc/apk/repositories</code>:
@ -3463,6 +3521,8 @@ actions=Дії
unit.desc=Керувати діями
status.unknown=Невідомий
status.waiting=Очікування
status.running=Виконується
status.success=Успіх
status.failure=Невдача
status.cancelled=Скасовано
@ -3470,6 +3530,7 @@ status.skipped=Пропущено
status.blocked=Заблоковано
runners.status=Статус
runners.id=ID
runners.name=Назва
runners.owner_type=Тип
runners.description=Опис
@ -3492,23 +3553,36 @@ runs.all_workflows=Всі робочі процеси
runs.commit=Коміт
runs.scheduled=Заплановано
runs.pushed_by=завантажено
runs.invalid_workflow_helper=Файл конфігурації робочих процесів недійсний. Будь ласка, перевірте файл конфігурації: %s
runs.no_job_without_needs=Робочий процес повинен містити принаймні одну задачу без залежностей.
runs.no_job=Робочий процес повинен містити принаймні одну задачу
runs.actor=Актор
runs.status=Статус
runs.actors_no_select=Усі актори
runs.status_no_select=Всі статуси
runs.no_results=Збігів немає.
runs.no_workflows=Робочих процесів наразі немає.
runs.no_workflows.quick_start=Не знаєте, як почати з Gitea Дії? Дивіться <a target="_blank" rel="noopener noreferrer" href="%s">посібник швидкого старту</a>.
runs.no_workflows.documentation=Для отримання додаткової інформації про Gitea Дії, перегляньте <a target="_blank" rel="noopener noreferrer" href="%s">документацію</a>.
runs.no_runs=Робочий процес ще не виконувався.
runs.empty_commit_message=(порожнє повідомлення коміту)
runs.expire_log_message=Журнали були очищені, тому що вони були занадто старі.
runs.delete=Видалити запущений робочий процес
runs.delete.description=Ви впевнені, що хочете остаточно видалити цей робочий процес? Цю дію неможливо скасувати.
runs.not_done=Виконання цього робочого процесу не завершено.
runs.view_workflow_file=Перегляд файлу робочого процесу
workflow.disable=Вимкнути робочий процес
workflow.disable_success=Робочий процес '%s' успішно вимкнено.
workflow.enable=Увімкнути робочий процес
workflow.enable_success=Робочий процес '%s' успішно ввімкнено.
workflow.disabled=Робочий процес вимкнений.
workflow.run=Запустити робочий процес
workflow.not_found=Робочий процес '%s' не знайдено.
workflow.run_success=Робочий процес '%s' завершився успішно.
workflow.from_ref=Використати робочий процес з
workflow.has_workflow_dispatch=Цей робочий процес має тригер події workflow_dispatch.
workflow.has_no_workflow_dispatch=Робочий процес “%s” не має тригера події workflow_dispatch.
variables=Змінні

View File

@ -1394,6 +1394,7 @@ editor.require_signed_commit=分支需要签名提交
editor.cherry_pick=拣选提交 %s 到:
editor.revert=将 %s 还原到:
commits.desc=浏览代码修改历史
commits.commits=次代码提交
commits.no_commits=没有共同的提交。「%s」和「%s」的历史完全不同。

View File

@ -374,6 +374,7 @@ editor.create_new_branch=建立 <strong>新的分支</strong> 為此提交和開
editor.cancel=取消
editor.no_changes_to_show=沒有可以顯示的變更。
commits.commits=次程式碼提交
commits.author=作者
commits.message=備註

View File

@ -1356,6 +1356,7 @@ editor.require_signed_commit=分支僅接受經簽署的提交
editor.cherry_pick=Cherry-pick %s 到:
editor.revert=還原 %s 到:
commits.desc=瀏覽原始碼修改歷程。
commits.commits=次程式碼提交
commits.no_commits=沒有共同的提交。「%s」和「%s」的歷史完全不同。

View File

@ -721,7 +721,7 @@ func CreateIssue(ctx *context.APIContext) {
form.Labels = make([]int64, 0)
}
if err := issue_service.NewIssue(ctx, ctx.Repo.Repository, issue, form.Labels, nil, assigneeIDs, 0); err != nil {
if err := issue_service.NewIssue(ctx, ctx.Repo.Repository, issue, form.Labels, nil, assigneeIDs, nil); err != nil {
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) {
ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, user_model.ErrBlockedUser) {

View File

@ -318,7 +318,7 @@ func prepareWorkflowList(ctx *context.Context, workflows []Workflow) {
ctx.Data["Page"] = pager
ctx.Data["HasWorkflowsOrRuns"] = len(workflows) > 0 || len(runs) > 0
ctx.Data["AllowDeleteWorkflowRuns"] = ctx.Repo.CanWrite(unit.TypeActions)
ctx.Data["CanWriteRepoUnitActions"] = ctx.Repo.CanWrite(unit.TypeActions)
}
// loadIsRefDeleted loads the IsRefDeleted field for each run in the list.

View File

@ -189,7 +189,7 @@ func SearchIssues(ctx *context.Context) {
IsClosed: isClosed,
IncludedAnyLabelIDs: includedAnyLabels,
MilestoneIDs: includedMilestones,
ProjectID: projectID,
ProjectIDs: projectID,
SortBy: issue_indexer.SortByCreatedDesc,
}
@ -345,12 +345,12 @@ func SearchRepoIssuesJSON(ctx *context.Context) {
Page: ctx.FormInt("page"),
PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")),
},
Keyword: keyword,
RepoIDs: []int64{ctx.Repo.Repository.ID},
IsPull: isPull,
IsClosed: isClosed,
ProjectID: projectID,
SortBy: issue_indexer.SortByCreatedDesc,
Keyword: keyword,
RepoIDs: []int64{ctx.Repo.Repository.ID},
IsPull: isPull,
IsClosed: isClosed,
ProjectIDs: projectID,
SortBy: issue_indexer.SortByCreatedDesc,
}
if since != 0 {
searchOpt.UpdatedAfterUnix = optional.Some(since)
@ -542,7 +542,6 @@ func prepareIssueFilterAndList(ctx *context.Context, milestoneID, projectID int6
RepoIDs: []int64{repo.ID},
LabelIDs: preparedLabelFilter.SelectedLabelIDs,
MilestoneIDs: mileIDs,
ProjectID: projectID,
AssigneeID: assigneeID,
MentionedID: mentionedID,
PosterID: posterUserID,
@ -551,6 +550,11 @@ func prepareIssueFilterAndList(ctx *context.Context, milestoneID, projectID int6
IsPull: isPullOption,
IssueIDs: nil,
}
if projectID != 0 {
statsOpts.ProjectIDs = []int64{projectID}
}
if keyword != "" {
keywordMatchedIssueIDs, _, err = issue_indexer.SearchIssues(ctx, issue_indexer.ToSearchOptions(keyword, statsOpts))
if err != nil {
@ -629,7 +633,7 @@ func prepareIssueFilterAndList(ctx *context.Context, milestoneID, projectID int6
ReviewRequestedID: reviewRequestedID,
ReviewedID: reviewedID,
MilestoneIDs: mileIDs,
ProjectID: projectID,
ProjectIDs: []int64{projectID},
IsClosed: isShowClosed,
IsPull: isPullOption,
LabelIDs: preparedLabelFilter.SelectedLabelIDs,

View File

@ -121,8 +121,8 @@ func NewIssue(ctx *context.Context) {
}
pageMetaData.MilestonesData.SelectedMilestoneID = ctx.FormInt64("milestone")
pageMetaData.ProjectsData.SelectedProjectID = ctx.FormInt64("project")
if pageMetaData.ProjectsData.SelectedProjectID > 0 {
pageMetaData.ProjectsData.SelectedProjectID = ctx.FormString("project")
if len(pageMetaData.ProjectsData.SelectedProjectID) > 0 {
if len(ctx.Req.URL.Query().Get("project")) > 0 {
ctx.Data["redirect_after_creation"] = "project"
}
@ -239,8 +239,9 @@ func toSet[ItemType any, KeyType comparable](slice []ItemType, keyFunc func(Item
// ValidateRepoMetasForNewIssue check and returns repository's meta information
func ValidateRepoMetasForNewIssue(ctx *context.Context, form forms.CreateIssueForm, isPull bool) (ret struct {
LabelIDs, AssigneeIDs []int64
MilestoneID, ProjectID int64
LabelIDs, AssigneeIDs []int64
MilestoneID int64
ProjectIDs []int64
Reviewers []*user_model.User
TeamReviewers []*organization.Team
@ -269,11 +270,13 @@ func ValidateRepoMetasForNewIssue(ctx *context.Context, form forms.CreateIssueFo
allProjects := append(slices.Clone(pageMetaData.ProjectsData.OpenProjects), pageMetaData.ProjectsData.ClosedProjects...)
candidateProjects := toSet(allProjects, func(project *project_model.Project) int64 { return project.ID })
if form.ProjectID > 0 && !candidateProjects.Contains(form.ProjectID) {
ctx.NotFound(nil)
return ret
}
pageMetaData.ProjectsData.SelectedProjectID = form.ProjectID
inputProjectIDs, _ := base.StringsToInt64s(strings.Split(form.ProjectIDs, ","))
pageMetaData.ProjectsData.SelectedProjectID = util.JoinSlice(inputProjectIDs, func(v int64) string {
if candidateProjects.Contains(v) {
return strconv.FormatInt(v, 10)
}
return ""
})
// prepare assignees
candidateAssignees := toSet(pageMetaData.AssigneesData.CandidateAssignees, func(user *user_model.User) int64 { return user.ID })
@ -318,7 +321,7 @@ func ValidateRepoMetasForNewIssue(ctx *context.Context, form forms.CreateIssueFo
}
}
ret.LabelIDs, ret.AssigneeIDs, ret.MilestoneID, ret.ProjectID = inputLabelIDs, inputAssigneeIDs, form.MilestoneID, form.ProjectID
ret.LabelIDs, ret.AssigneeIDs, ret.MilestoneID, ret.ProjectIDs = inputLabelIDs, inputAssigneeIDs, form.MilestoneID, inputProjectIDs
ret.Reviewers, ret.TeamReviewers = reviewers, teamReviewers
return ret
}
@ -343,9 +346,9 @@ func NewIssuePost(ctx *context.Context) {
return
}
labelIDs, assigneeIDs, milestoneID, projectID := validateRet.LabelIDs, validateRet.AssigneeIDs, validateRet.MilestoneID, validateRet.ProjectID
labelIDs, assigneeIDs, milestoneID, projectIDs := validateRet.LabelIDs, validateRet.AssigneeIDs, validateRet.MilestoneID, validateRet.ProjectIDs
if projectID > 0 {
if len(projectIDs) > 0 {
if !ctx.Repo.CanRead(unit.TypeProjects) {
// User must also be able to see the project.
ctx.HTTPError(http.StatusBadRequest, "user hasn't permissions to read projects")
@ -385,7 +388,7 @@ func NewIssuePost(ctx *context.Context) {
Ref: form.Ref,
}
if err := issue_service.NewIssue(ctx, repo, issue, labelIDs, attachments, assigneeIDs, projectID); err != nil {
if err := issue_service.NewIssue(ctx, repo, issue, labelIDs, attachments, assigneeIDs, projectIDs); err != nil {
if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) {
ctx.HTTPError(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err.Error())
} else if errors.Is(err, user_model.ErrBlockedUser) {
@ -397,8 +400,8 @@ func NewIssuePost(ctx *context.Context) {
}
log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
if ctx.FormString("redirect_after_creation") == "project" && projectID > 0 {
project, err := project_model.GetProjectByID(ctx, projectID)
if ctx.FormString("redirect_after_creation") == "project" && len(projectIDs) > 0 {
project, err := project_model.GetProjectByID(ctx, projectIDs[0])
if err == nil {
if project.Type == project_model.TypeOrganization {
ctx.JSONRedirect(project_model.ProjectLinkForOrg(ctx.Repo.Owner, project.ID))

View File

@ -16,6 +16,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/util"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
"code.gitea.io/gitea/services/context"
issue_service "code.gitea.io/gitea/services/issue"
@ -34,7 +35,7 @@ type issueSidebarAssigneesData struct {
}
type issueSidebarProjectsData struct {
SelectedProjectID int64
SelectedProjectID string
OpenProjects []*project_model.Project
ClosedProjects []*project_model.Project
}
@ -160,8 +161,10 @@ func (d *IssuePageMetaData) retrieveAssigneesData(ctx *context.Context) {
}
func (d *IssuePageMetaData) retrieveProjectsDataForIssueWriter(ctx *context.Context) {
if d.Issue != nil && d.Issue.Project != nil {
d.ProjectsData.SelectedProjectID = d.Issue.Project.ID
if d.Issue != nil && len(d.Issue.Projects) > 0 {
d.ProjectsData.SelectedProjectID = util.JoinSlice(d.Issue.Projects, func(v *project_model.Project) string {
return strconv.FormatInt(v.ID, 10)
})
}
d.ProjectsData.OpenProjects, d.ProjectsData.ClosedProjects = retrieveProjectsInternal(ctx, ctx.Repo.Repository)
}

View File

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/models/renderhelper"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/optional"
@ -441,12 +442,9 @@ func UpdateIssueProject(ctx *context.Context) {
return
}
projectID := ctx.FormInt64("id")
projectIDs, _ := base.StringsToInt64s(strings.Split(ctx.FormString("id"), ","))
for _, issue := range issues {
if issue.Project != nil && issue.Project.ID == projectID {
continue
}
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, projectID, 0); err != nil {
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, projectIDs, 0); err != nil {
if errors.Is(err, util.ErrPermissionDenied) {
continue
}

View File

@ -1308,7 +1308,7 @@ func CompareAndPullRequestPost(ctx *context.Context) {
return
}
labelIDs, assigneeIDs, milestoneID, projectID := validateRet.LabelIDs, validateRet.AssigneeIDs, validateRet.MilestoneID, validateRet.ProjectID
labelIDs, assigneeIDs, milestoneID, projectIDs := validateRet.LabelIDs, validateRet.AssigneeIDs, validateRet.MilestoneID, validateRet.ProjectIDs
if setting.Attachment.Enabled {
attachments = form.Files
@ -1414,8 +1414,8 @@ func CompareAndPullRequestPost(ctx *context.Context) {
return
}
if projectID > 0 && ctx.Repo.CanWrite(unit.TypeProjects) {
if err := issues_model.IssueAssignOrRemoveProject(ctx, pullIssue, ctx.Doer, projectID, 0); err != nil {
if ctx.Repo.CanWrite(unit.TypeProjects) {
if err := issues_model.IssueAssignOrRemoveProject(ctx, pullIssue, ctx.Doer, projectIDs, 0); err != nil {
if !errors.Is(err, util.ErrPermissionDenied) {
ctx.ServerError("IssueAssignOrRemoveProject", err)
return

View File

@ -426,7 +426,7 @@ type CreateIssueForm struct {
ReviewerIDs string `form:"reviewer_ids"`
Ref string `form:"ref"`
MilestoneID int64
ProjectID int64
ProjectIDs string `form:"project_ids"`
Content string
Files []string
AllowMaintainerEdit bool

View File

@ -23,7 +23,7 @@ import (
)
// NewIssue creates new issue with labels for repository.
func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *issues_model.Issue, labelIDs []int64, uuids []string, assigneeIDs []int64, projectID int64) error {
func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *issues_model.Issue, labelIDs []int64, uuids []string, assigneeIDs, projectIDs []int64) error {
if err := issue.LoadPoster(ctx); err != nil {
return err
}
@ -41,12 +41,7 @@ func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *issues_mo
return err
}
}
if projectID > 0 {
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, issue.Poster, projectID, 0); err != nil {
return err
}
}
return nil
return issues_model.IssueAssignOrRemoveProject(ctx, issue, issue.Poster, projectIDs, 0)
}); err != nil {
return err
}

View File

@ -77,7 +77,12 @@ func MoveIssuesOnProjectColumn(ctx context.Context, doer *user_model.User, colum
}
}
_, err = db.Exec(ctx, "UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?", column.ID, sorting, issueID)
_, err = db.GetEngine(ctx).Table("project_issue").
Where("issue_id = ? AND project_id = ?", issueID, column.ProjectID).
Update(map[string]any{
"project_board_id": column.ID,
"sorting": sorting,
})
if err != nil {
return err
}
@ -89,7 +94,7 @@ func MoveIssuesOnProjectColumn(ctx context.Context, doer *user_model.User, colum
// LoadIssuesFromProject load issues assigned to each project column inside the given project
func LoadIssuesFromProject(ctx context.Context, project *project_model.Project, opts *issues_model.IssuesOptions) (map[int64]issues_model.IssueList, error) {
issueList, err := issues_model.Issues(ctx, opts.Copy(func(o *issues_model.IssuesOptions) {
o.ProjectID = project.ID
o.ProjectIDs = []int64{project.ID}
o.SortType = "project-column-sorting"
}))
if err != nil {
@ -180,10 +185,10 @@ func LoadIssueNumbersForProject(ctx context.Context, project *project_model.Proj
// for user or org projects, we need to check access permissions
opts := issues_model.IssuesOptions{
ProjectID: project.ID,
Doer: doer,
AllPublic: doer == nil,
Owner: project.Owner,
ProjectIDs: []int64{project.ID},
Doer: doer,
AllPublic: doer == nil,
Owner: project.Owner,
}
var err error

View File

@ -117,12 +117,12 @@ func Test_Projects(t *testing.T) {
// issue 6 belongs to private repo 3 under org 3
issue6 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 6})
err = issues_model.IssueAssignOrRemoveProject(db.DefaultContext, issue6, user2, project1.ID, column1.ID)
err = issues_model.IssueAssignOrRemoveProject(db.DefaultContext, issue6, user2, []int64{project1.ID}, column1.ID)
assert.NoError(t, err)
// issue 16 belongs to public repo 16 under org 3
issue16 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 16})
err = issues_model.IssueAssignOrRemoveProject(db.DefaultContext, issue16, user2, project1.ID, column1.ID)
err = issues_model.IssueAssignOrRemoveProject(db.DefaultContext, issue16, user2, []int64{project1.ID}, column1.ID)
assert.NoError(t, err)
projects, err := db.Find[project_model.Project](db.DefaultContext, project_model.SearchOptions{

View File

@ -38,20 +38,23 @@ func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus,
}
requiredCommitStatuses := make([]*git_model.CommitStatus, 0, len(commitStatuses))
allRequiredContextsMatched := true
for _, gp := range requiredContextsGlob {
requiredContextMatched := false
for _, commitStatus := range commitStatuses {
if gp.Match(commitStatus.Context) {
requiredCommitStatuses = append(requiredCommitStatuses, commitStatus)
break
requiredContextMatched = true
}
}
allRequiredContextsMatched = allRequiredContextsMatched && requiredContextMatched
}
if len(requiredCommitStatuses) == 0 {
return commitstatus.CommitStatusPending
}
returnedStatus := git_model.CalcCommitStatus(requiredCommitStatuses).State
if len(requiredCommitStatuses) == len(requiredContexts) {
if allRequiredContextsMatched {
return returnedStatus
}

View File

@ -58,6 +58,15 @@ func TestMergeRequiredContextsCommitStatus(t *testing.T) {
requiredContexts: []string{"Build*", "Build 2t*"},
expected: commitstatus.CommitStatusFailure,
},
{
commitStatuses: []*git_model.CommitStatus{
{Context: "Build 1", State: commitstatus.CommitStatusSuccess},
{Context: "Build 2", State: commitstatus.CommitStatusSuccess},
{Context: "Build 2t", State: commitstatus.CommitStatusFailure},
},
requiredContexts: []string{"Build*"},
expected: commitstatus.CommitStatusFailure,
},
{
commitStatuses: []*git_model.CommitStatus{
{Context: "Build 1", State: commitstatus.CommitStatusSuccess},

View File

@ -40,7 +40,12 @@
{{svg "octicon-kebab-horizontal"}}
<div class="menu flex-items-menu">
<a class="item" href="{{$run.Link}}/workflow">{{svg "octicon-play"}}{{ctx.Locale.Tr "actions.runs.view_workflow_file"}}</a>
{{if and $.AllowDeleteWorkflowRuns $run.Status.IsDone}}
{{if and $.CanWriteRepoUnitActions (not $run.Status.IsDone)}}
<a class="item link-action" data-url="{{$run.Link}}/cancel">
{{svg "octicon-x"}}{{ctx.Locale.Tr "actions.runs.cancel"}}
</a>
{{end}}
{{if and $.CanWriteRepoUnitActions $run.Status.IsDone}}
<a class="item link-action"
data-url="{{$run.Link}}/delete"
data-modal-confirm="{{ctx.Locale.Tr "actions.runs.delete.description"}}"

View File

@ -4,7 +4,7 @@
data-actions-url="{{.ActionsURL}}"
data-locale-approve="{{ctx.Locale.Tr "repo.diff.review.approve"}}"
data-locale-cancel="{{ctx.Locale.Tr "cancel"}}"
data-locale-cancel="{{ctx.Locale.Tr "actions.runs.cancel"}}"
data-locale-rerun="{{ctx.Locale.Tr "rerun"}}"
data-locale-rerun-all="{{ctx.Locale.Tr "rerun_all"}}"
data-locale-runs-scheduled="{{ctx.Locale.Tr "actions.runs.scheduled"}}"

View File

@ -1,11 +1,11 @@
{{$pageMeta := .}}
{{$data := .ProjectsData}}
{{$issueProject := NIL}}{{if and $pageMeta.Issue $pageMeta.Issue.Project}}{{$issueProject = $pageMeta.Issue.Project}}{{end}}
{{$issueProject := NIL}}{{if and $pageMeta.Issue $pageMeta.Issue.Projects}}{{$issueProject = $pageMeta.Issue.Projects}}{{end}}
<div class="divider"></div>
<div class="issue-sidebar-combo" data-selection-mode="single" data-update-algo="all"
<div class="issue-sidebar-combo" data-selection-mode="multiple" data-update-algo="all"
{{if $pageMeta.Issue}}data-update-url="{{$pageMeta.RepoLink}}/issues/projects?issue_ids={{$pageMeta.Issue.ID}}"{{end}}
>
<input class="combo-value" name="project_id" type="hidden" value="{{$data.SelectedProjectID}}">
<input class="combo-value" name="project_ids" type="hidden" value="{{$data.SelectedProjectID}}">
<div class="ui dropdown full-width {{if not $pageMeta.CanModifyIssueOrPull}}disabled{{end}}">
<a class="fixed-text muted">
<strong>{{ctx.Locale.Tr "repo.issues.new.projects"}}</strong> {{if $pageMeta.CanModifyIssueOrPull}}{{svg "octicon-gear"}}{{end}}
@ -24,6 +24,7 @@
<div class="header">{{ctx.Locale.Tr "repo.issues.new.open_projects"}}</div>
{{range $data.OpenProjects}}
<a class="item muted" data-value="{{.ID}}" href="{{.Link ctx}}">
<span class="item-check-mark">{{svg "octicon-check"}}</span>
{{svg .IconName 18}} {{.Title}}
</a>
{{end}}
@ -33,6 +34,7 @@
<div class="header">{{ctx.Locale.Tr "repo.issues.new.closed_projects"}}</div>
{{range $data.ClosedProjects}}
<a class="item muted" data-value="{{.ID}}" href="{{.Link ctx}}">
<span class="item-check-mark">{{svg "octicon-check"}}</span>
{{svg .IconName 18}} {{.Title}}
</a>
{{end}}
@ -42,9 +44,9 @@
</div>
<div class="ui list muted-links flex-items-block">
<span class="item empty-list {{if $issueProject}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_projects"}}</span>
{{if $issueProject}}
<a class="item" href="{{$issueProject.Link ctx}}">
{{svg $issueProject.IconName 18}} {{$issueProject.Title}}
{{range $issueProject}}
<a class="item" href="{{.Link ctx}}">
{{svg .IconName 18}} {{.Title}}
</a>
{{end}}
</div>

View File

@ -73,10 +73,10 @@
<span class="gt-ellipsis">{{.Milestone.Name}}</span>
</a>
{{end}}
{{if .Project}}
<a class="project flex-text-inline tw-max-w-[300px]" href="{{.Project.Link ctx}}">
{{svg .Project.IconName 14}}
<span class="gt-ellipsis">{{.Project.Title}}</span>
{{range .Projects}}
<a class="project flex-text-inline tw-max-w-[300px]" href="{{.Link ctx}}">
{{svg .IconName 14}}
<span class="gt-ellipsis">{{.Title}}</span>
</a>
{{end}}
{{if .Ref}}{{/* TODO: RemoveIssueRef: see "repo/issue/branch_selector_field.tmpl" */}}