@ -6,7 +6,6 @@
package models
import (
"bytes"
"context"
"crypto/md5"
"errors"
@ -38,7 +37,6 @@ import (
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"github.com/mcuadros/go-version"
"github.com/unknwon/com"
"xorm.io/builder"
)
@ -715,7 +713,7 @@ func (repo *Repository) IsOwnedBy(userID int64) bool {
func ( repo * Repository ) updateSize ( e Engine ) error {
size , err := util . GetDirectorySize ( repo . RepoPath ( ) )
if err != nil {
return fmt . Errorf ( "U pdateSize: %v" , err )
return fmt . Errorf ( "u pdateSize: %v" , err )
}
repo . Size = size
@ -724,8 +722,8 @@ func (repo *Repository) updateSize(e Engine) error {
}
// UpdateSize updates the repository size, calculating it using util.GetDirectorySize
func ( repo * Repository ) UpdateSize ( ) error {
return repo . updateSize ( x )
func ( repo * Repository ) UpdateSize ( ctx DBContext ) error {
return repo . updateSize ( ct x. e )
}
// CanUserFork returns true if specified user can fork repository.
@ -966,64 +964,6 @@ func createDelegateHooks(repoPath string) (err error) {
return nil
}
// initRepoCommit temporarily changes with work directory.
func initRepoCommit ( tmpPath string , repo * Repository , u * User ) ( err error ) {
commitTimeStr := time . Now ( ) . Format ( time . RFC3339 )
sig := u . NewGitSig ( )
// Because this may call hooks we should pass in the environment
env := append ( os . Environ ( ) ,
"GIT_AUTHOR_NAME=" + sig . Name ,
"GIT_AUTHOR_EMAIL=" + sig . Email ,
"GIT_AUTHOR_DATE=" + commitTimeStr ,
"GIT_COMMITTER_NAME=" + sig . Name ,
"GIT_COMMITTER_EMAIL=" + sig . Email ,
"GIT_COMMITTER_DATE=" + commitTimeStr ,
)
if stdout , err := git . NewCommand ( "add" , "--all" ) .
SetDescription ( fmt . Sprintf ( "initRepoCommit (git add): %s" , tmpPath ) ) .
RunInDir ( tmpPath ) ; err != nil {
log . Error ( "git add --all failed: Stdout: %s\nError: %v" , stdout , err )
return fmt . Errorf ( "git add --all: %v" , err )
}
binVersion , err := git . BinVersion ( )
if err != nil {
return fmt . Errorf ( "Unable to get git version: %v" , err )
}
args := [ ] string {
"commit" , fmt . Sprintf ( "--author='%s <%s>'" , sig . Name , sig . Email ) ,
"-m" , "Initial commit" ,
}
if version . Compare ( binVersion , "1.7.9" , ">=" ) {
sign , keyID := SignInitialCommit ( tmpPath , u )
if sign {
args = append ( args , "-S" + keyID )
} else if version . Compare ( binVersion , "2.0.0" , ">=" ) {
args = append ( args , "--no-gpg-sign" )
}
}
if stdout , err := git . NewCommand ( args ... ) .
SetDescription ( fmt . Sprintf ( "initRepoCommit (git commit): %s" , tmpPath ) ) .
RunInDirWithEnv ( tmpPath , env ) ; err != nil {
log . Error ( "Failed to commit: %v: Stdout: %s\nError: %v" , args , stdout , err )
return fmt . Errorf ( "git commit: %v" , err )
}
if stdout , err := git . NewCommand ( "push" , "origin" , "master" ) .
SetDescription ( fmt . Sprintf ( "initRepoCommit (git push): %s" , tmpPath ) ) .
RunInDirWithEnv ( tmpPath , InternalPushingEnvironment ( u , repo ) ) ; err != nil {
log . Error ( "Failed to push back to master: Stdout: %s\nError: %v" , stdout , err )
return fmt . Errorf ( "git push: %v" , err )
}
return nil
}
// CreateRepoOptions contains the create repository options
type CreateRepoOptions struct {
Name string
@ -1040,7 +980,8 @@ type CreateRepoOptions struct {
Status RepositoryStatus
}
func getRepoInitFile ( tp , name string ) ( [ ] byte , error ) {
// GetRepoInitFile returns repository init files
func GetRepoInitFile ( tp , name string ) ( [ ] byte , error ) {
cleanedName := strings . TrimLeft ( path . Clean ( "/" + name ) , "/" )
relPath := path . Join ( "options" , tp , cleanedName )
@ -1064,140 +1005,6 @@ func getRepoInitFile(tp, name string) ([]byte, error) {
}
}
func prepareRepoCommit ( e Engine , repo * Repository , tmpDir , repoPath string , opts CreateRepoOptions ) error {
commitTimeStr := time . Now ( ) . Format ( time . RFC3339 )
authorSig := repo . Owner . NewGitSig ( )
// Because this may call hooks we should pass in the environment
env := append ( os . Environ ( ) ,
"GIT_AUTHOR_NAME=" + authorSig . Name ,
"GIT_AUTHOR_EMAIL=" + authorSig . Email ,
"GIT_AUTHOR_DATE=" + commitTimeStr ,
"GIT_COMMITTER_NAME=" + authorSig . Name ,
"GIT_COMMITTER_EMAIL=" + authorSig . Email ,
"GIT_COMMITTER_DATE=" + commitTimeStr ,
)
// Clone to temporary path and do the init commit.
if stdout , err := git . NewCommand ( "clone" , repoPath , tmpDir ) .
SetDescription ( fmt . Sprintf ( "initRepository (git clone): %s to %s" , repoPath , tmpDir ) ) .
RunInDirWithEnv ( "" , env ) ; err != nil {
log . Error ( "Failed to clone from %v into %s: stdout: %s\nError: %v" , repo , tmpDir , stdout , err )
return fmt . Errorf ( "git clone: %v" , err )
}
// README
data , err := getRepoInitFile ( "readme" , opts . Readme )
if err != nil {
return fmt . Errorf ( "getRepoInitFile[%s]: %v" , opts . Readme , err )
}
cloneLink := repo . cloneLink ( false )
match := map [ string ] string {
"Name" : repo . Name ,
"Description" : repo . Description ,
"CloneURL.SSH" : cloneLink . SSH ,
"CloneURL.HTTPS" : cloneLink . HTTPS ,
}
if err = ioutil . WriteFile ( filepath . Join ( tmpDir , "README.md" ) ,
[ ] byte ( com . Expand ( string ( data ) , match ) ) , 0644 ) ; err != nil {
return fmt . Errorf ( "write README.md: %v" , err )
}
// .gitignore
if len ( opts . Gitignores ) > 0 {
var buf bytes . Buffer
names := strings . Split ( opts . Gitignores , "," )
for _ , name := range names {
data , err = getRepoInitFile ( "gitignore" , name )
if err != nil {
return fmt . Errorf ( "getRepoInitFile[%s]: %v" , name , err )
}
buf . WriteString ( "# ---> " + name + "\n" )
buf . Write ( data )
buf . WriteString ( "\n" )
}
if buf . Len ( ) > 0 {
if err = ioutil . WriteFile ( filepath . Join ( tmpDir , ".gitignore" ) , buf . Bytes ( ) , 0644 ) ; err != nil {
return fmt . Errorf ( "write .gitignore: %v" , err )
}
}
}
// LICENSE
if len ( opts . License ) > 0 {
data , err = getRepoInitFile ( "license" , opts . License )
if err != nil {
return fmt . Errorf ( "getRepoInitFile[%s]: %v" , opts . License , err )
}
if err = ioutil . WriteFile ( filepath . Join ( tmpDir , "LICENSE" ) , data , 0644 ) ; err != nil {
return fmt . Errorf ( "write LICENSE: %v" , err )
}
}
return nil
}
func checkInitRepository ( repoPath string ) ( err error ) {
// Somehow the directory could exist.
if com . IsExist ( repoPath ) {
return fmt . Errorf ( "initRepository: path already exists: %s" , repoPath )
}
// Init git bare new repository.
if err = git . InitRepository ( repoPath , true ) ; err != nil {
return fmt . Errorf ( "InitRepository: %v" , err )
} else if err = createDelegateHooks ( repoPath ) ; err != nil {
return fmt . Errorf ( "createDelegateHooks: %v" , err )
}
return nil
}
// InitRepository initializes README and .gitignore if needed.
func initRepository ( e Engine , repoPath string , u * User , repo * Repository , opts CreateRepoOptions ) ( err error ) {
if err = checkInitRepository ( repoPath ) ; err != nil {
return err
}
// Initialize repository according to user's choice.
if opts . AutoInit {
tmpDir , err := ioutil . TempDir ( os . TempDir ( ) , "gitea-" + repo . Name )
if err != nil {
return fmt . Errorf ( "Failed to create temp dir for repository %s: %v" , repo . RepoPath ( ) , err )
}
defer os . RemoveAll ( tmpDir )
if err = prepareRepoCommit ( e , repo , tmpDir , repoPath , opts ) ; err != nil {
return fmt . Errorf ( "prepareRepoCommit: %v" , err )
}
// Apply changes and commit.
if err = initRepoCommit ( tmpDir , repo , u ) ; err != nil {
return fmt . Errorf ( "initRepoCommit: %v" , err )
}
}
// Re-fetch the repository from database before updating it (else it would
// override changes that were done earlier with sql)
if repo , err = getRepositoryByID ( e , repo . ID ) ; err != nil {
return fmt . Errorf ( "getRepositoryByID: %v" , err )
}
if ! opts . AutoInit {
repo . IsEmpty = true
}
repo . DefaultBranch = "master"
if err = updateRepository ( e , repo , false ) ; err != nil {
return fmt . Errorf ( "updateRepository: %v" , err )
}
return nil
}
var (
reservedRepoNames = [ ] string { "." , ".." }
reservedRepoPatterns = [ ] string { "*.git" , "*.wiki" }
@ -1208,22 +1015,23 @@ func IsUsableRepoName(name string) error {
return isUsableName ( reservedRepoNames , reservedRepoPatterns , name )
}
func createRepository ( e Engine , doer , u * User , repo * Repository ) ( err error ) {
// CreateRepository creates a repository for the user/organization.
func CreateRepository ( ctx DBContext , doer , u * User , repo * Repository ) ( err error ) {
if err = IsUsableRepoName ( repo . Name ) ; err != nil {
return err
}
has , err := isRepositoryExist ( e , u , repo . Name )
has , err := isRepositoryExist ( ctx . e , u , repo . Name )
if err != nil {
return fmt . Errorf ( "IsRepositoryExist: %v" , err )
} else if has {
return ErrRepoAlreadyExist { u . Name , repo . Name }
}
if _ , err = e . Insert ( repo ) ; err != nil {
if _ , err = ctx . e . Insert ( repo ) ; err != nil {
return err
}
if err = deleteRepoRedirect ( e , u . ID , repo . Name ) ; err != nil {
if err = deleteRepoRedirect ( ctx . e , u . ID , repo . Name ) ; err != nil {
return err
}
@ -1252,20 +1060,19 @@ func createRepository(e Engine, doer, u *User, repo *Repository) (err error) {
Type : tp ,
} )
}
}
if _ , err = e . Insert ( & units ) ; err != nil {
if _ , err = ctx . e . Insert ( & units ) ; err != nil {
return err
}
// Remember visibility preference.
u . LastRepoVisibility = repo . IsPrivate
if err = updateUserCols ( e , u , "last_repo_visibility" ) ; err != nil {
if err = updateUserCols ( ctx . e , u , "last_repo_visibility" ) ; err != nil {
return fmt . Errorf ( "updateUser: %v" , err )
}
if _ , err = e . Incr ( "num_repos" ) . ID ( u . ID ) . Update ( new ( User ) ) ; err != nil {
if _ , err = ctx . e . Incr ( "num_repos" ) . ID ( u . ID ) . Update ( new ( User ) ) ; err != nil {
return fmt . Errorf ( "increment user total_repos: %v" , err )
}
u . NumRepos ++
@ -1277,107 +1084,41 @@ func createRepository(e Engine, doer, u *User, repo *Repository) (err error) {
}
for _ , t := range u . Teams {
if t . IncludesAllRepositories {
if err := t . addRepository ( e , repo ) ; err != nil {
if err := t . addRepository ( ctx . e , repo ) ; err != nil {
return fmt . Errorf ( "addRepository: %v" , err )
}
}
}
if isAdmin , err := isUserRepoAdmin ( e , repo , doer ) ; err != nil {
if isAdmin , err := isUserRepoAdmin ( ctx . e , repo , doer ) ; err != nil {
return fmt . Errorf ( "isUserRepoAdmin: %v" , err )
} else if ! isAdmin {
// Make creator repo admin if it wan't assigned automatically
if err = repo . addCollaborator ( e , doer ) ; err != nil {
if err = repo . addCollaborator ( ctx . e , doer ) ; err != nil {
return fmt . Errorf ( "AddCollaborator: %v" , err )
}
if err = repo . changeCollaborationAccessMode ( e , doer . ID , AccessModeAdmin ) ; err != nil {
if err = repo . changeCollaborationAccessMode ( ctx . e , doer . ID , AccessModeAdmin ) ; err != nil {
return fmt . Errorf ( "ChangeCollaborationAccessMode: %v" , err )
}
}
} else if err = repo . recalculateAccesses ( e ) ; err != nil {
} else if err = repo . recalculateAccesses ( ctx . e ) ; err != nil {
// Organization automatically called this in addRepository method.
return fmt . Errorf ( "recalculateAccesses: %v" , err )
}
if setting . Service . AutoWatchNewRepos {
if err = watchRepo ( e , doer . ID , repo . ID , true ) ; err != nil {
if err = watchRepo ( ctx . e , doer . ID , repo . ID , true ) ; err != nil {
return fmt . Errorf ( "watchRepo: %v" , err )
}
}
if err = copyDefaultWebhooksToRepo ( e , repo . ID ) ; err != nil {
if err = copyDefaultWebhooksToRepo ( ctx . e , repo . ID ) ; err != nil {
return fmt . Errorf ( "copyDefaultWebhooksToRepo: %v" , err )
}
return nil
}
// CreateRepository creates a repository for the user/organization.
func CreateRepository ( doer , u * User , opts CreateRepoOptions ) ( _ * Repository , err error ) {
if ! doer . IsAdmin && ! u . CanCreateRepo ( ) {
return nil , ErrReachLimitOfRepo { u . MaxRepoCreation }
}
repo := & Repository {
OwnerID : u . ID ,
Owner : u ,
OwnerName : u . Name ,
Name : opts . Name ,
LowerName : strings . ToLower ( opts . Name ) ,
Description : opts . Description ,
OriginalURL : opts . OriginalURL ,
OriginalServiceType : opts . GitServiceType ,
IsPrivate : opts . IsPrivate ,
IsFsckEnabled : ! opts . IsMirror ,
CloseIssuesViaCommitInAnyBranch : setting . Repository . DefaultCloseIssuesViaCommitsInAnyBranch ,
Status : opts . Status ,
IsEmpty : ! opts . AutoInit ,
}
sess := x . NewSession ( )
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
return nil , err
}
if err = createRepository ( sess , doer , u , repo ) ; err != nil {
return nil , err
}
// No need for init mirror.
if ! opts . IsMirror {
repoPath := RepoPath ( u . Name , repo . Name )
if err = initRepository ( sess , repoPath , u , repo , opts ) ; err != nil {
if err2 := os . RemoveAll ( repoPath ) ; err2 != nil {
log . Error ( "initRepository: %v" , err )
return nil , fmt . Errorf (
"delete repo directory %s/%s failed(2): %v" , u . Name , repo . Name , err2 )
}
return nil , fmt . Errorf ( "initRepository: %v" , err )
}
// Initialize Issue Labels if selected
if len ( opts . IssueLabels ) > 0 {
if err = initalizeLabels ( sess , repo . ID , opts . IssueLabels ) ; err != nil {
return nil , fmt . Errorf ( "initalizeLabels: %v" , err )
}
}
if stdout , err := git . NewCommand ( "update-server-info" ) .
SetDescription ( fmt . Sprintf ( "CreateRepository(git update-server-info): %s" , repoPath ) ) .
RunInDir ( repoPath ) ; err != nil {
log . Error ( "CreateRepitory(git update-server-info) in %v: Stdout: %s\nError: %v" , repo , stdout , err )
return nil , fmt . Errorf ( "CreateRepository(git update-server-info): %v" , err )
}
}
if err = sess . Commit ( ) ; err != nil {
return nil , err
}
return repo , err
}
func countRepositories ( userID int64 , private bool ) int64 {
sess := x . Where ( "id > 0" )
@ -1414,6 +1155,12 @@ func RepoPath(userName, repoName string) string {
return filepath . Join ( UserPath ( userName ) , strings . ToLower ( repoName ) + ".git" )
}
// IncrementRepoForkNum increment repository fork number
func IncrementRepoForkNum ( ctx DBContext , repoID int64 ) error {
_ , err := ctx . e . Exec ( "UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?" , repoID )
return err
}
// TransferOwnership transfers all corresponding setting from old user to new one.
func TransferOwnership ( doer * User , newOwnerName string , repo * Repository ) error {
newOwner , err := GetUserByName ( newOwnerName )
@ -1672,6 +1419,11 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
return nil
}
// UpdateRepositoryCtx updates a repository with db context
func UpdateRepositoryCtx ( ctx DBContext , repo * Repository , visibilityChanged bool ) error {
return updateRepository ( ctx . e , repo , visibilityChanged )
}
// UpdateRepository updates a repository
func UpdateRepository ( repo * Repository , visibilityChanged bool ) ( err error ) {
sess := x . NewSession ( )
@ -1987,6 +1739,11 @@ func GetRepositoryByID(id int64) (*Repository, error) {
return getRepositoryByID ( x , id )
}
// GetRepositoryByIDCtx returns the repository by given id if exists.
func GetRepositoryByIDCtx ( ctx DBContext , id int64 ) ( * Repository , error ) {
return getRepositoryByID ( ctx . e , id )
}
// GetRepositoriesMapByIDs returns the repositories by given id slice.
func GetRepositoriesMapByIDs ( ids [ ] int64 ) ( map [ int64 ] * Repository , error ) {
var repos = make ( map [ int64 ] * Repository , len ( ids ) )
@ -2436,20 +2193,16 @@ func HasForkedRepo(ownerID, repoID int64) (*Repository, bool) {
}
// CopyLFS copies LFS data from one repo to another
func CopyLFS ( newRepo , oldRepo * Repository ) error {
return copyLFS ( x , newRepo , oldRepo )
}
func copyLFS ( e Engine , newRepo , oldRepo * Repository ) error {
func CopyLFS ( ctx DBContext , newRepo , oldRepo * Repository ) error {
var lfsObjects [ ] * LFSMetaObject
if err := e . Where ( "repository_id=?" , oldRepo . ID ) . Find ( & lfsObjects ) ; err != nil {
if err := ctx . e . Where ( "repository_id=?" , oldRepo . ID ) . Find ( & lfsObjects ) ; err != nil {
return err
}
for _ , v := range lfsObjects {
v . ID = 0
v . RepositoryID = newRepo . ID
if _ , err := e . Insert ( v ) ; err != nil {
if _ , err := ctx . e . Insert ( v ) ; err != nil {
return err
}
}
@ -2457,81 +2210,6 @@ func copyLFS(e Engine, newRepo, oldRepo *Repository) error {
return nil
}
// ForkRepository forks a repository
func ForkRepository ( doer , owner * User , oldRepo * Repository , name , desc string ) ( _ * Repository , err error ) {
forkedRepo , err := oldRepo . GetUserFork ( owner . ID )
if err != nil {
return nil , err
}
if forkedRepo != nil {
return nil , ErrForkAlreadyExist {
Uname : owner . Name ,
RepoName : oldRepo . FullName ( ) ,
ForkName : forkedRepo . FullName ( ) ,
}
}
repo := & Repository {
OwnerID : owner . ID ,
Owner : owner ,
OwnerName : owner . Name ,
Name : name ,
LowerName : strings . ToLower ( name ) ,
Description : desc ,
DefaultBranch : oldRepo . DefaultBranch ,
IsPrivate : oldRepo . IsPrivate ,
IsEmpty : oldRepo . IsEmpty ,
IsFork : true ,
ForkID : oldRepo . ID ,
}
sess := x . NewSession ( )
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
return nil , err
}
if err = createRepository ( sess , doer , owner , repo ) ; err != nil {
return nil , err
}
if _ , err = sess . Exec ( "UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?" , oldRepo . ID ) ; err != nil {
return nil , err
}
repoPath := RepoPath ( owner . Name , repo . Name )
if stdout , err := git . NewCommand (
"clone" , "--bare" , oldRepo . RepoPath ( ) , repoPath ) .
SetDescription ( fmt . Sprintf ( "ForkRepository(git clone): %s to %s" , oldRepo . FullName ( ) , repo . FullName ( ) ) ) .
RunInDirTimeout ( 10 * time . Minute , "" ) ; err != nil {
log . Error ( "Fork Repository (git clone) Failed for %v (from %v):\nStdout: %s\nError: %v" , repo , oldRepo , stdout , err )
return nil , fmt . Errorf ( "git clone: %v" , err )
}
if stdout , err := git . NewCommand ( "update-server-info" ) .
SetDescription ( fmt . Sprintf ( "ForkRepository(git update-server-info): %s" , repo . FullName ( ) ) ) .
RunInDir ( repoPath ) ; err != nil {
log . Error ( "Fork Repository (git update-server-info) failed for %v:\nStdout: %s\nError: %v" , repo , stdout , err )
return nil , fmt . Errorf ( "git update-server-info: %v" , err )
}
if err = createDelegateHooks ( repoPath ) ; err != nil {
return nil , fmt . Errorf ( "createDelegateHooks: %v" , err )
}
//Commit repo to get Fork ID
err = sess . Commit ( )
if err != nil {
return nil , err
}
if err = repo . UpdateSize ( ) ; err != nil {
log . Error ( "Failed to update size for repository: %v" , err )
}
return repo , CopyLFS ( repo , oldRepo )
}
// GetForks returns all the forks of the repository
func ( repo * Repository ) GetForks ( ) ( [ ] * Repository , error ) {
forks := make ( [ ] * Repository , 0 , repo . NumForks )