@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
)
// ForkRepository forks a repository
@ -45,38 +46,60 @@ func ForkRepository(doer, owner *models.User, opts models.ForkRepoOptions) (_ *m
oldRepoPath := opts . BaseRepo . RepoPath ( )
needsRollback := false
rollbackFn := func ( ) {
if ! needsRollback {
return
}
repoPath := models . RepoPath ( owner . Name , repo . Name )
if exists , _ := util . IsExist ( repoPath ) ; ! exists {
return
}
// As the transaction will be failed and hence database changes will be destroyed we only need
// to delete the related repository on the filesystem
if errDelete := util . RemoveAll ( repoPath ) ; errDelete != nil {
log . Error ( "Failed to remove fork repo" )
}
}
needsRollbackInPanic := true
defer func ( ) {
panicErr := recover ( )
if panicErr == nil {
return
}
if needsRollbackInPanic {
rollbackFn ( )
}
panic ( panicErr )
} ( )
err = models . WithTx ( func ( ctx models . DBContext ) error {
if err = models . CreateRepository ( ctx , doer , owner , repo , false ) ; err != nil {
return err
}
rollbackRemoveFn := func ( ) {
if repo . ID == 0 {
return
}
if errDelete := models . DeleteRepository ( doer , owner . ID , repo . ID ) ; errDelete != nil {
log . Error ( "Rollback deleteRepository: %v" , errDelete )
}
}
if err = models . IncrementRepoForkNum ( ctx , opts . BaseRepo . ID ) ; err != nil {
rollbackRemoveFn ( )
return err
}
// copy lfs files failure should not be ignored
if err := models . CopyLFS ( ctx , repo , opts . BaseRepo ) ; err != nil {
rollbackRemoveFn ( )
if err = models . CopyLFS ( ctx , repo , opts . BaseRepo ) ; err != nil {
return err
}
needsRollback = true
repoPath := models . RepoPath ( owner . Name , repo . Name )
if stdout , err := git . NewCommand (
"clone" , "--bare" , oldRepoPath , repoPath ) .
SetDescription ( fmt . Sprintf ( "ForkRepository(git clone): %s to %s" , opts . BaseRepo . 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 , opts . BaseRepo , stdout , err )
rollbackRemoveFn ( )
return fmt . Errorf ( "git clone: %v" , err )
}
@ -84,23 +107,23 @@ func ForkRepository(doer, owner *models.User, opts models.ForkRepoOptions) (_ *m
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 )
rollbackRemoveFn ( )
return fmt . Errorf ( "git update-server-info: %v" , err )
}
if err = createDelegateHooks ( repoPath ) ; err != nil {
rollbackRemoveFn ( )
return fmt . Errorf ( "createDelegateHooks: %v" , err )
}
return nil
} )
needsRollbackInPanic = false
if err != nil {
rollbackFn ( )
return nil , err
}
// even if below operations failed, it could be ignored. And they will be retried
ctx := models . DefaultDBContext ( )
if err = repo . UpdateSize ( ctx ) ; err != nil {
if err : = repo . UpdateSize ( ctx ) ; err != nil {
log . Error ( "Failed to update size for repository: %v" , err )
}
if err := models . CopyLanguageStat ( opts . BaseRepo , repo ) ; err != nil {