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 (
2015-08-28 13:33:09 +03:00
"bytes"
2014-03-10 04:06:29 +04:00
"errors"
2014-03-11 04:48:58 +04:00
"fmt"
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"
2015-08-08 12:10:34 +03:00
"github.com/go-xorm/xorm"
2014-03-17 19:56:50 +04:00
2014-10-05 01:15:22 +04:00
"github.com/gogits/gogs/modules/base"
2015-03-18 13:37:44 +03:00
"github.com/gogits/gogs/modules/bindata"
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 (
2015-02-09 13:27:15 +03:00
_TPL_UPDATE_HOOK = "#!/usr/bin/env %s\n%s update $1 $2 $3 --config='%s'\n"
2014-06-11 03:11:53 +04:00
)
2014-03-11 09:32:36 +04:00
var (
2014-06-02 05:47:48 +04:00
ErrRepoFileNotExist = errors . New ( "Repository file does not exist" )
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" )
2015-04-20 08:28:19 +03:00
ErrNameEmpty = errors . New ( "Name is empty" )
2014-03-11 09:32:36 +04:00
)
2014-03-10 04:06:29 +04:00
var (
2015-08-28 11:44:04 +03:00
Gitignores , Licenses , Readmes [ ] string
2014-03-10 04:06:29 +04:00
)
2014-05-26 04:11:25 +04:00
func LoadRepoConfig ( ) {
2015-08-28 11:44:04 +03:00
// Load .gitignore and license files and readme templates.
types := [ ] string { "gitignore" , "license" , "readme" }
typeFiles := make ( [ ] [ ] string , 3 )
2014-05-11 22:03:51 +04:00
for i , t := range types {
2015-03-18 13:37:44 +03:00
files , err := bindata . AssetDir ( "conf/" + t )
2014-07-26 08:24:27 +04:00
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 ]
2015-08-28 11:44:04 +03:00
Readmes = typeFiles [ 2 ]
2014-07-26 08:24:27 +04:00
sort . Strings ( Gitignores )
2014-06-03 07:17:21 +04:00
sort . Strings ( Licenses )
2015-08-28 11:44:04 +03:00
sort . Strings ( Readmes )
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
}
2015-03-25 01:41:41 +03:00
// Git requires setting user.name and user.email in order to commit changes.
for configKey , defaultValue := range map [ string ] string { "user.name" : "Gogs" , "user.email" : "gogs@fake.local" } {
2014-12-28 06:07:54 +03:00
if stdout , stderr , err := process . Exec ( "NewRepoContext(get setting)" , "git" , "config" , "--get" , configKey ) ; err != nil || strings . TrimSpace ( stdout ) == "" {
// ExitError indicates this config is not set
if _ , ok := err . ( * exec . ExitError ) ; ok || strings . TrimSpace ( stdout ) == "" {
if _ , stderr , gerr := process . Exec ( "NewRepoContext(set " + configKey + ")" , "git" , "config" , "--global" , configKey , defaultValue ) ; gerr != nil {
log . Fatal ( 4 , "Fail to set git %s(%s): %s" , configKey , gerr , stderr )
}
log . Info ( "Git config %s set to %s" , configKey , defaultValue )
} else {
log . Fatal ( 4 , "Fail to get git %s(%s): %s" , configKey , err , stderr )
2014-09-04 14:03:29 +04:00
}
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 {
2015-08-08 17:43:14 +03:00
ID int64 ` xorm:"pk autoincr" `
OwnerID int64 ` xorm:"UNIQUE(s)" `
2014-10-19 09:35:24 +04:00
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
IsMirror bool
* Mirror ` xorm:"-" `
IsFork bool ` xorm:"NOT NULL DEFAULT false" `
2015-08-08 17:43:14 +03:00
ForkID int64
BaseRepo * Repository ` xorm:"-" `
2014-10-19 09:35:24 +04:00
Created time . Time ` xorm:"CREATED" `
Updated time . Time ` xorm:"UPDATED" `
2014-03-21 00:04:56 +04:00
}
2015-08-20 15:18:49 +03:00
func ( repo * Repository ) AfterSet ( colName string , _ xorm . Cell ) {
switch colName {
case "updated" :
repo . Updated = regulateTimeZone ( repo . Updated )
}
}
2015-02-13 08:58:46 +03:00
func ( repo * Repository ) getOwner ( e Engine ) ( err error ) {
2014-10-10 03:01:22 +04:00
if repo . Owner == nil {
2015-08-08 17:43:14 +03:00
repo . Owner , err = getUserByID ( e , repo . OwnerID )
2014-10-10 03:01:22 +04:00
}
2014-05-08 16:18:03 +04:00
return err
}
2015-08-17 23:03:11 +03:00
func ( repo * Repository ) GetOwner ( ) error {
2015-02-13 08:58:46 +03:00
return repo . getOwner ( x )
}
2015-08-10 16:47:23 +03:00
// GetAssignees returns all users that have write access of repository.
func ( repo * Repository ) GetAssignees ( ) ( _ [ ] * User , err error ) {
if err = repo . GetOwner ( ) ; err != nil {
return nil , err
}
accesses := make ( [ ] * Access , 0 , 10 )
if err = x . Where ( "repo_id=? AND mode>=?" , repo . ID , ACCESS_MODE_WRITE ) . Find ( & accesses ) ; err != nil {
return nil , err
}
users := make ( [ ] * User , 0 , len ( accesses ) + 1 ) // Just waste 1 unit does not matter.
if ! repo . Owner . IsOrganization ( ) {
users = append ( users , repo . Owner )
}
var u * User
for i := range accesses {
u , err = GetUserByID ( accesses [ i ] . UserID )
if err != nil {
return nil , err
}
users = append ( users , u )
}
return users , nil
}
// GetAssigneeByID returns the user that has write access of repository by given ID.
func ( repo * Repository ) GetAssigneeByID ( userID int64 ) ( * User , error ) {
return GetAssigneeByID ( repo , userID )
}
// GetMilestoneByID returns the milestone belongs to repository by given ID.
func ( repo * Repository ) GetMilestoneByID ( milestoneID int64 ) ( * Milestone , error ) {
return GetRepoMilestoneByID ( repo . ID , milestoneID )
}
2015-08-25 17:58:34 +03:00
// IssueStats returns number of open and closed repository issues by given filter mode.
func ( repo * Repository ) IssueStats ( uid int64 , filterMode int ) ( int64 , int64 ) {
return GetRepoIssueStats ( repo . ID , uid , filterMode )
}
2014-08-11 07:11:18 +04:00
func ( repo * Repository ) GetMirror ( ) ( err error ) {
2015-08-08 17:43:14 +03:00
repo . Mirror , err = GetMirror ( repo . ID )
2014-08-11 07:11:18 +04:00
return err
}
2015-08-08 17:43:14 +03:00
func ( repo * Repository ) GetBaseRepo ( ) ( err error ) {
2014-10-19 09:35:24 +04:00
if ! repo . IsFork {
return nil
}
2015-08-08 17:43:14 +03:00
repo . BaseRepo , err = GetRepositoryByID ( repo . ForkID )
2014-10-19 09:35:24 +04:00
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
}
2015-02-13 10:14:57 +03:00
func ( repo * Repository ) HasAccess ( u * User ) bool {
has , _ := HasAccess ( u , repo , ACCESS_MODE_READ )
return has
}
2015-08-13 21:43:40 +03:00
func ( repo * Repository ) IsOwnedBy ( userID int64 ) bool {
return repo . OwnerID == userID
2014-10-13 23:23:30 +04:00
}
2015-08-29 10:45:58 +03:00
var (
DescPattern = regexp . MustCompile ( ` https?://\S+ ` )
)
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 {
2015-01-31 02:12:30 +03:00
return fmt . Sprintf ( ` <a href="%[1]s" target="_blank">%[1]s</a> ` , s )
2014-07-22 22:08:04 +04:00
}
2015-01-31 02:12:30 +03:00
return template . HTML ( DescPattern . ReplaceAllStringFunc ( base . Sanitizer . Sanitize ( repo . Description ) , sanitize ) )
2014-07-22 21:52:37 +04:00
}
2015-08-08 12:10:34 +03:00
func isRepositoryExist ( e Engine , u * User , repoName string ) ( bool , error ) {
has , err := e . Get ( & Repository {
2015-08-08 17:43:14 +03:00
OwnerID : u . Id ,
2015-02-24 08:27:22 +03:00
LowerName : strings . ToLower ( repoName ) ,
} )
2015-03-27 00:11:47 +03:00
return has && com . IsDir ( RepoPath ( u . Name , repoName ) ) , err
2014-02-14 18:20:57 +04:00
}
2015-08-08 12:10:34 +03:00
// IsRepositoryExist returns true if the repository with given name under user has already existed.
func IsRepositoryExist ( u * User , repoName string ) ( bool , error ) {
return isRepositoryExist ( x , u , repoName )
}
2014-12-14 00:46:00 +03:00
// CloneLink represents different types of clone URLs of repository.
type CloneLink struct {
SSH string
HTTPS string
Git string
}
// CloneLink returns clone URLs of repository.
func ( repo * Repository ) CloneLink ( ) ( cl CloneLink , err error ) {
if err = repo . GetOwner ( ) ; err != nil {
return cl , err
}
2015-04-18 13:21:07 +03:00
2015-02-07 18:46:57 +03:00
if setting . SSHPort != 22 {
2015-04-18 13:21:07 +03:00
cl . SSH = fmt . Sprintf ( "ssh://%s@%s:%d/%s/%s.git" , setting . RunUser , setting . SSHDomain , setting . SSHPort , repo . Owner . LowerName , repo . LowerName )
2014-12-14 00:46:00 +03:00
} else {
2015-04-18 13:21:07 +03:00
cl . SSH = fmt . Sprintf ( "%s@%s:%s/%s.git" , setting . RunUser , setting . SSHDomain , repo . Owner . LowerName , repo . LowerName )
2014-12-14 00:46:00 +03:00
}
cl . HTTPS = fmt . Sprintf ( "%s%s/%s.git" , setting . AppUrl , repo . Owner . LowerName , repo . LowerName )
return cl , nil
}
2014-03-20 19:41:24 +04:00
var (
2015-03-27 00:11:47 +03:00
reservedNames = [ ] string { "debug" , "raw" , "install" , "api" , "avatar" , "user" , "org" , "help" , "stars" , "issues" , "pulls" , "commits" , "repo" , "template" , "admin" , "new" }
reservedPatterns = [ ] string { "*.git" , "*.keys" }
2014-03-20 19:41:24 +04:00
)
2015-03-27 00:11:47 +03:00
// IsUsableName checks if name is reserved or pattern of name is not allowed.
func IsUsableName ( name string ) error {
2015-04-20 08:28:19 +03:00
name = strings . TrimSpace ( strings . ToLower ( name ) )
if utf8 . RuneCountInString ( name ) == 0 {
return ErrNameEmpty
}
2015-03-27 00:11:47 +03:00
for i := range reservedNames {
if name == reservedNames [ i ] {
return ErrNameReserved { name }
2014-03-30 01:50:51 +04:00
}
}
2015-03-27 00:11:47 +03:00
for _ , pat := range reservedPatterns {
if pat [ 0 ] == '*' && strings . HasSuffix ( name , pat [ 1 : ] ) ||
( pat [ len ( pat ) - 1 ] == '*' && strings . HasPrefix ( name , pat [ : len ( pat ) - 1 ] ) ) {
return ErrNamePatternNotAllowed { pat }
2014-03-20 19:41:24 +04:00
}
}
2015-03-27 00:11:47 +03:00
return nil
2014-03-20 19:41:24 +04:00
}
2014-04-13 04:35:35 +04:00
// Mirror represents a mirror information of repository.
type Mirror struct {
2015-08-08 17:43:14 +03:00
ID int64 ` xorm:"pk autoincr" `
RepoID int64
2015-08-17 23:03:11 +03:00
Repo * Repository ` xorm:"-" `
Interval int // Hour.
Updated time . Time ` xorm:"UPDATED" `
2014-04-13 04:35:35 +04:00
NextUpdate time . Time
}
2015-08-17 23:03:11 +03:00
func ( m * Mirror ) AfterSet ( colName string , _ xorm . Cell ) {
var err error
switch colName {
case "repo_id" :
m . Repo , err = GetRepositoryByID ( m . RepoID )
if err != nil {
log . Error ( 3 , "GetRepositoryByID[%d]: %v" , m . ID , err )
}
}
}
2015-03-26 00:50:18 +03:00
func getMirror ( e Engine , repoId int64 ) ( * Mirror , error ) {
2015-08-08 17:43:14 +03:00
m := & Mirror { RepoID : repoId }
2015-03-26 00:50:18 +03:00
has , err := e . Get ( m )
2014-07-27 07:53:16 +04:00
if err != nil {
return nil , err
} else if ! has {
return nil , ErrMirrorNotExist
}
return m , nil
}
2015-03-26 00:50:18 +03:00
// GetMirror returns mirror object by given repository ID.
func GetMirror ( repoId int64 ) ( * Mirror , error ) {
return getMirror ( x , repoId )
}
func updateMirror ( e Engine , m * Mirror ) error {
2015-08-08 17:43:14 +03:00
_ , err := e . Id ( m . ID ) . Update ( m )
2014-07-27 07:53:16 +04:00
return err
}
2015-03-26 00:50:18 +03:00
func UpdateMirror ( m * Mirror ) error {
return updateMirror ( x , m )
}
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 {
2015-08-08 17:43:14 +03:00
RepoID : repoId ,
2014-06-09 01:53:53 +04:00
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 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 ) {
2015-08-28 13:33:09 +03:00
repo , err := CreateRepository ( u , CreateRepoOptions {
Name : name ,
Description : desc ,
IsPrivate : private ,
IsMirror : mirror ,
} )
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 {
2015-08-08 17:43:14 +03: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
2015-03-16 11:52:11 +03:00
return repo , UpdateRepository ( repo , false )
2014-09-02 15:11:39 +04:00
} else {
os . RemoveAll ( repoPath )
2014-04-13 04:35:35 +04:00
}
2015-02-09 20:42:53 +03:00
// FIXME: 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 ) ,
2015-07-28 16:24:24 +03:00
"git" , "clone" , "--mirror" , "--bare" , "--quiet" , url , repoPath )
2014-09-02 07:57:06 +04:00
if err != nil {
2015-07-28 16:24:24 +03:00
return repo , fmt . Errorf ( "git clone --mirror --bare --quiet: %v" , stderr )
2015-02-09 20:42:53 +03:00
} else if err = createUpdateHook ( repoPath ) ; err != nil {
return repo , fmt . Errorf ( "create update hook: %v" , err )
2014-09-02 07:57:06 +04:00
}
2015-02-09 20:42:53 +03:00
2015-07-05 13:34:16 +03:00
// Check if repository has master branch, if so set it to default branch.
gitRepo , err := git . OpenRepository ( repoPath )
if err != nil {
return repo , fmt . Errorf ( "open git repository: %v" , err )
}
if gitRepo . IsBranchExist ( "master" ) {
repo . DefaultBranch = "master"
}
2015-03-16 11:52:11 +03:00
return repo , UpdateRepository ( repo , false )
2014-04-13 04:35:35 +04:00
}
2014-03-17 19:56:50 +04:00
// 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 ) ,
2015-08-28 13:49:44 +03:00
"-m" , "initial 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
}
2015-02-09 20:42:53 +03:00
func createUpdateHook ( repoPath string ) error {
2015-08-14 20:13:23 +03:00
hookPath := path . Join ( repoPath , "hooks/update" )
os . MkdirAll ( path . Dir ( hookPath ) , os . ModePerm )
return ioutil . WriteFile ( hookPath ,
2015-02-09 05:26:14 +03:00
[ ] byte ( fmt . Sprintf ( _TPL_UPDATE_HOOK , setting . ScriptType , "\"" + appPath + "\"" , setting . CustomConf ) ) , 0777 )
2014-03-26 17:40:02 +04:00
}
2015-08-28 13:33:09 +03:00
type CreateRepoOptions struct {
Name string
Description string
Gitignores string
License string
Readme string
IsPrivate bool
IsMirror bool
AutoInit bool
}
func getRepoInitFile ( tp , name string ) ( [ ] byte , error ) {
relPath := path . Join ( "conf" , tp , name )
// Use custom file when available.
customPath := path . Join ( setting . CustomPath , relPath )
if com . IsFile ( customPath ) {
return ioutil . ReadFile ( customPath )
}
return bindata . Asset ( relPath )
}
func prepareRepoCommit ( repo * Repository , tmpDir , repoPath string , opts CreateRepoOptions ) error {
// Clone to temprory path and do the init commit.
_ , stderr , err := process . Exec (
fmt . Sprintf ( "initRepository(git clone): %s" , repoPath ) , "git" , "clone" , repoPath , tmpDir )
if err != nil {
return fmt . Errorf ( "git clone: %v - %s" , err , stderr )
}
// README
data , err := getRepoInitFile ( "readme" , opts . Readme )
if err != nil {
return fmt . Errorf ( "getRepoInitFile[%s]: %v" , opts . Readme , err )
}
2015-08-28 13:36:32 +03:00
cloneLink , err := repo . CloneLink ( )
if err != nil {
return fmt . Errorf ( "CloneLink: %v" , err )
}
2015-08-28 13:33:09 +03:00
match := map [ string ] string {
2015-08-28 13:36:32 +03:00
"Name" : repo . Name ,
"Description" : repo . Description ,
"CloneURL.SSH" : cloneLink . SSH ,
"CloneURL.HTTPS" : cloneLink . HTTPS ,
2015-08-28 13:33:09 +03:00
}
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
}
2014-03-11 04:48:58 +04:00
// InitRepository initializes README and .gitignore if needed.
2015-08-28 13:33:09 +03:00
func initRepository ( e Engine , repoPath string , u * User , repo * Repository , opts CreateRepoOptions ) error {
2015-07-26 14:22:17 +03:00
// Somehow the directory could exist.
if com . IsExist ( repoPath ) {
return fmt . Errorf ( "initRepository: path already exists: %s" , repoPath )
}
2015-03-18 13:37:44 +03:00
// Init bare new repository.
os . MkdirAll ( repoPath , os . ModePerm )
_ , stderr , err := process . ExecDir ( - 1 , repoPath ,
2015-08-28 13:33:09 +03:00
fmt . Sprintf ( "initRepository(git init --bare): %s" , repoPath ) , "git" , "init" , "--bare" )
2015-03-18 13:37:44 +03:00
if err != nil {
2015-08-28 13:33:09 +03:00
return fmt . Errorf ( "git init --bare: %v - %s" , err , stderr )
2014-03-17 19:56:50 +04:00
}
2015-02-09 20:42:53 +03:00
if err := createUpdateHook ( repoPath ) ; err != nil {
2014-03-18 01:00:35 +04:00
return err
}
2015-08-29 11:07:02 +03:00
tmpDir := filepath . Join ( os . TempDir ( ) , "gogs" , repo . Name , com . ToStr ( time . Now ( ) . Nanosecond ( ) ) )
2014-03-11 08:18:44 +04:00
2015-08-28 13:33:09 +03:00
// Initialize repository according to user's choice.
if opts . AutoInit {
os . MkdirAll ( tmpDir , os . ModePerm )
defer os . RemoveAll ( tmpDir )
2015-03-18 13:37:44 +03:00
2015-08-28 13:33:09 +03:00
if err = prepareRepoCommit ( repo , tmpDir , repoPath , opts ) ; err != nil {
return fmt . Errorf ( "prepareRepoCommit: %v" , err )
2014-03-11 09:32:36 +04:00
}
2014-03-11 08:53:53 +04:00
2015-08-28 13:33:09 +03:00
// Apply changes and commit.
if err = initRepoCommit ( tmpDir , u . NewGitSig ( ) ) ; err != nil {
return fmt . Errorf ( "initRepoCommit: %v" , err )
2014-03-11 09:32:36 +04:00
}
}
2014-03-11 08:18:44 +04:00
2015-08-26 07:26:01 +03:00
// 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 )
}
2015-08-28 13:33:09 +03:00
2015-08-28 14:34:23 +03:00
if ! opts . AutoInit {
repo . IsBare = true
}
2015-08-26 07:26:01 +03:00
repo . DefaultBranch = "master"
if err = updateRepository ( e , repo , false ) ; err != nil {
return fmt . Errorf ( "updateRepository: %v" , err )
}
2015-08-28 13:33:09 +03:00
return nil
2014-03-11 04:48:58 +04:00
}
2015-08-08 12:10:34 +03:00
func createRepository ( e * xorm . Session , u * User , repo * Repository ) ( err error ) {
if err = IsUsableName ( repo . Name ) ; err != nil {
return err
2014-06-19 09:08:03 +04:00
}
2015-08-08 12:10:34 +03:00
has , err := isRepositoryExist ( e , u , repo . Name )
2015-03-27 00:11:47 +03:00
if err != nil {
2015-08-08 12:10:34 +03:00
return fmt . Errorf ( "IsRepositoryExist: %v" , err )
2015-03-27 00:11:47 +03:00
} else if has {
2015-08-08 12:10:34 +03:00
return ErrRepoAlreadyExist { u . Name , repo . Name }
2014-06-19 09:08:03 +04:00
}
2015-08-08 12:10:34 +03:00
if _ , err = e . Insert ( repo ) ; err != nil {
return err
} else if _ , err = e . Exec ( "UPDATE `user` SET num_repos=num_repos+1 WHERE id=?" , u . Id ) ; err != nil {
return err
}
// Give access to all members in owner team.
if u . IsOrganization ( ) {
t , err := u . getOwnerTeam ( e )
if err != nil {
return fmt . Errorf ( "getOwnerTeam: %v" , err )
} else if err = t . addRepository ( e , repo ) ; err != nil {
return fmt . Errorf ( "addRepository: %v" , err )
}
} else {
// Organization automatically called this in addRepository method.
if err = repo . recalculateAccesses ( e ) ; err != nil {
return fmt . Errorf ( "recalculateAccesses: %v" , err )
}
}
2015-08-08 17:43:14 +03:00
if err = watchRepo ( e , u . Id , repo . ID , true ) ; err != nil {
2015-08-08 12:10:34 +03:00
return fmt . Errorf ( "watchRepo: %v" , err )
} else if err = newRepoAction ( e , u , repo ) ; err != nil {
return fmt . Errorf ( "newRepoAction: %v" , err )
}
return nil
}
// CreateRepository creates a repository for given user or organization.
2015-08-28 13:33:09 +03:00
func CreateRepository ( u * User , opts CreateRepoOptions ) ( _ * Repository , err error ) {
2014-06-19 09:08:03 +04:00
repo := & Repository {
2015-08-08 17:43:14 +03:00
OwnerID : u . Id ,
2014-06-25 13:27:17 +04:00
Owner : u ,
2015-08-28 13:33:09 +03:00
Name : opts . Name ,
LowerName : strings . ToLower ( opts . Name ) ,
Description : opts . Description ,
IsPrivate : opts . IsPrivate ,
2014-06-19 09:08:03 +04:00
}
2015-02-13 08:58:46 +03:00
sess := x . NewSession ( )
defer sessionRelease ( sess )
if err = sess . Begin ( ) ; err != nil {
return nil , err
}
2015-08-08 12:10:34 +03:00
if err = createRepository ( sess , u , repo ) ; err != nil {
2015-02-13 10:14:57 +03:00
return nil , err
2014-06-19 09:08:03 +04:00
}
2014-07-26 08:24:27 +04:00
// No need for init mirror.
2015-08-28 13:33:09 +03:00
if ! opts . IsMirror {
2015-02-13 08:58:46 +03:00
repoPath := RepoPath ( u . Name , repo . Name )
2015-08-28 13:33:09 +03:00
if err = initRepository ( sess , repoPath , u , repo , opts ) ; err != nil {
2015-02-13 08:58:46 +03:00
if err2 := os . RemoveAll ( repoPath ) ; err2 != nil {
log . Error ( 4 , "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 )
2014-06-25 13:14:36 +04:00
}
2014-06-19 09:08:03 +04:00
2015-02-13 08:58:46 +03:00
_ , stderr , err := process . ExecDir ( - 1 ,
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 )
}
2014-06-19 09:08:03 +04:00
}
2015-02-13 08:58:46 +03:00
return repo , sess . Commit ( )
2014-06-19 09:08:03 +04:00
}
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 {
2015-08-08 17:43:14 +03:00
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 {
2015-08-08 17:43:14 +03:00
return nil , ErrUserNotExist { repo . OwnerID , "" }
2014-03-21 09:09:22 +04:00
}
}
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.
2015-02-23 10:15:53 +03:00
func TransferOwnership ( u * User , newOwnerName string , repo * Repository ) error {
newOwner , err := GetUserByName ( newOwnerName )
2014-04-05 02:31:09 +04:00
if err != nil {
2015-02-24 08:27:22 +03:00
return fmt . Errorf ( "get new owner '%s': %v" , newOwnerName , 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.
2015-03-27 00:11:47 +03:00
has , err := IsRepositoryExist ( newOwner , repo . Name )
if err != nil {
return fmt . Errorf ( "IsRepositoryExist: %v" , err )
} else if has {
2015-08-08 12:10:34 +03:00
return ErrRepoAlreadyExist { newOwnerName , repo . Name }
2014-09-13 02:29:58 +04:00
}
2014-06-21 08:51:41 +04:00
sess := x . NewSession ( )
2015-02-13 08:58:46 +03:00
defer sessionRelease ( sess )
2014-04-05 02:55:17 +04:00
if err = sess . Begin ( ) ; err != nil {
2015-02-24 08:27:22 +03:00
return fmt . Errorf ( "sess.Begin: %v" , err )
2014-04-05 02:55:17 +04:00
}
2014-09-26 06:36:07 +04:00
owner := repo . Owner
2014-04-05 02:31:09 +04:00
2015-02-24 08:27:22 +03:00
// Note: we have to set value here to make sure recalculate accesses is based on
// new owner.
2015-08-08 17:43:14 +03:00
repo . OwnerID = newOwner . Id
2015-02-23 10:15:53 +03:00
repo . Owner = newOwner
2015-02-24 08:27:22 +03:00
// Update repository.
2015-08-08 17:43:14 +03:00
if _ , err := sess . Id ( repo . ID ) . Update ( repo ) ; err != nil {
2015-02-24 08:27:22 +03:00
return fmt . Errorf ( "update owner: %v" , err )
2014-04-05 02:31:09 +04:00
}
2015-02-24 08:27:22 +03:00
// Remove redundant collaborators.
2015-02-16 14:25:55 +03:00
collaborators , err := repo . GetCollaborators ( )
if err != nil {
2015-02-24 08:27:22 +03:00
return fmt . Errorf ( "GetCollaborators: %v" , err )
2015-02-16 14:25:55 +03:00
}
2015-02-24 08:27:22 +03:00
// Dummy object.
2015-08-08 17:43:14 +03:00
collaboration := & Collaboration { RepoID : repo . ID }
2015-02-16 14:25:55 +03:00
for _ , c := range collaborators {
2015-02-24 08:27:22 +03:00
collaboration . UserID = c . Id
2015-02-23 10:15:53 +03:00
if c . Id == newOwner . Id || newOwner . IsOrgMember ( c . Id ) {
2015-02-24 08:27:22 +03:00
if _ , err = sess . Delete ( collaboration ) ; err != nil {
return fmt . Errorf ( "remove collaborator '%d': %v" , c . Id , err )
2015-02-16 14:25:55 +03:00
}
}
}
2015-03-01 05:44:09 +03:00
// Remove old team-repository relations.
if owner . IsOrganization ( ) {
if err = owner . getTeams ( sess ) ; err != nil {
return fmt . Errorf ( "getTeams: %v" , err )
}
for _ , t := range owner . Teams {
2015-08-08 17:43:14 +03:00
if ! t . hasRepository ( sess , repo . ID ) {
2015-03-01 05:44:09 +03:00
continue
}
t . NumRepos --
if _ , err := sess . Id ( t . ID ) . AllCols ( ) . Update ( t ) ; err != nil {
return fmt . Errorf ( "decrease team repository count '%d': %v" , t . ID , err )
}
}
2015-08-08 17:43:14 +03:00
if err = owner . removeOrgRepo ( sess , repo . ID ) ; err != nil {
2015-03-01 05:44:09 +03:00
return fmt . Errorf ( "removeOrgRepo: %v" , err )
}
}
2015-02-23 10:15:53 +03:00
if newOwner . IsOrganization ( ) {
t , err := newOwner . GetOwnerTeam ( )
if err != nil {
2015-02-24 08:27:22 +03:00
return fmt . Errorf ( "GetOwnerTeam: %v" , err )
2015-02-23 10:15:53 +03:00
} else if err = t . addRepository ( sess , repo ) ; err != nil {
2015-02-24 08:27:22 +03:00
return fmt . Errorf ( "add to owner team: %v" , err )
}
} else {
// Organization called this in addRepository method.
if err = repo . recalculateAccesses ( sess ) ; err != nil {
return fmt . Errorf ( "recalculateAccesses: %v" , err )
2015-02-23 10:15:53 +03:00
}
}
2015-02-24 08:27:22 +03:00
// Update repository count.
if _ , err = sess . Exec ( "UPDATE `user` SET num_repos=num_repos+1 WHERE id=?" , newOwner . Id ) ; err != nil {
return fmt . Errorf ( "increase new owner repository count: %v" , err )
} else if _ , err = sess . Exec ( "UPDATE `user` SET num_repos=num_repos-1 WHERE id=?" , owner . Id ) ; err != nil {
return fmt . Errorf ( "decrease old owner repository count: %v" , err )
}
2015-08-08 17:43:14 +03:00
if err = watchRepo ( sess , newOwner . Id , repo . ID , true ) ; err != nil {
2015-02-24 08:27:22 +03:00
return fmt . Errorf ( "watchRepo: %v" , err )
2015-02-23 10:15:53 +03:00
} else if err = transferRepoAction ( sess , u , owner , newOwner , repo ) ; err != nil {
2015-02-24 08:27:22 +03:00
return fmt . Errorf ( "transferRepoAction: %v" , err )
2014-07-05 06:43:39 +04:00
}
2015-02-13 08:58:46 +03:00
// Change repository directory name.
2015-02-23 10:15:53 +03:00
if err = os . Rename ( RepoPath ( owner . Name , repo . Name ) , RepoPath ( newOwner . Name , repo . Name ) ) ; err != nil {
2015-02-24 08:27:22 +03:00
return fmt . Errorf ( "rename directory: %v" , err )
2014-04-05 02:55:17 +04:00
}
2015-02-13 08:58:46 +03:00
return sess . Commit ( )
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.
2015-03-27 00:11:47 +03:00
func ChangeRepositoryName ( u * User , oldRepoName , newRepoName string ) ( err error ) {
2015-01-02 07:47:33 +03:00
oldRepoName = strings . ToLower ( oldRepoName )
2014-12-12 09:29:36 +03:00
newRepoName = strings . ToLower ( newRepoName )
2015-03-27 00:11:47 +03:00
if err = IsUsableName ( newRepoName ) ; err != nil {
return err
}
has , err := IsRepositoryExist ( u , newRepoName )
if err != nil {
return fmt . Errorf ( "IsRepositoryExist: %v" , err )
} else if has {
2015-08-08 12:10:34 +03:00
return ErrRepoAlreadyExist { u . Name , newRepoName }
2014-08-24 17:09:05 +04:00
}
2014-04-03 23:50:55 +04:00
// Change repository directory name.
2015-03-27 00:11:47 +03:00
return os . Rename ( RepoPath ( u . LowerName , oldRepoName ) , RepoPath ( u . LowerName , newRepoName ) )
2014-04-03 23:50:55 +04:00
}
2015-03-16 11:52:11 +03:00
func updateRepository ( e Engine , repo * Repository , visibilityChanged bool ) ( err 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 ]
}
2015-03-16 11:52:11 +03:00
2015-08-08 17:43:14 +03:00
if _ , err = e . Id ( repo . ID ) . AllCols ( ) . Update ( repo ) ; err != nil {
2015-03-16 11:52:11 +03:00
return fmt . Errorf ( "update: %v" , err )
}
if visibilityChanged {
if err = repo . getOwner ( e ) ; err != nil {
return fmt . Errorf ( "getOwner: %v" , err )
}
if ! repo . Owner . IsOrganization ( ) {
return nil
}
// Organization repository need to recalculate access table when visivility is changed.
if err = repo . recalculateTeamAccesses ( e , 0 ) ; err != nil {
return fmt . Errorf ( "recalculateTeamAccesses: %v" , err )
}
}
return nil
2014-03-22 12:44:57 +04:00
}
2015-03-16 11:52:11 +03:00
func UpdateRepository ( repo * Repository , visibilityChanged bool ) ( err error ) {
sess := x . NewSession ( )
defer sessionRelease ( sess )
if err = sess . Begin ( ) ; err != nil {
return err
}
if err = updateRepository ( x , repo , visibilityChanged ) ; err != nil {
return fmt . Errorf ( "updateRepository: %v" , err )
}
return sess . Commit ( )
2015-02-13 08:58:46 +03:00
}
2014-12-07 04:22:48 +03:00
// DeleteRepository deletes a repository for a user or organization.
2015-02-23 10:15:53 +03:00
func DeleteRepository ( uid , repoID int64 , userName string ) error {
2015-08-08 17:43:14 +03:00
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 {
2015-03-16 11:04:27 +03:00
return ErrRepoNotExist { repoID , uid , "" }
2014-03-21 00:04:56 +04:00
}
2014-08-27 12:39:36 +04:00
// In case is a organization.
2015-08-08 17:43:14 +03:00
org , err := GetUserByID ( uid )
2014-08-27 12:39:36 +04:00
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 ( )
2015-02-13 10:14:57 +03:00
defer sessionRelease ( sess )
2014-04-05 02:31:09 +04:00
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-08-27 12:39:36 +04:00
if org . IsOrganization ( ) {
for _ , t := range org . Teams {
2015-02-23 10:15:53 +03:00
if ! t . hasRepository ( sess , repoID ) {
2014-08-27 12:39:36 +04:00
continue
2015-03-01 05:44:09 +03:00
} else if err = t . removeRepository ( sess , repo , false ) ; err != nil {
2014-08-27 12:39:36 +04:00
return err
}
}
}
2015-08-08 17:43:14 +03:00
if _ , err = sess . Delete ( & Repository { ID : repoID } ) ; err != nil {
2014-03-21 00:04:56 +04:00
return err
2015-08-08 17:43:14 +03:00
} else if _ , err = sess . Delete ( & Access { RepoID : repo . ID } ) ; err != nil {
2014-03-21 00:04:56 +04:00
return err
2015-08-08 17:43:14 +03:00
} else if _ , err = sess . Delete ( & Action { RepoID : repo . ID } ) ; err != nil {
2014-04-13 06:30:00 +04:00
return err
2015-03-18 04:51:39 +03:00
} else if _ , err = sess . Delete ( & Watch { RepoID : repoID } ) ; err != nil {
2014-05-14 16:51:04 +04:00
return err
2015-08-08 17:43:14 +03:00
} else if _ , err = sess . Delete ( & Mirror { RepoID : repoID } ) ; err != nil {
2014-05-14 16:51:04 +04:00
return err
2015-08-10 16:47:23 +03:00
} else if _ , err = sess . Delete ( & IssueUser { RepoID : repoID } ) ; err != nil {
2015-02-13 10:14:57 +03:00
return err
2015-08-04 17:24:04 +03:00
} else if _ , err = sess . Delete ( & Milestone { RepoID : repoID } ) ; err != nil {
2015-02-13 10:14:57 +03:00
return err
2015-02-23 10:15:53 +03:00
} else if _ , err = sess . Delete ( & Release { RepoId : repoID } ) ; err != nil {
2014-05-14 21:04:57 +04:00
return err
2015-02-23 10:15:53 +03:00
} else if _ , err = sess . Delete ( & Collaboration { RepoID : repoID } ) ; err != nil {
2015-02-16 14:25:55 +03:00
return err
2014-05-14 21:04:57 +04:00
}
2015-08-13 22:07:20 +03:00
// Delete comments and attachments.
2015-02-23 10:15:53 +03:00
issues := make ( [ ] * Issue , 0 , 25 )
2015-08-13 22:07:20 +03:00
attachmentPaths := make ( [ ] string , 0 , len ( issues ) )
2015-02-23 10:15:53 +03:00
if err = sess . Where ( "repo_id=?" , repoID ) . Find ( & issues ) ; err != nil {
return err
}
for i := range issues {
2015-08-13 11:07:11 +03:00
if _ , err = sess . Delete ( & Comment { IssueID : issues [ i ] . ID } ) ; err != nil {
2014-05-14 21:04:57 +04:00
return err
}
2015-08-13 22:07:20 +03:00
attachments := make ( [ ] * Attachment , 0 , 5 )
if err = sess . Where ( "issue_id=?" , issues [ i ] . ID ) . Find ( & attachments ) ; err != nil {
return err
}
for j := range attachments {
attachmentPaths = append ( attachmentPaths , attachments [ j ] . LocalPath ( ) )
}
if _ , err = sess . Delete ( & Attachment { IssueID : issues [ i ] . ID } ) ; err != nil {
return err
}
2014-05-14 21:04:57 +04:00
}
2015-08-05 15:23:08 +03:00
if _ , err = sess . Delete ( & Issue { RepoID : repoID } ) ; err != nil {
2014-05-14 17:23:33 +04:00
return err
}
2014-10-19 09:35:24 +04:00
2014-10-13 23:23:30 +04:00
if repo . IsFork {
2015-08-08 17:43:14 +03:00
if _ , err = sess . Exec ( "UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?" , repo . ForkID ) ; err != nil {
2014-10-19 09:35:24 +04:00
return err
}
2014-10-13 23:23:30 +04:00
}
2014-04-13 04:35:35 +04:00
2015-02-23 10:15:53 +03: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
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 {
2015-02-23 10:15:53 +03:00
desc := fmt . Sprintf ( "delete repository files(%s/%s): %v" , userName , repo . Name , err )
2014-10-09 02:29:18 +04:00
log . Warn ( desc )
if err = CreateRepositoryNotice ( desc ) ; err != nil {
2015-02-23 10:15:53 +03:00
log . Error ( 4 , "add notice: %v" , err )
2014-10-09 02:29:18 +04:00
}
2014-03-21 00:04:56 +04:00
}
2015-02-13 10:14:57 +03:00
2015-08-13 22:07:20 +03:00
// Remove attachment files.
for i := range attachmentPaths {
if err = os . Remove ( attachmentPaths [ i ] ) ; err != nil {
log . Warn ( "delete attachment: %v" , err )
}
}
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 {
2015-08-08 17:43:14 +03: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 {
2015-03-16 11:04:27 +03:00
return nil , ErrRepoNotExist { 0 , uid , repoName }
2014-03-13 06:27:11 +04:00
}
return repo , err
}
2015-08-08 17:43:14 +03:00
func getRepositoryByID ( e Engine , id int64 ) ( * Repository , error ) {
2015-03-16 11:04:27 +03:00
repo := new ( Repository )
2015-02-13 08:58:46 +03:00
has , err := e . Id ( id ) . Get ( repo )
2014-03-13 06:27:11 +04:00
if err != nil {
return nil , err
} else if ! has {
2015-03-16 11:04:27 +03:00
return nil , ErrRepoNotExist { id , 0 , "" }
2014-03-13 06:27:11 +04:00
}
2014-05-06 05:36:08 +04:00
return repo , nil
2014-03-13 06:27:11 +04:00
}
2015-08-08 17:43:14 +03:00
// GetRepositoryByID returns the repository by given id if exists.
func GetRepositoryByID ( id int64 ) ( * Repository , error ) {
return getRepositoryByID ( x , id )
2015-02-13 08:58:46 +03: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 )
}
2015-08-08 17:43:14 +03:00
return repos , sess . Find ( & repos , & Repository { OwnerID : uid } )
2014-02-14 18:20:57 +04:00
}
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.
2015-08-08 17:43:14 +03:00
func GetRepositoryCount ( u * User ) ( int64 , error ) {
return x . Count ( & Repository { OwnerID : u . Id } )
2014-03-11 10:17:05 +04:00
}
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
}
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 ) {
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" ) )
} )
}
2015-02-09 05:26:14 +03:00
// RewriteRepositoryUpdateHook rewrites all repositories' update hook.
func RewriteRepositoryUpdateHook ( ) 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
}
2015-02-09 20:42:53 +03:00
return createUpdateHook ( RepoPath ( repo . Owner . Name , repo . Name ) )
2015-02-09 05:26:14 +03:00
} )
}
2014-12-05 07:07:51 +03:00
var (
2015-08-17 21:19:29 +03:00
// Prevent duplicate running tasks.
2014-12-05 07:07:51 +03:00
isMirrorUpdating = false
isGitFscking = false
2015-07-25 02:47:43 +03:00
isCheckingRepos = false
2014-12-05 07:07:51 +03:00
)
// MirrorUpdate checks and updates mirror repositories.
func MirrorUpdate ( ) {
if isMirrorUpdating {
return
}
isMirrorUpdating = true
defer func ( ) { isMirrorUpdating = false } ( )
2015-08-17 23:03:11 +03:00
log . Trace ( "Doing: MirrorUpdate" )
2015-01-02 08:30:45 +03:00
2015-08-17 23:03:11 +03:00
mirrors := make ( [ ] * Mirror , 0 , 10 )
2014-12-05 07:07:51 +03:00
if err := x . Iterate ( new ( Mirror ) , func ( idx int , bean interface { } ) error {
m := bean . ( * Mirror )
if m . NextUpdate . After ( time . Now ( ) ) {
return nil
}
2015-08-17 23:03:11 +03:00
if m . Repo == nil {
log . Error ( 4 , "Disconnected mirror repository found: %d" , m . ID )
return nil
}
repoPath , err := m . Repo . RepoPath ( )
if err != nil {
return fmt . Errorf ( "Repo.RepoPath: %v" , err )
}
2014-12-05 07:07:51 +03:00
if _ , stderr , err := process . ExecDir ( 10 * time . Minute ,
repoPath , fmt . Sprintf ( "MirrorUpdate: %s" , repoPath ) ,
2015-07-19 12:44:03 +03:00
"git" , "remote" , "update" , "--prune" ) ; err != nil {
2015-01-02 08:30:45 +03:00
desc := fmt . Sprintf ( "Fail to update mirror repository(%s): %s" , repoPath , stderr )
log . Error ( 4 , desc )
if err = CreateRepositoryNotice ( desc ) ; err != nil {
2015-08-17 23:03:11 +03:00
log . Error ( 4 , "CreateRepositoryNotice: %v" , err )
2015-01-02 08:30:45 +03:00
}
return nil
2014-12-05 07:07:51 +03:00
}
m . NextUpdate = time . Now ( ) . Add ( time . Duration ( m . Interval ) * time . Hour )
2015-01-02 08:30:45 +03:00
mirrors = append ( mirrors , m )
return nil
2014-12-05 07:07:51 +03:00
} ) ; err != nil {
2015-01-02 08:30:45 +03:00
log . Error ( 4 , "MirrorUpdate: %v" , err )
}
for i := range mirrors {
if err := UpdateMirror ( mirrors [ i ] ) ; err != nil {
2015-08-17 23:03:11 +03:00
log . Error ( 4 , "UpdateMirror[%d]: %v" , mirrors [ i ] . ID , err )
2015-01-02 08:30:45 +03:00
}
2014-12-05 07:07:51 +03:00
}
}
2014-11-30 10:26:29 +03:00
// GitFsck calls 'git fsck' to check repository health.
func GitFsck ( ) {
2014-12-05 07:07:51 +03:00
if isGitFscking {
return
}
isGitFscking = true
defer func ( ) { isGitFscking = false } ( )
2015-08-17 23:03:11 +03:00
log . Trace ( "Doing: GitFsck" )
2015-08-17 21:19:29 +03:00
args := append ( [ ] string { "fsck" } , setting . Cron . RepoHealthCheck . Args ... )
2015-08-17 23:03:11 +03:00
if err := x . Where ( "id>0" ) . Iterate ( new ( Repository ) ,
2014-11-30 10:26:29 +03:00
func ( idx int , bean interface { } ) error {
repo := bean . ( * Repository )
2015-08-17 23:03:11 +03:00
repoPath , err := repo . RepoPath ( )
if err != nil {
return fmt . Errorf ( "RepoPath: %v" , err )
2014-11-30 10:26:29 +03:00
}
2015-08-17 23:03:11 +03:00
_ , _ , err = process . ExecDir ( - 1 , repoPath , "Repository health check" , "git" , args ... )
2014-11-30 10:26:29 +03:00
if err != nil {
desc := fmt . Sprintf ( "Fail to health check repository(%s)" , repoPath )
log . Warn ( desc )
if err = CreateRepositoryNotice ( desc ) ; err != nil {
2015-08-17 23:03:11 +03:00
log . Error ( 4 , "CreateRepositoryNotice: %v" , err )
2014-11-30 10:26:29 +03:00
}
}
return nil
} ) ; err != nil {
2015-08-17 23:03:11 +03:00
log . Error ( 4 , "GitFsck: %v" , err )
2014-11-30 10:26:29 +03:00
}
}
func GitGcRepos ( ) error {
2015-01-02 15:14:43 +03:00
args := append ( [ ] string { "gc" } , setting . Git . GcArgs ... )
2014-11-30 10:26:29 +03:00
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
}
_ , stderr , err := process . ExecDir ( - 1 , RepoPath ( repo . Owner . Name , repo . Name ) , "Repository garbage collection" , "git" , args ... )
if err != nil {
return fmt . Errorf ( "%v: %v" , err , stderr )
}
return nil
} )
}
2015-03-21 15:55:00 +03:00
func CheckRepoStats ( ) {
2015-07-25 02:47:43 +03:00
if isCheckingRepos {
2015-03-21 15:55:00 +03:00
return
}
2015-07-25 02:47:43 +03:00
isCheckingRepos = true
defer func ( ) { isCheckingRepos = false } ( )
2015-03-21 15:55:00 +03:00
2015-08-17 23:03:11 +03:00
log . Trace ( "Doing: CheckRepoStats" )
// ***** START: Watch *****
results , err := x . Query ( "SELECT repo.id FROM `repository` repo WHERE repo.num_watches!=(SELECT COUNT(*) FROM `watch` WHERE repo_id=repo.id)" )
if err != nil {
log . Error ( 4 , "Select repository check 'watch': %v" , err )
return
}
for _ , watch := range results {
repoID := com . StrTo ( watch [ "id" ] ) . MustInt64 ( )
log . Trace ( "Updating repository count 'watch': %d" , repoID )
_ , err = x . Exec ( "UPDATE `repository` SET num_watches=(SELECT COUNT(*) FROM `watch` WHERE repo_id=?) WHERE id=?" , repoID , repoID )
if err != nil {
log . Error ( 4 , "Update repository check 'watch'[%d]: %v" , repoID , err )
}
}
// ***** END: Watch *****
// ***** START: Star *****
results , err = x . Query ( "SELECT repo.id FROM `repository` repo WHERE repo.num_stars!=(SELECT COUNT(*) FROM `star` WHERE repo_id=repo.id)" )
2015-03-21 15:55:00 +03:00
if err != nil {
2015-08-17 23:03:11 +03:00
log . Error ( 4 , "Select repository check 'star': %v" , err )
return
2015-03-21 15:55:00 +03:00
}
2015-08-17 23:03:11 +03:00
for _ , star := range results {
repoID := com . StrTo ( star [ "id" ] ) . MustInt64 ( )
log . Trace ( "Updating repository count 'star': %d" , repoID )
_ , err = x . Exec ( "UPDATE `repository` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE repo_id=?) WHERE id=?" , repoID , repoID )
2015-03-21 15:55:00 +03:00
if err != nil {
2015-08-17 23:03:11 +03:00
log . Error ( 4 , "Update repository check 'star'[%d]: %v" , repoID , err )
2015-03-21 15:55:00 +03:00
}
}
2015-08-17 23:03:11 +03:00
// ***** END: Star *****
2015-03-21 15:55:00 +03:00
2015-08-17 23:03:11 +03:00
// ***** START: Label *****
results , err = x . Query ( "SELECT label.id FROM `label` WHERE label.num_issues!=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=label.id)" )
2015-03-21 15:55:00 +03:00
if err != nil {
2015-08-17 23:03:11 +03:00
log . Error ( 4 , "Select label check 'num_issues': %v" , err )
return
2015-03-21 15:55:00 +03:00
}
2015-08-17 23:03:11 +03:00
for _ , label := range results {
labelID := com . StrTo ( label [ "id" ] ) . MustInt64 ( )
log . Trace ( "Updating label count 'num_issues': %d" , labelID )
_ , err = x . Exec ( "UPDATE `label` SET num_issues=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=?) WHERE id=?" , labelID , labelID )
2015-03-21 15:55:00 +03:00
if err != nil {
2015-08-17 23:03:11 +03:00
log . Error ( 4 , "Update label check 'num_issues'[%d]: %v" , labelID , err )
2015-03-21 15:55:00 +03:00
}
}
2015-08-17 23:03:11 +03:00
// ***** END: Label *****
2015-03-21 15:55:00 +03:00
}
2015-02-12 05:58:37 +03:00
// _________ .__ .__ ___. __ .__
// \_ ___ \ ____ | | | | _____ \_ |__ ________________ _/ |_|__| ____ ____
// / \ \/ / _ \| | | | \__ \ | __ \ / _ \_ __ \__ \\ __\ |/ _ \ / \
// \ \___( <_> ) |_| |__/ __ \| \_\ ( <_> ) | \// __ \| | | ( <_> ) | \
// \______ /\____/|____/____(____ /___ /\____/|__| (____ /__| |__|\____/|___| /
// \/ \/ \/ \/ \/
// A Collaboration is a relation between an individual and a repository
type Collaboration struct {
ID int64 ` xorm:"pk autoincr" `
RepoID int64 ` xorm:"UNIQUE(s) INDEX NOT NULL" `
UserID int64 ` xorm:"UNIQUE(s) INDEX NOT NULL" `
Created time . Time ` xorm:"CREATED" `
}
// Add collaborator and accompanying access
2015-02-13 08:58:46 +03:00
func ( repo * Repository ) AddCollaborator ( u * User ) error {
collaboration := & Collaboration {
2015-08-08 17:43:14 +03:00
RepoID : repo . ID ,
2015-02-13 08:58:46 +03:00
UserID : u . Id ,
}
2015-02-05 16:29:08 +03:00
2015-02-12 05:58:37 +03:00
has , err := x . Get ( collaboration )
if err != nil {
return err
2015-02-13 08:58:46 +03:00
} else if has {
2015-02-05 16:29:08 +03:00
return nil
2015-02-12 05:58:37 +03:00
}
2015-03-25 01:14:04 +03:00
if err = repo . GetOwner ( ) ; err != nil {
return fmt . Errorf ( "GetOwner: %v" , err )
}
2015-02-13 08:58:46 +03:00
sess := x . NewSession ( )
defer sessionRelease ( sess )
if err = sess . Begin ( ) ; err != nil {
2015-02-12 05:58:37 +03:00
return err
}
2015-02-13 08:58:46 +03:00
if _ , err = sess . InsertOne ( collaboration ) ; err != nil {
return err
2015-03-25 01:14:04 +03:00
}
if repo . Owner . IsOrganization ( ) {
err = repo . recalculateTeamAccesses ( sess , 0 )
} else {
err = repo . recalculateAccesses ( sess )
}
if err != nil {
return fmt . Errorf ( "recalculateAccesses 'team=%v': %v" , repo . Owner . IsOrganization ( ) , err )
2015-02-12 05:58:37 +03:00
}
2015-02-13 08:58:46 +03:00
return sess . Commit ( )
2015-02-12 05:58:37 +03:00
}
2015-02-13 08:58:46 +03:00
func ( repo * Repository ) getCollaborators ( e Engine ) ( [ ] * User , error ) {
2015-02-05 16:29:08 +03:00
collaborations := make ( [ ] * Collaboration , 0 )
2015-08-08 17:43:14 +03:00
if err := e . Find ( & collaborations , & Collaboration { RepoID : repo . ID } ) ; err != nil {
2015-02-12 05:58:37 +03:00
return nil , err
}
users := make ( [ ] * User , len ( collaborations ) )
for i , c := range collaborations {
2015-08-08 17:43:14 +03:00
user , err := getUserByID ( e , c . UserID )
2015-02-12 05:58:37 +03:00
if err != nil {
return nil , err
}
users [ i ] = user
}
return users , nil
}
2015-02-13 08:58:46 +03:00
// GetCollaborators returns the collaborators for a repository
func ( repo * Repository ) GetCollaborators ( ) ( [ ] * User , error ) {
return repo . getCollaborators ( x )
}
2015-02-12 05:58:37 +03:00
// Delete collaborator and accompanying access
2015-02-13 08:58:46 +03:00
func ( repo * Repository ) DeleteCollaborator ( u * User ) ( err error ) {
collaboration := & Collaboration {
2015-08-08 17:43:14 +03:00
RepoID : repo . ID ,
2015-02-13 08:58:46 +03:00
UserID : u . Id ,
}
sess := x . NewSession ( )
defer sessionRelease ( sess )
if err = sess . Begin ( ) ; err != nil {
return err
}
2015-02-12 05:58:37 +03:00
2015-02-13 08:58:46 +03:00
if has , err := sess . Delete ( collaboration ) ; err != nil || has == 0 {
return err
} else if err = repo . recalculateAccesses ( sess ) ; err != nil {
2015-02-12 05:58:37 +03:00
return err
}
2015-02-13 08:58:46 +03:00
return sess . Commit ( )
2015-02-12 05:58:37 +03:00
}
2014-09-23 23:30:04 +04:00
// __ __ __ .__
// / \ / \_____ _/ |_ ____ | |__
// \ \/\/ /\__ \\ __\/ ___\| | \
// \ / / __ \| | \ \___| Y \
// \__/\ / (____ /__| \___ >___| /
// \/ \/ \/ \/
2014-12-07 04:22:48 +03:00
// Watch is connection request for receiving repository notification.
2014-03-21 00:04:56 +04:00
type Watch struct {
2015-03-18 04:51:39 +03:00
ID int64 ` xorm:"pk autoincr" `
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
}
2015-02-11 07:44:16 +03:00
func watchRepo ( 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
}
2015-03-18 04:51:39 +03: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
}
2015-03-18 04:51:39 +03: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 ) {
2015-02-11 07:44:16 +03:00
return watchRepo ( x , uid , repoId , watch )
2014-08-11 07:11:18 +04:00
}
2015-02-13 08:58:46 +03:00
func getWatchers ( e Engine , rid int64 ) ( [ ] * Watch , error ) {
2014-05-08 00:51:14 +04:00
watches := make ( [ ] * Watch , 0 , 10 )
2015-03-18 04:51:39 +03:00
err := e . Find ( & watches , & Watch { RepoID : rid } )
2014-03-21 00:04:56 +04:00
return watches , err
}
2015-02-13 08:58:46 +03:00
// GetWatchers returns all watchers of given repository.
func GetWatchers ( rid int64 ) ( [ ] * Watch , error ) {
return getWatchers ( x , rid )
}
func notifyWatchers ( e Engine , act * Action ) error {
2014-03-25 22:04:57 +04:00
// Add feeds for user self and all watchers.
2015-03-18 04:51:39 +03:00
watches , err := getWatchers ( e , act . RepoID )
2014-03-20 09:31:24 +04:00
if err != nil {
2015-02-13 08:58:46 +03:00
return fmt . Errorf ( "get watchers: %v" , err )
2014-03-20 09:31:24 +04:00
}
2014-03-27 20:48:29 +04:00
// Add feed for actioner.
2015-03-18 04:51:39 +03:00
act . UserID = act . ActUserID
2015-02-13 08:58:46 +03:00
if _ , err = e . InsertOne ( act ) ; err != nil {
return fmt . Errorf ( "insert new actioner: %v" , err )
2014-03-27 20:48:29 +04:00
}
2014-03-20 09:31:24 +04:00
2014-03-25 22:04:57 +04:00
for i := range watches {
2015-03-18 04:51:39 +03:00
if act . ActUserID == watches [ i ] . UserID {
2014-03-27 20:48:29 +04:00
continue
2014-03-20 09:31:24 +04:00
}
2015-03-18 04:51:39 +03:00
act . ID = 0
act . UserID = watches [ i ] . UserID
2015-02-13 08:58:46 +03:00
if _ , err = e . InsertOne ( act ) ; err != nil {
return fmt . Errorf ( "insert new action: %v" , err )
2014-03-25 22:04:57 +04:00
}
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
}
2015-02-13 08:58:46 +03:00
// NotifyWatchers creates batch of actions for every watcher.
func NotifyWatchers ( act * Action ) error {
return notifyWatchers ( x , act )
}
2014-09-23 23:30:04 +04:00
// _________ __
// / _____// |______ _______
// \_____ \\ __\__ \\_ __ \
// / \| | / __ \| | \/
// /_______ /|__| (____ /__|
// \/ \/
2014-08-11 07:11:18 +04:00
type Star struct {
2015-08-17 12:05:37 +03:00
ID int64 ` xorm:"pk autoincr" `
UID int64 ` xorm:"uid UNIQUE(s)" `
RepoID int64 ` xorm:"UNIQUE(s)" `
2014-08-11 07:11:18 +04:00
}
// Star or unstar repository.
func StarRepo ( uid , repoId int64 , star bool ) ( err error ) {
if star {
if IsStaring ( uid , repoId ) {
return nil
}
2015-08-17 12:05:37 +03:00
if _ , err = x . Insert ( & Star { UID : uid , RepoID : repoId } ) ; err != nil {
2014-08-11 07:11:18 +04:00
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
// ___________ __
// \_ _____/__________| | __
// | __)/ _ \_ __ \ |/ /
// | \( <_> ) | \/ <
// \___ / \____/|__| |__|_ \
// \/ \/
2015-08-08 12:24:10 +03:00
// HasForkedRepo checks if given user has already forked a repository with given ID.
func HasForkedRepo ( ownerID , repoID int64 ) ( * Repository , bool ) {
repo := new ( Repository )
has , _ := x . Where ( "owner_id=? AND fork_id=?" , ownerID , repoID ) . Get ( repo )
return repo , has
}
2015-02-24 08:27:22 +03:00
func ForkRepository ( u * User , oldRepo * Repository , name , desc string ) ( _ * Repository , err error ) {
2014-10-19 09:35:24 +04:00
repo := & Repository {
2015-08-08 17:43:14 +03:00
OwnerID : u . Id ,
2014-10-19 09:35:24 +04:00
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 ,
2015-08-08 17:43:14 +03:00
ForkID : oldRepo . ID ,
2014-10-19 09:35:24 +04:00
}
2015-02-13 08:58:46 +03:00
sess := x . NewSession ( )
defer sessionRelease ( sess )
if err = sess . Begin ( ) ; err != nil {
return nil , err
}
2015-08-08 12:10:34 +03:00
if err = createRepository ( sess , u , repo ) ; err != nil {
2014-10-19 09:35:24 +04:00
return nil , err
}
2014-03-18 07:22:19 +04:00
2015-08-08 17:43:14 +03:00
if _ , err = sess . Exec ( "UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?" , oldRepo . ID ) ; err != nil {
2014-10-19 09:35:24 +04:00
return nil , err
}
oldRepoPath , err := oldRepo . RepoPath ( )
if err != nil {
2015-02-13 08:58:46 +03:00
return nil , fmt . Errorf ( "get old repository path: %v" , err )
2014-10-19 09:35:24 +04:00
}
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 {
2015-02-13 08:58:46 +03:00
return nil , fmt . Errorf ( "git clone: %v" , stderr )
2014-10-19 09:35:24 +04:00
}
_ , stderr , err = process . ExecDir ( - 1 ,
repoPath , fmt . Sprintf ( "ForkRepository(git update-server-info): %s" , repoPath ) ,
"git" , "update-server-info" )
if err != nil {
2015-02-13 08:58:46 +03:00
return nil , fmt . Errorf ( "git update-server-info: %v" , err )
2014-10-19 09:35:24 +04:00
}
2015-03-13 03:18:42 +03:00
if err = createUpdateHook ( repoPath ) ; err != nil {
return nil , fmt . Errorf ( "createUpdateHook: %v" , err )
}
2015-02-13 08:58:46 +03:00
return repo , sess . Commit ( )
2014-07-23 15:48:06 +04:00
}