2014-02-15 03:16:54 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2014-02-14 18:20:57 +04:00
package models
import (
2014-03-10 04:06:29 +04:00
"errors"
2014-03-11 04:48:58 +04:00
"fmt"
2014-03-11 08:18:44 +04:00
"io/ioutil"
2014-02-14 18:20:57 +04:00
"os"
"path/filepath"
"strings"
"time"
2014-03-11 08:53:53 +04:00
"unicode/utf8"
2014-02-14 18:20:57 +04:00
2014-03-11 09:32:36 +04:00
"github.com/Unknwon/com"
2014-02-14 18:20:57 +04:00
git "github.com/libgit2/git2go"
2014-03-08 02:22:15 +04:00
2014-03-11 09:32:36 +04:00
"github.com/gogits/gogs/modules/base"
2014-03-08 02:22:15 +04:00
"github.com/gogits/gogs/modules/log"
2014-02-14 18:20:57 +04:00
)
2014-03-10 04:06:29 +04:00
type Repository struct {
Id int64
OwnerId int64 ` xorm:"unique(s)" `
ForkId int64
LowerName string ` xorm:"unique(s) index not null" `
Name string ` xorm:"index not null" `
Description string
Private bool
NumWatchs int
NumStars int
NumForks int
Created time . Time ` xorm:"created" `
Updated time . Time ` xorm:"updated" `
2014-02-14 18:20:57 +04:00
}
2014-03-02 17:58:20 +04:00
type Star struct {
Id int64
RepoId int64
UserId int64
Created time . Time ` xorm:"created" `
}
2014-03-11 09:32:36 +04:00
var (
LanguageIgns , Licenses [ ] string
)
2014-03-10 04:06:29 +04:00
var (
ErrRepoAlreadyExist = errors . New ( "Repository already exist" )
2014-03-13 06:27:11 +04:00
ErrRepoNotExist = errors . New ( "Repository does not exist" )
2014-03-10 04:06:29 +04:00
)
2014-03-11 09:32:36 +04:00
func init ( ) {
LanguageIgns = strings . Split ( base . Cfg . MustValue ( "repository" , "LANG_IGNS" ) , "|" )
Licenses = strings . Split ( base . Cfg . MustValue ( "repository" , "LICENSES" ) , "|" )
}
2014-02-14 18:20:57 +04:00
// check if repository is exist
2014-03-10 04:06:29 +04:00
func IsRepositoryExist ( user * User , repoName string ) ( bool , error ) {
repo := Repository { OwnerId : user . Id }
has , err := orm . Where ( "lower_name = ?" , strings . ToLower ( repoName ) ) . Get ( & repo )
2014-02-19 13:50:53 +04:00
if err != nil {
return has , err
}
2014-03-10 04:06:29 +04:00
s , err := os . Stat ( RepoPath ( user . Name , repoName ) )
2014-02-19 13:50:53 +04:00
if err != nil {
2014-02-20 10:53:56 +04:00
return false , nil
2014-02-19 13:50:53 +04:00
}
return s . IsDir ( ) , nil
2014-02-14 18:20:57 +04:00
}
2014-03-08 02:08:21 +04:00
// CreateRepository creates a repository for given user or orgnaziation.
2014-03-11 09:32:36 +04:00
func CreateRepository ( user * User , repoName , desc , repoLang , license string , private bool , initReadme bool ) ( * Repository , error ) {
2014-03-10 04:06:29 +04:00
isExist , err := IsRepositoryExist ( user , repoName )
2014-02-14 18:20:57 +04:00
if err != nil {
return nil , err
2014-03-10 04:06:29 +04:00
} else if isExist {
return nil , ErrRepoAlreadyExist
}
repo := & Repository {
OwnerId : user . Id ,
Name : repoName ,
LowerName : strings . ToLower ( repoName ) ,
Description : desc ,
Private : private ,
}
2014-03-11 08:53:53 +04:00
f := RepoPath ( user . Name , repoName )
2014-03-11 09:32:36 +04:00
if err = initRepository ( f , user , repo , initReadme , repoLang , license ) ; err != nil {
2014-03-11 08:53:53 +04:00
return nil , err
}
2014-02-14 18:20:57 +04:00
session := orm . NewSession ( )
defer session . Close ( )
session . Begin ( )
2014-03-10 04:06:29 +04:00
if _ , err = session . Insert ( repo ) ; err != nil {
if err2 := os . RemoveAll ( f ) ; err2 != nil {
2014-03-11 04:48:58 +04:00
return nil , errors . New ( fmt . Sprintf (
"delete repo directory %s/%s failed" , user . Name , repoName ) )
2014-02-20 10:53:56 +04:00
}
2014-02-14 18:20:57 +04:00
session . Rollback ( )
return nil , err
}
2014-03-10 04:06:29 +04:00
// TODO: RemoveAll may fail due to not root access.
access := Access {
UserName : user . Name ,
2014-02-25 11:11:54 +04:00
RepoName : repo . Name ,
Mode : AU_WRITABLE ,
}
2014-03-10 04:06:29 +04:00
if _ , err = session . Insert ( & access ) ; err != nil {
2014-03-11 08:18:44 +04:00
session . Rollback ( )
2014-03-10 04:06:29 +04:00
if err2 := os . RemoveAll ( f ) ; err2 != nil {
2014-03-11 04:48:58 +04:00
return nil , errors . New ( fmt . Sprintf (
"delete repo directory %s/%s failed" , user . Name , repoName ) )
2014-02-25 11:11:54 +04:00
}
return nil , err
}
2014-03-10 04:06:29 +04:00
if _ , err = session . Exec ( "update user set num_repos = num_repos + 1 where id = ?" , user . Id ) ; err != nil {
2014-03-11 08:18:44 +04:00
session . Rollback ( )
2014-03-10 04:06:29 +04:00
if err2 := os . RemoveAll ( f ) ; err2 != nil {
2014-03-11 04:48:58 +04:00
return nil , errors . New ( fmt . Sprintf (
"delete repo directory %s/%s failed" , user . Name , repoName ) )
2014-02-20 10:53:56 +04:00
}
2014-02-14 18:20:57 +04:00
return nil , err
}
2014-03-10 04:06:29 +04:00
if err = session . Commit ( ) ; err != nil {
2014-03-11 08:18:44 +04:00
session . Rollback ( )
2014-03-10 04:06:29 +04:00
if err2 := os . RemoveAll ( f ) ; err2 != nil {
2014-03-11 04:48:58 +04:00
return nil , errors . New ( fmt . Sprintf (
"delete repo directory %s/%s failed" , user . Name , repoName ) )
2014-02-20 10:53:56 +04:00
}
2014-02-14 18:20:57 +04:00
return nil , err
}
2014-03-10 04:06:29 +04:00
return repo , nil
2014-02-14 18:20:57 +04:00
}
2014-03-11 04:48:58 +04:00
// InitRepository initializes README and .gitignore if needed.
2014-03-11 09:32:36 +04:00
func initRepository ( f string , user * User , repo * Repository , initReadme bool , repoLang , license string ) error {
2014-03-11 14:10:19 +04:00
fileName := map [ string ] string { }
if initReadme {
fileName [ "readme" ] = "README.md"
}
if repoLang != "" {
fileName [ "gitign" ] = ".gitignore"
2014-03-11 08:53:53 +04:00
}
2014-03-11 14:10:19 +04:00
if license != "" {
fileName [ "license" ] = "LICENSE"
}
2014-03-12 06:09:33 +04:00
workdir := filepath . Join ( os . TempDir ( ) , fmt . Sprintf ( "%d" , time . Now ( ) . Nanosecond ( ) ) )
2014-03-11 09:32:36 +04:00
os . MkdirAll ( workdir , os . ModePerm )
2014-03-11 08:18:44 +04:00
2014-03-11 14:10:19 +04:00
sig := user . NewGitSig ( )
2014-03-11 08:18:44 +04:00
// README
2014-03-11 14:10:19 +04:00
if initReadme {
defaultReadme := repo . Name + "\n" + strings . Repeat ( "=" ,
utf8 . RuneCountInString ( repo . Name ) ) + "\n\n" + repo . Description
if err := ioutil . WriteFile ( filepath . Join ( workdir , fileName [ "readme" ] ) ,
[ ] byte ( defaultReadme ) , 0644 ) ; err != nil {
return err
}
2014-03-11 08:18:44 +04:00
}
2014-03-11 04:48:58 +04:00
2014-03-11 14:10:19 +04:00
if repoLang != "" {
// .gitignore
filePath := "conf/gitignore/" + repoLang
if com . IsFile ( filePath ) {
if _ , err := com . Copy ( filePath ,
filepath . Join ( workdir , fileName [ "gitign" ] ) ) ; err != nil {
return err
}
2014-03-11 09:32:36 +04:00
}
}
2014-03-11 08:53:53 +04:00
2014-03-11 14:10:19 +04:00
if license != "" {
// LICENSE
filePath := "conf/license/" + license
if com . IsFile ( filePath ) {
if _ , err := com . Copy ( filePath ,
filepath . Join ( workdir , fileName [ "license" ] ) ) ; err != nil {
return err
}
2014-03-11 09:32:36 +04:00
}
}
2014-03-11 08:18:44 +04:00
rp , err := git . InitRepository ( f , true )
if err != nil {
return err
}
rp . SetWorkdir ( workdir , false )
idx , err := rp . Index ( )
if err != nil {
return err
}
2014-03-11 09:32:36 +04:00
for _ , name := range fileName {
if err = idx . AddByPath ( name ) ; err != nil {
return err
}
2014-03-11 08:18:44 +04:00
}
treeId , err := idx . WriteTree ( )
if err != nil {
return err
}
2014-03-11 08:53:53 +04:00
message := "Init commit"
2014-03-11 08:18:44 +04:00
tree , err := rp . LookupTree ( treeId )
if err != nil {
return err
}
2014-03-11 08:53:53 +04:00
if _ , err = rp . CreateCommit ( "HEAD" , sig , sig , message , tree ) ; err != nil {
2014-03-11 08:18:44 +04:00
return err
}
2014-03-11 04:48:58 +04:00
return nil
}
2014-03-13 06:27:11 +04:00
func GetRepositoryByName ( user * User , repoName string ) ( * Repository , error ) {
repo := & Repository {
OwnerId : user . Id ,
LowerName : strings . ToLower ( repoName ) ,
}
has , err := orm . Get ( repo )
if err != nil {
return nil , err
} else if ! has {
return nil , ErrRepoNotExist
}
return repo , err
}
func GetRepositoryById ( id int64 ) ( repo * Repository , err error ) {
has , err := orm . Id ( id ) . Get ( repo )
if err != nil {
return nil , err
} else if ! has {
return nil , ErrRepoNotExist
}
return repo , err
}
2014-02-19 22:04:31 +04:00
// GetRepositories returns the list of repositories of given user.
2014-03-10 04:06:29 +04:00
func GetRepositories ( user * User ) ( [ ] Repository , error ) {
repos := make ( [ ] Repository , 0 , 10 )
err := orm . Find ( & repos , & Repository { OwnerId : user . Id } )
2014-02-14 18:20:57 +04:00
return repos , err
}
2014-03-11 10:17:05 +04:00
func GetRepositoryCount ( user * User ) ( int64 , error ) {
return orm . Count ( & Repository { OwnerId : user . Id } )
}
2014-03-11 14:10:19 +04:00
const (
RFile = iota + 1
RDir
)
type RepoFile struct {
Type int
Name string
Created time . Time
}
func GetReposFiles ( userName , reposName , treeName , rpath string ) ( [ ] RepoFile , error ) {
f := RepoPath ( userName , reposName )
repo , err := git . OpenRepository ( f )
if err != nil {
return nil , err
}
obj , err := repo . RevparseSingle ( "HEAD" )
if err != nil {
return nil , err
}
lastCommit := obj . ( * git . Commit )
var repofiles [ ] RepoFile
tree , err := lastCommit . Tree ( )
if err != nil {
return nil , err
}
var i uint64 = 0
for ; i < tree . EntryCount ( ) ; i ++ {
entry := tree . EntryByIndex ( i )
repofiles = append ( repofiles , RepoFile {
entry . Filemode ,
entry . Name ,
time . Now ( ) ,
} )
}
return repofiles , nil
}
2014-02-14 18:20:57 +04:00
func StarReposiory ( user * User , repoName string ) error {
return nil
}
func UnStarRepository ( ) {
}
func WatchRepository ( ) {
}
func UnWatchRepository ( ) {
}
2014-03-02 17:58:20 +04:00
func ForkRepository ( reposName string , userId int64 ) {
}
2014-02-20 10:53:56 +04:00
func RepoPath ( userName , repoName string ) string {
return filepath . Join ( UserPath ( userName ) , repoName + ".git" )
}
2014-02-15 03:16:54 +04:00
// DeleteRepository deletes a repository for a user or orgnaztion.
func DeleteRepository ( user * User , reposName string ) ( err error ) {
2014-02-14 18:20:57 +04:00
session := orm . NewSession ( )
2014-03-10 04:06:29 +04:00
if _ , err = session . Delete ( & Repository { OwnerId : user . Id , Name : reposName } ) ; err != nil {
2014-02-14 18:20:57 +04:00
session . Rollback ( )
return err
}
2014-02-15 03:16:54 +04:00
if _ , err = session . Exec ( "update user set num_repos = num_repos - 1 where id = ?" , user . Id ) ; err != nil {
2014-02-14 18:20:57 +04:00
session . Rollback ( )
return err
}
2014-02-15 03:16:54 +04:00
if err = session . Commit ( ) ; err != nil {
2014-02-14 18:20:57 +04:00
session . Rollback ( )
return err
}
2014-02-20 10:53:56 +04:00
if err = os . RemoveAll ( RepoPath ( user . Name , reposName ) ) ; err != nil {
2014-02-14 18:20:57 +04:00
// TODO: log and delete manully
2014-02-20 10:53:56 +04:00
log . Error ( "delete repo %s/%s failed" , user . Name , reposName )
2014-02-14 18:20:57 +04:00
return err
}
return nil
}