diff --git a/models/git/lfs_lock.go b/models/git/lfs_lock.go
index 2f65833fe3..07ce7d4abf 100644
--- a/models/git/lfs_lock.go
+++ b/models/git/lfs_lock.go
@@ -6,6 +6,7 @@ package git
import (
"context"
"errors"
+ "fmt"
"strings"
"time"
@@ -21,11 +22,12 @@ import (
// LFSLock represents a git lfs lock of repository.
type LFSLock struct {
- ID int64 `xorm:"pk autoincr"`
- RepoID int64 `xorm:"INDEX NOT NULL"`
- OwnerID int64 `xorm:"INDEX NOT NULL"`
- Path string `xorm:"TEXT"`
- Created time.Time `xorm:"created"`
+ ID int64 `xorm:"pk autoincr"`
+ RepoID int64 `xorm:"INDEX NOT NULL"`
+ OwnerID int64 `xorm:"INDEX NOT NULL"`
+ Owner *user_model.User `xorm:"-"`
+ Path string `xorm:"TEXT"`
+ Created time.Time `xorm:"created"`
}
func init() {
@@ -37,6 +39,35 @@ func (l *LFSLock) BeforeInsert() {
l.Path = util.PathJoinRel(l.Path)
}
+// LoadAttributes loads attributes of the lock.
+func (l *LFSLock) LoadAttributes(ctx context.Context) error {
+ // Load owner
+ if err := l.LoadOwner(ctx); err != nil {
+ return fmt.Errorf("load owner: %w", err)
+ }
+
+ return nil
+}
+
+// LoadOwner loads owner of the lock.
+func (l *LFSLock) LoadOwner(ctx context.Context) error {
+ if l.Owner != nil {
+ return nil
+ }
+
+ owner, err := user_model.GetUserByID(ctx, l.OwnerID)
+ if err != nil {
+ if user_model.IsErrUserNotExist(err) {
+ l.Owner = user_model.NewGhostUser()
+ return nil
+ }
+ return err
+ }
+ l.Owner = owner
+
+ return nil
+}
+
// CreateLFSLock creates a new lock.
func CreateLFSLock(ctx context.Context, repo *repo_model.Repository, lock *LFSLock) (*LFSLock, error) {
dbCtx, committer, err := db.TxContext(ctx)
@@ -94,7 +125,7 @@ func GetLFSLockByID(ctx context.Context, id int64) (*LFSLock, error) {
}
// GetLFSLockByRepoID returns a list of locks of repository.
-func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) ([]*LFSLock, error) {
+func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) (LFSLockList, error) {
e := db.GetEngine(ctx)
if page >= 0 && pageSize > 0 {
start := 0
@@ -103,7 +134,7 @@ func GetLFSLockByRepoID(ctx context.Context, repoID int64, page, pageSize int) (
}
e.Limit(pageSize, start)
}
- lfsLocks := make([]*LFSLock, 0, pageSize)
+ lfsLocks := make(LFSLockList, 0, pageSize)
return lfsLocks, e.Find(&lfsLocks, &LFSLock{RepoID: repoID})
}
diff --git a/models/git/lfs_lock_list.go b/models/git/lfs_lock_list.go
new file mode 100644
index 0000000000..cab1e61cab
--- /dev/null
+++ b/models/git/lfs_lock_list.go
@@ -0,0 +1,54 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package git
+
+import (
+ "context"
+ "fmt"
+
+ "code.gitea.io/gitea/models/db"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/container"
+)
+
+// LFSLockList is a list of LFSLock
+type LFSLockList []*LFSLock
+
+// LoadAttributes loads the attributes for the given locks
+func (locks LFSLockList) LoadAttributes(ctx context.Context) error {
+ if len(locks) == 0 {
+ return nil
+ }
+
+ if err := locks.LoadOwner(ctx); err != nil {
+ return fmt.Errorf("load owner: %w", err)
+ }
+
+ return nil
+}
+
+// LoadOwner loads the owner of the locks
+func (locks LFSLockList) LoadOwner(ctx context.Context) error {
+ if len(locks) == 0 {
+ return nil
+ }
+
+ usersIDs := container.FilterSlice(locks, func(lock *LFSLock) (int64, bool) {
+ return lock.OwnerID, true
+ })
+ users := make(map[int64]*user_model.User, len(usersIDs))
+ if err := db.GetEngine(ctx).
+ In("id", usersIDs).
+ Find(&users); err != nil {
+ return fmt.Errorf("find users: %w", err)
+ }
+ for _, v := range locks {
+ v.Owner = users[v.OwnerID]
+ if v.Owner == nil { // not exist
+ v.Owner = user_model.NewGhostUser()
+ }
+ }
+
+ return nil
+}
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index cca068a3a2..b30504edd7 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1891,6 +1891,7 @@ pulls.cmd_instruction_checkout_title = Checkout
pulls.cmd_instruction_checkout_desc = From your project repository, check out a new branch and test the changes.
pulls.cmd_instruction_merge_title = Merge
pulls.cmd_instruction_merge_desc = Merge the changes and update on Gitea.
+pulls.cmd_instruction_merge_warning = Warning: This operation can not merge pull request because "autodetect manual merge" was not enable
pulls.clear_merge_message = Clear merge message
pulls.clear_merge_message_hint = Clearing the merge message will only remove the commit message content and keep generated git trailers such as "Co-Authored-By …".
@@ -2465,6 +2466,18 @@ settings.thread_id = Thread ID
settings.matrix.homeserver_url = Homeserver URL
settings.matrix.room_id = Room ID
settings.matrix.message_type = Message Type
+settings.visibility.private.button = Make Private
+settings.visibility.private.text = Changing the visibility to private will not only make the repo visible to only allowed members but may remove the relation between it and forks, watchers, and stars.
+settings.visibility.private.bullet_title = Changing the visibility to private will:
+settings.visibility.private.bullet_one = Make the repo visible to only allowed members.
+settings.visibility.private.bullet_two = May remove the relation between it and forks, watchers, and stars.
+settings.visibility.public.button = Make Public
+settings.visibility.public.text = Changing the visibility to public will make the repo visible to anyone.
+settings.visibility.public.bullet_title= Changing the visibility to public will:
+settings.visibility.public.bullet_one = Make the repo visible to anyone.
+settings.visibility.success = Repository visibility changed.
+settings.visibility.error = An error occurred while trying to change the repo visibility.
+settings.visibility.fork_error = Can't change the visibility of a forked repo.
settings.archive.button = Archive Repo
settings.archive.header = Archive This Repo
settings.archive.text = Archiving the repo will make it entirely read-only. It will be hidden from the dashboard. Nobody (not even you!) will be able to make new commits, or open any issues or pull requests.
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index 89cb776b69..5526a00fc3 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -1475,6 +1475,7 @@ issues.remove_labels=removeu os rótulos %s %s
issues.add_remove_labels=adicionou o(s) rótulo(s) %s e removeu %s %s
issues.add_milestone_at=`adicionou esta questão à etapa %s %s`
issues.add_project_at=`adicionou esta questão ao planeamento %s %s`
+issues.move_to_column_of_project=`isto foi movido para %s dentro de %s em %s`
issues.change_milestone_at=`modificou a etapa de %s para %s %s`
issues.change_project_at=`modificou o planeamento de %s para %s %s`
issues.remove_milestone_at=`removeu esta questão da etapa %s %s`
diff --git a/package-lock.json b/package-lock.json
index 846cf6f838..5c56531ec0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -108,6 +108,7 @@
"stylelint-declaration-strict-value": "1.10.6",
"stylelint-value-no-unknown-custom-properties": "6.0.1",
"svgo": "3.3.2",
+ "type-fest": "4.23.0",
"updates": "16.3.7",
"vite-string-plugin": "1.3.4",
"vitest": "2.0.5"
@@ -7439,6 +7440,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/globals/node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/globby": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
@@ -12287,13 +12301,13 @@
}
},
"node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "version": "4.23.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.23.0.tgz",
+ "integrity": "sha512-ZiBujro2ohr5+Z/hZWHESLz3g08BBdrdLMieYFULJO+tWc437sn8kQsWLJoZErY8alNhxre9K4p3GURAG11n+w==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
"engines": {
- "node": ">=10"
+ "node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
diff --git a/package.json b/package.json
index 730c47f90d..d1a624f116 100644
--- a/package.json
+++ b/package.json
@@ -107,6 +107,7 @@
"stylelint-declaration-strict-value": "1.10.6",
"stylelint-value-no-unknown-custom-properties": "6.0.1",
"svgo": "3.3.2",
+ "type-fest": "4.23.0",
"updates": "16.3.7",
"vite-string-plugin": "1.3.4",
"vitest": "2.0.5"
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 4773cc9adc..691de94290 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -1869,6 +1869,8 @@ func ViewIssue(ctx *context.Context) {
}
prConfig := prUnit.PullRequestsConfig()
+ ctx.Data["AutodetectManualMerge"] = prConfig.AutodetectManualMerge
+
var mergeStyle repo_model.MergeStyle
// Check correct values and select default
if ms, ok := ctx.Data["MergeStyle"].(repo_model.MergeStyle); !ok ||
diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go
index 3b96f93ff2..fad6359668 100644
--- a/routers/web/repo/setting/lfs.go
+++ b/routers/web/repo/setting/lfs.go
@@ -95,6 +95,11 @@ func LFSLocks(ctx *context.Context) {
ctx.ServerError("LFSLocks", err)
return
}
+ if err := lfsLocks.LoadAttributes(ctx); err != nil {
+ ctx.ServerError("LFSLocks", err)
+ return
+ }
+
ctx.Data["LFSLocks"] = lfsLocks
if len(lfsLocks) == 0 {
diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go
index 1e0349cdee..3f9140857a 100644
--- a/routers/web/repo/setting/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -170,15 +170,7 @@ func SettingsPost(ctx *context.Context) {
form.Private = repo.BaseRepo.IsPrivate || repo.BaseRepo.Owner.Visibility == structs.VisibleTypePrivate
}
- visibilityChanged := repo.IsPrivate != form.Private
- // when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
- if visibilityChanged && setting.Repository.ForcePrivate && !form.Private && !ctx.Doer.IsAdmin {
- ctx.RenderWithErr(ctx.Tr("form.repository_force_private"), tplSettingsOptions, form)
- return
- }
-
- repo.IsPrivate = form.Private
- if err := repo_service.UpdateRepository(ctx, repo, visibilityChanged); err != nil {
+ if err := repo_service.UpdateRepository(ctx, repo, false); err != nil {
ctx.ServerError("UpdateRepository", err)
return
}
@@ -940,6 +932,39 @@ func SettingsPost(ctx *context.Context) {
log.Trace("Repository was un-archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+ case "visibility":
+ if repo.IsFork {
+ ctx.Flash.Error(ctx.Tr("repo.settings.visibility.fork_error"))
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+ return
+ }
+
+ var err error
+
+ // when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
+ if setting.Repository.ForcePrivate && repo.IsPrivate && !ctx.Doer.IsAdmin {
+ ctx.RenderWithErr(ctx.Tr("form.repository_force_private"), tplSettingsOptions, form)
+ return
+ }
+
+ if repo.IsPrivate {
+ err = repo_service.MakeRepoPublic(ctx, repo)
+ } else {
+ err = repo_service.MakeRepoPrivate(ctx, repo)
+ }
+
+ if err != nil {
+ log.Error("Tried to change the visibility of the repo: %s", err)
+ ctx.Flash.Error(ctx.Tr("repo.settings.visibility.error"))
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+ return
+ }
+
+ ctx.Flash.Success(ctx.Tr("repo.settings.visibility.success"))
+
+ log.Trace("Repository visibility changed: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+ ctx.Redirect(ctx.Repo.RepoLink + "/settings")
+
default:
ctx.NotFound("", nil)
}
diff --git a/services/repository/repository.go b/services/repository/repository.go
index b7aac3cfe0..5306e7d45c 100644
--- a/services/repository/repository.go
+++ b/services/repository/repository.go
@@ -122,6 +122,31 @@ func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibili
return committer.Commit()
}
+func UpdateRepositoryVisibility(ctx context.Context, repo *repo_model.Repository, isPrivate bool) (err error) {
+ ctx, committer, err := db.TxContext(ctx)
+ if err != nil {
+ return err
+ }
+
+ defer committer.Close()
+
+ repo.IsPrivate = isPrivate
+
+ if err = repo_module.UpdateRepository(ctx, repo, true); err != nil {
+ return fmt.Errorf("UpdateRepositoryVisibility: %w", err)
+ }
+
+ return committer.Commit()
+}
+
+func MakeRepoPublic(ctx context.Context, repo *repo_model.Repository) (err error) {
+ return UpdateRepositoryVisibility(ctx, repo, false)
+}
+
+func MakeRepoPrivate(ctx context.Context, repo *repo_model.Repository) (err error) {
+ return UpdateRepositoryVisibility(ctx, repo, true)
+}
+
// LinkedRepository returns the linked repo if any
func LinkedRepository(ctx context.Context, a *repo_model.Attachment) (*repo_model.Repository, unit.Type, error) {
if a.IssueID != 0 {
diff --git a/templates/repo/issue/view_content/pull_merge_instruction.tmpl b/templates/repo/issue/view_content/pull_merge_instruction.tmpl
index bb59b49719..9a3e2cb7d7 100644
--- a/templates/repo/issue/view_content/pull_merge_instruction.tmpl
+++ b/templates/repo/issue/view_content/pull_merge_instruction.tmpl
@@ -15,7 +15,13 @@
git checkout {{$localBranch}}
{{if .ShowMergeInstructions}}
- {{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_title"}}
{{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_desc"}}
+
+
{{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_title"}}
+ {{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_desc"}}
+ {{if not .AutodetectManualMerge}}
+
{{ctx.Locale.Tr "repo.pulls.cmd_instruction_merge_warning"}}
+ {{end}}
+
git checkout {{.PullRequest.BaseBranch}}
diff --git a/templates/repo/settings/lfs_locks.tmpl b/templates/repo/settings/lfs_locks.tmpl
index 98f0e49097..9a18f525e9 100644
--- a/templates/repo/settings/lfs_locks.tmpl
+++ b/templates/repo/settings/lfs_locks.tmpl
@@ -30,9 +30,9 @@
{{end}}
-
- {{ctx.AvatarUtils.Avatar $.Owner}}
- {{$.Owner.DisplayName}}
+
+ {{ctx.AvatarUtils.Avatar $lock.Owner}}
+ {{$lock.Owner.DisplayName}}
|
{{TimeSince .Created ctx.Locale}} |
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 4f98133df3..f12bbbdf4a 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -23,20 +23,6 @@
- {{if not .Repository.IsFork}}
-
- {{end}}
@@ -786,6 +772,27 @@
+ {{if not .Repository.IsFork}}
+
+
+
{{ctx.Locale.Tr "repo.visibility"}}
+ {{if .Repository.IsPrivate}}
+
{{ctx.Locale.Tr "repo.settings.visibility.public.text"}}
+ {{else}}
+
{{ctx.Locale.Tr "repo.settings.visibility.private.text"}}
+ {{end}}
+
+
+
+
+
+ {{end}}
{{if .Repository.IsMirror}}
+ {{if not .Repository.IsFork}}
+
+
+
+ {{if .Repository.IsPrivate}}
+
{{ctx.Locale.Tr "repo.settings.visibility.public.bullet_title"}}
+
+ - {{ctx.Locale.Tr "repo.settings.visibility.public.bullet_one"}}
+
+ {{else}}
+
{{ctx.Locale.Tr "repo.settings.visibility.private.bullet_title"}}
+
+ - {{ctx.Locale.Tr "repo.settings.visibility.private.bullet_one"}}
+ - {{ctx.Locale.Tr "repo.settings.visibility.private.bullet_two"}}{{if .Repository.NumForks}}{{ctx.Locale.Tr "repo.visibility_fork_helper"}}{{end}}
+
+ {{end}}
+
+
+
+ {{end}}
+
{{if .Repository.UnitEnabled $.Context ctx.Consts.RepoUnitTypeWiki}}