0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-05-17 21:00:38 +02:00
This commit is contained in:
☙◦ The Tablet ❀ GamerGirlandCo ◦❧ 2025-08-17 19:36:48 -04:00
parent 296131c63c
commit e602134b3e
No known key found for this signature in database
GPG Key ID: 924A5F6AF051E87C
14 changed files with 252 additions and 105 deletions

View File

@ -228,13 +228,21 @@ func init() {
db.RegisterModel(new(Repository)) db.RegisterModel(new(Repository))
} }
func RelativePath(ownerName, repoName string) string { func RelativePathBaseName(ownerName, repoName string, groupID int64) string {
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".git" var groupSegment string
if groupID > 0 {
groupSegment = strconv.FormatInt(groupID, 10) + "/"
}
return strings.ToLower(ownerName) + "/" + groupSegment + strings.ToLower(repoName)
}
func RelativePath(ownerName, repoName string, groupID int64) string {
return RelativePathBaseName(ownerName, repoName, groupID) + ".git"
} }
// RelativePath should be an unix style path like username/reponame.git // RelativePath should be an unix style path like username/reponame.git
func (repo *Repository) RelativePath() string { func (repo *Repository) RelativePath() string {
return RelativePath(repo.OwnerName, repo.Name) return RelativePath(repo.OwnerName, repo.Name, repo.GroupID)
} }
type StorageRepo string type StorageRepo string
@ -583,13 +591,19 @@ func (repo *Repository) IsGenerated() bool {
} }
// RepoPath returns repository path by given user and repository name. // RepoPath returns repository path by given user and repository name.
func RepoPath(userName, repoName string) string { //revive:disable-line:exported func RepoPath(userName, repoName string, groupID int64) string { //revive:disable-line:exported
return filepath.Join(setting.RepoRootPath, filepath.Clean(strings.ToLower(userName)), filepath.Clean(strings.ToLower(repoName)+".git")) var joinArgs []string
joinArgs = append(joinArgs, user_model.UserPath(userName))
if groupID > 0 {
joinArgs = append(joinArgs, strconv.FormatInt(groupID, 10))
}
joinArgs = append(joinArgs, strings.ToLower(repoName)+".git")
return filepath.Join(joinArgs...)
} }
// RepoPath returns the repository path // RepoPath returns the repository path
func (repo *Repository) RepoPath() string { func (repo *Repository) RepoPath() string {
return RepoPath(repo.OwnerName, repo.Name) return RepoPath(repo.OwnerName, repo.Name, repo.GroupID)
} }
// Link returns the repository relative url // Link returns the repository relative url
@ -659,13 +673,25 @@ type CloneLink struct {
Tea string Tea string
} }
func getGroupSegment(gid int64) string {
var groupSegment string
if gid > 0 {
groupSegment = fmt.Sprintf("%d", gid)
}
return groupSegment
}
func groupSegmentWithTrailingSlash(gid int64) string {
return getGroupSegment(gid) + "/"
}
// ComposeHTTPSCloneURL returns HTTPS clone URL based on the given owner and repository name. // ComposeHTTPSCloneURL returns HTTPS clone URL based on the given owner and repository name.
func ComposeHTTPSCloneURL(ctx context.Context, owner, repo string) string { func ComposeHTTPSCloneURL(ctx context.Context, owner, repo string, groupID int64) string {
return fmt.Sprintf("%s%s/%s.git", httplib.GuessCurrentAppURL(ctx), url.PathEscape(owner), url.PathEscape(repo)) return fmt.Sprintf("%s%s/%s%s.git", httplib.GuessCurrentAppURL(ctx), url.PathEscape(owner), groupSegmentWithTrailingSlash(groupID), url.PathEscape(repo))
} }
// ComposeSSHCloneURL returns SSH clone URL based on the given owner and repository name. // ComposeSSHCloneURL returns SSH clone URL based on the given owner and repository name.
func ComposeSSHCloneURL(doer *user_model.User, ownerName, repoName string) string { func ComposeSSHCloneURL(doer *user_model.User, ownerName, repoName string, groupID int64) string {
sshUser := setting.SSH.User sshUser := setting.SSH.User
sshDomain := setting.SSH.Domain sshDomain := setting.SSH.Domain
@ -684,7 +710,7 @@ func ComposeSSHCloneURL(doer *user_model.User, ownerName, repoName string) strin
// non-standard port, it must use full URI // non-standard port, it must use full URI
if setting.SSH.Port != 22 { if setting.SSH.Port != 22 {
sshHost := net.JoinHostPort(sshDomain, strconv.Itoa(setting.SSH.Port)) sshHost := net.JoinHostPort(sshDomain, strconv.Itoa(setting.SSH.Port))
return fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, sshHost, url.PathEscape(ownerName), url.PathEscape(repoName)) return fmt.Sprintf("ssh://%s@%s/%s%s/%s.git", sshUser, sshHost, url.PathEscape(ownerName), groupSegmentWithTrailingSlash(groupID), url.PathEscape(repoName))
} }
// for standard port, it can use a shorter URI (without the port) // for standard port, it can use a shorter URI (without the port)
@ -699,25 +725,25 @@ func ComposeSSHCloneURL(doer *user_model.User, ownerName, repoName string) strin
} }
// ComposeTeaCloneCommand returns Tea CLI clone command based on the given owner and repository name. // ComposeTeaCloneCommand returns Tea CLI clone command based on the given owner and repository name.
func ComposeTeaCloneCommand(ctx context.Context, owner, repo string) string { func ComposeTeaCloneCommand(ctx context.Context, owner, repo string, groupID int64) string {
return fmt.Sprintf("tea clone %s/%s", url.PathEscape(owner), url.PathEscape(repo)) return fmt.Sprintf("tea clone %s/%s%s", url.PathEscape(owner), url.PathEscape(repo), groupSegmentWithTrailingSlash(groupID))
} }
func (repo *Repository) cloneLink(ctx context.Context, doer *user_model.User, repoPathName string) *CloneLink { func (repo *Repository) cloneLink(ctx context.Context, doer *user_model.User, repoPathName string, groupID int64) *CloneLink {
return &CloneLink{ return &CloneLink{
SSH: ComposeSSHCloneURL(doer, repo.OwnerName, repoPathName), SSH: ComposeSSHCloneURL(doer, repo.OwnerName, repoPathName, groupID),
HTTPS: ComposeHTTPSCloneURL(ctx, repo.OwnerName, repoPathName), HTTPS: ComposeHTTPSCloneURL(ctx, repo.OwnerName, repoPathName, groupID),
Tea: ComposeTeaCloneCommand(ctx, repo.OwnerName, repoPathName), Tea: ComposeTeaCloneCommand(ctx, repo.OwnerName, repoPathName, groupID),
} }
} }
// CloneLink returns clone URLs of repository. // CloneLink returns clone URLs of repository.
func (repo *Repository) CloneLink(ctx context.Context, doer *user_model.User) (cl *CloneLink) { func (repo *Repository) CloneLink(ctx context.Context, doer *user_model.User) (cl *CloneLink) {
return repo.cloneLink(ctx, doer, repo.Name) return repo.cloneLink(ctx, doer, repo.Name, repo.GroupID)
} }
func (repo *Repository) CloneLinkGeneral(ctx context.Context) (cl *CloneLink) { func (repo *Repository) CloneLinkGeneral(ctx context.Context) (cl *CloneLink) {
return repo.cloneLink(ctx, nil /* no doer, use a general git user */, repo.Name) return repo.cloneLink(ctx, nil /* no doer, use a general git user */, repo.Name, repo.GroupID)
} }
// GetOriginalURLHostname returns the hostname of a URL or the URL // GetOriginalURLHostname returns the hostname of a URL or the URL
@ -855,10 +881,11 @@ func GetRepositoriesMapByIDs(ctx context.Context, ids []int64) (map[int64]*Repos
return repos, db.GetEngine(ctx).In("id", ids).Find(&repos) return repos, db.GetEngine(ctx).In("id", ids).Find(&repos)
} }
func IsRepositoryModelExist(ctx context.Context, u *user_model.User, repoName string) (bool, error) { func IsRepositoryModelExist(ctx context.Context, u *user_model.User, repoName string, groupID int64) (bool, error) {
return db.GetEngine(ctx).Get(&Repository{ return db.GetEngine(ctx).Get(&Repository{
OwnerID: u.ID, OwnerID: u.ID,
LowerName: strings.ToLower(repoName), LowerName: strings.ToLower(repoName),
GroupID: groupID,
}) })
} }

View File

@ -254,7 +254,7 @@ func CreatePendingRepositoryTransfer(ctx context.Context, doer, newOwner *user_m
} }
// Check if new owner has repository with same name. // Check if new owner has repository with same name.
if has, err := IsRepositoryModelExist(ctx, newOwner, repo.Name); err != nil { if has, err := IsRepositoryModelExist(ctx, newOwner, repo.Name, repo.GroupID); err != nil {
return fmt.Errorf("IsRepositoryExist: %w", err) return fmt.Errorf("IsRepositoryExist: %w", err)
} else if has { } else if has {
return ErrRepoAlreadyExist{ return ErrRepoAlreadyExist{

View File

@ -5,12 +5,10 @@
package repo package repo
import ( import (
"context"
"fmt"
"strings"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"context"
"fmt"
) )
// ErrWikiAlreadyExist represents a "WikiAlreadyExist" kind of error. // ErrWikiAlreadyExist represents a "WikiAlreadyExist" kind of error.
@ -72,15 +70,13 @@ func (err ErrWikiInvalidFileName) Unwrap() error {
// WikiCloneLink returns clone URLs of repository wiki. // WikiCloneLink returns clone URLs of repository wiki.
func (repo *Repository) WikiCloneLink(ctx context.Context, doer *user_model.User) *CloneLink { func (repo *Repository) WikiCloneLink(ctx context.Context, doer *user_model.User) *CloneLink {
return repo.cloneLink(ctx, doer, repo.Name+".wiki") return repo.cloneLink(ctx, doer, repo.Name+".wiki", repo.GroupID)
} }
func RelativeWikiPath(ownerName, repoName string) string { func RelativeWikiPath(ownerName, repoName string, groupID int64) string {
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".wiki.git" return RelativePathBaseName(ownerName, repoName, groupID) + ".wiki.git"
} }
// WikiStorageRepo returns the storage repo for the wiki
// The wiki repository should have the same object format as the code repository
func (repo *Repository) WikiStorageRepo() StorageRepo { func (repo *Repository) WikiStorageRepo() StorageRepo {
return StorageRepo(RelativeWikiPath(repo.OwnerName, repo.Name)) return StorageRepo(RelativeWikiPath(repo.OwnerName, repo.Name, repo.GroupID))
} }

View File

@ -55,6 +55,47 @@ func ListUnadoptedRepositories(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, repoNames) ctx.JSON(http.StatusOK, repoNames)
} }
func commonAdoptRepository(ctx *context.APIContext) {
ownerName := ctx.PathParam("username")
repoName := ctx.PathParam("reponame")
groupID := ctx.PathParamInt64("group_id")
ctxUser, err := user_model.GetUserByName(ctx, ownerName)
if err != nil {
if user_model.IsErrUserNotExist(err) {
ctx.APIErrorNotFound()
return
}
ctx.APIErrorInternal(err)
return
}
// check not a repo
has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, repoName, groupID)
if err != nil {
ctx.APIErrorInternal(err)
return
}
exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, repoName, groupID)))
if err != nil {
ctx.APIErrorInternal(err)
return
}
if has || !exist {
ctx.APIErrorNotFound()
return
}
if _, err := repo_service.AdoptRepository(ctx, ctx.Doer, ctxUser, repo_service.CreateRepoOptions{
Name: repoName,
IsPrivate: true,
}); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
// AdoptRepository will adopt an unadopted repository // AdoptRepository will adopt an unadopted repository
func AdoptRepository(ctx *context.APIContext) { func AdoptRepository(ctx *context.APIContext) {
// swagger:operation POST /admin/unadopted/{owner}/{repo} admin adminAdoptRepository // swagger:operation POST /admin/unadopted/{owner}/{repo} admin adminAdoptRepository
@ -80,8 +121,40 @@ func AdoptRepository(ctx *context.APIContext) {
// "$ref": "#/responses/notFound" // "$ref": "#/responses/notFound"
// "403": // "403":
// "$ref": "#/responses/forbidden" // "$ref": "#/responses/forbidden"
commonAdoptRepository(ctx)
}
func AdoptGroupRepository(ctx *context.APIContext) {
// swagger:operation POST /admin/unadopted/{owner}/{group_id}/{repo} admin adminAdoptRepository
// ---
// summary: Adopt unadopted files as a repository
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"
// "403":
// "$ref": "#/responses/forbidden"
commonAdoptRepository(ctx)
}
func commonDeleteUnadoptedRepo(ctx *context.APIContext) {
ownerName := ctx.PathParam("username") ownerName := ctx.PathParam("username")
repoName := ctx.PathParam("reponame") repoName := ctx.PathParam("reponame")
groupID := ctx.PathParamInt64("group_id")
ctxUser, err := user_model.GetUserByName(ctx, ownerName) ctxUser, err := user_model.GetUserByName(ctx, ownerName)
if err != nil { if err != nil {
@ -94,12 +167,12 @@ func AdoptRepository(ctx *context.APIContext) {
} }
// check not a repo // check not a repo
has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, repoName) has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, repoName, groupID)
if err != nil { if err != nil {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
return return
} }
exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, repoName))) exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, repoName, groupID)))
if err != nil { if err != nil {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
return return
@ -108,10 +181,8 @@ func AdoptRepository(ctx *context.APIContext) {
ctx.APIErrorNotFound() ctx.APIErrorNotFound()
return return
} }
if _, err := repo_service.AdoptRepository(ctx, ctx.Doer, ctxUser, repo_service.CreateRepoOptions{
Name: repoName, if err := repo_service.DeleteUnadoptedRepository(ctx, ctx.Doer, ctxUser, repoName, groupID); err != nil {
IsPrivate: true,
}); err != nil {
ctx.APIErrorInternal(err) ctx.APIErrorInternal(err)
return return
} }
@ -142,39 +213,30 @@ func DeleteUnadoptedRepository(ctx *context.APIContext) {
// "$ref": "#/responses/empty" // "$ref": "#/responses/empty"
// "403": // "403":
// "$ref": "#/responses/forbidden" // "$ref": "#/responses/forbidden"
ownerName := ctx.PathParam("username") commonDeleteUnadoptedRepo(ctx)
repoName := ctx.PathParam("reponame") }
ctxUser, err := user_model.GetUserByName(ctx, ownerName) func DeleteUnadoptedRepositoryInGroup(ctx *context.APIContext) {
if err != nil { // swagger:operation DELETE /admin/unadopted/{owner}/{group_id}/{repo} admin adminDeleteUnadoptedRepository
if user_model.IsErrUserNotExist(err) { // ---
ctx.APIErrorNotFound() // summary: Delete unadopted files
return // produces:
} // - application/json
ctx.APIErrorInternal(err) // parameters:
return // - name: owner
} // in: path
// description: owner of the repo
// check not a repo // type: string
has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, repoName) // required: true
if err != nil { // - name: repo
ctx.APIErrorInternal(err) // in: path
return // description: name of the repo
} // type: string
exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, repoName))) // required: true
if err != nil { // responses:
ctx.APIErrorInternal(err) // "204":
return // "$ref": "#/responses/empty"
} // "403":
if has || !exist { // "$ref": "#/responses/forbidden"
ctx.APIErrorNotFound() commonDeleteUnadoptedRepo(ctx)
return
}
if err := repo_service.DeleteUnadoptedRepository(ctx, ctx.Doer, ctxUser, repoName); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
} }

View File

@ -66,6 +66,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strconv"
"strings" "strings"
actions_model "code.gitea.io/gitea/models/actions" actions_model "code.gitea.io/gitea/models/actions"
@ -140,7 +141,16 @@ func repoAssignment() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) { return func(ctx *context.APIContext) {
userName := ctx.PathParam("username") userName := ctx.PathParam("username")
repoName := ctx.PathParam("reponame") repoName := ctx.PathParam("reponame")
var gid int64
group := ctx.PathParam("group_id")
if group != "" {
gid, _ = strconv.ParseInt(group, 10, 64)
if gid == 0 {
ctx.Redirect(strings.Replace(ctx.Req.URL.RequestURI(), "/0/", "/", 1))
return
}
group += "/"
}
var ( var (
owner *user_model.User owner *user_model.User
err error err error
@ -186,6 +196,10 @@ func repoAssignment() func(ctx *context.APIContext) {
} }
return return
} }
if repo.GroupID != gid {
ctx.APIErrorNotFound()
return
}
repo.Owner = owner repo.Owner = owner
ctx.Repo.Repository = repo ctx.Repo.Repository = repo
@ -1777,8 +1791,13 @@ func Routes() *web.Router {
}) })
m.Group("/unadopted", func() { m.Group("/unadopted", func() {
m.Get("", admin.ListUnadoptedRepositories) m.Get("", admin.ListUnadoptedRepositories)
m.Post("/{username}/{reponame}", admin.AdoptRepository) m.Group("/{username}", func() {
m.Delete("/{username}/{reponame}", admin.DeleteUnadoptedRepository) m.Post("/{reponame}", admin.AdoptRepository)
m.Delete("/{reponame}", admin.DeleteUnadoptedRepository)
m.Post("/{group_id}/{reponame}", admin.AdoptGroupRepository)
m.Delete("/{group_id}/{reponame}", admin.DeleteUnadoptedRepositoryInGroup)
})
}) })
m.Group("/hooks", func() { m.Group("/hooks", func() {
m.Combo("").Get(admin.ListHooks). m.Combo("").Get(admin.ListHooks).

View File

@ -6,6 +6,7 @@ package admin
import ( import (
"net/http" "net/http"
"net/url" "net/url"
"strconv"
"strings" "strings"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
@ -127,14 +128,18 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
} }
repoName := dirSplit[1] repoName := dirSplit[1]
var groupID int64
if len(dirSplit) >= 3 {
groupID, _ = strconv.ParseInt(dirSplit[2], 10, 64)
}
// check not a repo // check not a repo
has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, repoName) has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, repoName, groupID)
if err != nil { if err != nil {
ctx.ServerError("IsRepositoryExist", err) ctx.ServerError("IsRepositoryExist", err)
return return
} }
exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, repoName))) exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, repoName, groupID)))
if err != nil { if err != nil {
ctx.ServerError("IsDir", err) ctx.ServerError("IsDir", err)
return return
@ -151,7 +156,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
} }
ctx.Flash.Success(ctx.Tr("repo.adopt_preexisting_success", dir)) ctx.Flash.Success(ctx.Tr("repo.adopt_preexisting_success", dir))
} else if action == "delete" { } else if action == "delete" {
if err := repo_service.DeleteUnadoptedRepository(ctx, ctx.Doer, ctxUser, dirSplit[1]); err != nil { if err := repo_service.DeleteUnadoptedRepository(ctx, ctx.Doer, ctxUser, dirSplit[1], groupID); err != nil {
ctx.ServerError("repository.AdoptRepository", err) ctx.ServerError("repository.AdoptRepository", err)
return return
} }

View File

@ -9,7 +9,7 @@ import (
) )
func addOwnerRepoGitHTTPRouters(m *web.Router, middlewares ...any) { func addOwnerRepoGitHTTPRouters(m *web.Router, middlewares ...any) {
m.Group("/{username}/{reponame}", func() { m.Group("/{username}/{group_id}?/{reponame}", func() {
m.Methods("POST,OPTIONS", "/git-upload-pack", repo.ServiceUploadPack) m.Methods("POST,OPTIONS", "/git-upload-pack", repo.ServiceUploadPack)
m.Methods("POST,OPTIONS", "/git-receive-pack", repo.ServiceReceivePack) m.Methods("POST,OPTIONS", "/git-receive-pack", repo.ServiceReceivePack)
m.Methods("POST,OPTIONS", "/git-upload-archive", repo.ServiceUploadArchive) m.Methods("POST,OPTIONS", "/git-upload-archive", repo.ServiceUploadArchive)

View File

@ -9,6 +9,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
"strconv"
"strings" "strings"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
@ -22,14 +23,17 @@ func goGet(ctx *context.Context) {
return return
} }
parts := strings.SplitN(ctx.Req.URL.EscapedPath(), "/", 4) parts := strings.SplitN(ctx.Req.URL.EscapedPath(), "/", 5)
if len(parts) < 3 { if len(parts) < 3 {
return return
} }
var group string
ownerName := parts[1] ownerName := parts[1]
repoName := parts[2] repoName := parts[2]
if len(parts) > 3 {
group = parts[3]
}
// Quick responses appropriate go-get meta with status 200 // Quick responses appropriate go-get meta with status 200
// regardless of if user have access to the repository, // regardless of if user have access to the repository,
@ -56,7 +60,11 @@ func goGet(ctx *context.Context) {
if err == nil && len(repo.DefaultBranch) > 0 { if err == nil && len(repo.DefaultBranch) > 0 {
branchName = repo.DefaultBranch branchName = repo.DefaultBranch
} }
prefix := setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName)) prefix := setting.AppURL + url.PathEscape(ownerName)
if group != "" {
prefix = path.Join(prefix, group)
}
prefix = path.Join(prefix, url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName))
appURL, _ := url.Parse(setting.AppURL) appURL, _ := url.Parse(setting.AppURL)
@ -68,10 +76,11 @@ func goGet(ctx *context.Context) {
goGetImport := context.ComposeGoGetImport(ctx, ownerName, trimmedRepoName) goGetImport := context.ComposeGoGetImport(ctx, ownerName, trimmedRepoName)
var cloneURL string var cloneURL string
gid, _ := strconv.ParseInt(group, 10, 64)
if setting.Repository.GoGetCloneURLProtocol == "ssh" { if setting.Repository.GoGetCloneURLProtocol == "ssh" {
cloneURL = repo_model.ComposeSSHCloneURL(ctx.Doer, ownerName, repoName) cloneURL = repo_model.ComposeSSHCloneURL(ctx.Doer, ownerName, repoName, gid)
} else { } else {
cloneURL = repo_model.ComposeHTTPSCloneURL(ctx, ownerName, repoName) cloneURL = repo_model.ComposeHTTPSCloneURL(ctx, ownerName, repoName, gid)
} }
goImportContent := fmt.Sprintf("%s git %s", goGetImport, cloneURL /*CloneLink*/) goImportContent := fmt.Sprintf("%s git %s", goGetImport, cloneURL /*CloneLink*/)
goSourceContent := fmt.Sprintf("%s _ %s %s", goGetImport, prefix+"{/dir}" /*GoDocDirectory*/, prefix+"{/dir}/{file}#L{line}" /*GoDocFile*/) goSourceContent := fmt.Sprintf("%s _ %s %s", goGetImport, prefix+"{/dir}" /*GoDocDirectory*/, prefix+"{/dir}/{file}#L{line}" /*GoDocFile*/)

View File

@ -4,6 +4,9 @@
package setting package setting
import ( import (
"strconv"
"strings"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@ -21,18 +24,24 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
ctx.Data["allowDelete"] = allowDelete ctx.Data["allowDelete"] = allowDelete
dir := ctx.FormString("id") dir := ctx.FormString("id")
var gid int64
if len(strings.Split(dir, "/")) > 1 {
split := strings.Split(dir, "/")
dir = split[0]
gid, _ = strconv.ParseInt(split[1], 10, 64)
}
action := ctx.FormString("action") action := ctx.FormString("action")
ctxUser := ctx.Doer ctxUser := ctx.Doer
// check not a repo // check not a repo
has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, dir) has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, dir, 0)
if err != nil { if err != nil {
ctx.ServerError("IsRepositoryExist", err) ctx.ServerError("IsRepositoryExist", err)
return return
} }
exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, dir))) exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, dir, gid)))
if err != nil { if err != nil {
ctx.ServerError("IsDir", err) ctx.ServerError("IsDir", err)
return return
@ -49,7 +58,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
} }
ctx.Flash.Success(ctx.Tr("repo.adopt_preexisting_success", dir)) ctx.Flash.Success(ctx.Tr("repo.adopt_preexisting_success", dir))
} else if action == "delete" && allowDelete { } else if action == "delete" && allowDelete {
if err := repo_service.DeleteUnadoptedRepository(ctx, ctxUser, ctxUser, dir); err != nil { if err := repo_service.DeleteUnadoptedRepository(ctx, ctxUser, ctxUser, dir, gid); err != nil {
ctx.ServerError("repository.AdoptRepository", err) ctx.ServerError("repository.AdoptRepository", err)
return return
} }

View File

@ -8,6 +8,7 @@ import (
"strings" "strings"
auth_model "code.gitea.io/gitea/models/auth" auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"

View File

@ -12,6 +12,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
"strconv"
"strings" "strings"
asymkey_model "code.gitea.io/gitea/models/asymkey" asymkey_model "code.gitea.io/gitea/models/asymkey"
@ -369,6 +370,7 @@ func ComposeGoGetImport(ctx context.Context, owner, repo string) string {
func EarlyResponseForGoGetMeta(ctx *Context) { func EarlyResponseForGoGetMeta(ctx *Context) {
username := ctx.PathParam("username") username := ctx.PathParam("username")
reponame := strings.TrimSuffix(ctx.PathParam("reponame"), ".git") reponame := strings.TrimSuffix(ctx.PathParam("reponame"), ".git")
groupID := ctx.PathParamInt64("group_id")
if username == "" || reponame == "" { if username == "" || reponame == "" {
ctx.PlainText(http.StatusBadRequest, "invalid repository path") ctx.PlainText(http.StatusBadRequest, "invalid repository path")
return return
@ -376,9 +378,9 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
var cloneURL string var cloneURL string
if setting.Repository.GoGetCloneURLProtocol == "ssh" { if setting.Repository.GoGetCloneURLProtocol == "ssh" {
cloneURL = repo_model.ComposeSSHCloneURL(ctx.Doer, username, reponame) cloneURL = repo_model.ComposeSSHCloneURL(ctx.Doer, username, reponame, groupID)
} else { } else {
cloneURL = repo_model.ComposeHTTPSCloneURL(ctx, username, reponame) cloneURL = repo_model.ComposeHTTPSCloneURL(ctx, username, reponame, groupID)
} }
goImportContent := fmt.Sprintf("%s git %s", ComposeGoGetImport(ctx, username, reponame), cloneURL) goImportContent := fmt.Sprintf("%s git %s", ComposeGoGetImport(ctx, username, reponame), cloneURL)
htmlMeta := fmt.Sprintf(`<meta name="go-import" content="%s">`, html.EscapeString(goImportContent)) htmlMeta := fmt.Sprintf(`<meta name="go-import" content="%s">`, html.EscapeString(goImportContent))
@ -481,6 +483,20 @@ func repoAssignmentPrepareData(ctx *Context) *repoAssignmentPrepareDataStruct {
// HINT: here it doesn't handle ".wiki" extension, it is handled in repoAssignmentAutoRedirectWiki, need to be refactored in the future // HINT: here it doesn't handle ".wiki" extension, it is handled in repoAssignmentAutoRedirectWiki, need to be refactored in the future
userName := ctx.PathParam("username") userName := ctx.PathParam("username")
repoName := ctx.PathParam("reponame") repoName := ctx.PathParam("reponame")
group := ctx.PathParam("group_id")
var gid int64
if group != "" {
gid, _ = strconv.ParseInt(group, 10, 64)
if gid == 0 {
q := ctx.Req.URL.RawQuery
if q != "" {
q = "?" + q
}
ctx.Redirect(strings.Replace(ctx.Link, "/0/", "/", 1) + q)
return
}
group += "/"
}
repoName = strings.TrimSuffix(repoName, ".git") repoName = strings.TrimSuffix(repoName, ".git")
if setting.Other.EnableFeed { if setting.Other.EnableFeed {
ctx.Data["EnableFeed"] = true ctx.Data["EnableFeed"] = true
@ -535,7 +551,7 @@ func repoAssignmentAutoRedirectWiki(ctx *Context, data *repoAssignmentPrepareDat
redirectRepoName += originalRepoName[len(redirectRepoName)+5:] redirectRepoName += originalRepoName[len(redirectRepoName)+5:]
redirectPath := strings.Replace( redirectPath := strings.Replace(
ctx.Req.URL.EscapedPath(), ctx.Req.URL.EscapedPath(),
url.PathEscape(userName)+"/"+url.PathEscape(originalRepoName), url.PathEscape(userName)+"/"+group+url.PathEscape(originalRepoName),
url.PathEscape(userName)+"/"+url.PathEscape(redirectRepoName)+"/wiki", url.PathEscape(userName)+"/"+url.PathEscape(redirectRepoName)+"/wiki",
1, 1,
) )
@ -570,6 +586,9 @@ func repoAssignmentPrepareRepo(ctx *Context, data *repoAssignmentPrepareDataStru
} }
return return
} }
if repo.GroupID != gid {
ctx.NotFound(nil)
}
repo.Owner = ctx.Repo.Owner repo.Owner = ctx.Repo.Owner
data.repo = repo data.repo = repo
} }

View File

@ -208,12 +208,12 @@ func adoptRepository(ctx context.Context, repo *repo_model.Repository, defaultBr
} }
// DeleteUnadoptedRepository deletes unadopted repository files from the filesystem // DeleteUnadoptedRepository deletes unadopted repository files from the filesystem
func DeleteUnadoptedRepository(ctx context.Context, doer, u *user_model.User, repoName string) error { func DeleteUnadoptedRepository(ctx context.Context, doer, u *user_model.User, repoName string, groupID int64) error {
if err := repo_model.IsUsableRepoName(repoName); err != nil { if err := repo_model.IsUsableRepoName(repoName); err != nil {
return err return err
} }
relativePath := repo_model.RelativePath(u.Name, repoName) relativePath := repo_model.RelativePath(u.Name, repoName, groupID)
exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(relativePath)) exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(relativePath))
if err != nil { if err != nil {
log.Error("Unable to check if %s exists. Error: %v", relativePath, err) log.Error("Unable to check if %s exists. Error: %v", relativePath, err)
@ -226,7 +226,7 @@ func DeleteUnadoptedRepository(ctx context.Context, doer, u *user_model.User, re
} }
} }
if exist, err := repo_model.IsRepositoryModelExist(ctx, u, repoName); err != nil { if exist, err := repo_model.IsRepositoryModelExist(ctx, u, repoName, groupID); err != nil {
return err return err
} else if exist { } else if exist {
return repo_model.ErrRepoAlreadyExist{ return repo_model.ErrRepoAlreadyExist{

View File

@ -369,7 +369,7 @@ func createRepositoryInDB(ctx context.Context, doer, u *user_model.User, repo *r
return err return err
} }
has, err := repo_model.IsRepositoryModelExist(ctx, u, repo.Name) has, err := repo_model.IsRepositoryModelExist(ctx, u, repo.Name, repo.GroupID)
if err != nil { if err != nil {
return fmt.Errorf("IsRepositoryExist: %w", err) return fmt.Errorf("IsRepositoryExist: %w", err)
} else if has { } else if has {

View File

@ -91,12 +91,12 @@ func AcceptTransferOwnership(ctx context.Context, repo *repo_model.Repository, d
} }
// isRepositoryModelOrDirExist returns true if the repository with given name under user has already existed. // isRepositoryModelOrDirExist returns true if the repository with given name under user has already existed.
func isRepositoryModelOrDirExist(ctx context.Context, u *user_model.User, repoName string) (bool, error) { func isRepositoryModelOrDirExist(ctx context.Context, u *user_model.User, repoName string, groupID int64) (bool, error) {
has, err := repo_model.IsRepositoryModelExist(ctx, u, repoName) has, err := repo_model.IsRepositoryModelExist(ctx, u, repoName, groupID)
if err != nil { if err != nil {
return false, err return false, err
} }
repo := repo_model.StorageRepo(repo_model.RelativePath(u.Name, repoName)) repo := repo_model.StorageRepo(repo_model.RelativePath(u.Name, repoName, groupID))
isExist, err := gitrepo.IsRepositoryExist(ctx, repo) isExist, err := gitrepo.IsRepositoryExist(ctx, repo)
return has || isExist, err return has || isExist, err
} }
@ -118,7 +118,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
} }
if repoRenamed { if repoRenamed {
oldRelativePath, newRelativePath := repo_model.RelativePath(newOwnerName, repo.Name), repo_model.RelativePath(oldOwnerName, repo.Name) oldRelativePath, newRelativePath := repo_model.RelativePath(newOwnerName, repo.Name, 0), repo_model.RelativePath(oldOwnerName, repo.Name, repo.GroupID)
if err := gitrepo.RenameRepository(ctx, repo_model.StorageRepo(oldRelativePath), repo_model.StorageRepo(newRelativePath)); err != nil { if err := gitrepo.RenameRepository(ctx, repo_model.StorageRepo(oldRelativePath), repo_model.StorageRepo(newRelativePath)); err != nil {
log.Critical("Unable to move repository %s/%s directory from %s back to correct place %s: %v", oldOwnerName, repo.Name, log.Critical("Unable to move repository %s/%s directory from %s back to correct place %s: %v", oldOwnerName, repo.Name,
oldRelativePath, newRelativePath, err) oldRelativePath, newRelativePath, err)
@ -126,7 +126,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
} }
if wikiRenamed { if wikiRenamed {
oldRelativePath, newRelativePath := repo_model.RelativeWikiPath(newOwnerName, repo.Name), repo_model.RelativeWikiPath(oldOwnerName, repo.Name) oldRelativePath, newRelativePath := repo_model.RelativeWikiPath(newOwnerName, repo.Name, 0), repo_model.RelativeWikiPath(oldOwnerName, repo.Name, repo.GroupID)
if err := gitrepo.RenameRepository(ctx, repo_model.StorageRepo(oldRelativePath), repo_model.StorageRepo(newRelativePath)); err != nil { if err := gitrepo.RenameRepository(ctx, repo_model.StorageRepo(oldRelativePath), repo_model.StorageRepo(newRelativePath)); err != nil {
log.Critical("Unable to move wiki for repository %s/%s directory from %s back to correct place %s: %v", oldOwnerName, repo.Name, log.Critical("Unable to move wiki for repository %s/%s directory from %s back to correct place %s: %v", oldOwnerName, repo.Name,
oldRelativePath, newRelativePath, err) oldRelativePath, newRelativePath, err)
@ -154,7 +154,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
newOwnerName = newOwner.Name // ensure capitalisation matches newOwnerName = newOwner.Name // ensure capitalisation matches
// Check if new owner has repository with same name. // Check if new owner has repository with same name.
if has, err := isRepositoryModelOrDirExist(ctx, newOwner, repo.Name); err != nil { if has, err := isRepositoryModelOrDirExist(ctx, newOwner, repo.Name, 0); err != nil {
return fmt.Errorf("IsRepositoryExist: %w", err) return fmt.Errorf("IsRepositoryExist: %w", err)
} else if has { } else if has {
return repo_model.ErrRepoAlreadyExist{ return repo_model.ErrRepoAlreadyExist{
@ -304,19 +304,19 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
} }
// Rename remote repository to new path and delete local copy. // Rename remote repository to new path and delete local copy.
oldRelativePath, newRelativePath := repo_model.RelativePath(oldOwner.Name, repo.Name), repo_model.RelativePath(newOwner.Name, repo.Name) oldRelativePath, newRelativePath := repo_model.RelativePath(oldOwner.Name, repo.Name, repo.GroupID), repo_model.RelativePath(newOwner.Name, repo.Name, repo.GroupID)
if err := gitrepo.RenameRepository(ctx, repo_model.StorageRepo(oldRelativePath), repo_model.StorageRepo(newRelativePath)); err != nil { if err := gitrepo.RenameRepository(ctx, repo_model.StorageRepo(oldRelativePath), repo_model.StorageRepo(newRelativePath)); err != nil {
return fmt.Errorf("rename repository directory: %w", err) return fmt.Errorf("rename repository directory: %w", err)
} }
repoRenamed = true repoRenamed = true
// Rename remote wiki repository to new path and delete local copy. // Rename remote wiki repository to new path and delete local copy.
wikiStorageRepo := repo_model.StorageRepo(repo_model.RelativeWikiPath(oldOwner.Name, repo.Name)) wikiStorageRepo := repo_model.StorageRepo(repo_model.RelativeWikiPath(oldOwner.Name, repo.Name, repo.GroupID))
if isExist, err := gitrepo.IsRepositoryExist(ctx, wikiStorageRepo); err != nil { if isExist, err := gitrepo.IsRepositoryExist(ctx, wikiStorageRepo); err != nil {
log.Error("Unable to check if %s exists. Error: %v", wikiStorageRepo.RelativePath(), err) log.Error("Unable to check if %s exists. Error: %v", wikiStorageRepo.RelativePath(), err)
return err return err
} else if isExist { } else if isExist {
if err := gitrepo.RenameRepository(ctx, wikiStorageRepo, repo_model.StorageRepo(repo_model.RelativeWikiPath(newOwner.Name, repo.Name))); err != nil { if err := gitrepo.RenameRepository(ctx, wikiStorageRepo, repo_model.StorageRepo(repo_model.RelativeWikiPath(newOwner.Name, repo.Name, repo.GroupID))); err != nil {
return fmt.Errorf("rename repository wiki: %w", err) return fmt.Errorf("rename repository wiki: %w", err)
} }
wikiRenamed = true wikiRenamed = true
@ -365,7 +365,7 @@ func changeRepositoryName(ctx context.Context, repo *repo_model.Repository, newR
return err return err
} }
has, err := isRepositoryModelOrDirExist(ctx, repo.Owner, newRepoName) has, err := isRepositoryModelOrDirExist(ctx, repo.Owner, newRepoName, repo.GroupID)
if err != nil { if err != nil {
return fmt.Errorf("IsRepositoryExist: %w", err) return fmt.Errorf("IsRepositoryExist: %w", err)
} else if has { } else if has {
@ -376,13 +376,13 @@ func changeRepositoryName(ctx context.Context, repo *repo_model.Repository, newR
} }
if err = gitrepo.RenameRepository(ctx, repo, if err = gitrepo.RenameRepository(ctx, repo,
repo_model.StorageRepo(repo_model.RelativePath(repo.OwnerName, newRepoName))); err != nil { repo_model.StorageRepo(repo_model.RelativePath(repo.OwnerName, newRepoName, 0))); err != nil {
return fmt.Errorf("rename repository directory: %w", err) return fmt.Errorf("rename repository directory: %w", err)
} }
if HasWiki(ctx, repo) { if HasWiki(ctx, repo) {
if err = gitrepo.RenameRepository(ctx, repo.WikiStorageRepo(), repo_model.StorageRepo( if err = gitrepo.RenameRepository(ctx, repo.WikiStorageRepo(), repo_model.StorageRepo(
repo_model.RelativeWikiPath(repo.OwnerName, newRepoName))); err != nil { repo_model.RelativeWikiPath(repo.OwnerName, newRepoName, repo.GroupID))); err != nil {
return fmt.Errorf("rename repository wiki: %w", err) return fmt.Errorf("rename repository wiki: %w", err)
} }
} }