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-07-22 22:08:04 +04:00
"html"
2014-07-22 21:52:37 +04:00
"html/template"
2014-07-23 15:48:06 +04:00
"io/ioutil"
2014-02-14 18:20:57 +04:00
"os"
2014-07-27 02:37:18 +04:00
"os/exec"
2014-03-30 06:18:36 +04:00
"path"
2014-02-14 18:20:57 +04:00
"path/filepath"
2014-07-22 21:52:37 +04:00
"regexp"
2014-06-03 07:17:21 +04:00
"sort"
2014-02-14 18:20:57 +04:00
"strings"
"time"
2014-03-11 08:53:53 +04:00
"unicode/utf8"
2014-02-14 18:20:57 +04:00
2014-03-17 19:56:50 +04:00
"github.com/Unknwon/cae/zip"
2014-03-11 09:32:36 +04:00
"github.com/Unknwon/com"
2014-03-17 19:56:50 +04:00
2014-10-05 01:15:22 +04:00
"github.com/gogits/gogs/modules/base"
2014-07-26 08:24:27 +04:00
"github.com/gogits/gogs/modules/git"
2014-03-08 02:22:15 +04:00
"github.com/gogits/gogs/modules/log"
2014-06-19 09:08:03 +04:00
"github.com/gogits/gogs/modules/process"
2014-05-26 04:11:25 +04:00
"github.com/gogits/gogs/modules/setting"
2014-02-14 18:20:57 +04:00
)
2014-06-11 03:11:53 +04:00
const (
TPL_UPDATE_HOOK = "#!/usr/bin/env %s\n%s update $1 $2 $3\n"
)
2014-03-11 09:32:36 +04:00
var (
2014-03-21 00:04:56 +04:00
ErrRepoAlreadyExist = errors . New ( "Repository already exist" )
ErrRepoNotExist = errors . New ( "Repository does not exist" )
2014-06-02 05:47:48 +04:00
ErrRepoFileNotExist = errors . New ( "Repository file does not exist" )
2014-03-21 00:04:56 +04:00
ErrRepoNameIllegal = errors . New ( "Repository name contains illegal characters" )
2014-06-02 05:47:48 +04:00
ErrRepoFileNotLoaded = errors . New ( "Repository file not loaded" )
2014-04-13 06:30:00 +04:00
ErrMirrorNotExist = errors . New ( "Mirror does not exist" )
2014-07-23 15:48:06 +04:00
ErrInvalidReference = errors . New ( "Invalid reference specified" )
2014-03-11 09:32:36 +04:00
)
2014-03-10 04:06:29 +04:00
var (
2014-07-26 08:24:27 +04:00
Gitignores , Licenses [ ] string
2014-03-10 04:06:29 +04:00
)
2014-07-22 21:52:37 +04:00
var (
2014-10-05 01:15:22 +04:00
DescPattern = regexp . MustCompile ( ` https?://\S+ ` )
2014-07-22 21:52:37 +04:00
)
2014-05-26 04:11:25 +04:00
func LoadRepoConfig ( ) {
2014-05-11 22:03:51 +04:00
// Load .gitignore and license files.
types := [ ] string { "gitignore" , "license" }
typeFiles := make ( [ ] [ ] string , 2 )
for i , t := range types {
2014-07-26 08:24:27 +04:00
files , err := com . StatDir ( path . Join ( "conf" , t ) )
if err != nil {
log . Fatal ( 4 , "Fail to get %s files: %v" , t , err )
}
2014-05-26 04:11:25 +04:00
customPath := path . Join ( setting . CustomPath , "conf" , t )
if com . IsDir ( customPath ) {
customFiles , err := com . StatDir ( customPath )
2014-05-11 22:03:51 +04:00
if err != nil {
2014-07-26 08:24:27 +04:00
log . Fatal ( 4 , "Fail to get custom %s files: %v" , t , err )
2014-05-11 22:03:51 +04:00
}
for _ , f := range customFiles {
if ! com . IsSliceContainsStr ( files , f ) {
files = append ( files , f )
}
}
}
typeFiles [ i ] = files
}
2014-07-26 08:24:27 +04:00
Gitignores = typeFiles [ 0 ]
2014-05-11 22:03:51 +04:00
Licenses = typeFiles [ 1 ]
2014-07-26 08:24:27 +04:00
sort . Strings ( Gitignores )
2014-06-03 07:17:21 +04:00
sort . Strings ( Licenses )
2014-03-21 09:48:10 +04:00
}
2014-03-17 19:56:50 +04:00
2014-03-21 09:48:10 +04:00
func NewRepoContext ( ) {
2014-03-17 19:56:50 +04:00
zip . Verbose = false
2014-03-17 23:58:32 +04:00
2014-07-27 02:37:18 +04:00
// Check Git installation.
if _ , err := exec . LookPath ( "git" ) ; err != nil {
log . Fatal ( 4 , "Fail to test 'git' command: %v (forgotten install?)" , err )
}
2014-07-26 08:24:27 +04:00
// Check Git version.
ver , err := git . GetVersion ( )
if err != nil {
log . Fatal ( 4 , "Fail to get Git version: %v" , err )
}
2014-09-16 18:10:33 +04:00
reqVer , err := git . ParseVersion ( "1.7.1" )
if err != nil {
log . Fatal ( 4 , "Fail to parse required Git version: %v" , err )
}
2014-09-16 19:29:53 +04:00
if ver . LessThan ( reqVer ) {
2014-09-16 18:10:33 +04:00
log . Fatal ( 4 , "Gogs requires Git version greater or equal to 1.7.1" )
2014-07-26 08:24:27 +04:00
}
2014-09-04 14:03:29 +04:00
// Check if server has basic git setting and set if not.
if stdout , stderr , err := process . Exec ( "NewRepoContext(get setting)" , "git" , "config" , "--get" , "user.name" ) ; err != nil || strings . TrimSpace ( stdout ) == "" {
// ExitError indicates user.name is not set
if _ , ok := err . ( * exec . ExitError ) ; ok || strings . TrimSpace ( stdout ) == "" {
stndrdUserName := "Gogs"
stndrdUserEmail := "gogitservice@gmail.com"
if _ , stderr , gerr := process . Exec ( "NewRepoContext(set name)" , "git" , "config" , "--global" , "user.name" , stndrdUserName ) ; gerr != nil {
log . Fatal ( 4 , "Fail to set git user.name(%s): %s" , gerr , stderr )
}
if _ , stderr , gerr := process . Exec ( "NewRepoContext(set email)" , "git" , "config" , "--global" , "user.email" , stndrdUserEmail ) ; gerr != nil {
log . Fatal ( 4 , "Fail to set git user.email(%s): %s" , gerr , stderr )
}
log . Info ( "Git user.name and user.email set to %s <%s>" , stndrdUserName , stndrdUserEmail )
} else {
log . Fatal ( 4 , "Fail to get git user.name(%s): %s" , err , stderr )
2014-03-17 23:58:32 +04:00
}
}
2014-08-23 19:58:56 +04:00
// Set git some configurations.
2014-09-04 14:03:29 +04:00
if _ , stderr , err := process . Exec ( "NewRepoContext(git config --global core.quotepath false)" ,
2014-08-23 19:58:56 +04:00
"git" , "config" , "--global" , "core.quotepath" , "false" ) ; err != nil {
log . Fatal ( 4 , "Fail to execute 'git config --global core.quotepath false': %s" , stderr )
}
2014-03-11 09:32:36 +04:00
}
2014-03-21 00:04:56 +04:00
// Repository represents a git repository.
type Repository struct {
2014-10-19 09:35:24 +04:00
Id int64
OwnerId int64 ` xorm:"UNIQUE(s)" `
Owner * User ` xorm:"-" `
LowerName string ` xorm:"UNIQUE(s) INDEX NOT NULL" `
Name string ` xorm:"INDEX NOT NULL" `
Description string
Website string
DefaultBranch string
2014-05-12 22:06:42 +04:00
NumWatches int
NumStars int
NumForks int
NumIssues int
NumClosedIssues int
NumOpenIssues int ` xorm:"-" `
2014-07-26 08:24:27 +04:00
NumPulls int
NumClosedPulls int
NumOpenPulls int ` xorm:"-" `
2014-05-12 22:06:42 +04:00
NumMilestones int ` xorm:"NOT NULL DEFAULT 0" `
NumClosedMilestones int ` xorm:"NOT NULL DEFAULT 0" `
NumOpenMilestones int ` xorm:"-" `
NumTags int ` xorm:"-" `
2014-10-19 09:35:24 +04:00
IsPrivate bool
IsBare bool
IsGoget bool
IsMirror bool
* Mirror ` xorm:"-" `
IsFork bool ` xorm:"NOT NULL DEFAULT false" `
ForkId int64
ForkRepo * Repository ` xorm:"-" `
Created time . Time ` xorm:"CREATED" `
Updated time . Time ` xorm:"UPDATED" `
2014-03-21 00:04:56 +04:00
}
2014-05-08 16:18:03 +04:00
func ( repo * Repository ) GetOwner ( ) ( err error ) {
2014-10-10 03:01:22 +04:00
if repo . Owner == nil {
repo . Owner , err = GetUserById ( repo . OwnerId )
}
2014-05-08 16:18:03 +04:00
return err
}
2014-08-11 07:11:18 +04:00
func ( repo * Repository ) GetMirror ( ) ( err error ) {
repo . Mirror , err = GetMirror ( repo . Id )
return err
}
2014-10-19 09:35:24 +04:00
func ( repo * Repository ) GetForkRepo ( ) ( err error ) {
if ! repo . IsFork {
return nil
}
repo . ForkRepo , err = GetRepositoryById ( repo . ForkId )
return err
}
func ( repo * Repository ) RepoPath ( ) ( string , error ) {
if err := repo . GetOwner ( ) ; err != nil {
return "" , err
}
return RepoPath ( repo . Owner . Name , repo . Name ) , nil
}
func ( repo * Repository ) RepoLink ( ) ( string , error ) {
if err := repo . GetOwner ( ) ; err != nil {
return "" , err
}
return setting . AppSubUrl + "/" + repo . Owner . Name + "/" + repo . Name , nil
2014-10-04 21:19:14 +04:00
}
2014-10-13 23:23:30 +04:00
func ( repo * Repository ) IsOwnedBy ( u * User ) bool {
2014-10-19 09:35:24 +04:00
return repo . OwnerId == u . Id
2014-10-13 23:23:30 +04:00
}
2014-10-10 03:01:22 +04:00
func ( repo * Repository ) HasAccess ( uname string ) bool {
if err := repo . GetOwner ( ) ; err != nil {
return false
}
has , _ := HasAccess ( uname , path . Join ( repo . Owner . Name , repo . Name ) , READABLE )
return has
}
2014-07-26 08:24:27 +04:00
// DescriptionHtml does special handles to description and return HTML string.
2014-07-22 21:52:37 +04:00
func ( repo * Repository ) DescriptionHtml ( ) template . HTML {
2014-07-22 22:08:04 +04:00
sanitize := func ( s string ) string {
// TODO(nuss-justin): Improve sanitization. Strip all tags?
ss := html . EscapeString ( s )
return fmt . Sprintf ( ` <a href="%s" target="_blank">%s</a> ` , ss , ss )
}
2014-10-05 01:15:22 +04:00
return template . HTML ( DescPattern . ReplaceAllStringFunc ( base . XSSString ( repo . Description ) , sanitize ) )
2014-07-22 21:52:37 +04:00
}
2014-03-17 22:03:58 +04:00
// IsRepositoryExist returns true if the repository with given name under user has already existed.
2014-05-16 21:41:08 +04:00
func IsRepositoryExist ( u * User , repoName string ) ( bool , error ) {
repo := Repository { OwnerId : u . Id }
2014-06-21 08:51:41 +04:00
has , err := x . Where ( "lower_name = ?" , strings . ToLower ( repoName ) ) . Get ( & repo )
2014-02-19 13:50:53 +04:00
if err != nil {
return has , err
2014-03-27 23:24:11 +04:00
} else if ! has {
return false , nil
2014-02-19 13:50:53 +04:00
}
2014-03-27 23:24:11 +04:00
2014-05-16 21:41:08 +04:00
return com . IsDir ( RepoPath ( u . Name , repoName ) ) , nil
2014-02-14 18:20:57 +04:00
}
2014-03-20 19:41:24 +04:00
var (
2014-07-04 13:41:43 +04:00
illegalEquals = [ ] string { "debug" , "raw" , "install" , "api" , "avatar" , "user" , "org" , "help" , "stars" , "issues" , "pulls" , "commits" , "repo" , "template" , "admin" , "new" }
2014-03-30 01:50:51 +04:00
illegalSuffixs = [ ] string { ".git" }
2014-03-20 19:41:24 +04:00
)
// IsLegalName returns false if name contains illegal characters.
func IsLegalName ( repoName string ) bool {
2014-03-30 01:50:51 +04:00
repoName = strings . ToLower ( repoName )
for _ , char := range illegalEquals {
if repoName == char {
return false
}
}
for _ , char := range illegalSuffixs {
if strings . HasSuffix ( repoName , char ) {
2014-03-20 19:41:24 +04:00
return false
}
}
return true
}
2014-04-13 04:35:35 +04:00
// Mirror represents a mirror information of repository.
type Mirror struct {
Id int64
RepoId int64
RepoName string // <user name>/<repo name>
Interval int // Hour.
Updated time . Time ` xorm:"UPDATED" `
NextUpdate time . Time
}
2014-07-27 07:53:16 +04:00
func GetMirror ( repoId int64 ) ( * Mirror , error ) {
m := & Mirror { RepoId : repoId }
has , err := x . Get ( m )
if err != nil {
return nil , err
} else if ! has {
return nil , ErrMirrorNotExist
}
return m , nil
}
func UpdateMirror ( m * Mirror ) error {
_ , err := x . Id ( m . Id ) . Update ( m )
return err
}
2014-06-09 01:53:53 +04:00
// MirrorRepository creates a mirror repository from source.
func MirrorRepository ( repoId int64 , userName , repoName , repoPath , url string ) error {
2014-07-07 01:32:36 +04:00
_ , stderr , err := process . ExecTimeout ( 10 * time . Minute ,
fmt . Sprintf ( "MirrorRepository: %s/%s" , userName , repoName ) ,
2014-06-19 09:08:03 +04:00
"git" , "clone" , "--mirror" , url , repoPath )
2014-06-09 01:53:53 +04:00
if err != nil {
return errors . New ( "git clone --mirror: " + stderr )
}
2014-06-21 08:51:41 +04:00
if _ , err = x . InsertOne ( & Mirror {
2014-06-09 01:53:53 +04:00
RepoId : repoId ,
RepoName : strings . ToLower ( userName + "/" + repoName ) ,
Interval : 24 ,
NextUpdate : time . Now ( ) . Add ( 24 * time . Hour ) ,
} ) ; err != nil {
return err
}
2014-07-26 08:24:27 +04:00
return nil
2014-06-09 01:53:53 +04:00
}
2014-04-13 05:30:09 +04:00
// MirrorUpdate checks and updates mirror repositories.
func MirrorUpdate ( ) {
2014-06-21 08:51:41 +04:00
if err := x . Iterate ( new ( Mirror ) , func ( idx int , bean interface { } ) error {
2014-04-13 05:30:09 +04:00
m := bean . ( * Mirror )
if m . NextUpdate . After ( time . Now ( ) ) {
return nil
}
2014-05-26 04:11:25 +04:00
repoPath := filepath . Join ( setting . RepoRootPath , m . RepoName + ".git" )
2014-07-07 01:32:36 +04:00
if _ , stderr , err := process . ExecDir ( 10 * time . Minute ,
2014-06-19 09:08:03 +04:00
repoPath , fmt . Sprintf ( "MirrorUpdate: %s" , repoPath ) ,
"git" , "remote" , "update" ) ; err != nil {
2014-04-27 11:05:13 +04:00
return errors . New ( "git remote update: " + stderr )
2014-08-01 13:20:33 +04:00
}
2014-04-13 05:30:09 +04:00
m . NextUpdate = time . Now ( ) . Add ( time . Duration ( m . Interval ) * time . Hour )
2014-04-13 06:30:00 +04:00
return UpdateMirror ( m )
2014-04-13 05:30:09 +04:00
} ) ; err != nil {
2014-07-26 08:24:27 +04:00
log . Error ( 4 , "repo.MirrorUpdate: %v" , err )
2014-04-13 05:30:09 +04:00
}
}
2014-04-13 04:35:35 +04:00
// MigrateRepository migrates a existing repository from other project hosting.
2014-06-25 13:35:23 +04:00
func MigrateRepository ( u * User , name , desc string , private , mirror bool , url string ) ( * Repository , error ) {
2014-09-02 15:11:39 +04:00
repo , err := CreateRepository ( u , name , desc , "" , "" , private , mirror , false )
2014-04-13 04:35:35 +04:00
if err != nil {
return nil , err
}
// Clone to temprory path and do the init commit.
tmpDir := filepath . Join ( os . TempDir ( ) , fmt . Sprintf ( "%d" , time . Now ( ) . Nanosecond ( ) ) )
os . MkdirAll ( tmpDir , os . ModePerm )
2014-06-25 13:35:23 +04:00
repoPath := RepoPath ( u . Name , name )
2014-04-13 04:35:35 +04:00
2014-08-27 12:39:36 +04:00
if u . IsOrganization ( ) {
t , err := u . GetOwnerTeam ( )
if err != nil {
return nil , err
}
repo . NumWatches = t . NumMembers
} else {
repo . NumWatches = 1
}
2014-04-13 04:35:35 +04:00
repo . IsBare = false
if mirror {
2014-06-25 13:35:23 +04:00
if err = MirrorRepository ( repo . Id , u . Name , repo . Name , repoPath , url ) ; err != nil {
2014-04-13 04:35:35 +04:00
return repo , err
}
repo . IsMirror = true
return repo , UpdateRepository ( repo )
2014-09-02 15:11:39 +04:00
} else {
os . RemoveAll ( repoPath )
2014-04-13 04:35:35 +04:00
}
2014-09-02 07:57:06 +04:00
// this command could for both migrate and mirror
2014-07-07 01:32:36 +04:00
_ , stderr , err := process . ExecTimeout ( 10 * time . Minute ,
2014-09-02 07:57:06 +04:00
fmt . Sprintf ( "MigrateRepository: %s" , repoPath ) ,
"git" , "clone" , "--mirror" , "--bare" , url , repoPath )
if err != nil {
return repo , errors . New ( "git clone: " + stderr )
}
2014-04-13 04:35:35 +04:00
return repo , UpdateRepository ( repo )
}
2014-03-17 19:56:50 +04:00
// extractGitBareZip extracts git-bare.zip to repository path.
func extractGitBareZip ( repoPath string ) error {
2014-07-26 08:24:27 +04:00
z , err := zip . Open ( path . Join ( setting . ConfRootPath , "content/git-bare.zip" ) )
2014-03-17 19:56:50 +04:00
if err != nil {
return err
}
defer z . Close ( )
return z . ExtractTo ( repoPath )
}
// initRepoCommit temporarily changes with work directory.
2014-03-26 07:53:01 +04:00
func initRepoCommit ( tmpPath string , sig * git . Signature ) ( err error ) {
2014-03-17 23:58:32 +04:00
var stderr string
2014-07-07 01:32:36 +04:00
if _ , stderr , err = process . ExecDir ( - 1 ,
2014-06-19 09:08:03 +04:00
tmpPath , fmt . Sprintf ( "initRepoCommit(git add): %s" , tmpPath ) ,
"git" , "add" , "--all" ) ; err != nil {
2014-04-14 12:11:33 +04:00
return errors . New ( "git add: " + stderr )
2014-03-27 23:24:11 +04:00
}
2014-06-19 09:08:03 +04:00
2014-07-07 01:32:36 +04:00
if _ , stderr , err = process . ExecDir ( - 1 ,
2014-06-19 09:08:03 +04:00
tmpPath , fmt . Sprintf ( "initRepoCommit(git commit): %s" , tmpPath ) ,
"git" , "commit" , fmt . Sprintf ( "--author='%s <%s>'" , sig . Name , sig . Email ) ,
2014-03-17 19:56:50 +04:00
"-m" , "Init commit" ) ; err != nil {
2014-04-14 12:11:33 +04:00
return errors . New ( "git commit: " + stderr )
2014-03-27 23:24:11 +04:00
}
2014-07-07 01:32:36 +04:00
if _ , stderr , err = process . ExecDir ( - 1 ,
2014-06-19 09:08:03 +04:00
tmpPath , fmt . Sprintf ( "initRepoCommit(git push): %s" , tmpPath ) ,
"git" , "push" , "origin" , "master" ) ; err != nil {
2014-04-14 12:11:33 +04:00
return errors . New ( "git push: " + stderr )
2014-03-27 23:24:11 +04:00
}
2014-03-17 19:56:50 +04:00
return nil
2014-02-14 18:20:57 +04:00
}
2014-03-26 17:40:02 +04:00
func createHookUpdate ( hookPath , content string ) error {
pu , err := os . OpenFile ( hookPath , os . O_CREATE | os . O_WRONLY , 0777 )
if err != nil {
return err
}
defer pu . Close ( )
2014-03-27 23:24:11 +04:00
_ , err = pu . WriteString ( content )
return err
2014-03-26 17:40:02 +04:00
}
2014-03-11 04:48:58 +04:00
// InitRepository initializes README and .gitignore if needed.
2014-07-26 08:24:27 +04:00
func initRepository ( f string , u * User , repo * Repository , initReadme bool , repoLang , license string ) error {
repoPath := RepoPath ( u . Name , repo . Name )
2014-03-11 14:10:19 +04:00
2014-03-17 19:56:50 +04:00
// Create bare new repository.
if err := extractGitBareZip ( repoPath ) ; err != nil {
return err
}
2014-03-18 01:00:35 +04:00
// hook/post-update
2014-03-26 17:40:02 +04:00
if err := createHookUpdate ( filepath . Join ( repoPath , "hooks" , "update" ) ,
2014-07-26 08:24:27 +04:00
fmt . Sprintf ( TPL_UPDATE_HOOK , setting . ScriptType , "\"" + appPath + "\"" ) ) ; err != nil {
2014-03-18 01:00:35 +04:00
return err
}
2014-03-17 19:56:50 +04:00
// Initialize repository according to user's choice.
fileName := map [ string ] string { }
2014-03-11 14:10:19 +04:00
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-17 19:56:50 +04:00
// Clone to temprory path and do the init commit.
2014-07-26 08:24:27 +04:00
tmpDir := filepath . Join ( os . TempDir ( ) , com . ToStr ( time . Now ( ) . Nanosecond ( ) ) )
2014-03-17 19:56:50 +04:00
os . MkdirAll ( tmpDir , os . ModePerm )
2014-03-11 08:18:44 +04:00
2014-06-19 09:08:03 +04:00
_ , stderr , err := process . Exec (
fmt . Sprintf ( "initRepository(git clone): %s" , repoPath ) ,
"git" , "clone" , repoPath , tmpDir )
2014-04-13 04:35:35 +04:00
if err != nil {
2014-06-19 09:08:03 +04:00
return errors . New ( "initRepository(git clone): " + stderr )
2014-04-13 04:35:35 +04:00
}
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
2014-03-17 19:56:50 +04:00
if err := ioutil . WriteFile ( filepath . Join ( tmpDir , fileName [ "readme" ] ) ,
2014-03-11 14:10:19 +04:00
[ ] 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-17 19:56:50 +04:00
// .gitignore
2014-07-26 08:24:27 +04:00
filePath := "conf/gitignore/" + repoLang
if com . IsFile ( filePath ) {
2014-06-04 05:51:25 +04:00
targetPath := path . Join ( tmpDir , fileName [ "gitign" ] )
2014-07-26 08:24:27 +04:00
if com . IsFile ( filePath ) {
if err = com . Copy ( filePath , targetPath ) ; err != nil {
2014-03-11 14:10:19 +04:00
return err
}
2014-06-04 05:51:25 +04:00
} else {
// Check custom files.
filePath = path . Join ( setting . CustomPath , "conf/gitignore" , repoLang )
if com . IsFile ( filePath ) {
if err := com . Copy ( filePath , targetPath ) ; err != nil {
return err
}
}
2014-03-11 09:32:36 +04:00
}
2014-07-26 08:24:27 +04:00
} else {
delete ( fileName , "gitign" )
2014-03-11 09:32:36 +04:00
}
2014-03-11 08:53:53 +04:00
2014-03-17 19:56:50 +04:00
// LICENSE
2014-07-26 08:24:27 +04:00
filePath = "conf/license/" + license
if com . IsFile ( filePath ) {
2014-06-04 05:51:25 +04:00
targetPath := path . Join ( tmpDir , fileName [ "license" ] )
2014-07-26 08:24:27 +04:00
if com . IsFile ( filePath ) {
if err = com . Copy ( filePath , targetPath ) ; err != nil {
2014-03-11 14:10:19 +04:00
return err
}
2014-06-04 05:51:25 +04:00
} else {
// Check custom files.
filePath = path . Join ( setting . CustomPath , "conf/license" , license )
if com . IsFile ( filePath ) {
if err := com . Copy ( filePath , targetPath ) ; err != nil {
return err
}
}
2014-03-11 09:32:36 +04:00
}
2014-07-26 08:24:27 +04:00
} else {
delete ( fileName , "license" )
2014-03-11 09:32:36 +04:00
}
2014-03-11 08:18:44 +04:00
2014-03-18 01:00:35 +04:00
if len ( fileName ) == 0 {
2014-08-06 03:40:01 +04:00
repo . IsBare = true
repo . DefaultBranch = "master"
return UpdateRepository ( repo )
2014-03-18 01:00:35 +04:00
}
2014-03-17 19:56:50 +04:00
// Apply changes and commit.
2014-07-26 08:24:27 +04:00
return initRepoCommit ( tmpDir , u . NewGitSig ( ) )
2014-03-11 04:48:58 +04:00
}
2014-06-25 13:14:36 +04:00
// CreateRepository creates a repository for given user or organization.
func CreateRepository ( u * User , name , desc , lang , license string , private , mirror , initReadme bool ) ( * Repository , error ) {
2014-06-19 09:08:03 +04:00
if ! IsLegalName ( name ) {
return nil , ErrRepoNameIllegal
}
2014-06-25 13:14:36 +04:00
isExist , err := IsRepositoryExist ( u , name )
2014-06-19 09:08:03 +04:00
if err != nil {
return nil , err
} else if isExist {
return nil , ErrRepoAlreadyExist
}
2014-06-25 13:14:36 +04:00
sess := x . NewSession ( )
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
return nil , err
}
2014-06-19 09:08:03 +04:00
repo := & Repository {
2014-06-25 13:14:36 +04:00
OwnerId : u . Id ,
2014-06-25 13:27:17 +04:00
Owner : u ,
2014-06-19 09:08:03 +04:00
Name : name ,
LowerName : strings . ToLower ( name ) ,
Description : desc ,
IsPrivate : private ,
}
if _ , err = sess . Insert ( repo ) ; err != nil {
sess . Rollback ( )
return nil , err
}
2014-06-25 13:14:36 +04:00
var t * Team // Owner team.
2014-06-25 08:44:48 +04:00
mode := WRITABLE
2014-06-19 09:08:03 +04:00
if mirror {
2014-06-25 08:44:48 +04:00
mode = READABLE
2014-06-19 09:08:03 +04:00
}
2014-06-25 13:14:36 +04:00
access := & Access {
UserName : u . LowerName ,
2014-08-16 12:58:32 +04:00
RepoName : path . Join ( u . LowerName , repo . LowerName ) ,
2014-06-19 09:08:03 +04:00
Mode : mode ,
}
2014-06-25 13:14:36 +04:00
// Give access to all members in owner team.
if u . IsOrganization ( ) {
t , err = u . GetOwnerTeam ( )
if err != nil {
sess . Rollback ( )
return nil , err
}
2014-08-26 14:11:15 +04:00
if err = t . GetMembers ( ) ; err != nil {
2014-06-25 13:14:36 +04:00
sess . Rollback ( )
return nil , err
}
2014-08-26 14:11:15 +04:00
for _ , u := range t . Members {
2014-08-24 17:09:05 +04:00
access . Id = 0
2014-06-25 13:14:36 +04:00
access . UserName = u . LowerName
if _ , err = sess . Insert ( access ) ; err != nil {
sess . Rollback ( )
return nil , err
}
}
} else {
if _ , err = sess . Insert ( access ) ; err != nil {
sess . Rollback ( )
return nil , err
2014-06-19 09:08:03 +04:00
}
}
2014-07-26 08:24:27 +04:00
if _ , err = sess . Exec (
"UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?" , u . Id ) ; err != nil {
2014-06-19 09:08:03 +04:00
sess . Rollback ( )
return nil , err
}
2014-06-25 13:14:36 +04:00
// Update owner team info and count.
if u . IsOrganization ( ) {
2014-07-26 08:24:27 +04:00
t . RepoIds += "$" + com . ToStr ( repo . Id ) + "|"
2014-06-25 13:14:36 +04:00
t . NumRepos ++
if _ , err = sess . Id ( t . Id ) . AllCols ( ) . Update ( t ) ; err != nil {
sess . Rollback ( )
return nil , err
2014-06-19 09:08:03 +04:00
}
2014-06-25 13:14:36 +04:00
}
if err = sess . Commit ( ) ; err != nil {
2014-06-19 09:08:03 +04:00
return nil , err
}
2014-06-25 13:14:36 +04:00
if u . IsOrganization ( ) {
2014-08-27 12:39:36 +04:00
t , err := u . GetOwnerTeam ( )
2014-06-25 13:14:36 +04:00
if err != nil {
2014-08-27 12:39:36 +04:00
log . Error ( 4 , "GetOwnerTeam: %v" , err )
2014-06-25 13:14:36 +04:00
} else {
2014-08-27 12:39:36 +04:00
if err = t . GetMembers ( ) ; err != nil {
log . Error ( 4 , "GetMembers: %v" , err )
} else {
for _ , u := range t . Members {
if err = WatchRepo ( u . Id , repo . Id , true ) ; err != nil {
log . Error ( 4 , "WatchRepo2: %v" , err )
}
2014-06-25 13:14:36 +04:00
}
}
}
2014-08-27 12:39:36 +04:00
} else {
if err = WatchRepo ( u . Id , repo . Id , true ) ; err != nil {
log . Error ( 4 , "WatchRepo3: %v" , err )
}
2014-06-19 09:08:03 +04:00
}
2014-06-25 13:14:36 +04:00
if err = NewRepoAction ( u , repo ) ; err != nil {
2014-07-26 08:24:27 +04:00
log . Error ( 4 , "NewRepoAction: %v" , err )
2014-06-19 09:08:03 +04:00
}
2014-07-26 08:24:27 +04:00
// No need for init mirror.
2014-06-19 09:08:03 +04:00
if mirror {
return repo , nil
}
2014-06-25 13:14:36 +04:00
repoPath := RepoPath ( u . Name , repo . Name )
if err = initRepository ( repoPath , u , repo , initReadme , lang , license ) ; err != nil {
if err2 := os . RemoveAll ( repoPath ) ; err2 != nil {
2014-07-26 08:24:27 +04:00
log . Error ( 4 , "initRepository: %v" , err )
return nil , fmt . Errorf (
"delete repo directory %s/%s failed(2): %v" , u . Name , repo . Name , err2 )
2014-06-25 13:14:36 +04:00
}
2014-07-26 08:24:27 +04:00
return nil , fmt . Errorf ( "initRepository: %v" , err )
2014-06-19 09:08:03 +04:00
}
2014-07-07 01:32:36 +04:00
_ , stderr , err := process . ExecDir ( - 1 ,
2014-06-19 09:08:03 +04:00
repoPath , fmt . Sprintf ( "CreateRepository(git update-server-info): %s" , repoPath ) ,
"git" , "update-server-info" )
if err != nil {
return nil , errors . New ( "CreateRepository(git update-server-info): " + stderr )
}
return repo , nil
}
2014-07-07 12:15:08 +04:00
// CountRepositories returns number of repositories.
func CountRepositories ( ) int64 {
count , _ := x . Count ( new ( Repository ) )
return count
}
2014-05-08 00:51:14 +04:00
// GetRepositoriesWithUsers returns given number of repository objects with offset.
// It also auto-gets corresponding users.
func GetRepositoriesWithUsers ( num , offset int ) ( [ ] * Repository , error ) {
repos := make ( [ ] * Repository , 0 , num )
2014-06-21 08:51:41 +04:00
if err := x . Limit ( num , offset ) . Asc ( "id" ) . Find ( & repos ) ; err != nil {
2014-03-21 09:09:22 +04:00
return nil , err
}
2014-05-08 00:51:14 +04:00
for _ , repo := range repos {
repo . Owner = & User { Id : repo . OwnerId }
2014-06-21 08:51:41 +04:00
has , err := x . Get ( repo . Owner )
2014-03-21 09:09:22 +04:00
if err != nil {
return nil , err
} else if ! has {
return nil , ErrUserNotExist
}
}
2014-05-08 00:51:14 +04:00
return repos , nil
2014-03-21 00:04:56 +04:00
}
2014-04-13 04:35:35 +04:00
// RepoPath returns repository path by given user and repository name.
2014-03-21 00:04:56 +04:00
func RepoPath ( userName , repoName string ) string {
2014-03-30 06:13:02 +04:00
return filepath . Join ( UserPath ( userName ) , strings . ToLower ( repoName ) + ".git" )
2014-03-21 00:04:56 +04:00
}
2014-04-05 02:31:09 +04:00
// TransferOwnership transfers all corresponding setting from old user to new one.
2014-09-13 02:29:58 +04:00
func TransferOwnership ( u * User , newOwner string , repo * Repository ) error {
2014-04-05 02:31:09 +04:00
newUser , err := GetUserByName ( newOwner )
if err != nil {
2014-10-12 02:02:48 +04:00
return fmt . Errorf ( "fail to get new owner(%s): %v" , newOwner , err )
2014-04-05 02:31:09 +04:00
}
2014-09-13 02:29:58 +04:00
// Check if new owner has repository with same name.
2014-09-17 22:22:51 +04:00
has , err := IsRepositoryExist ( newUser , repo . Name )
2014-09-13 02:29:58 +04:00
if err != nil {
return err
} else if has {
return ErrRepoAlreadyExist
}
2014-06-21 08:51:41 +04:00
sess := x . NewSession ( )
2014-04-05 02:55:17 +04:00
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
return err
}
2014-09-26 06:36:07 +04:00
owner := repo . Owner
oldRepoLink := path . Join ( owner . LowerName , repo . LowerName )
2014-09-26 00:36:19 +04:00
// Delete all access first if current owner is an organization.
2014-09-26 06:36:07 +04:00
if owner . IsOrganization ( ) {
if _ , err = sess . Where ( "repo_name=?" , oldRepoLink ) . Delete ( new ( Access ) ) ; err != nil {
2014-09-26 00:36:19 +04:00
sess . Rollback ( )
return fmt . Errorf ( "fail to delete current accesses: %v" , err )
}
} else {
2014-09-29 12:04:48 +04:00
// Delete current owner access.
2014-09-26 06:36:07 +04:00
if _ , err = sess . Where ( "repo_name=?" , oldRepoLink ) . And ( "user_name=?" , owner . LowerName ) .
2014-09-29 12:04:48 +04:00
Delete ( new ( Access ) ) ; err != nil {
2014-09-26 00:36:19 +04:00
sess . Rollback ( )
2014-09-29 12:04:48 +04:00
return fmt . Errorf ( "fail to delete access(owner): %v" , err )
}
// In case new owner has access.
if _ , err = sess . Where ( "repo_name=?" , oldRepoLink ) . And ( "user_name=?" , newUser . LowerName ) .
Delete ( new ( Access ) ) ; err != nil {
sess . Rollback ( )
return fmt . Errorf ( "fail to delete access(new user): %v" , err )
2014-09-26 00:36:19 +04:00
}
2014-07-05 06:25:45 +04:00
}
2014-07-05 06:37:29 +04:00
2014-09-29 12:04:48 +04:00
// Change accesses to new repository path.
2014-09-26 06:36:07 +04:00
if _ , err = sess . Where ( "repo_name=?" , oldRepoLink ) .
2014-09-26 00:36:19 +04:00
Update ( & Access { RepoName : path . Join ( newUser . LowerName , repo . LowerName ) } ) ; err != nil {
2014-07-05 06:25:45 +04:00
sess . Rollback ( )
2014-09-29 12:04:48 +04:00
return fmt . Errorf ( "fail to update access(change reponame): %v" , err )
2014-04-05 02:31:09 +04:00
}
// Update repository.
repo . OwnerId = newUser . Id
2014-04-05 02:55:17 +04:00
if _ , err := sess . Id ( repo . Id ) . Update ( repo ) ; err != nil {
sess . Rollback ( )
2014-04-05 02:31:09 +04:00
return err
}
// Update user repository number.
2014-08-27 12:39:36 +04:00
if _ , err = sess . Exec ( "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?" , newUser . Id ) ; err != nil {
2014-04-05 02:55:17 +04:00
sess . Rollback ( )
2014-04-05 02:31:09 +04:00
return err
}
2014-07-05 06:37:29 +04:00
2014-09-26 06:36:07 +04:00
if _ , err = sess . Exec ( "UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?" , owner . Id ) ; err != nil {
2014-04-05 02:55:17 +04:00
sess . Rollback ( )
2014-04-05 02:31:09 +04:00
return err
}
2014-09-26 00:36:19 +04:00
mode := WRITABLE
if repo . IsMirror {
mode = READABLE
}
2014-08-27 12:39:36 +04:00
// New owner is organization.
if newUser . IsOrganization ( ) {
access := & Access {
RepoName : path . Join ( newUser . LowerName , repo . LowerName ) ,
Mode : mode ,
}
// Give access to all members in owner team.
t , err := newUser . GetOwnerTeam ( )
if err != nil {
sess . Rollback ( )
return err
}
if err = t . GetMembers ( ) ; err != nil {
sess . Rollback ( )
return err
}
for _ , u := range t . Members {
access . Id = 0
access . UserName = u . LowerName
if _ , err = sess . Insert ( access ) ; err != nil {
sess . Rollback ( )
return err
}
}
// Update owner team info and count.
t . RepoIds += "$" + com . ToStr ( repo . Id ) + "|"
t . NumRepos ++
if _ , err = sess . Id ( t . Id ) . AllCols ( ) . Update ( t ) ; err != nil {
sess . Rollback ( )
return err
}
2014-09-26 00:36:19 +04:00
} else {
access := & Access {
RepoName : path . Join ( newUser . LowerName , repo . LowerName ) ,
UserName : newUser . LowerName ,
Mode : mode ,
}
if _ , err = sess . Insert ( access ) ; err != nil {
sess . Rollback ( )
2014-09-29 12:04:48 +04:00
return fmt . Errorf ( "fail to insert access: %v" , err )
2014-09-26 00:36:19 +04:00
}
2014-08-27 12:39:36 +04:00
}
2014-07-05 06:43:39 +04:00
// Change repository directory name.
2014-09-26 06:36:07 +04:00
if err = os . Rename ( RepoPath ( owner . Name , repo . Name ) , RepoPath ( newUser . Name , repo . Name ) ) ; err != nil {
2014-07-05 06:43:39 +04:00
sess . Rollback ( )
return err
}
if err = sess . Commit ( ) ; err != nil {
return err
}
2014-09-26 06:36:07 +04:00
if err = WatchRepo ( newUser . Id , repo . Id , true ) ; err != nil {
log . Error ( 4 , "WatchRepo" , err )
2014-08-27 12:39:36 +04:00
}
2014-04-05 02:31:09 +04:00
2014-07-04 22:44:27 +04:00
if err = TransferRepoAction ( u , newUser , repo ) ; err != nil {
2014-04-05 02:55:17 +04:00
return err
}
2014-07-05 06:43:39 +04:00
return nil
2014-04-05 02:31:09 +04:00
}
2014-04-03 23:50:55 +04:00
// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
func ChangeRepositoryName ( userName , oldRepoName , newRepoName string ) ( err error ) {
2014-08-24 17:09:05 +04:00
if ! IsLegalName ( newRepoName ) {
return ErrRepoNameIllegal
}
2014-04-03 23:50:55 +04:00
// Update accesses.
2014-04-04 00:33:27 +04:00
accesses := make ( [ ] Access , 0 , 10 )
2014-06-21 08:51:41 +04:00
if err = x . Find ( & accesses , & Access { RepoName : strings . ToLower ( userName + "/" + oldRepoName ) } ) ; err != nil {
2014-04-03 23:50:55 +04:00
return err
}
2014-04-05 02:55:17 +04:00
2014-06-21 08:51:41 +04:00
sess := x . NewSession ( )
2014-04-05 02:55:17 +04:00
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
return err
}
2014-04-03 23:50:55 +04:00
for i := range accesses {
accesses [ i ] . RepoName = userName + "/" + newRepoName
2014-04-05 02:55:17 +04:00
if err = UpdateAccessWithSession ( sess , & accesses [ i ] ) ; err != nil {
2014-04-03 23:50:55 +04:00
return err
}
}
// Change repository directory name.
2014-04-05 02:55:17 +04:00
if err = os . Rename ( RepoPath ( userName , oldRepoName ) , RepoPath ( userName , newRepoName ) ) ; err != nil {
sess . Rollback ( )
return err
}
return sess . Commit ( )
2014-04-03 23:50:55 +04:00
}
2014-03-22 12:44:57 +04:00
func UpdateRepository ( repo * Repository ) error {
2014-04-03 23:50:55 +04:00
repo . LowerName = strings . ToLower ( repo . Name )
2014-03-23 00:00:46 +04:00
if len ( repo . Description ) > 255 {
repo . Description = repo . Description [ : 255 ]
}
if len ( repo . Website ) > 255 {
repo . Website = repo . Website [ : 255 ]
}
2014-06-21 08:51:41 +04:00
_ , err := x . Id ( repo . Id ) . AllCols ( ) . Update ( repo )
2014-03-22 12:44:57 +04:00
return err
}
2014-03-21 00:04:56 +04:00
// DeleteRepository deletes a repository for a user or orgnaztion.
2014-08-27 12:39:36 +04:00
func DeleteRepository ( uid , repoId int64 , userName string ) error {
repo := & Repository { Id : repoId , OwnerId : uid }
2014-06-21 08:51:41 +04:00
has , err := x . Get ( repo )
2014-03-21 00:04:56 +04:00
if err != nil {
return err
} else if ! has {
return ErrRepoNotExist
}
2014-08-27 12:39:36 +04:00
// In case is a organization.
org , err := GetUserById ( uid )
if err != nil {
return err
}
if org . IsOrganization ( ) {
if err = org . GetTeams ( ) ; err != nil {
return err
}
}
2014-06-21 08:51:41 +04:00
sess := x . NewSession ( )
2014-04-05 02:31:09 +04:00
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
2014-03-21 00:04:56 +04:00
return err
}
2014-06-28 08:40:07 +04:00
2014-04-05 02:31:09 +04:00
if _ , err = sess . Delete ( & Repository { Id : repoId } ) ; err != nil {
sess . Rollback ( )
2014-03-21 00:04:56 +04:00
return err
}
2014-08-27 12:39:36 +04:00
// Delete all access.
2014-04-05 02:31:09 +04:00
if _ , err := sess . Delete ( & Access { RepoName : strings . ToLower ( path . Join ( userName , repo . Name ) ) } ) ; err != nil {
sess . Rollback ( )
2014-03-21 00:04:56 +04:00
return err
}
2014-08-27 12:39:36 +04:00
if org . IsOrganization ( ) {
idStr := "$" + com . ToStr ( repoId ) + "|"
for _ , t := range org . Teams {
if ! strings . Contains ( t . RepoIds , idStr ) {
continue
}
t . NumRepos --
t . RepoIds = strings . Replace ( t . RepoIds , idStr , "" , 1 )
if _ , err = sess . Id ( t . Id ) . AllCols ( ) . Update ( t ) ; err != nil {
sess . Rollback ( )
return err
}
}
}
2014-04-13 04:35:35 +04:00
if _ , err := sess . Delete ( & Action { RepoId : repo . Id } ) ; err != nil {
2014-04-05 02:31:09 +04:00
sess . Rollback ( )
2014-03-21 00:04:56 +04:00
return err
}
2014-04-05 02:31:09 +04:00
if _ , err = sess . Delete ( & Watch { RepoId : repoId } ) ; err != nil {
sess . Rollback ( )
2014-03-21 00:04:56 +04:00
return err
}
2014-04-13 06:30:00 +04:00
if _ , err = sess . Delete ( & Mirror { RepoId : repoId } ) ; err != nil {
sess . Rollback ( )
return err
}
2014-05-14 21:04:57 +04:00
if _ , err = sess . Delete ( & IssueUser { RepoId : repoId } ) ; err != nil {
2014-05-14 16:51:04 +04:00
sess . Rollback ( )
return err
}
2014-05-14 21:04:57 +04:00
if _ , err = sess . Delete ( & Milestone { RepoId : repoId } ) ; err != nil {
2014-05-14 16:51:04 +04:00
sess . Rollback ( )
return err
}
2014-05-14 21:04:57 +04:00
if _ , err = sess . Delete ( & Release { RepoId : repoId } ) ; err != nil {
sess . Rollback ( )
return err
}
// Delete comments.
2014-06-21 08:51:41 +04:00
if err = x . Iterate ( & Issue { RepoId : repoId } , func ( idx int , bean interface { } ) error {
2014-05-14 21:04:57 +04:00
issue := bean . ( * Issue )
if _ , err = sess . Delete ( & Comment { IssueId : issue . Id } ) ; err != nil {
sess . Rollback ( )
return err
}
return nil
} ) ; err != nil {
sess . Rollback ( )
return err
}
if _ , err = sess . Delete ( & Issue { RepoId : repoId } ) ; err != nil {
2014-05-14 17:23:33 +04:00
sess . Rollback ( )
return err
}
2014-10-19 09:35:24 +04:00
2014-10-13 23:23:30 +04:00
if repo . IsFork {
2014-10-19 09:35:24 +04:00
if _ , err = sess . Exec ( "UPDATE `repository` SET num_forks = num_forks - 1 WHERE id = ?" , repo . ForkId ) ; err != nil {
sess . Rollback ( )
return err
}
2014-10-13 23:23:30 +04:00
}
2014-04-13 04:35:35 +04:00
2014-08-27 12:39:36 +04:00
if _ , err = sess . Exec ( "UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?" , uid ) ; err != nil {
2014-04-13 04:35:35 +04:00
sess . Rollback ( )
return err
}
2014-10-09 02:29:18 +04:00
// Remove repository files.
2014-03-21 00:04:56 +04:00
if err = os . RemoveAll ( RepoPath ( userName , repo . Name ) ) ; err != nil {
2014-10-09 02:29:18 +04:00
desc := fmt . Sprintf ( "Fail to delete repository files(%s/%s): %v" , userName , repo . Name , err )
log . Warn ( desc )
if err = CreateRepositoryNotice ( desc ) ; err != nil {
log . Error ( 4 , "Fail to add notice: %v" , err )
}
2014-03-21 00:04:56 +04:00
}
2014-06-09 01:53:53 +04:00
return sess . Commit ( )
2014-03-21 00:04:56 +04:00
}
2014-07-23 15:48:06 +04:00
// GetRepositoryByRef returns a Repository specified by a GFM reference.
// See https://help.github.com/articles/writing-on-github#references for more information on the syntax.
func GetRepositoryByRef ( ref string ) ( * Repository , error ) {
n := strings . IndexByte ( ref , byte ( '/' ) )
if n < 2 {
return nil , ErrInvalidReference
}
userName , repoName := ref [ : n ] , ref [ n + 1 : ]
user , err := GetUserByName ( userName )
if err != nil {
return nil , err
}
return GetRepositoryByName ( user . Id , repoName )
}
2014-03-17 22:03:58 +04:00
// GetRepositoryByName returns the repository by given name under user if exists.
2014-09-13 02:29:58 +04:00
func GetRepositoryByName ( uid int64 , repoName string ) ( * Repository , error ) {
2014-03-13 06:27:11 +04:00
repo := & Repository {
2014-09-13 02:29:58 +04:00
OwnerId : uid ,
2014-03-13 06:27:11 +04:00
LowerName : strings . ToLower ( repoName ) ,
}
2014-06-21 08:51:41 +04:00
has , err := x . Get ( repo )
2014-03-13 06:27:11 +04:00
if err != nil {
return nil , err
} else if ! has {
return nil , ErrRepoNotExist
}
return repo , err
}
2014-03-17 22:03:58 +04:00
// GetRepositoryById returns the repository by given id if exists.
2014-03-28 03:42:10 +04:00
func GetRepositoryById ( id int64 ) ( * Repository , error ) {
repo := & Repository { }
2014-06-21 08:51:41 +04:00
has , err := x . Id ( id ) . Get ( repo )
2014-03-13 06:27:11 +04:00
if err != nil {
return nil , err
} else if ! has {
return nil , ErrRepoNotExist
}
2014-05-06 05:36:08 +04:00
return repo , nil
2014-03-13 06:27:11 +04:00
}
2014-05-12 23:14:22 +04:00
// GetRepositories returns a list of repositories of given user.
2014-05-24 23:28:31 +04:00
func GetRepositories ( uid int64 , private bool ) ( [ ] * Repository , error ) {
2014-05-07 20:09:30 +04:00
repos := make ( [ ] * Repository , 0 , 10 )
2014-06-21 08:51:41 +04:00
sess := x . Desc ( "updated" )
2014-04-14 02:12:07 +04:00
if ! private {
sess . Where ( "is_private=?" , false )
}
2014-05-24 23:28:31 +04:00
err := sess . Find ( & repos , & Repository { OwnerId : uid } )
2014-02-14 18:20:57 +04:00
return repos , err
}
2014-04-14 12:11:33 +04:00
// GetRecentUpdatedRepositories returns the list of repositories that are recently updated.
2014-09-06 01:28:09 +04:00
func GetRecentUpdatedRepositories ( num int ) ( repos [ ] * Repository , err error ) {
err = x . Where ( "is_private=?" , false ) . Limit ( num ) . Desc ( "updated" ) . Find ( & repos )
2014-04-14 12:11:33 +04:00
return repos , err
}
// GetRepositoryCount returns the total number of repositories of user.
2014-03-11 10:17:05 +04:00
func GetRepositoryCount ( user * User ) ( int64 , error ) {
2014-06-21 08:51:41 +04:00
return x . Count ( & Repository { OwnerId : user . Id } )
2014-03-11 10:17:05 +04:00
}
2014-05-08 20:24:11 +04:00
// GetCollaboratorNames returns a list of user name of repository's collaborators.
func GetCollaboratorNames ( repoName string ) ( [ ] string , error ) {
2014-05-01 19:32:12 +04:00
accesses := make ( [ ] * Access , 0 , 10 )
2014-06-21 08:51:41 +04:00
if err := x . Find ( & accesses , & Access { RepoName : strings . ToLower ( repoName ) } ) ; err != nil {
2014-05-01 19:32:12 +04:00
return nil , err
}
names := make ( [ ] string , len ( accesses ) )
for i := range accesses {
names [ i ] = accesses [ i ] . UserName
}
return names , nil
}
2014-11-13 10:32:18 +03:00
// CollaborativeRepository represents a repository with collaborative information.
type CollaborativeRepository struct {
* Repository
CanPush bool
}
2014-05-12 23:14:22 +04:00
// GetCollaborativeRepos returns a list of repositories that user is collaborator.
2014-11-13 10:32:18 +03:00
func GetCollaborativeRepos ( uname string ) ( [ ] * CollaborativeRepository , error ) {
2014-05-12 23:14:22 +04:00
uname = strings . ToLower ( uname )
accesses := make ( [ ] * Access , 0 , 10 )
2014-06-21 08:51:41 +04:00
if err := x . Find ( & accesses , & Access { UserName : uname } ) ; err != nil {
2014-05-12 23:14:22 +04:00
return nil , err
}
2014-11-13 10:32:18 +03:00
repos := make ( [ ] * CollaborativeRepository , 0 , 10 )
2014-05-12 23:14:22 +04:00
for _ , access := range accesses {
2014-06-11 12:54:25 +04:00
infos := strings . Split ( access . RepoName , "/" )
if infos [ 0 ] == uname {
2014-05-12 23:14:22 +04:00
continue
}
u , err := GetUserByName ( infos [ 0 ] )
if err != nil {
return nil , err
}
repo , err := GetRepositoryByName ( u . Id , infos [ 1 ] )
if err != nil {
return nil , err
}
repo . Owner = u
2014-11-13 10:32:18 +03:00
repos = append ( repos , & CollaborativeRepository { repo , access . Mode == WRITABLE } )
2014-05-12 23:14:22 +04:00
}
return repos , nil
}
2014-05-08 20:24:11 +04:00
// GetCollaborators returns a list of users of repository's collaborators.
func GetCollaborators ( repoName string ) ( us [ ] * User , err error ) {
accesses := make ( [ ] * Access , 0 , 10 )
2014-06-21 08:51:41 +04:00
if err = x . Find ( & accesses , & Access { RepoName : strings . ToLower ( repoName ) } ) ; err != nil {
2014-05-08 20:24:11 +04:00
return nil , err
}
us = make ( [ ] * User , len ( accesses ) )
for i := range accesses {
us [ i ] , err = GetUserByName ( accesses [ i ] . UserName )
if err != nil {
return nil , err
}
}
return us , nil
}
2014-08-26 14:11:15 +04:00
type SearchOption struct {
Keyword string
Uid int64
Limit int
2014-10-25 15:50:19 +04:00
Private bool
}
// FilterSQLInject tries to prevent SQL injection.
func FilterSQLInject ( key string ) string {
key = strings . TrimSpace ( key )
key = strings . Split ( key , " " ) [ 0 ]
key = strings . Replace ( key , "," , "" , - 1 )
return key
2014-08-26 14:11:15 +04:00
}
// SearchRepositoryByName returns given number of repositories whose name contains keyword.
func SearchRepositoryByName ( opt SearchOption ) ( repos [ ] * Repository , err error ) {
// Prevent SQL inject.
2014-10-25 15:50:19 +04:00
opt . Keyword = FilterSQLInject ( opt . Keyword )
2014-08-26 14:11:15 +04:00
if len ( opt . Keyword ) == 0 {
return repos , nil
}
opt . Keyword = strings . ToLower ( opt . Keyword )
repos = make ( [ ] * Repository , 0 , opt . Limit )
// Append conditions.
sess := x . Limit ( opt . Limit )
if opt . Uid > 0 {
sess . Where ( "owner_id=?" , opt . Uid )
}
2014-10-25 15:50:19 +04:00
if ! opt . Private {
sess . And ( "is_private=false" )
}
2014-11-04 19:37:15 +03:00
sess . And ( "lower_name like ?" , "%" + opt . Keyword + "%" ) . Find ( & repos )
2014-08-26 14:11:15 +04:00
return repos , err
}
2014-11-19 03:05:33 +03:00
// DeleteRepositoryArchives deletes all repositories' archives.
func DeleteRepositoryArchives ( ) error {
return x . Where ( "id > 0" ) . Iterate ( new ( Repository ) ,
func ( idx int , bean interface { } ) error {
repo := bean . ( * Repository )
if err := repo . GetOwner ( ) ; err != nil {
return err
}
return os . RemoveAll ( filepath . Join ( RepoPath ( repo . Owner . Name , repo . Name ) , "archives" ) )
} )
}
2014-09-23 23:30:04 +04:00
// __ __ __ .__
// / \ / \_____ _/ |_ ____ | |__
// \ \/\/ /\__ \\ __\/ ___\| | \
// \ / / __ \| | \ \___| Y \
// \__/\ / (____ /__| \___ >___| /
// \/ \/ \/ \/
2014-03-21 00:04:56 +04:00
// Watch is connection request for receiving repository notifycation.
type Watch struct {
Id int64
2014-05-08 01:38:03 +04:00
UserId int64 ` xorm:"UNIQUE(watch)" `
RepoId int64 ` xorm:"UNIQUE(watch)" `
2014-03-21 00:04:56 +04:00
}
2014-10-19 09:35:24 +04:00
// IsWatching checks if user has watched given repository.
func IsWatching ( uid , repoId int64 ) bool {
has , _ := x . Get ( & Watch { 0 , uid , repoId } )
return has
}
func watchRepoWithEngine ( e Engine , uid , repoId int64 , watch bool ) ( err error ) {
2014-03-21 00:04:56 +04:00
if watch {
2014-08-11 07:11:18 +04:00
if IsWatching ( uid , repoId ) {
return nil
}
2014-10-19 09:35:24 +04:00
if _ , err = e . Insert ( & Watch { RepoId : repoId , UserId : uid } ) ; err != nil {
2014-03-21 00:14:50 +04:00
return err
}
2014-10-19 09:35:24 +04:00
_ , err = e . Exec ( "UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?" , repoId )
2014-03-21 00:04:56 +04:00
} else {
2014-08-11 07:11:18 +04:00
if ! IsWatching ( uid , repoId ) {
return nil
}
2014-10-19 09:35:24 +04:00
if _ , err = e . Delete ( & Watch { 0 , uid , repoId } ) ; err != nil {
2014-03-21 00:14:50 +04:00
return err
}
2014-10-19 09:35:24 +04:00
_ , err = e . Exec ( "UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?" , repoId )
2014-03-21 00:04:56 +04:00
}
return err
}
2014-10-19 09:35:24 +04:00
// Watch or unwatch repository.
func WatchRepo ( uid , repoId int64 , watch bool ) ( err error ) {
return watchRepoWithEngine ( x , uid , repoId , watch )
2014-08-11 07:11:18 +04:00
}
2014-05-08 00:51:14 +04:00
// GetWatchers returns all watchers of given repository.
func GetWatchers ( rid int64 ) ( [ ] * Watch , error ) {
watches := make ( [ ] * Watch , 0 , 10 )
2014-06-21 08:51:41 +04:00
err := x . Find ( & watches , & Watch { RepoId : rid } )
2014-03-21 00:04:56 +04:00
return watches , err
}
2014-03-25 22:04:57 +04:00
// NotifyWatchers creates batch of actions for every watcher.
2014-03-27 19:37:33 +04:00
func NotifyWatchers ( act * Action ) error {
2014-03-25 22:04:57 +04:00
// Add feeds for user self and all watchers.
2014-05-08 00:51:14 +04:00
watches , err := GetWatchers ( act . RepoId )
2014-03-20 09:31:24 +04:00
if err != nil {
2014-03-25 22:04:57 +04:00
return errors . New ( "repo.NotifyWatchers(get watches): " + err . Error ( ) )
2014-03-20 09:31:24 +04:00
}
2014-03-27 20:48:29 +04:00
// Add feed for actioner.
act . UserId = act . ActUserId
2014-06-21 08:51:41 +04:00
if _ , err = x . InsertOne ( act ) ; err != nil {
2014-03-27 20:48:29 +04:00
return errors . New ( "repo.NotifyWatchers(create action): " + err . Error ( ) )
}
2014-03-20 09:31:24 +04:00
2014-03-25 22:04:57 +04:00
for i := range watches {
2014-05-08 01:23:02 +04:00
if act . ActUserId == watches [ i ] . UserId {
2014-03-27 20:48:29 +04:00
continue
2014-03-20 09:31:24 +04:00
}
2014-04-02 18:38:30 +04:00
act . Id = 0
2014-05-08 01:23:02 +04:00
act . UserId = watches [ i ] . UserId
2014-06-21 08:51:41 +04:00
if _ , err = x . InsertOne ( act ) ; err != nil {
2014-03-25 22:04:57 +04:00
return errors . New ( "repo.NotifyWatchers(create action): " + err . Error ( ) )
}
2014-03-20 09:31:24 +04:00
}
2014-03-25 22:04:57 +04:00
return nil
2014-03-20 09:31:24 +04:00
}
2014-09-23 23:30:04 +04:00
// _________ __
// / _____// |______ _______
// \_____ \\ __\__ \\_ __ \
// / \| | / __ \| | \/
// /_______ /|__| (____ /__|
// \/ \/
2014-08-11 07:11:18 +04:00
type Star struct {
Id int64
Uid int64 ` xorm:"UNIQUE(s)" `
RepoId int64 ` xorm:"UNIQUE(s)" `
}
// Star or unstar repository.
func StarRepo ( uid , repoId int64 , star bool ) ( err error ) {
if star {
if IsStaring ( uid , repoId ) {
return nil
}
if _ , err = x . Insert ( & Star { Uid : uid , RepoId : repoId } ) ; err != nil {
return err
2014-09-26 03:33:39 +04:00
} else if _ , err = x . Exec ( "UPDATE `repository` SET num_stars = num_stars + 1 WHERE id = ?" , repoId ) ; err != nil {
return err
2014-08-11 07:11:18 +04:00
}
2014-09-26 03:33:39 +04:00
_ , err = x . Exec ( "UPDATE `user` SET num_stars = num_stars + 1 WHERE id = ?" , uid )
2014-08-11 07:11:18 +04:00
} else {
if ! IsStaring ( uid , repoId ) {
return nil
}
if _ , err = x . Delete ( & Star { 0 , uid , repoId } ) ; err != nil {
return err
2014-09-26 03:33:39 +04:00
} else if _ , err = x . Exec ( "UPDATE `repository` SET num_stars = num_stars - 1 WHERE id = ?" , repoId ) ; err != nil {
return err
2014-08-11 07:11:18 +04:00
}
2014-09-26 03:33:39 +04:00
_ , err = x . Exec ( "UPDATE `user` SET num_stars = num_stars - 1 WHERE id = ?" , uid )
2014-08-11 07:11:18 +04:00
}
return err
}
// IsStaring checks if user has starred given repository.
func IsStaring ( uid , repoId int64 ) bool {
has , _ := x . Get ( & Star { 0 , uid , repoId } )
2014-03-21 00:04:56 +04:00
return has
2014-03-17 22:03:58 +04:00
}
2014-10-19 09:35:24 +04:00
// ___________ __
// \_ _____/__________| | __
// | __)/ _ \_ __ \ |/ /
// | \( <_> ) | \/ <
// \___ / \____/|__| |__|_ \
// \/ \/
2014-11-06 07:30:04 +03:00
func ForkRepository ( u * User , oldRepo * Repository , name , desc string ) ( * Repository , error ) {
isExist , err := IsRepositoryExist ( u , name )
2014-10-19 09:35:24 +04:00
if err != nil {
return nil , err
} else if isExist {
return nil , ErrRepoAlreadyExist
}
// In case the old repository is a fork.
if oldRepo . IsFork {
oldRepo , err = GetRepositoryById ( oldRepo . ForkId )
if err != nil {
return nil , err
}
}
sess := x . NewSession ( )
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
return nil , err
}
repo := & Repository {
OwnerId : u . Id ,
Owner : u ,
2014-11-06 07:30:04 +03:00
Name : name ,
LowerName : strings . ToLower ( name ) ,
Description : desc ,
2014-10-19 09:35:24 +04:00
IsPrivate : oldRepo . IsPrivate ,
IsFork : true ,
ForkId : oldRepo . Id ,
}
if _ , err = sess . Insert ( repo ) ; err != nil {
sess . Rollback ( )
return nil , err
}
2014-03-18 07:22:19 +04:00
2014-10-19 09:35:24 +04:00
var t * Team // Owner team.
mode := WRITABLE
access := & Access {
UserName : u . LowerName ,
RepoName : path . Join ( u . LowerName , repo . LowerName ) ,
Mode : mode ,
}
// Give access to all members in owner team.
if u . IsOrganization ( ) {
t , err = u . GetOwnerTeam ( )
if err != nil {
sess . Rollback ( )
return nil , err
}
if err = t . GetMembers ( ) ; err != nil {
sess . Rollback ( )
return nil , err
}
for _ , u := range t . Members {
access . Id = 0
access . UserName = u . LowerName
if _ , err = sess . Insert ( access ) ; err != nil {
sess . Rollback ( )
return nil , err
}
}
} else {
if _ , err = sess . Insert ( access ) ; err != nil {
sess . Rollback ( )
return nil , err
}
}
if _ , err = sess . Exec (
"UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?" , u . Id ) ; err != nil {
sess . Rollback ( )
return nil , err
}
// Update owner team info and count.
if u . IsOrganization ( ) {
t . RepoIds += "$" + com . ToStr ( repo . Id ) + "|"
t . NumRepos ++
if _ , err = sess . Id ( t . Id ) . AllCols ( ) . Update ( t ) ; err != nil {
sess . Rollback ( )
return nil , err
}
}
if u . IsOrganization ( ) {
t , err := u . GetOwnerTeam ( )
if err != nil {
log . Error ( 4 , "GetOwnerTeam: %v" , err )
} else {
if err = t . GetMembers ( ) ; err != nil {
log . Error ( 4 , "GetMembers: %v" , err )
} else {
for _ , u := range t . Members {
if err = watchRepoWithEngine ( sess , u . Id , repo . Id , true ) ; err != nil {
log . Error ( 4 , "WatchRepo2: %v" , err )
}
}
}
}
} else {
if err = watchRepoWithEngine ( sess , u . Id , repo . Id , true ) ; err != nil {
log . Error ( 4 , "WatchRepo3: %v" , err )
}
}
if err = NewRepoAction ( u , repo ) ; err != nil {
log . Error ( 4 , "NewRepoAction: %v" , err )
}
if _ , err = sess . Exec (
"UPDATE `repository` SET num_forks = num_forks + 1 WHERE id = ?" , oldRepo . Id ) ; err != nil {
sess . Rollback ( )
return nil , err
}
oldRepoPath , err := oldRepo . RepoPath ( )
if err != nil {
sess . Rollback ( )
return nil , fmt . Errorf ( "fail to get repo path(%s): %v" , oldRepo . Name , err )
}
if err = sess . Commit ( ) ; err != nil {
return nil , err
}
repoPath := RepoPath ( u . Name , repo . Name )
_ , stderr , err := process . ExecTimeout ( 10 * time . Minute ,
fmt . Sprintf ( "ForkRepository(git clone): %s/%s" , u . Name , repo . Name ) ,
2014-10-25 02:43:17 +04:00
"git" , "clone" , "--bare" , oldRepoPath , repoPath )
2014-10-19 09:35:24 +04:00
if err != nil {
return nil , errors . New ( "ForkRepository(git clone): " + stderr )
}
_ , stderr , err = process . ExecDir ( - 1 ,
repoPath , fmt . Sprintf ( "ForkRepository(git update-server-info): %s" , repoPath ) ,
"git" , "update-server-info" )
if err != nil {
return nil , errors . New ( "ForkRepository(git update-server-info): " + stderr )
}
return repo , nil
2014-07-23 15:48:06 +04:00
}