mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-14 19:38:04 +02:00
update repository storage layout as per https://github.com/go-gitea/gitea/issues/1872#issuecomment-3194681583
This commit is contained in:
parent
ba0c7caabb
commit
d1af473553
@ -228,13 +228,21 @@ func init() {
|
||||
db.RegisterModel(new(Repository))
|
||||
}
|
||||
|
||||
func RelativePath(ownerName, repoName string) string {
|
||||
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".git"
|
||||
func RelativePathBaseName(ownerName, repoName string, groupID int64) string {
|
||||
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
|
||||
func (repo *Repository) RelativePath() string {
|
||||
return RelativePath(repo.OwnerName, repo.Name)
|
||||
return RelativePath(repo.OwnerName, repo.Name, repo.GroupID)
|
||||
}
|
||||
|
||||
type StorageRepo string
|
||||
@ -582,13 +590,19 @@ func (repo *Repository) IsGenerated() bool {
|
||||
}
|
||||
|
||||
// RepoPath returns repository path by given user and repository name.
|
||||
func RepoPath(userName, repoName string) string { //revive:disable-line:exported
|
||||
return filepath.Join(setting.RepoRootPath, filepath.Clean(strings.ToLower(userName)), filepath.Clean(strings.ToLower(repoName)+".git"))
|
||||
func RepoPath(userName, repoName string, groupID int64) string { //revive:disable-line:exported
|
||||
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
|
||||
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
|
||||
@ -658,13 +672,25 @@ type CloneLink struct {
|
||||
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.
|
||||
func ComposeHTTPSCloneURL(ctx context.Context, owner, repo string) string {
|
||||
return fmt.Sprintf("%s%s/%s.git", httplib.GuessCurrentAppURL(ctx), url.PathEscape(owner), url.PathEscape(repo))
|
||||
func ComposeHTTPSCloneURL(ctx context.Context, owner, repo string, groupID int64) string {
|
||||
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.
|
||||
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
|
||||
sshDomain := setting.SSH.Domain
|
||||
|
||||
@ -683,7 +709,7 @@ func ComposeSSHCloneURL(doer *user_model.User, ownerName, repoName string) strin
|
||||
// non-standard port, it must use full URI
|
||||
if setting.SSH.Port != 22 {
|
||||
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)
|
||||
@ -698,25 +724,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.
|
||||
func ComposeTeaCloneCommand(ctx context.Context, owner, repo string) string {
|
||||
return fmt.Sprintf("tea clone %s/%s", url.PathEscape(owner), url.PathEscape(repo))
|
||||
func ComposeTeaCloneCommand(ctx context.Context, owner, repo string, groupID int64) string {
|
||||
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{
|
||||
SSH: ComposeSSHCloneURL(doer, repo.OwnerName, repoPathName),
|
||||
HTTPS: ComposeHTTPSCloneURL(ctx, repo.OwnerName, repoPathName),
|
||||
Tea: ComposeTeaCloneCommand(ctx, repo.OwnerName, repoPathName),
|
||||
SSH: ComposeSSHCloneURL(doer, repo.OwnerName, repoPathName, groupID),
|
||||
HTTPS: ComposeHTTPSCloneURL(ctx, repo.OwnerName, repoPathName, groupID),
|
||||
Tea: ComposeTeaCloneCommand(ctx, repo.OwnerName, repoPathName, groupID),
|
||||
}
|
||||
}
|
||||
|
||||
// CloneLink returns clone URLs of repository.
|
||||
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) {
|
||||
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
|
||||
@ -854,10 +880,11 @@ func GetRepositoriesMapByIDs(ctx context.Context, ids []int64) (map[int64]*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{
|
||||
OwnerID: u.ID,
|
||||
LowerName: strings.ToLower(repoName),
|
||||
GroupID: groupID,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -254,7 +254,7 @@ func CreatePendingRepositoryTransfer(ctx context.Context, doer, newOwner *user_m
|
||||
}
|
||||
|
||||
// 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)
|
||||
} else if has {
|
||||
return ErrRepoAlreadyExist{
|
||||
|
||||
@ -5,12 +5,10 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ErrWikiAlreadyExist represents a "WikiAlreadyExist" kind of error.
|
||||
@ -72,15 +70,13 @@ func (err ErrWikiInvalidFileName) Unwrap() error {
|
||||
|
||||
// WikiCloneLink returns clone URLs of repository wiki.
|
||||
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 {
|
||||
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".wiki.git"
|
||||
func RelativeWikiPath(ownerName, repoName string, groupID int64) string {
|
||||
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 {
|
||||
return StorageRepo(RelativeWikiPath(repo.OwnerName, repo.Name))
|
||||
return StorageRepo(RelativeWikiPath(repo.OwnerName, repo.Name, repo.GroupID))
|
||||
}
|
||||
|
||||
@ -55,6 +55,47 @@ func ListUnadoptedRepositories(ctx *context.APIContext) {
|
||||
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
|
||||
func AdoptRepository(ctx *context.APIContext) {
|
||||
// swagger:operation POST /admin/unadopted/{owner}/{repo} admin adminAdoptRepository
|
||||
@ -80,8 +121,40 @@ func AdoptRepository(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/notFound"
|
||||
// "403":
|
||||
// "$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")
|
||||
repoName := ctx.PathParam("reponame")
|
||||
groupID := ctx.PathParamInt64("group_id")
|
||||
|
||||
ctxUser, err := user_model.GetUserByName(ctx, ownerName)
|
||||
if err != nil {
|
||||
@ -94,12 +167,12 @@ func AdoptRepository(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
// check not a repo
|
||||
has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, repoName)
|
||||
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)))
|
||||
exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, repoName, groupID)))
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
@ -108,10 +181,8 @@ func AdoptRepository(ctx *context.APIContext) {
|
||||
ctx.APIErrorNotFound()
|
||||
return
|
||||
}
|
||||
if _, err := repo_service.AdoptRepository(ctx, ctx.Doer, ctxUser, repo_service.CreateRepoOptions{
|
||||
Name: repoName,
|
||||
IsPrivate: true,
|
||||
}); err != nil {
|
||||
|
||||
if err := repo_service.DeleteUnadoptedRepository(ctx, ctx.Doer, ctxUser, repoName, groupID); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
@ -142,39 +213,30 @@ func DeleteUnadoptedRepository(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/empty"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
ownerName := ctx.PathParam("username")
|
||||
repoName := ctx.PathParam("reponame")
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
exist, err := gitrepo.IsRepositoryExist(ctx, repo_model.StorageRepo(repo_model.RelativePath(ctxUser.Name, repoName)))
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
if has || !exist {
|
||||
ctx.APIErrorNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if err := repo_service.DeleteUnadoptedRepository(ctx, ctx.Doer, ctxUser, repoName); err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNoContent)
|
||||
commonDeleteUnadoptedRepo(ctx)
|
||||
}
|
||||
|
||||
func DeleteUnadoptedRepositoryInGroup(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /admin/unadopted/{owner}/{group_id}/{repo} admin adminDeleteUnadoptedRepository
|
||||
// ---
|
||||
// summary: Delete unadopted files
|
||||
// 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"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
commonDeleteUnadoptedRepo(ctx)
|
||||
}
|
||||
|
||||
@ -66,6 +66,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
@ -140,7 +141,16 @@ func repoAssignment() func(ctx *context.APIContext) {
|
||||
return func(ctx *context.APIContext) {
|
||||
userName := ctx.PathParam("username")
|
||||
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 (
|
||||
owner *user_model.User
|
||||
err error
|
||||
@ -186,6 +196,10 @@ func repoAssignment() func(ctx *context.APIContext) {
|
||||
}
|
||||
return
|
||||
}
|
||||
if repo.GroupID != gid {
|
||||
ctx.APIErrorNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
repo.Owner = owner
|
||||
ctx.Repo.Repository = repo
|
||||
@ -1771,8 +1785,13 @@ func Routes() *web.Router {
|
||||
})
|
||||
m.Group("/unadopted", func() {
|
||||
m.Get("", admin.ListUnadoptedRepositories)
|
||||
m.Post("/{username}/{reponame}", admin.AdoptRepository)
|
||||
m.Delete("/{username}/{reponame}", admin.DeleteUnadoptedRepository)
|
||||
m.Group("/{username}", func() {
|
||||
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.Combo("").Get(admin.ListHooks).
|
||||
|
||||
@ -6,6 +6,7 @@ package admin
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
@ -127,14 +128,18 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
|
||||
}
|
||||
|
||||
repoName := dirSplit[1]
|
||||
var groupID int64
|
||||
if len(dirSplit) >= 3 {
|
||||
groupID, _ = strconv.ParseInt(dirSplit[2], 10, 64)
|
||||
}
|
||||
|
||||
// check not a repo
|
||||
has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, repoName)
|
||||
has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, repoName, groupID)
|
||||
if err != nil {
|
||||
ctx.ServerError("IsRepositoryExist", err)
|
||||
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 {
|
||||
ctx.ServerError("IsDir", err)
|
||||
return
|
||||
@ -151,7 +156,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
|
||||
}
|
||||
ctx.Flash.Success(ctx.Tr("repo.adopt_preexisting_success", dir))
|
||||
} 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)
|
||||
return
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
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-receive-pack", repo.ServiceReceivePack)
|
||||
m.Methods("POST,OPTIONS", "/git-upload-archive", repo.ServiceUploadArchive)
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
@ -22,14 +23,17 @@ func goGet(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
parts := strings.SplitN(ctx.Req.URL.EscapedPath(), "/", 4)
|
||||
parts := strings.SplitN(ctx.Req.URL.EscapedPath(), "/", 5)
|
||||
|
||||
if len(parts) < 3 {
|
||||
return
|
||||
}
|
||||
|
||||
var group string
|
||||
ownerName := parts[1]
|
||||
repoName := parts[2]
|
||||
if len(parts) > 3 {
|
||||
group = parts[3]
|
||||
}
|
||||
|
||||
// Quick responses appropriate go-get meta with status 200
|
||||
// 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 {
|
||||
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)
|
||||
|
||||
@ -68,10 +76,11 @@ func goGet(ctx *context.Context) {
|
||||
goGetImport := context.ComposeGoGetImport(ctx, ownerName, trimmedRepoName)
|
||||
|
||||
var cloneURL string
|
||||
gid, _ := strconv.ParseInt(group, 10, 64)
|
||||
if setting.Repository.GoGetCloneURLProtocol == "ssh" {
|
||||
cloneURL = repo_model.ComposeSSHCloneURL(ctx.Doer, ownerName, repoName)
|
||||
cloneURL = repo_model.ComposeSSHCloneURL(ctx.Doer, ownerName, repoName, gid)
|
||||
} 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*/)
|
||||
goSourceContent := fmt.Sprintf("%s _ %s %s", goGetImport, prefix+"{/dir}" /*GoDocDirectory*/, prefix+"{/dir}/{file}#L{line}" /*GoDocFile*/)
|
||||
|
||||
@ -4,6 +4,9 @@
|
||||
package setting
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@ -21,18 +24,24 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
|
||||
ctx.Data["allowDelete"] = allowDelete
|
||||
|
||||
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")
|
||||
|
||||
ctxUser := ctx.Doer
|
||||
|
||||
// check not a repo
|
||||
has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, dir)
|
||||
has, err := repo_model.IsRepositoryModelExist(ctx, ctxUser, dir, 0)
|
||||
if err != nil {
|
||||
ctx.ServerError("IsRepositoryExist", err)
|
||||
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 {
|
||||
ctx.ServerError("IsDir", err)
|
||||
return
|
||||
@ -49,7 +58,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
|
||||
}
|
||||
ctx.Flash.Success(ctx.Tr("repo.adopt_preexisting_success", dir))
|
||||
} 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)
|
||||
return
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
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/unit"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
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) {
|
||||
username := ctx.PathParam("username")
|
||||
reponame := strings.TrimSuffix(ctx.PathParam("reponame"), ".git")
|
||||
groupID := ctx.PathParamInt64("group_id")
|
||||
if username == "" || reponame == "" {
|
||||
ctx.PlainText(http.StatusBadRequest, "invalid repository path")
|
||||
return
|
||||
@ -376,9 +378,9 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
|
||||
|
||||
var cloneURL string
|
||||
if setting.Repository.GoGetCloneURLProtocol == "ssh" {
|
||||
cloneURL = repo_model.ComposeSSHCloneURL(ctx.Doer, username, reponame)
|
||||
cloneURL = repo_model.ComposeSSHCloneURL(ctx.Doer, username, reponame, groupID)
|
||||
} 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)
|
||||
htmlMeta := fmt.Sprintf(`<meta name="go-import" content="%s">`, html.EscapeString(goImportContent))
|
||||
@ -468,6 +470,20 @@ func RepoAssignment(ctx *Context) {
|
||||
var err error
|
||||
userName := ctx.PathParam("username")
|
||||
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")
|
||||
if setting.Other.EnableFeed {
|
||||
ctx.Data["EnableFeed"] = true
|
||||
@ -514,7 +530,7 @@ func RepoAssignment(ctx *Context) {
|
||||
redirectRepoName += originalRepoName[len(redirectRepoName)+5:]
|
||||
redirectPath := strings.Replace(
|
||||
ctx.Req.URL.EscapedPath(),
|
||||
url.PathEscape(userName)+"/"+url.PathEscape(originalRepoName),
|
||||
url.PathEscape(userName)+"/"+group+url.PathEscape(originalRepoName),
|
||||
url.PathEscape(userName)+"/"+url.PathEscape(redirectRepoName)+"/wiki",
|
||||
1,
|
||||
)
|
||||
@ -546,6 +562,9 @@ func RepoAssignment(ctx *Context) {
|
||||
}
|
||||
return
|
||||
}
|
||||
if repo.GroupID != gid {
|
||||
ctx.NotFound(nil)
|
||||
}
|
||||
repo.Owner = ctx.Repo.Owner
|
||||
|
||||
repoAssignment(ctx, repo)
|
||||
|
||||
@ -208,12 +208,12 @@ func adoptRepository(ctx context.Context, repo *repo_model.Repository, defaultBr
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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))
|
||||
if err != nil {
|
||||
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
|
||||
} else if exist {
|
||||
return repo_model.ErrRepoAlreadyExist{
|
||||
|
||||
@ -366,7 +366,7 @@ func createRepositoryInDB(ctx context.Context, doer, u *user_model.User, repo *r
|
||||
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 {
|
||||
return fmt.Errorf("IsRepositoryExist: %w", err)
|
||||
} else if has {
|
||||
|
||||
@ -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.
|
||||
func isRepositoryModelOrDirExist(ctx context.Context, u *user_model.User, repoName string) (bool, error) {
|
||||
has, err := repo_model.IsRepositoryModelExist(ctx, u, repoName)
|
||||
func isRepositoryModelOrDirExist(ctx context.Context, u *user_model.User, repoName string, groupID int64) (bool, error) {
|
||||
has, err := repo_model.IsRepositoryModelExist(ctx, u, repoName, groupID)
|
||||
if err != nil {
|
||||
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)
|
||||
return has || isExist, err
|
||||
}
|
||||
@ -118,7 +118,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
|
||||
}
|
||||
|
||||
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 {
|
||||
log.Critical("Unable to move repository %s/%s directory from %s back to correct place %s: %v", oldOwnerName, repo.Name,
|
||||
oldRelativePath, newRelativePath, err)
|
||||
@ -126,7 +126,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
@ -154,7 +154,7 @@ func transferOwnership(ctx context.Context, doer *user_model.User, newOwnerName
|
||||
newOwnerName = newOwner.Name // ensure capitalisation matches
|
||||
|
||||
// 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)
|
||||
} else if has {
|
||||
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.
|
||||
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 {
|
||||
return fmt.Errorf("rename repository directory: %w", err)
|
||||
}
|
||||
repoRenamed = true
|
||||
|
||||
// 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 {
|
||||
log.Error("Unable to check if %s exists. Error: %v", wikiStorageRepo.RelativePath(), err)
|
||||
return err
|
||||
} 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)
|
||||
}
|
||||
wikiRenamed = true
|
||||
@ -365,7 +365,7 @@ func changeRepositoryName(ctx context.Context, repo *repo_model.Repository, newR
|
||||
return err
|
||||
}
|
||||
|
||||
has, err := isRepositoryModelOrDirExist(ctx, repo.Owner, newRepoName)
|
||||
has, err := isRepositoryModelOrDirExist(ctx, repo.Owner, newRepoName, repo.GroupID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("IsRepositoryExist: %w", err)
|
||||
} else if has {
|
||||
@ -376,13 +376,13 @@ func changeRepositoryName(ctx context.Context, repo *repo_model.Repository, newR
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if HasWiki(ctx, repo) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user