2014-02-14 18:16:54 -05: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 09:20:57 -05:00
package models
import (
2015-08-28 06:33:09 -04:00
"bytes"
2014-03-09 20:06:29 -04:00
"errors"
2014-03-10 20:48:58 -04:00
"fmt"
2014-07-22 13:52:37 -04:00
"html/template"
2014-07-23 07:48:06 -04:00
"io/ioutil"
2014-02-14 09:20:57 -05:00
"os"
2014-07-26 18:37:18 -04:00
"os/exec"
2014-03-29 22:18:36 -04:00
"path"
2014-02-14 09:20:57 -05:00
"path/filepath"
2014-07-22 13:52:37 -04:00
"regexp"
2014-06-02 23:17:21 -04:00
"sort"
2014-02-14 09:20:57 -05:00
"strings"
"time"
2016-11-11 07:11:45 -05:00
"code.gitea.io/git"
2016-11-10 11:24:48 -05:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markdown"
2016-12-22 13:12:23 -05:00
"code.gitea.io/gitea/modules/options"
2016-11-10 11:24:48 -05:00
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sync"
2016-11-11 04:39:44 -05:00
api "code.gitea.io/sdk/gitea"
2017-01-01 13:15:09 -05:00
2016-11-11 07:11:45 -05:00
"github.com/Unknwon/cae/zip"
"github.com/Unknwon/com"
"github.com/go-xorm/xorm"
2016-11-05 13:04:47 -04:00
version "github.com/mcuadros/go-version"
2016-11-05 12:56:35 -04:00
ini "gopkg.in/ini.v1"
2014-02-14 09:20:57 -05:00
)
2014-06-10 19:11:53 -04:00
const (
2016-11-07 11:33:03 -05:00
tplUpdateHook = "#!/usr/bin/env %s\n%s update $1 $2 $3 --config='%s'\n"
2014-06-10 19:11:53 -04:00
)
2016-08-30 18:19:53 -04:00
var repoWorkingPool = sync . NewExclusivePool ( )
2016-08-14 20:44:20 -04:00
2014-03-11 01:32:36 -04:00
var (
2016-11-28 12:27:55 -05:00
// ErrRepoFileNotExist repository file does not exist error
ErrRepoFileNotExist = errors . New ( "Repository file does not exist" )
// ErrRepoFileNotLoaded repository file not loaded error
2014-06-01 21:47:48 -04:00
ErrRepoFileNotLoaded = errors . New ( "Repository file not loaded" )
2016-11-28 12:27:55 -05:00
// ErrMirrorNotExist mirror does not exist error
ErrMirrorNotExist = errors . New ( "Mirror does not exist" )
// ErrInvalidReference invalid reference specified error
ErrInvalidReference = errors . New ( "Invalid reference specified" )
// ErrNameEmpty name is empty error
ErrNameEmpty = errors . New ( "Name is empty" )
2014-03-11 01:32:36 -04:00
)
2014-03-09 20:06:29 -04:00
var (
2016-11-28 12:27:55 -05:00
// Gitignores contains the gitiginore files
Gitignores [ ] string
// Licenses contains the license files
Licenses [ ] string
// Readmes contains the readme files
Readmes [ ] string
// LabelTemplates contains the label template files
LabelTemplates [ ] string
2015-10-01 09:17:27 -04:00
2016-11-28 12:27:55 -05:00
// ItemsPerPage maximum items per page in forks, watchers and stars of a repo
2015-11-16 23:28:46 -05:00
ItemsPerPage = 40
2014-03-09 20:06:29 -04:00
)
2016-11-28 12:27:55 -05:00
// LoadRepoConfig loads the repository config
2014-05-25 20:11:25 -04:00
func LoadRepoConfig ( ) {
2015-08-28 04:44:04 -04:00
// Load .gitignore and license files and readme templates.
2016-08-29 22:02:49 -04:00
types := [ ] string { "gitignore" , "license" , "readme" , "label" }
typeFiles := make ( [ ] [ ] string , 4 )
2014-05-11 14:03:51 -04:00
for i , t := range types {
2016-12-22 13:12:23 -05:00
files , err := options . Dir ( t )
2014-07-26 00:24:27 -04:00
if err != nil {
log . Fatal ( 4 , "Fail to get %s files: %v" , t , err )
}
2016-12-22 13:12:23 -05:00
customPath := path . Join ( setting . CustomPath , "options" , t )
2014-05-25 20:11:25 -04:00
if com . IsDir ( customPath ) {
customFiles , err := com . StatDir ( customPath )
2014-05-11 14:03:51 -04:00
if err != nil {
2014-07-26 00:24:27 -04:00
log . Fatal ( 4 , "Fail to get custom %s files: %v" , t , err )
2014-05-11 14:03:51 -04:00
}
for _ , f := range customFiles {
if ! com . IsSliceContainsStr ( files , f ) {
files = append ( files , f )
}
}
}
typeFiles [ i ] = files
}
2014-07-26 00:24:27 -04:00
Gitignores = typeFiles [ 0 ]
2014-05-11 14:03:51 -04:00
Licenses = typeFiles [ 1 ]
2015-08-28 04:44:04 -04:00
Readmes = typeFiles [ 2 ]
2016-08-29 22:02:49 -04:00
LabelTemplates = typeFiles [ 3 ]
2014-07-26 00:24:27 -04:00
sort . Strings ( Gitignores )
2014-06-02 23:17:21 -04:00
sort . Strings ( Licenses )
2015-08-28 04:44:04 -04:00
sort . Strings ( Readmes )
2016-08-29 22:02:49 -04:00
sort . Strings ( LabelTemplates )
2016-08-28 03:06:22 -04:00
// Filter out invalid names and promote preferred licenses.
sortedLicenses := make ( [ ] string , 0 , len ( Licenses ) )
for _ , name := range setting . Repository . PreferredLicenses {
if com . IsSliceContainsStr ( Licenses , name ) {
sortedLicenses = append ( sortedLicenses , name )
}
}
for _ , name := range Licenses {
if ! com . IsSliceContainsStr ( setting . Repository . PreferredLicenses , name ) {
sortedLicenses = append ( sortedLicenses , name )
}
}
Licenses = sortedLicenses
2014-03-21 01:48:10 -04:00
}
2014-03-17 11:56:50 -04:00
2016-11-28 12:27:55 -05:00
// NewRepoContext creates a new repository context
2014-03-21 01:48:10 -04:00
func NewRepoContext ( ) {
2014-03-17 11:56:50 -04:00
zip . Verbose = false
2014-03-17 15:58:32 -04:00
2014-07-26 18: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 00:24:27 -04:00
// Check Git version.
2015-12-09 20:46:05 -05:00
gitVer , err := git . BinVersion ( )
2014-07-26 00:24:27 -04:00
if err != nil {
log . Fatal ( 4 , "Fail to get Git version: %v" , err )
}
2014-09-16 10:10:33 -04:00
2015-11-26 17:33:45 -05:00
log . Info ( "Git Version: %s" , gitVer )
if version . Compare ( "1.7.1" , gitVer , ">" ) {
2016-12-28 03:33:21 -05:00
log . Fatal ( 4 , "Gitea requires Git version greater or equal to 1.7.1" )
2014-07-26 00:24:27 -04:00
}
2015-03-24 18:41:41 -04:00
// Git requires setting user.name and user.email in order to commit changes.
2016-12-28 03:33:21 -05:00
for configKey , defaultValue := range map [ string ] string { "user.name" : "Gitea" , "user.email" : "gitea@fake.local" } {
2017-01-17 00:58:58 -05:00
if stdout , stderr , err := process . GetManager ( ) . Exec ( "NewRepoContext(get setting)" , "git" , "config" , "--get" , configKey ) ; err != nil || strings . TrimSpace ( stdout ) == "" {
2014-12-27 22:07:54 -05:00
// ExitError indicates this config is not set
if _ , ok := err . ( * exec . ExitError ) ; ok || strings . TrimSpace ( stdout ) == "" {
2017-01-17 00:58:58 -05:00
if _ , stderr , gerr := process . GetManager ( ) . Exec ( "NewRepoContext(set " + configKey + ")" , "git" , "config" , "--global" , configKey , defaultValue ) ; gerr != nil {
2014-12-27 22:07:54 -05:00
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 06:03:29 -04:00
}
2014-03-17 15:58:32 -04:00
}
}
2014-08-23 11:58:56 -04:00
// Set git some configurations.
2017-01-17 00:58:58 -05:00
if _ , stderr , err := process . GetManager ( ) . Exec ( "NewRepoContext(git config --global core.quotepath false)" ,
2014-08-23 11: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 )
}
2016-03-23 03:16:53 -04:00
RemoveAllWithNotice ( "Clean up repository temporary data" , filepath . Join ( setting . AppDataPath , "tmp" ) )
2014-03-11 01:32:36 -04:00
}
2014-03-20 16:04:56 -04:00
// Repository represents a git repository.
type Repository struct {
2015-08-08 10:43:14 -04:00
ID int64 ` xorm:"pk autoincr" `
OwnerID int64 ` xorm:"UNIQUE(s)" `
2014-10-19 01: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 14:06:42 -04:00
NumWatches int
NumStars int
NumForks int
NumIssues int
NumClosedIssues int
NumOpenIssues int ` xorm:"-" `
2014-07-26 00:24:27 -04:00
NumPulls int
NumClosedPulls int
NumOpenPulls int ` xorm:"-" `
2014-05-12 14: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 01:35:24 -04:00
2017-01-06 10:14:33 -05:00
IsPrivate bool ` xorm:"INDEX" `
IsBare bool ` xorm:"INDEX" `
2014-10-19 01:35:24 -04:00
2017-01-06 10:14:33 -05:00
IsMirror bool ` xorm:"INDEX" `
2014-10-19 01:35:24 -04:00
* Mirror ` xorm:"-" `
2015-12-04 21:30:33 -05:00
// Advanced settings
EnableWiki bool ` xorm:"NOT NULL DEFAULT true" `
2015-12-11 04:55:08 -05:00
EnableExternalWiki bool
ExternalWikiURL string
2015-12-04 21:30:33 -05:00
EnableIssues bool ` xorm:"NOT NULL DEFAULT true" `
EnableExternalTracker bool
2016-11-04 04:06:54 -04:00
ExternalTrackerURL string
2015-12-04 21:30:33 -05:00
ExternalTrackerFormat string
2016-04-22 18:28:08 -04:00
ExternalTrackerStyle string
2015-12-04 21:30:33 -05:00
ExternalMetas map [ string ] string ` xorm:"-" `
EnablePulls bool ` xorm:"NOT NULL DEFAULT true" `
2017-01-06 10:14:33 -05:00
IsFork bool ` xorm:"INDEX NOT NULL DEFAULT false" `
ForkID int64 ` xorm:"INDEX" `
2015-08-08 10:43:14 -04:00
BaseRepo * Repository ` xorm:"-" `
2014-10-19 01:35:24 -04:00
2016-03-09 19:53:30 -05:00
Created time . Time ` xorm:"-" `
2017-01-06 10:14:33 -05:00
CreatedUnix int64 ` xorm:"INDEX" `
2016-03-09 19:53:30 -05:00
Updated time . Time ` xorm:"-" `
2017-01-06 10:14:33 -05:00
UpdatedUnix int64 ` xorm:"INDEX" `
2016-03-09 19:53:30 -05:00
}
2016-11-28 12:27:55 -05:00
// BeforeInsert is invoked from XORM before inserting an object of this type.
2016-03-09 19:53:30 -05:00
func ( repo * Repository ) BeforeInsert ( ) {
2016-07-23 08:24:44 -04:00
repo . CreatedUnix = time . Now ( ) . Unix ( )
2016-03-09 19:53:30 -05:00
repo . UpdatedUnix = repo . CreatedUnix
}
2016-11-28 12:27:55 -05:00
// BeforeUpdate is invoked from XORM before updating this object.
2016-03-09 19:53:30 -05:00
func ( repo * Repository ) BeforeUpdate ( ) {
2016-07-23 08:24:44 -04:00
repo . UpdatedUnix = time . Now ( ) . Unix ( )
2014-03-20 16:04:56 -04:00
}
2016-11-28 12:27:55 -05:00
// AfterSet is invoked from XORM after setting the value of a field of this object.
2015-08-20 08:18:49 -04:00
func ( repo * Repository ) AfterSet ( colName string , _ xorm . Cell ) {
switch colName {
2016-03-04 15:43:01 -05:00
case "default_branch" :
// FIXME: use models migration to solve all at once.
if len ( repo . DefaultBranch ) == 0 {
repo . DefaultBranch = "master"
}
2015-09-02 16:18:09 -04:00
case "num_closed_issues" :
repo . NumOpenIssues = repo . NumIssues - repo . NumClosedIssues
case "num_closed_pulls" :
repo . NumOpenPulls = repo . NumPulls - repo . NumClosedPulls
case "num_closed_milestones" :
repo . NumOpenMilestones = repo . NumMilestones - repo . NumClosedMilestones
2016-04-22 18:36:05 -04:00
case "external_tracker_style" :
if len ( repo . ExternalTrackerStyle ) == 0 {
2016-11-24 20:58:05 -05:00
repo . ExternalTrackerStyle = markdown . IssueNameStyleNumeric
2016-04-22 18:36:05 -04:00
}
2016-03-09 19:53:30 -05:00
case "created_unix" :
repo . Created = time . Unix ( repo . CreatedUnix , 0 ) . Local ( )
case "updated_unix" :
repo . Updated = time . Unix ( repo . UpdatedUnix , 0 )
2015-08-20 08:18:49 -04:00
}
}
2016-08-14 06:32:24 -04:00
// MustOwner always returns a valid *User object to avoid
// conceptually impossible error handling.
2017-01-04 19:50:34 -05:00
// It creates a fake object that contains error details
2016-08-14 06:32:24 -04:00
// when error occurs.
func ( repo * Repository ) MustOwner ( ) * User {
return repo . mustOwner ( x )
}
2016-11-28 12:27:55 -05:00
// FullName returns the repository full name
2016-08-14 06:32:24 -04:00
func ( repo * Repository ) FullName ( ) string {
return repo . MustOwner ( ) . Name + "/" + repo . Name
}
2016-11-28 12:27:55 -05:00
// HTMLURL returns the repository HTML URL
2016-08-16 13:19:09 -04:00
func ( repo * Repository ) HTMLURL ( ) string {
2016-11-27 05:14:25 -05:00
return setting . AppURL + repo . FullName ( )
2016-08-14 06:32:24 -04:00
}
2016-11-28 12:27:55 -05:00
// APIFormat converts a Repository to api.Repository
2016-12-05 18:48:51 -05:00
func ( repo * Repository ) APIFormat ( mode AccessMode ) * api . Repository {
2016-08-14 06:32:24 -04:00
cloneLink := repo . CloneLink ( )
2016-12-05 18:48:51 -05:00
permission := & api . Permission {
Admin : mode >= AccessModeAdmin ,
Push : mode >= AccessModeWrite ,
Pull : mode >= AccessModeRead ,
}
2016-08-14 06:32:24 -04:00
return & api . Repository {
ID : repo . ID ,
Owner : repo . Owner . APIFormat ( ) ,
Name : repo . Name ,
FullName : repo . FullName ( ) ,
Description : repo . Description ,
Private : repo . IsPrivate ,
Fork : repo . IsFork ,
2016-08-16 13:19:09 -04:00
HTMLURL : repo . HTMLURL ( ) ,
2016-08-14 06:32:24 -04:00
SSHURL : cloneLink . SSH ,
CloneURL : cloneLink . HTTPS ,
Website : repo . Website ,
Stars : repo . NumStars ,
Forks : repo . NumForks ,
Watchers : repo . NumWatches ,
OpenIssues : repo . NumOpenIssues ,
DefaultBranch : repo . DefaultBranch ,
Created : repo . Created ,
Updated : repo . Updated ,
Permissions : permission ,
}
}
2015-02-13 00:58:46 -05:00
func ( repo * Repository ) getOwner ( e Engine ) ( err error ) {
2015-10-24 03:36:47 -04:00
if repo . Owner != nil {
return nil
2014-10-09 19:01:22 -04:00
}
2015-10-24 03:36:47 -04:00
repo . Owner , err = getUserByID ( e , repo . OwnerID )
2014-05-08 08:18:03 -04:00
return err
}
2016-11-28 12:27:55 -05:00
// GetOwner returns the repository owner
2015-08-17 16:03:11 -04:00
func ( repo * Repository ) GetOwner ( ) error {
2015-02-13 00:58:46 -05:00
return repo . getOwner ( x )
}
2015-11-26 17:33:45 -05:00
func ( repo * Repository ) mustOwner ( e Engine ) * User {
if err := repo . getOwner ( e ) ; err != nil {
return & User {
Name : "error" ,
FullName : err . Error ( ) ,
}
}
return repo . Owner
}
2015-12-04 21:30:33 -05:00
// ComposeMetas composes a map of metas for rendering external issue tracker URL.
func ( repo * Repository ) ComposeMetas ( ) map [ string ] string {
if ! repo . EnableExternalTracker {
return nil
} else if repo . ExternalMetas == nil {
repo . ExternalMetas = map [ string ] string {
"format" : repo . ExternalTrackerFormat ,
"user" : repo . MustOwner ( ) . Name ,
"repo" : repo . Name ,
}
2016-04-22 18:28:08 -04:00
switch repo . ExternalTrackerStyle {
2016-11-24 20:58:05 -05:00
case markdown . IssueNameStyleAlphanumeric :
repo . ExternalMetas [ "style" ] = markdown . IssueNameStyleAlphanumeric
2016-04-22 18:28:08 -04:00
default :
2016-11-24 20:58:05 -05:00
repo . ExternalMetas [ "style" ] = markdown . IssueNameStyleNumeric
2016-04-22 18:28:08 -04:00
}
2015-12-04 21:30:33 -05:00
}
return repo . ExternalMetas
}
2016-03-03 23:24:22 -05:00
// DeleteWiki removes the actual and local copy of repository wiki.
2016-03-03 15:38:25 -05:00
func ( repo * Repository ) DeleteWiki ( ) {
wikiPaths := [ ] string { repo . WikiPath ( ) , repo . LocalWikiPath ( ) }
for _ , wikiPath := range wikiPaths {
RemoveAllWithNotice ( "Delete repository wiki" , wikiPath )
}
}
2016-08-15 21:40:32 -04:00
func ( repo * Repository ) getAssignees ( e Engine ) ( _ [ ] * User , err error ) {
if err = repo . getOwner ( e ) ; err != nil {
2015-08-10 09:47:23 -04:00
return nil , err
}
accesses := make ( [ ] * Access , 0 , 10 )
2016-11-10 10:16:32 -05:00
if err = e .
Where ( "repo_id = ? AND mode >= ?" , repo . ID , AccessModeWrite ) .
Find ( & accesses ) ; err != nil {
2015-08-10 09:47:23 -04:00
return nil , err
}
2016-08-15 21:40:32 -04:00
// Leave a seat for owner itself to append later, but if owner is an organization
// and just waste 1 unit is cheaper than re-allocate memory once.
2016-08-15 21:48:20 -04:00
users := make ( [ ] * User , 0 , len ( accesses ) + 1 )
if len ( accesses ) > 0 {
userIDs := make ( [ ] int64 , len ( accesses ) )
for i := 0 ; i < len ( accesses ) ; i ++ {
userIDs [ i ] = accesses [ i ] . UserID
}
if err = e . In ( "id" , userIDs ) . Find ( & users ) ; err != nil {
return nil , err
}
2016-08-15 21:40:32 -04:00
}
2015-08-10 09:47:23 -04:00
if ! repo . Owner . IsOrganization ( ) {
users = append ( users , repo . Owner )
}
return users , nil
}
2016-08-15 21:40:32 -04:00
// GetAssignees returns all users that have write access and can be assigned to issues
// of the repository,
func ( repo * Repository ) GetAssignees ( ) ( _ [ ] * User , err error ) {
return repo . getAssignees ( x )
}
2015-08-10 09:47:23 -04:00
// 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 ) {
2016-08-24 19:05:56 -04:00
return GetMilestoneByRepoID ( repo . ID , milestoneID )
2015-08-10 09:47:23 -04:00
}
2015-08-25 10:58:34 -04:00
// IssueStats returns number of open and closed repository issues by given filter mode.
2015-09-02 16:18:09 -04:00
func ( repo * Repository ) IssueStats ( uid int64 , filterMode int , isPull bool ) ( int64 , int64 ) {
return GetRepoIssueStats ( repo . ID , uid , filterMode , isPull )
2015-08-25 10:58:34 -04:00
}
2016-11-28 12:27:55 -05:00
// GetMirror sets the repository mirror, returns an error upon failure
2014-08-10 23:11:18 -04:00
func ( repo * Repository ) GetMirror ( ) ( err error ) {
2016-08-30 19:18:33 -04:00
repo . Mirror , err = GetMirrorByRepoID ( repo . ID )
2014-08-10 23:11:18 -04:00
return err
}
2016-11-28 12:27:55 -05:00
// GetBaseRepo returns the base repository
2015-08-08 10:43:14 -04:00
func ( repo * Repository ) GetBaseRepo ( ) ( err error ) {
2014-10-19 01:35:24 -04:00
if ! repo . IsFork {
return nil
}
2015-08-08 10:43:14 -04:00
repo . BaseRepo , err = GetRepositoryByID ( repo . ForkID )
2014-10-19 01:35:24 -04:00
return err
}
2015-11-26 17:33:45 -05:00
func ( repo * Repository ) repoPath ( e Engine ) string {
return RepoPath ( repo . mustOwner ( e ) . Name , repo . Name )
2014-10-19 01:35:24 -04:00
}
2016-11-28 12:27:55 -05:00
// RepoPath returns the repository path
2015-11-26 17:33:45 -05:00
func ( repo * Repository ) RepoPath ( ) string {
2015-09-03 08:09:08 -04:00
return repo . repoPath ( x )
}
2016-11-28 12:27:55 -05:00
// GitConfigPath returns the repository git config path
2015-12-08 20:06:12 -05:00
func ( repo * Repository ) GitConfigPath ( ) string {
return filepath . Join ( repo . RepoPath ( ) , "config" )
}
2016-11-28 12:27:55 -05:00
// RelLink returns the repository relative link
2016-08-17 02:06:38 -04:00
func ( repo * Repository ) RelLink ( ) string {
return "/" + repo . FullName ( )
2014-10-04 13:19:14 -04:00
}
2016-11-28 12:27:55 -05:00
// Link returns the repository link
2016-08-17 02:06:38 -04:00
func ( repo * Repository ) Link ( ) string {
2016-11-27 05:14:25 -05:00
return setting . AppSubURL + "/" + repo . FullName ( )
2016-03-04 14:50:34 -05:00
}
2016-11-28 12:27:55 -05:00
// ComposeCompareURL returns the repository comparison URL
2015-12-09 20:46:05 -05:00
func ( repo * Repository ) ComposeCompareURL ( oldCommitID , newCommitID string ) string {
return fmt . Sprintf ( "%s/%s/compare/%s...%s" , repo . MustOwner ( ) . Name , repo . Name , oldCommitID , newCommitID )
}
2016-11-28 12:27:55 -05:00
// HasAccess returns true when user has access to this repository
2015-02-13 02:14:57 -05:00
func ( repo * Repository ) HasAccess ( u * User ) bool {
2016-11-07 11:20:37 -05:00
has , _ := HasAccess ( u , repo , AccessModeRead )
2015-02-13 02:14:57 -05:00
return has
}
2016-11-28 12:27:55 -05:00
// IsOwnedBy returns true when user owns this repository
2015-08-13 14:43:40 -04:00
func ( repo * Repository ) IsOwnedBy ( userID int64 ) bool {
return repo . OwnerID == userID
2014-10-13 15:23:30 -04:00
}
2015-09-05 14:31:52 -04:00
// CanBeForked returns true if repository meets the requirements of being forked.
func ( repo * Repository ) CanBeForked ( ) bool {
2016-02-19 14:33:06 -05:00
return ! repo . IsBare
}
// CanEnablePulls returns true if repository meets the requirements of accepting pulls.
func ( repo * Repository ) CanEnablePulls ( ) bool {
return ! repo . IsMirror
}
2016-11-28 12:27:55 -05:00
// AllowsPulls returns true if repository meets the requirements of accepting pulls and has them enabled.
2016-02-19 14:33:06 -05:00
func ( repo * Repository ) AllowsPulls ( ) bool {
2016-02-20 17:10:05 -05:00
return repo . CanEnablePulls ( ) && repo . EnablePulls
2015-09-05 14:31:52 -04:00
}
2016-08-28 07:56:41 -04:00
// CanEnableEditor returns true if repository meets the requirements of web editor.
func ( repo * Repository ) CanEnableEditor ( ) bool {
return ! repo . IsMirror
}
2016-11-28 12:27:55 -05:00
// NextIssueIndex returns the next issue index
2016-08-15 21:40:32 -04:00
// FIXME: should have a mutex to prevent producing same index for two issues that are created
// closely enough.
2015-09-03 03:49:50 -04:00
func ( repo * Repository ) NextIssueIndex ( ) int64 {
return int64 ( repo . NumIssues + repo . NumPulls ) + 1
}
2015-08-29 03:45:58 -04:00
var (
2016-11-28 12:27:55 -05:00
descPattern = regexp . MustCompile ( ` https?://\S+ ` )
2015-08-29 03:45:58 -04:00
)
2016-11-28 12:27:55 -05:00
// DescriptionHTML does special handles to description and return HTML string.
func ( repo * Repository ) DescriptionHTML ( ) template . HTML {
2014-07-22 14:08:04 -04:00
sanitize := func ( s string ) string {
2016-12-01 20:12:16 -05:00
return fmt . Sprintf ( ` <a href="%[1]s" target="_blank" rel="noopener">%[1]s</a> ` , s )
2014-07-22 14:08:04 -04:00
}
2016-11-28 12:27:55 -05:00
return template . HTML ( descPattern . ReplaceAllStringFunc ( markdown . Sanitizer . Sanitize ( repo . Description ) , sanitize ) )
2014-07-22 13:52:37 -04:00
}
2016-11-28 12:27:55 -05:00
// LocalCopyPath returns the local repository copy path
2015-10-04 20:54:06 -04:00
func ( repo * Repository ) LocalCopyPath ( ) string {
2016-12-16 11:00:15 -05:00
return path . Join ( setting . AppDataPath , "tmp/local-repo" , com . ToStr ( repo . ID ) )
2015-10-04 20:54:06 -04:00
}
2016-11-28 12:27:55 -05:00
// UpdateLocalCopyBranch pulls latest changes of given branch from repoPath to localPath.
2016-08-15 02:02:14 -04:00
// It creates a new clone if local copy does not exist.
// This function checks out target branch by default, it is safe to assume subsequent
// operations are operating against target branch when caller has confidence for no race condition.
func UpdateLocalCopyBranch ( repoPath , localPath , branch string ) error {
2015-10-04 20:54:06 -04:00
if ! com . IsExist ( localPath ) {
2016-02-29 19:29:49 -05:00
if err := git . Clone ( repoPath , localPath , git . CloneRepoOptions {
Timeout : time . Duration ( setting . Git . Timeout . Clone ) * time . Second ,
2016-08-11 08:48:08 -04:00
Branch : branch ,
2016-02-29 19:29:49 -05:00
} ) ; err != nil {
2016-08-16 01:20:55 -04:00
return fmt . Errorf ( "git clone %s: %v" , branch , err )
2015-10-04 20:54:06 -04:00
}
} else {
2016-08-11 08:48:08 -04:00
if err := git . Checkout ( localPath , git . CheckoutOptions {
2016-08-15 02:02:14 -04:00
Branch : branch ,
2016-08-11 08:48:08 -04:00
} ) ; err != nil {
2016-08-16 01:20:55 -04:00
return fmt . Errorf ( "git checkout %s: %v" , branch , err )
2016-08-11 08:48:08 -04:00
}
2016-02-29 19:29:49 -05:00
if err := git . Pull ( localPath , git . PullRemoteOptions {
2016-08-16 01:20:55 -04:00
Timeout : time . Duration ( setting . Git . Timeout . Pull ) * time . Second ,
2016-08-11 08:48:08 -04:00
Remote : "origin" ,
Branch : branch ,
2016-02-29 19:29:49 -05:00
} ) ; err != nil {
2016-08-16 01:20:55 -04:00
return fmt . Errorf ( "git pull origin %s: %v" , branch , err )
2015-10-04 20:54:06 -04:00
}
}
return nil
}
2016-08-16 01:20:55 -04:00
// UpdateLocalCopyBranch makes sure local copy of repository in given branch is up-to-date.
2016-08-15 02:02:14 -04:00
func ( repo * Repository ) UpdateLocalCopyBranch ( branch string ) error {
return UpdateLocalCopyBranch ( repo . RepoPath ( ) , repo . LocalCopyPath ( ) , branch )
2015-11-27 00:24:24 -05:00
}
2015-10-24 03:36:47 -04:00
// PatchPath returns corresponding patch file path of repository by given issue ID.
func ( repo * Repository ) PatchPath ( index int64 ) ( string , error ) {
if err := repo . GetOwner ( ) ; err != nil {
return "" , err
}
return filepath . Join ( RepoPath ( repo . Owner . Name , repo . Name ) , "pulls" , com . ToStr ( index ) + ".patch" ) , nil
}
// SavePatch saves patch data to corresponding location by given issue ID.
func ( repo * Repository ) SavePatch ( index int64 , patch [ ] byte ) error {
patchPath , err := repo . PatchPath ( index )
if err != nil {
return fmt . Errorf ( "PatchPath: %v" , err )
}
2016-11-30 18:56:15 -05:00
dir := filepath . Dir ( patchPath )
if err := os . MkdirAll ( dir , os . ModePerm ) ; err != nil {
return fmt . Errorf ( "Fail to create dir %s: %v" , dir , err )
}
2015-10-24 03:36:47 -04:00
if err = ioutil . WriteFile ( patchPath , patch , 0644 ) ; err != nil {
return fmt . Errorf ( "WriteFile: %v" , err )
}
return nil
}
2015-08-08 05:10:34 -04:00
func isRepositoryExist ( e Engine , u * User , repoName string ) ( bool , error ) {
has , err := e . Get ( & Repository {
2016-07-23 13:08:22 -04:00
OwnerID : u . ID ,
2015-02-24 00:27:22 -05:00
LowerName : strings . ToLower ( repoName ) ,
} )
2015-03-26 17:11:47 -04:00
return has && com . IsDir ( RepoPath ( u . Name , repoName ) ) , err
2014-02-14 09:20:57 -05:00
}
2015-08-08 05:10:34 -04: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-13 16:46:00 -05:00
// CloneLink represents different types of clone URLs of repository.
type CloneLink struct {
SSH string
HTTPS string
Git string
}
2016-08-07 17:29:16 -04:00
// ComposeHTTPSCloneURL returns HTTPS clone URL based on given owner and repository name.
func ComposeHTTPSCloneURL ( owner , repo string ) string {
2016-11-27 05:14:25 -05:00
return fmt . Sprintf ( "%s%s/%s.git" , setting . AppURL , owner , repo )
2016-08-07 17:29:16 -04:00
}
2015-11-30 20:45:55 -05:00
func ( repo * Repository ) cloneLink ( isWiki bool ) * CloneLink {
repoName := repo . Name
if isWiki {
repoName += ".wiki"
2014-12-13 16:46:00 -05:00
}
2015-04-18 06:21:07 -04:00
2015-11-30 20:45:55 -05:00
repo . Owner = repo . MustOwner ( )
cl := new ( CloneLink )
2016-02-27 20:48:39 -05:00
if setting . SSH . Port != 22 {
cl . SSH = fmt . Sprintf ( "ssh://%s@%s:%d/%s/%s.git" , setting . RunUser , setting . SSH . Domain , setting . SSH . Port , repo . Owner . Name , repoName )
2014-12-13 16:46:00 -05:00
} else {
2016-02-27 20:48:39 -05:00
cl . SSH = fmt . Sprintf ( "%s@%s:%s/%s.git" , setting . RunUser , setting . SSH . Domain , repo . Owner . Name , repoName )
2014-12-13 16:46:00 -05:00
}
2016-08-27 15:29:52 -04:00
cl . HTTPS = ComposeHTTPSCloneURL ( repo . Owner . Name , repoName )
2015-11-30 20:45:55 -05:00
return cl
}
// CloneLink returns clone URLs of repository.
func ( repo * Repository ) CloneLink ( ) ( cl * CloneLink ) {
return repo . cloneLink ( false )
2014-12-13 16:46:00 -05:00
}
2016-11-28 12:27:55 -05:00
// MigrateRepoOptions contains the repository migrate options
2015-10-25 04:26:26 -04:00
type MigrateRepoOptions struct {
Name string
Description string
IsPrivate bool
IsMirror bool
RemoteAddr string
}
2016-08-11 13:53:51 -04:00
/ *
GitHub , GitLab , Gogs : * . wiki . git
BitBucket : * . git / wiki
* /
var commonWikiURLSuffixes = [ ] string { ".wiki.git" , ".git/wiki" }
// wikiRemoteURL returns accessible repository URL for wiki if exists.
// Otherwise, it returns an empty string.
func wikiRemoteURL ( remote string ) string {
remote = strings . TrimSuffix ( remote , ".git" )
for _ , suffix := range commonWikiURLSuffixes {
wikiURL := remote + suffix
if git . IsRepoURLAccessible ( wikiURL ) {
return wikiURL
2016-08-11 13:18:51 -04:00
}
}
return ""
}
2014-04-12 20:35:35 -04:00
// MigrateRepository migrates a existing repository from other project hosting.
2015-10-25 04:26:26 -04:00
func MigrateRepository ( u * User , opts MigrateRepoOptions ) ( * Repository , error ) {
2015-08-28 06:33:09 -04:00
repo , err := CreateRepository ( u , CreateRepoOptions {
2015-10-25 04:26:26 -04:00
Name : opts . Name ,
Description : opts . Description ,
IsPrivate : opts . IsPrivate ,
IsMirror : opts . IsMirror ,
2015-08-28 06:33:09 -04:00
} )
2014-04-12 20:35:35 -04:00
if err != nil {
return nil , err
}
2015-10-25 04:26:26 -04:00
repoPath := RepoPath ( u . Name , opts . Name )
2016-08-11 13:18:51 -04:00
wikiPath := WikiPath ( u . Name , opts . Name )
2014-04-12 20:35:35 -04:00
2014-08-27 04: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
}
2016-08-11 13:53:51 -04:00
migrateTimeout := time . Duration ( setting . Git . Timeout . Migrate ) * time . Second
2016-11-30 18:56:15 -05:00
if err := os . RemoveAll ( repoPath ) ; err != nil {
return repo , fmt . Errorf ( "Fail to remove %s: %v" , repoPath , err )
}
2015-12-08 20:06:12 -05:00
if err = git . Clone ( opts . RemoteAddr , repoPath , git . CloneRepoOptions {
Mirror : true ,
Quiet : true ,
2016-08-11 13:53:51 -04:00
Timeout : migrateTimeout ,
2015-12-08 20:06:12 -05:00
} ) ; err != nil {
return repo , fmt . Errorf ( "Clone: %v" , err )
}
2016-08-11 13:53:51 -04:00
wikiRemotePath := wikiRemoteURL ( opts . RemoteAddr )
if len ( wikiRemotePath ) > 0 {
2016-11-30 18:56:15 -05:00
if err := os . RemoveAll ( wikiPath ) ; err != nil {
return repo , fmt . Errorf ( "Fail to remove %s: %v" , wikiPath , err )
}
2016-08-11 13:18:51 -04:00
if err = git . Clone ( wikiRemotePath , wikiPath , git . CloneRepoOptions {
Mirror : true ,
Quiet : true ,
2016-08-11 13:53:51 -04:00
Timeout : migrateTimeout ,
2016-12-31 04:34:34 -05:00
Branch : "master" ,
2016-08-11 13:18:51 -04:00
} ) ; err != nil {
2016-12-31 04:34:34 -05:00
log . Warn ( "Clone wiki: %v" , err )
if err := os . RemoveAll ( wikiPath ) ; err != nil {
return repo , fmt . Errorf ( "Fail to remove %s: %v" , wikiPath , err )
}
2016-08-11 13:18:51 -04:00
}
}
2016-03-03 12:07:43 -05:00
// Check if repository is empty.
_ , stderr , err := com . ExecCmdDir ( repoPath , "git" , "log" , "-1" )
if err != nil {
if strings . Contains ( stderr , "fatal: bad default revision 'HEAD'" ) {
repo . IsBare = true
} else {
return repo , fmt . Errorf ( "check bare: %v - %s" , err , stderr )
}
}
if ! repo . IsBare {
// Try to get HEAD branch and set it as default branch.
gitRepo , err := git . OpenRepository ( repoPath )
if err != nil {
2016-03-03 12:43:23 -05:00
return repo , fmt . Errorf ( "OpenRepository: %v" , err )
2016-03-03 12:07:43 -05:00
}
headBranch , err := gitRepo . GetHEADBranch ( )
if err != nil {
2016-03-03 12:43:23 -05:00
return repo , fmt . Errorf ( "GetHEADBranch: %v" , err )
2016-03-03 12:07:43 -05:00
}
if headBranch != nil {
repo . DefaultBranch = headBranch . Name
}
}
2015-10-25 04:26:26 -04:00
if opts . IsMirror {
2015-12-08 20:06:12 -05:00
if _ , err = x . InsertOne ( & Mirror {
2016-07-09 01:22:28 -04:00
RepoID : repo . ID ,
2016-08-10 02:47:16 -04:00
Interval : setting . Mirror . DefaultInterval ,
2016-07-09 01:22:28 -04:00
EnablePrune : true ,
2016-08-10 02:47:16 -04:00
NextUpdate : time . Now ( ) . Add ( time . Duration ( setting . Mirror . DefaultInterval ) * time . Hour ) ,
2015-12-08 20:06:12 -05:00
} ) ; err != nil {
return repo , fmt . Errorf ( "InsertOne: %v" , err )
2014-04-12 20:35:35 -04:00
}
2015-12-08 20:06:12 -05:00
2014-04-12 20:35:35 -04:00
repo . IsMirror = true
2015-03-16 04:52:11 -04:00
return repo , UpdateRepository ( repo , false )
2014-04-12 20:35:35 -04:00
}
2016-08-11 13:18:51 -04:00
return CleanUpMigrateInfo ( repo )
2016-02-14 15:12:00 -05:00
}
2016-08-11 13:53:51 -04:00
// cleanUpMigrateGitConfig removes mirror info which prevents "push --all".
// This also removes possible user credentials.
func cleanUpMigrateGitConfig ( configPath string ) error {
cfg , err := ini . Load ( configPath )
if err != nil {
return fmt . Errorf ( "open config file: %v" , err )
}
cfg . DeleteSection ( "remote \"origin\"" )
if err = cfg . SaveToIndent ( configPath , "\t" ) ; err != nil {
return fmt . Errorf ( "save config file: %v" , err )
}
return nil
}
2016-08-30 19:18:33 -04:00
func createUpdateHook ( repoPath string ) error {
return git . SetUpdateHook ( repoPath ,
2016-11-07 11:33:03 -05:00
fmt . Sprintf ( tplUpdateHook , setting . ScriptType , "\"" + setting . AppPath + "\"" , setting . CustomConf ) )
2016-08-30 19:18:33 -04:00
}
2016-11-28 12:27:55 -05:00
// CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors.
2016-08-11 13:18:51 -04:00
func CleanUpMigrateInfo ( repo * Repository ) ( * Repository , error ) {
repoPath := repo . RepoPath ( )
2016-02-14 15:12:00 -05:00
if err := createUpdateHook ( repoPath ) ; err != nil {
2015-12-08 20:06:12 -05:00
return repo , fmt . Errorf ( "createUpdateHook: %v" , err )
2014-09-01 23:57:06 -04:00
}
2016-08-11 13:53:51 -04:00
if repo . HasWiki ( ) {
if err := createUpdateHook ( repo . WikiPath ( ) ) ; err != nil {
return repo , fmt . Errorf ( "createUpdateHook (wiki): %v" , err )
2016-08-11 13:18:51 -04:00
}
}
2015-02-09 12:42:53 -05:00
2016-08-11 13:53:51 -04:00
if err := cleanUpMigrateGitConfig ( repo . GitConfigPath ( ) ) ; err != nil {
return repo , fmt . Errorf ( "cleanUpMigrateGitConfig: %v" , err )
2015-10-31 23:22:28 -04:00
}
2016-08-11 13:53:51 -04:00
if repo . HasWiki ( ) {
if err := cleanUpMigrateGitConfig ( path . Join ( repo . WikiPath ( ) , "config" ) ) ; err != nil {
return repo , fmt . Errorf ( "cleanUpMigrateGitConfig (wiki): %v" , err )
2016-08-11 13:18:51 -04:00
}
}
2015-10-31 23:22:28 -04:00
2015-03-16 04:52:11 -04:00
return repo , UpdateRepository ( repo , false )
2014-04-12 20:35:35 -04:00
}
2014-03-17 11:56:50 -04:00
// initRepoCommit temporarily changes with work directory.
2015-11-27 00:24:24 -05:00
func initRepoCommit ( tmpPath string , sig * git . Signature ) ( err error ) {
2014-03-17 15:58:32 -04:00
var stderr string
2017-01-17 00:58:58 -05:00
if _ , stderr , err = process . GetManager ( ) . ExecDir ( - 1 ,
2015-11-20 02:53:54 -05:00
tmpPath , fmt . Sprintf ( "initRepoCommit (git add): %s" , tmpPath ) ,
2014-06-19 01:08:03 -04:00
"git" , "add" , "--all" ) ; err != nil {
2015-09-02 09:26:56 -04:00
return fmt . Errorf ( "git add: %s" , stderr )
2014-03-27 15:24:11 -04:00
}
2014-06-19 01:08:03 -04:00
2017-01-17 00:58:58 -05:00
if _ , stderr , err = process . GetManager ( ) . ExecDir ( - 1 ,
2015-11-20 02:53:54 -05:00
tmpPath , fmt . Sprintf ( "initRepoCommit (git commit): %s" , tmpPath ) ,
2014-06-19 01:08:03 -04:00
"git" , "commit" , fmt . Sprintf ( "--author='%s <%s>'" , sig . Name , sig . Email ) ,
2016-08-29 14:31:12 -04:00
"-m" , "Initial commit" ) ; err != nil {
2015-09-02 09:26:56 -04:00
return fmt . Errorf ( "git commit: %s" , stderr )
2014-03-27 15:24:11 -04:00
}
2017-01-17 00:58:58 -05:00
if _ , stderr , err = process . GetManager ( ) . ExecDir ( - 1 ,
2015-11-20 02:53:54 -05:00
tmpPath , fmt . Sprintf ( "initRepoCommit (git push): %s" , tmpPath ) ,
2014-06-19 01:08:03 -04:00
"git" , "push" , "origin" , "master" ) ; err != nil {
2015-09-02 09:26:56 -04:00
return fmt . Errorf ( "git push: %s" , stderr )
2014-03-27 15:24:11 -04:00
}
2014-03-17 11:56:50 -04:00
return nil
2014-02-14 09:20:57 -05:00
}
2016-11-28 12:27:55 -05:00
// CreateRepoOptions contains the create repository options
2015-08-28 06:33:09 -04: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 ) {
2016-12-22 13:12:23 -05:00
cleanedName := strings . TrimLeft ( name , "./" )
relPath := path . Join ( "options" , tp , cleanedName )
2015-08-28 06:33:09 -04:00
// Use custom file when available.
customPath := path . Join ( setting . CustomPath , relPath )
if com . IsFile ( customPath ) {
return ioutil . ReadFile ( customPath )
}
2016-12-22 13:12:23 -05:00
switch tp {
case "readme" :
return options . Readme ( cleanedName )
case "gitignore" :
return options . Gitignore ( cleanedName )
case "license" :
return options . License ( cleanedName )
2016-12-23 02:18:05 -05:00
case "label" :
return options . Labels ( cleanedName )
2016-12-22 13:12:23 -05:00
default :
return [ ] byte { } , fmt . Errorf ( "Invalid init file type" )
}
2015-08-28 06:33:09 -04:00
}
func prepareRepoCommit ( repo * Repository , tmpDir , repoPath string , opts CreateRepoOptions ) error {
2017-01-04 19:50:34 -05:00
// Clone to temporary path and do the init commit.
2017-01-17 00:58:58 -05:00
_ , stderr , err := process . GetManager ( ) . Exec (
fmt . Sprintf ( "initRepository(git clone): %s" , repoPath ) ,
"git" , "clone" , repoPath , tmpDir ,
)
2015-08-28 06:33:09 -04:00
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-11-30 20:45:55 -05:00
cloneLink := repo . CloneLink ( )
2015-08-28 06:33:09 -04:00
match := map [ string ] string {
2015-08-28 06:36:32 -04:00
"Name" : repo . Name ,
"Description" : repo . Description ,
"CloneURL.SSH" : cloneLink . SSH ,
"CloneURL.HTTPS" : cloneLink . HTTPS ,
2015-08-28 06:33:09 -04: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-10 20:48:58 -04:00
// InitRepository initializes README and .gitignore if needed.
2015-11-27 00:24:24 -05:00
func initRepository ( e Engine , repoPath string , u * User , repo * Repository , opts CreateRepoOptions ) ( err error ) {
2015-07-26 07:22:17 -04:00
// Somehow the directory could exist.
if com . IsExist ( repoPath ) {
return fmt . Errorf ( "initRepository: path already exists: %s" , repoPath )
}
2015-03-18 06:37:44 -04:00
// Init bare new repository.
2015-11-27 00:24:24 -05:00
if err = git . InitRepository ( repoPath , true ) ; err != nil {
return fmt . Errorf ( "InitRepository: %v" , err )
} else if err = createUpdateHook ( repoPath ) ; err != nil {
return fmt . Errorf ( "createUpdateHook: %v" , err )
2014-03-17 17:00:35 -04:00
}
2016-12-28 03:33:21 -05:00
tmpDir := filepath . Join ( os . TempDir ( ) , "gitea-" + repo . Name + "-" + com . ToStr ( time . Now ( ) . Nanosecond ( ) ) )
2015-09-09 12:42:24 -04:00
2015-08-28 06:33:09 -04:00
// Initialize repository according to user's choice.
if opts . AutoInit {
2016-11-30 18:56:15 -05:00
if err := os . MkdirAll ( tmpDir , os . ModePerm ) ; err != nil {
return fmt . Errorf ( "Fail to create dir %s: %v" , tmpDir , err )
}
2015-08-28 06:33:09 -04:00
defer os . RemoveAll ( tmpDir )
2015-03-18 06:37:44 -04:00
2015-08-28 06:33:09 -04:00
if err = prepareRepoCommit ( repo , tmpDir , repoPath , opts ) ; err != nil {
return fmt . Errorf ( "prepareRepoCommit: %v" , err )
2014-03-11 01:32:36 -04:00
}
2014-03-11 00:53:53 -04:00
2015-08-28 06:33:09 -04:00
// Apply changes and commit.
if err = initRepoCommit ( tmpDir , u . NewGitSig ( ) ) ; err != nil {
return fmt . Errorf ( "initRepoCommit: %v" , err )
2014-03-11 01:32:36 -04:00
}
}
2014-03-11 00:18:44 -04:00
2015-08-26 00:26:01 -04: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 06:33:09 -04:00
2015-08-28 07:34:23 -04:00
if ! opts . AutoInit {
repo . IsBare = true
}
2015-08-26 00:26:01 -04:00
repo . DefaultBranch = "master"
if err = updateRepository ( e , repo , false ) ; err != nil {
return fmt . Errorf ( "updateRepository: %v" , err )
}
2015-08-28 06:33:09 -04:00
return nil
2014-03-10 20:48:58 -04:00
}
2016-07-23 06:58:18 -04:00
var (
reservedRepoNames = [ ] string { "." , ".." }
reservedRepoPatterns = [ ] string { "*.git" , "*.wiki" }
)
2016-11-28 12:27:55 -05:00
// IsUsableRepoName returns true when repository is usable
2016-07-23 06:58:18 -04:00
func IsUsableRepoName ( name string ) error {
return isUsableName ( reservedRepoNames , reservedRepoPatterns , name )
}
2015-08-08 05:10:34 -04:00
func createRepository ( e * xorm . Session , u * User , repo * Repository ) ( err error ) {
2016-07-23 06:58:18 -04:00
if err = IsUsableRepoName ( repo . Name ) ; err != nil {
2015-08-08 05:10:34 -04:00
return err
2014-06-19 01:08:03 -04:00
}
2015-08-08 05:10:34 -04:00
has , err := isRepositoryExist ( e , u , repo . Name )
2015-03-26 17:11:47 -04:00
if err != nil {
2015-08-08 05:10:34 -04:00
return fmt . Errorf ( "IsRepositoryExist: %v" , err )
2015-03-26 17:11:47 -04:00
} else if has {
2015-08-08 05:10:34 -04:00
return ErrRepoAlreadyExist { u . Name , repo . Name }
2014-06-19 01:08:03 -04:00
}
2015-08-08 05:10:34 -04:00
if _ , err = e . Insert ( repo ) ; err != nil {
return err
2015-08-29 13:13:24 -04:00
}
u . NumRepos ++
// Remember visibility preference.
u . LastRepoVisibility = repo . IsPrivate
if err = updateUser ( e , u ) ; err != nil {
return fmt . Errorf ( "updateUser: %v" , err )
2015-08-08 05:10:34 -04:00
}
// 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 )
}
}
2016-07-23 13:08:22 -04:00
if err = watchRepo ( e , u . ID , repo . ID , true ) ; err != nil {
2015-08-08 05:10:34 -04: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 06:33:09 -04:00
func CreateRepository ( u * User , opts CreateRepoOptions ) ( _ * Repository , err error ) {
2015-12-10 12:37:53 -05:00
if ! u . CanCreateRepo ( ) {
return nil , ErrReachLimitOfRepo { u . MaxRepoCreation }
}
2014-06-19 01:08:03 -04:00
repo := & Repository {
2016-07-23 13:08:22 -04:00
OwnerID : u . ID ,
2015-12-04 21:34:37 -05:00
Owner : u ,
Name : opts . Name ,
LowerName : strings . ToLower ( opts . Name ) ,
Description : opts . Description ,
IsPrivate : opts . IsPrivate ,
EnableWiki : true ,
EnableIssues : true ,
EnablePulls : true ,
2014-06-19 01:08:03 -04:00
}
2015-02-13 00:58:46 -05:00
sess := x . NewSession ( )
defer sessionRelease ( sess )
if err = sess . Begin ( ) ; err != nil {
return nil , err
}
2015-08-08 05:10:34 -04:00
if err = createRepository ( sess , u , repo ) ; err != nil {
2015-02-13 02:14:57 -05:00
return nil , err
2014-06-19 01:08:03 -04:00
}
2014-07-26 00:24:27 -04:00
// No need for init mirror.
2015-08-28 06:33:09 -04:00
if ! opts . IsMirror {
2015-02-13 00:58:46 -05:00
repoPath := RepoPath ( u . Name , repo . Name )
2015-08-28 06:33:09 -04:00
if err = initRepository ( sess , repoPath , u , repo , opts ) ; err != nil {
2015-02-13 00:58:46 -05: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 05:14:36 -04:00
}
2014-06-19 01:08:03 -04:00
2017-01-17 00:58:58 -05:00
_ , stderr , err := process . GetManager ( ) . ExecDir ( - 1 ,
2015-02-13 00:58:46 -05: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 )
}
2014-06-19 01:08:03 -04:00
}
2015-02-13 00:58:46 -05:00
return repo , sess . Commit ( )
2014-06-19 01:08:03 -04:00
}
2016-07-24 02:32:46 -04:00
func countRepositories ( userID int64 , private bool ) int64 {
sess := x . Where ( "id > 0" )
2015-09-04 05:54:22 -04:00
2016-07-24 02:32:46 -04:00
if userID > 0 {
sess . And ( "owner_id = ?" , userID )
}
if ! private {
sess . And ( "is_private=?" , false )
2015-09-04 05:54:22 -04:00
}
2016-02-10 17:30:24 -05:00
count , err := sess . Count ( new ( Repository ) )
if err != nil {
log . Error ( 4 , "countRepositories: %v" , err )
}
2015-09-04 05:54:22 -04:00
return count
}
2014-07-07 04:15:08 -04:00
// CountRepositories returns number of repositories.
2016-07-24 02:32:46 -04:00
// Argument private only takes effect when it is false,
// set it true to count all repositories.
func CountRepositories ( private bool ) int64 {
return countRepositories ( - 1 , private )
2015-09-04 05:54:22 -04:00
}
2016-07-24 02:32:46 -04:00
// CountUserRepositories returns number of repositories user owns.
// Argument private only takes effect when it is false,
// set it true to count all repositories.
func CountUserRepositories ( userID int64 , private bool ) int64 {
return countRepositories ( userID , private )
2014-07-07 04:15:08 -04:00
}
2015-09-25 19:07:21 -04:00
2016-11-28 12:27:55 -05:00
// Repositories returns all repositories
2016-12-24 09:42:26 -05:00
func Repositories ( opts * SearchRepoOptions ) ( _ [ ] * Repository , err error ) {
if len ( opts . OrderBy ) == 0 {
opts . OrderBy = "id ASC"
}
repos := make ( [ ] * Repository , 0 , opts . PageSize )
return repos , x . Limit ( opts . PageSize , ( opts . Page - 1 ) * opts . PageSize ) . OrderBy ( opts . OrderBy ) . Find ( & repos )
2016-03-11 15:33:12 -05:00
}
2015-09-25 18:38:43 -04:00
// RepositoriesWithUsers returns number of repos in given page.
2016-12-24 09:42:26 -05:00
func RepositoriesWithUsers ( opts * SearchRepoOptions ) ( _ [ ] * Repository , err error ) {
repos , err := Repositories ( opts )
2016-03-11 15:33:12 -05:00
if err != nil {
return nil , fmt . Errorf ( "Repositories: %v" , err )
2014-03-21 01:09:22 -04:00
}
2015-09-25 19:07:21 -04:00
2015-09-25 20:35:56 -04:00
for i := range repos {
if err = repos [ i ] . GetOwner ( ) ; err != nil {
2014-03-21 01:09:22 -04:00
return nil , err
}
}
2014-05-07 16:51:14 -04:00
return repos , nil
2014-03-20 16:04:56 -04:00
}
2014-04-12 20:35:35 -04:00
// RepoPath returns repository path by given user and repository name.
2014-03-20 16:04:56 -04:00
func RepoPath ( userName , repoName string ) string {
2014-03-29 22:13:02 -04:00
return filepath . Join ( UserPath ( userName ) , strings . ToLower ( repoName ) + ".git" )
2014-03-20 16:04:56 -04:00
}
2014-04-04 18:31:09 -04:00
// TransferOwnership transfers all corresponding setting from old user to new one.
2016-08-17 02:06:38 -04:00
func TransferOwnership ( doer * User , newOwnerName string , repo * Repository ) error {
2015-02-23 02:15:53 -05:00
newOwner , err := GetUserByName ( newOwnerName )
2014-04-04 18:31:09 -04:00
if err != nil {
2015-02-24 00:27:22 -05:00
return fmt . Errorf ( "get new owner '%s': %v" , newOwnerName , err )
2014-04-04 18:31:09 -04:00
}
2014-09-12 18:29:58 -04:00
// Check if new owner has repository with same name.
2015-03-26 17:11:47 -04:00
has , err := IsRepositoryExist ( newOwner , repo . Name )
if err != nil {
return fmt . Errorf ( "IsRepositoryExist: %v" , err )
} else if has {
2015-08-08 05:10:34 -04:00
return ErrRepoAlreadyExist { newOwnerName , repo . Name }
2014-09-12 18:29:58 -04:00
}
2014-06-21 00:51:41 -04:00
sess := x . NewSession ( )
2015-02-13 00:58:46 -05:00
defer sessionRelease ( sess )
2014-04-04 18:55:17 -04:00
if err = sess . Begin ( ) ; err != nil {
2015-02-24 00:27:22 -05:00
return fmt . Errorf ( "sess.Begin: %v" , err )
2014-04-04 18:55:17 -04:00
}
2014-09-25 22:36:07 -04:00
owner := repo . Owner
2014-04-04 18:31:09 -04:00
2015-02-24 00:27:22 -05:00
// Note: we have to set value here to make sure recalculate accesses is based on
2016-08-17 02:06:38 -04:00
// new owner.
2016-07-23 13:08:22 -04:00
repo . OwnerID = newOwner . ID
2015-02-23 02:15:53 -05:00
repo . Owner = newOwner
2015-02-24 00:27:22 -05:00
// Update repository.
2015-08-08 10:43:14 -04:00
if _ , err := sess . Id ( repo . ID ) . Update ( repo ) ; err != nil {
2015-02-24 00:27:22 -05:00
return fmt . Errorf ( "update owner: %v" , err )
2014-04-04 18:31:09 -04:00
}
2015-02-24 00:27:22 -05:00
// Remove redundant collaborators.
2015-11-03 13:00:04 -05:00
collaborators , err := repo . getCollaborators ( sess )
2015-02-16 06:25:55 -05:00
if err != nil {
2015-11-03 13:00:04 -05:00
return fmt . Errorf ( "getCollaborators: %v" , err )
2015-02-16 06:25:55 -05:00
}
2015-02-24 00:27:22 -05:00
// Dummy object.
2015-08-08 10:43:14 -04:00
collaboration := & Collaboration { RepoID : repo . ID }
2015-02-16 06:25:55 -05:00
for _ , c := range collaborators {
2016-07-23 13:08:22 -04:00
collaboration . UserID = c . ID
if c . ID == newOwner . ID || newOwner . IsOrgMember ( c . ID ) {
2015-02-24 00:27:22 -05:00
if _ , err = sess . Delete ( collaboration ) ; err != nil {
2016-07-23 13:08:22 -04:00
return fmt . Errorf ( "remove collaborator '%d': %v" , c . ID , err )
2015-02-16 06:25:55 -05:00
}
}
}
2015-02-28 21:44:09 -05: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 10:43:14 -04:00
if ! t . hasRepository ( sess , repo . ID ) {
2015-02-28 21:44:09 -05: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 10:43:14 -04:00
if err = owner . removeOrgRepo ( sess , repo . ID ) ; err != nil {
2015-02-28 21:44:09 -05:00
return fmt . Errorf ( "removeOrgRepo: %v" , err )
}
}
2015-02-23 02:15:53 -05:00
if newOwner . IsOrganization ( ) {
2015-11-03 13:00:04 -05:00
t , err := newOwner . getOwnerTeam ( sess )
2015-02-23 02:15:53 -05:00
if err != nil {
2015-11-03 13:00:04 -05:00
return fmt . Errorf ( "getOwnerTeam: %v" , err )
2015-02-23 02:15:53 -05:00
} else if err = t . addRepository ( sess , repo ) ; err != nil {
2015-02-24 00:27:22 -05: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 02:15:53 -05:00
}
}
2015-02-24 00:27:22 -05:00
// Update repository count.
2016-07-23 13:08:22 -04:00
if _ , err = sess . Exec ( "UPDATE `user` SET num_repos=num_repos+1 WHERE id=?" , newOwner . ID ) ; err != nil {
2015-02-24 00:27:22 -05:00
return fmt . Errorf ( "increase new owner repository count: %v" , err )
2016-07-23 13:08:22 -04:00
} else if _ , err = sess . Exec ( "UPDATE `user` SET num_repos=num_repos-1 WHERE id=?" , owner . ID ) ; err != nil {
2015-02-24 00:27:22 -05:00
return fmt . Errorf ( "decrease old owner repository count: %v" , err )
}
2016-07-23 13:08:22 -04:00
if err = watchRepo ( sess , newOwner . ID , repo . ID , true ) ; err != nil {
2015-02-24 00:27:22 -05:00
return fmt . Errorf ( "watchRepo: %v" , err )
2016-08-17 02:06:38 -04:00
} else if err = transferRepoAction ( sess , doer , owner , repo ) ; err != nil {
2015-02-24 00:27:22 -05:00
return fmt . Errorf ( "transferRepoAction: %v" , err )
2014-07-04 22:43:39 -04:00
}
2016-02-14 19:42:38 -05:00
// Rename remote repository to new path and delete local copy.
2016-11-30 18:56:15 -05:00
dir := UserPath ( newOwner . Name )
if err := os . MkdirAll ( dir , os . ModePerm ) ; err != nil {
return fmt . Errorf ( "Fail to create dir %s: %v" , dir , err )
}
2015-02-23 02:15:53 -05:00
if err = os . Rename ( RepoPath ( owner . Name , repo . Name ) , RepoPath ( newOwner . Name , repo . Name ) ) ; err != nil {
2015-11-30 20:45:55 -05:00
return fmt . Errorf ( "rename repository directory: %v" , err )
2015-12-03 02:08:25 -05:00
}
2016-02-14 19:42:38 -05:00
RemoveAllWithNotice ( "Delete repository local copy" , repo . LocalCopyPath ( ) )
2015-12-03 02:08:25 -05:00
2016-02-14 19:42:38 -05:00
// Rename remote wiki repository to new path and delete local copy.
2015-12-03 02:08:25 -05:00
wikiPath := WikiPath ( owner . Name , repo . Name )
if com . IsExist ( wikiPath ) {
2016-02-11 19:26:51 -05:00
RemoveAllWithNotice ( "Delete repository wiki local copy" , repo . LocalWikiPath ( ) )
2015-12-03 02:08:25 -05:00
if err = os . Rename ( wikiPath , WikiPath ( newOwner . Name , repo . Name ) ) ; err != nil {
return fmt . Errorf ( "rename repository wiki: %v" , err )
}
2014-04-04 18:55:17 -04:00
}
2015-02-13 00:58:46 -05:00
return sess . Commit ( )
2014-04-04 18:31:09 -04:00
}
2014-04-03 15:50:55 -04:00
// ChangeRepositoryName changes all corresponding setting from old repository name to new one.
2015-03-26 17:11:47 -04:00
func ChangeRepositoryName ( u * User , oldRepoName , newRepoName string ) ( err error ) {
2015-01-01 23:47:33 -05:00
oldRepoName = strings . ToLower ( oldRepoName )
2014-12-12 01:29:36 -05:00
newRepoName = strings . ToLower ( newRepoName )
2016-07-23 06:58:18 -04:00
if err = IsUsableRepoName ( newRepoName ) ; err != nil {
2015-03-26 17:11:47 -04:00
return err
}
has , err := IsRepositoryExist ( u , newRepoName )
if err != nil {
return fmt . Errorf ( "IsRepositoryExist: %v" , err )
} else if has {
2015-08-08 05:10:34 -04:00
return ErrRepoAlreadyExist { u . Name , newRepoName }
2014-08-24 09:09:05 -04:00
}
2016-07-23 13:08:22 -04:00
repo , err := GetRepositoryByName ( u . ID , oldRepoName )
2016-02-05 14:11:53 -05:00
if err != nil {
return fmt . Errorf ( "GetRepositoryByName: %v" , err )
}
2014-04-03 15:50:55 -04:00
// Change repository directory name.
2016-02-05 14:11:53 -05:00
if err = os . Rename ( repo . RepoPath ( ) , RepoPath ( u . Name , newRepoName ) ) ; err != nil {
2015-11-30 20:45:55 -05:00
return fmt . Errorf ( "rename repository directory: %v" , err )
}
2015-12-03 01:59:32 -05:00
2016-02-05 14:11:53 -05:00
wikiPath := repo . WikiPath ( )
2015-12-03 01:59:32 -05:00
if com . IsExist ( wikiPath ) {
2015-12-03 02:08:25 -05:00
if err = os . Rename ( wikiPath , WikiPath ( u . Name , newRepoName ) ) ; err != nil {
return fmt . Errorf ( "rename repository wiki: %v" , err )
}
2016-02-05 14:11:53 -05:00
RemoveAllWithNotice ( "Delete repository wiki local copy" , repo . LocalWikiPath ( ) )
2015-12-03 01:59:32 -05:00
}
2015-12-03 02:08:25 -05:00
return nil
2014-04-03 15:50:55 -04:00
}
2015-09-01 11:43:53 -04:00
func getRepositoriesByForkID ( e Engine , forkID int64 ) ( [ ] * Repository , error ) {
repos := make ( [ ] * Repository , 0 , 10 )
2016-11-10 10:16:32 -05:00
return repos , e .
Where ( "fork_id=?" , forkID ) .
Find ( & repos )
2015-09-01 11:43:53 -04:00
}
// GetRepositoriesByForkID returns all repositories with given fork ID.
func GetRepositoriesByForkID ( forkID int64 ) ( [ ] * Repository , error ) {
return getRepositoriesByForkID ( x , forkID )
}
2015-03-16 04:52:11 -04:00
func updateRepository ( e Engine , repo * Repository , visibilityChanged bool ) ( err error ) {
2014-04-03 15:50:55 -04:00
repo . LowerName = strings . ToLower ( repo . Name )
2014-03-22 16: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 04:52:11 -04:00
2015-08-08 10:43:14 -04:00
if _ , err = e . Id ( repo . ID ) . AllCols ( ) . Update ( repo ) ; err != nil {
2015-03-16 04:52:11 -04:00
return fmt . Errorf ( "update: %v" , err )
}
if visibilityChanged {
if err = repo . getOwner ( e ) ; err != nil {
return fmt . Errorf ( "getOwner: %v" , err )
}
2015-10-08 22:38:42 -04:00
if repo . Owner . IsOrganization ( ) {
2017-01-04 19:50:34 -05:00
// Organization repository need to recalculate access table when visibility is changed.
2015-10-08 22:38:42 -04:00
if err = repo . recalculateTeamAccesses ( e , 0 ) ; err != nil {
return fmt . Errorf ( "recalculateTeamAccesses: %v" , err )
}
2015-03-16 04:52:11 -04:00
}
2015-09-01 11:43:53 -04:00
2016-08-10 23:08:09 -04:00
// Create/Remove git-daemon-export-ok for git-daemon...
daemonExportFile := path . Join ( repo . RepoPath ( ) , ` git-daemon-export-ok ` )
if repo . IsPrivate && com . IsExist ( daemonExportFile ) {
if err = os . Remove ( daemonExportFile ) ; err != nil {
log . Error ( 4 , "Failed to remove %s: %v" , daemonExportFile , err )
}
} else if ! repo . IsPrivate && ! com . IsExist ( daemonExportFile ) {
if f , err := os . Create ( daemonExportFile ) ; err != nil {
log . Error ( 4 , "Failed to create %s: %v" , daemonExportFile , err )
} else {
f . Close ( )
}
}
2015-09-01 11:43:53 -04:00
forkRepos , err := getRepositoriesByForkID ( e , repo . ID )
if err != nil {
return fmt . Errorf ( "getRepositoriesByForkID: %v" , err )
}
for i := range forkRepos {
forkRepos [ i ] . IsPrivate = repo . IsPrivate
if err = updateRepository ( e , forkRepos [ i ] , true ) ; err != nil {
return fmt . Errorf ( "updateRepository[%d]: %v" , forkRepos [ i ] . ID , err )
}
}
2015-03-16 04:52:11 -04:00
}
return nil
2014-03-22 04:44:57 -04:00
}
2016-11-28 12:27:55 -05:00
// UpdateRepository updates a repository
2015-03-16 04:52:11 -04:00
func UpdateRepository ( repo * Repository , visibilityChanged bool ) ( err error ) {
sess := x . NewSession ( )
defer sessionRelease ( sess )
if err = sess . Begin ( ) ; err != nil {
return err
}
2017-01-13 21:07:04 -05:00
if err = updateRepository ( sess , repo , visibilityChanged ) ; err != nil {
2015-03-16 04:52:11 -04:00
return fmt . Errorf ( "updateRepository: %v" , err )
}
return sess . Commit ( )
2015-02-13 00:58:46 -05:00
}
2014-12-06 20:22:48 -05:00
// DeleteRepository deletes a repository for a user or organization.
2015-09-01 11:43:53 -04:00
func DeleteRepository ( uid , repoID int64 ) error {
2015-08-08 10:43:14 -04:00
repo := & Repository { ID : repoID , OwnerID : uid }
2014-06-21 00:51:41 -04:00
has , err := x . Get ( repo )
2014-03-20 16:04:56 -04:00
if err != nil {
return err
} else if ! has {
2015-03-16 04:04:27 -04:00
return ErrRepoNotExist { repoID , uid , "" }
2014-03-20 16:04:56 -04:00
}
2014-08-27 04:39:36 -04:00
// In case is a organization.
2015-08-08 10:43:14 -04:00
org , err := GetUserByID ( uid )
2014-08-27 04:39:36 -04:00
if err != nil {
return err
}
if org . IsOrganization ( ) {
if err = org . GetTeams ( ) ; err != nil {
return err
}
}
2014-06-21 00:51:41 -04:00
sess := x . NewSession ( )
2015-02-13 02:14:57 -05:00
defer sessionRelease ( sess )
2014-04-04 18:31:09 -04:00
if err = sess . Begin ( ) ; err != nil {
2014-03-20 16:04:56 -04:00
return err
}
2014-06-28 00:40:07 -04:00
2014-08-27 04:39:36 -04:00
if org . IsOrganization ( ) {
for _ , t := range org . Teams {
2015-02-23 02:15:53 -05:00
if ! t . hasRepository ( sess , repoID ) {
2014-08-27 04:39:36 -04:00
continue
2015-02-28 21:44:09 -05:00
} else if err = t . removeRepository ( sess , repo , false ) ; err != nil {
2014-08-27 04:39:36 -04:00
return err
}
}
}
2015-11-30 20:45:55 -05:00
if err = deleteBeans ( sess ,
& Repository { ID : repoID } ,
& Access { RepoID : repo . ID } ,
& Action { RepoID : repo . ID } ,
& Watch { RepoID : repoID } ,
2015-11-30 20:50:40 -05:00
& Star { RepoID : repoID } ,
2015-11-30 20:45:55 -05:00
& Mirror { RepoID : repoID } ,
& IssueUser { RepoID : repoID } ,
& Milestone { RepoID : repoID } ,
& Release { RepoID : repoID } ,
& Collaboration { RepoID : repoID } ,
& PullRequest { BaseRepoID : repoID } ,
) ; err != nil {
return fmt . Errorf ( "deleteBeans: %v" , err )
2014-05-14 13:04:57 -04:00
}
2015-08-13 15:07:20 -04:00
// Delete comments and attachments.
2015-02-23 02:15:53 -05:00
issues := make ( [ ] * Issue , 0 , 25 )
2015-08-13 15:07:20 -04:00
attachmentPaths := make ( [ ] string , 0 , len ( issues ) )
2016-11-10 10:16:32 -05:00
if err = sess .
Where ( "repo_id=?" , repoID ) .
Find ( & issues ) ; err != nil {
2015-02-23 02:15:53 -05:00
return err
}
for i := range issues {
2015-08-13 04:07:11 -04:00
if _ , err = sess . Delete ( & Comment { IssueID : issues [ i ] . ID } ) ; err != nil {
2014-05-14 13:04:57 -04:00
return err
}
2015-08-13 15:07:20 -04:00
attachments := make ( [ ] * Attachment , 0 , 5 )
2016-11-10 10:16:32 -05:00
if err = sess .
Where ( "issue_id=?" , issues [ i ] . ID ) .
Find ( & attachments ) ; err != nil {
2015-08-13 15:07:20 -04:00
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 13:04:57 -04:00
}
2015-08-05 08:23:08 -04:00
if _ , err = sess . Delete ( & Issue { RepoID : repoID } ) ; err != nil {
2014-05-14 09:23:33 -04:00
return err
}
2014-10-19 01:35:24 -04:00
2014-10-13 15:23:30 -04:00
if repo . IsFork {
2015-08-08 10:43:14 -04:00
if _ , err = sess . Exec ( "UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?" , repo . ForkID ) ; err != nil {
2015-09-01 11:43:53 -04:00
return fmt . Errorf ( "decrease fork count: %v" , err )
2014-10-19 01:35:24 -04:00
}
2014-10-13 15:23:30 -04:00
}
2014-04-12 20:35:35 -04:00
2015-02-23 02:15:53 -05:00
if _ , err = sess . Exec ( "UPDATE `user` SET num_repos=num_repos-1 WHERE id=?" , uid ) ; err != nil {
2014-04-12 20:35:35 -04:00
return err
}
2014-10-08 18:29:18 -04:00
// Remove repository files.
2015-11-26 17:33:45 -05:00
repoPath := repo . repoPath ( sess )
2016-02-05 14:11:53 -05:00
RemoveAllWithNotice ( "Delete repository files" , repoPath )
2015-11-30 20:45:55 -05:00
2016-03-03 15:38:25 -05:00
repo . DeleteWiki ( )
2015-02-13 02:14:57 -05:00
2015-08-13 15:07:20 -04:00
// Remove attachment files.
for i := range attachmentPaths {
2016-02-05 14:11:53 -05:00
RemoveAllWithNotice ( "Delete attachment" , attachmentPaths [ i ] )
2015-08-13 15:07:20 -04:00
}
2016-12-25 20:16:37 -05:00
// Remove LFS objects
var lfsObjects [ ] * LFSMetaObject
if err = sess . Where ( "repository_id=?" , repoID ) . Find ( & lfsObjects ) ; err != nil {
return err
}
for _ , v := range lfsObjects {
count , err := sess . Count ( & LFSMetaObject { Oid : v . Oid } )
if err != nil {
return err
}
if count > 1 {
continue
}
oidPath := filepath . Join ( v . Oid [ 0 : 2 ] , v . Oid [ 2 : 4 ] , v . Oid [ 4 : len ( v . Oid ) ] )
err = os . Remove ( filepath . Join ( setting . LFS . ContentPath , oidPath ) )
if err != nil {
return err
}
}
if _ , err := sess . Delete ( & LFSMetaObject { RepositoryID : repoID } ) ; err != nil {
return err
}
2015-09-01 11:43:53 -04:00
if err = sess . Commit ( ) ; err != nil {
return fmt . Errorf ( "Commit: %v" , err )
}
if repo . NumForks > 0 {
2016-07-09 01:42:05 -04:00
if _ , err = x . Exec ( "UPDATE `repository` SET fork_id=0,is_fork=? WHERE fork_id=?" , false , repo . ID ) ; err != nil {
log . Error ( 4 , "reset 'fork_id' and 'is_fork': %v" , err )
}
2015-09-01 11:43:53 -04:00
}
return nil
2014-03-20 16:04:56 -04:00
}
2014-07-23 07: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
}
2016-07-23 13:08:22 -04:00
return GetRepositoryByName ( user . ID , repoName )
2014-07-23 07:48:06 -04:00
}
2014-03-17 14:03:58 -04:00
// GetRepositoryByName returns the repository by given name under user if exists.
2016-08-17 02:06:38 -04:00
func GetRepositoryByName ( ownerID int64 , name string ) ( * Repository , error ) {
2014-03-12 22:27:11 -04:00
repo := & Repository {
2016-08-17 02:06:38 -04:00
OwnerID : ownerID ,
LowerName : strings . ToLower ( name ) ,
2014-03-12 22:27:11 -04:00
}
2014-06-21 00:51:41 -04:00
has , err := x . Get ( repo )
2014-03-12 22:27:11 -04:00
if err != nil {
return nil , err
} else if ! has {
2016-08-17 02:06:38 -04:00
return nil , ErrRepoNotExist { 0 , ownerID , name }
2014-03-12 22:27:11 -04:00
}
return repo , err
}
2015-08-08 10:43:14 -04:00
func getRepositoryByID ( e Engine , id int64 ) ( * Repository , error ) {
2015-03-16 04:04:27 -04:00
repo := new ( Repository )
2015-02-13 00:58:46 -05:00
has , err := e . Id ( id ) . Get ( repo )
2014-03-12 22:27:11 -04:00
if err != nil {
return nil , err
} else if ! has {
2015-03-16 04:04:27 -04:00
return nil , ErrRepoNotExist { id , 0 , "" }
2014-03-12 22:27:11 -04:00
}
2014-05-05 21:36:08 -04:00
return repo , nil
2014-03-12 22:27:11 -04:00
}
2015-08-08 10:43:14 -04:00
// GetRepositoryByID returns the repository by given id if exists.
func GetRepositoryByID ( id int64 ) ( * Repository , error ) {
return getRepositoryByID ( x , id )
2015-02-13 00:58:46 -05:00
}
2016-07-24 02:32:46 -04:00
// GetUserRepositories returns a list of repositories of given user.
func GetUserRepositories ( userID int64 , private bool , page , pageSize int ) ( [ ] * Repository , error ) {
2016-11-10 10:16:32 -05:00
sess := x .
Where ( "owner_id = ?" , userID ) .
Desc ( "updated_unix" )
2014-04-13 18:12:07 -04:00
if ! private {
2016-07-24 02:32:46 -04:00
sess . And ( "is_private=?" , false )
2014-04-13 18:12:07 -04:00
}
2016-07-24 02:32:46 -04:00
if page <= 0 {
page = 1
}
sess . Limit ( pageSize , ( page - 1 ) * pageSize )
repos := make ( [ ] * Repository , 0 , pageSize )
return repos , sess . Find ( & repos )
}
2016-11-28 12:27:55 -05:00
// GetUserMirrorRepositories returns a list of mirror repositories of given user.
2016-07-24 02:32:46 -04:00
func GetUserMirrorRepositories ( userID int64 ) ( [ ] * Repository , error ) {
repos := make ( [ ] * Repository , 0 , 10 )
2016-11-10 10:16:32 -05:00
return repos , x .
Where ( "owner_id = ?" , userID ) .
And ( "is_mirror = ?" , true ) .
Find ( & repos )
2014-02-14 09:20:57 -05:00
}
2014-04-14 04:11:33 -04:00
// GetRecentUpdatedRepositories returns the list of repositories that are recently updated.
2016-12-24 09:42:26 -05:00
func GetRecentUpdatedRepositories ( opts * SearchRepoOptions ) ( repos [ ] * Repository , err error ) {
if len ( opts . OrderBy ) == 0 {
opts . OrderBy = "updated_unix DESC"
}
sess := x . Where ( "is_private=?" , false ) .
Limit ( opts . PageSize , ( opts . Page - 1 ) * opts . PageSize ) .
Limit ( opts . PageSize )
if opts . Searcher != nil {
sess . Or ( "owner_id = ?" , opts . Searcher . ID )
err := opts . Searcher . GetOrganizations ( true )
if err != nil {
return nil , fmt . Errorf ( "Organization: %v" , err )
}
for _ , org := range opts . Searcher . Orgs {
sess . Or ( "owner_id = ?" , org . ID )
}
}
return repos , sess .
OrderBy ( opts . OrderBy ) .
2016-11-10 10:16:32 -05:00
Find ( & repos )
2014-04-14 04:11:33 -04:00
}
2015-09-06 08:54:08 -04:00
func getRepositoryCount ( e Engine , u * User ) ( int64 , error ) {
2016-07-23 13:08:22 -04:00
return x . Count ( & Repository { OwnerID : u . ID } )
2015-09-06 08:54:08 -04:00
}
2014-04-14 04:11:33 -04:00
// GetRepositoryCount returns the total number of repositories of user.
2015-08-08 10:43:14 -04:00
func GetRepositoryCount ( u * User ) ( int64 , error ) {
2015-09-06 08:54:08 -04:00
return getRepositoryCount ( x , u )
2014-03-11 02:17:05 -04:00
}
2016-11-28 12:27:55 -05:00
// SearchRepoOptions holds the search options
2016-03-11 15:33:12 -05:00
type SearchRepoOptions struct {
Keyword string
OwnerID int64
2016-12-24 09:42:26 -05:00
Searcher * User //ID of the person who's seeking
2016-03-11 15:33:12 -05:00
OrderBy string
Private bool // Include private repositories in results
Page int
PageSize int // Can be smaller than or equal to setting.ExplorePagingNum
2014-10-25 07:50:19 -04:00
}
2016-03-11 15:33:12 -05:00
// SearchRepositoryByName takes keyword and part of repository name to search,
// it returns results in given range and number of total results.
func SearchRepositoryByName ( opts * SearchRepoOptions ) ( repos [ ] * Repository , _ int64 , _ error ) {
if len ( opts . Keyword ) == 0 {
return repos , 0 , nil
}
opts . Keyword = strings . ToLower ( opts . Keyword )
if opts . Page <= 0 {
opts . Page = 1
2014-08-26 06:11:15 -04:00
}
2016-03-11 15:33:12 -05:00
repos = make ( [ ] * Repository , 0 , opts . PageSize )
2014-08-26 06:11:15 -04:00
2016-03-11 15:33:12 -05:00
// Append conditions
2016-03-16 16:55:19 -04:00
sess := x . Where ( "LOWER(lower_name) LIKE ?" , "%" + opts . Keyword + "%" )
2016-03-11 15:33:12 -05:00
if opts . OwnerID > 0 {
sess . And ( "owner_id = ?" , opts . OwnerID )
2014-08-26 06:11:15 -04:00
}
2016-03-11 15:33:12 -05:00
if ! opts . Private {
2015-09-09 18:07:10 -04:00
sess . And ( "is_private=?" , false )
2014-10-25 07:50:19 -04:00
}
2016-03-11 15:33:12 -05:00
2016-12-24 09:42:26 -05:00
if opts . Searcher != nil {
sess . Or ( "owner_id = ?" , opts . Searcher . ID )
err := opts . Searcher . GetOrganizations ( true )
if err != nil {
return nil , 0 , fmt . Errorf ( "Organization: %v" , err )
}
for _ , org := range opts . Searcher . Orgs {
sess . Or ( "owner_id = ?" , org . ID )
}
}
if len ( opts . OrderBy ) == 0 {
opts . OrderBy = "name ASC"
}
2016-03-11 15:33:12 -05:00
var countSess xorm . Session
countSess = * sess
count , err := countSess . Count ( new ( Repository ) )
if err != nil {
return nil , 0 , fmt . Errorf ( "Count: %v" , err )
}
2016-12-24 09:42:26 -05:00
return repos , count , sess .
Limit ( opts . PageSize , ( opts . Page - 1 ) * opts . PageSize ) .
OrderBy ( opts . OrderBy ) .
Find ( & repos )
2014-08-26 06:11:15 -04:00
}
2014-11-18 19:05:33 -05:00
// DeleteRepositoryArchives deletes all repositories' archives.
func DeleteRepositoryArchives ( ) error {
2016-11-10 10:16:32 -05:00
return x .
Where ( "id > 0" ) .
Iterate ( new ( Repository ) ,
func ( idx int , bean interface { } ) error {
repo := bean . ( * Repository )
return os . RemoveAll ( filepath . Join ( repo . RepoPath ( ) , "archives" ) )
} )
2014-11-18 19:05:33 -05:00
}
2016-02-04 12:51:00 -05:00
func gatherMissingRepoRecords ( ) ( [ ] * Repository , error ) {
repos := make ( [ ] * Repository , 0 , 10 )
2016-11-10 10:16:32 -05:00
if err := x .
Where ( "id > 0" ) .
Iterate ( new ( Repository ) ,
func ( idx int , bean interface { } ) error {
repo := bean . ( * Repository )
if ! com . IsDir ( repo . RepoPath ( ) ) {
repos = append ( repos , repo )
}
return nil
} ) ; err != nil {
2016-02-04 12:51:00 -05:00
if err2 := CreateRepositoryNotice ( fmt . Sprintf ( "gatherMissingRepoRecords: %v" , err ) ) ; err2 != nil {
return nil , fmt . Errorf ( "CreateRepositoryNotice: %v" , err )
2015-11-18 15:37:48 -05:00
}
2016-02-04 12:51:00 -05:00
}
return repos , nil
}
// DeleteMissingRepositories deletes all repository records that lost Git files.
func DeleteMissingRepositories ( ) error {
repos , err := gatherMissingRepoRecords ( )
if err != nil {
return fmt . Errorf ( "gatherMissingRepoRecords: %v" , err )
2015-11-18 15:37:48 -05:00
}
if len ( repos ) == 0 {
return nil
}
for _ , repo := range repos {
2015-11-21 19:11:57 -05:00
log . Trace ( "Deleting %d/%d..." , repo . OwnerID , repo . ID )
2015-11-18 15:37:48 -05:00
if err := DeleteRepository ( repo . OwnerID , repo . ID ) ; err != nil {
if err2 := CreateRepositoryNotice ( fmt . Sprintf ( "DeleteRepository [%d]: %v" , repo . ID , err ) ) ; err2 != nil {
2016-02-04 12:51:00 -05:00
return fmt . Errorf ( "CreateRepositoryNotice: %v" , err )
}
}
}
return nil
}
// ReinitMissingRepositories reinitializes all repository records that lost Git files.
func ReinitMissingRepositories ( ) error {
repos , err := gatherMissingRepoRecords ( )
if err != nil {
return fmt . Errorf ( "gatherMissingRepoRecords: %v" , err )
}
if len ( repos ) == 0 {
return nil
}
for _ , repo := range repos {
log . Trace ( "Initializing %d/%d..." , repo . OwnerID , repo . ID )
if err := git . InitRepository ( repo . RepoPath ( ) , true ) ; err != nil {
if err2 := CreateRepositoryNotice ( fmt . Sprintf ( "InitRepository [%d]: %v" , repo . ID , err ) ) ; err2 != nil {
return fmt . Errorf ( "CreateRepositoryNotice: %v" , err )
2015-11-18 15:37:48 -05:00
}
}
}
return nil
}
2015-02-08 21:26:14 -05:00
// RewriteRepositoryUpdateHook rewrites all repositories' update hook.
func RewriteRepositoryUpdateHook ( ) error {
2016-11-10 10:16:32 -05:00
return x .
Where ( "id > 0" ) .
Iterate ( new ( Repository ) ,
func ( idx int , bean interface { } ) error {
repo := bean . ( * Repository )
return createUpdateHook ( repo . RepoPath ( ) )
} )
2015-02-08 21:26:14 -05:00
}
2015-11-20 00:47:35 -05:00
// Prevent duplicate running tasks.
2016-08-14 20:44:20 -04:00
var taskStatusTable = sync . NewStatusTable ( )
2015-11-20 00:47:35 -05:00
const (
2016-11-07 11:41:28 -05:00
mirrorUpdate = "mirror_update"
gitFsck = "git_fsck"
checkRepos = "check_repos"
2014-12-04 23:07:51 -05:00
)
2014-11-30 02:26:29 -05:00
// GitFsck calls 'git fsck' to check repository health.
func GitFsck ( ) {
2016-11-07 11:41:28 -05:00
if taskStatusTable . IsRunning ( gitFsck ) {
2014-12-04 23:07:51 -05:00
return
}
2016-11-07 11:41:28 -05:00
taskStatusTable . Start ( gitFsck )
defer taskStatusTable . Stop ( gitFsck )
2014-12-04 23:07:51 -05:00
2015-08-17 16:03:11 -04:00
log . Trace ( "Doing: GitFsck" )
2016-11-10 10:16:32 -05:00
if err := x .
Where ( "id>0" ) .
Iterate ( new ( Repository ) ,
func ( idx int , bean interface { } ) error {
repo := bean . ( * Repository )
repoPath := repo . RepoPath ( )
if err := git . Fsck ( repoPath , setting . Cron . RepoHealthCheck . Timeout , setting . Cron . RepoHealthCheck . Args ... ) ; err != nil {
desc := fmt . Sprintf ( "Fail to health check repository (%s): %v" , repoPath , err )
log . Warn ( desc )
if err = CreateRepositoryNotice ( desc ) ; err != nil {
log . Error ( 4 , "CreateRepositoryNotice: %v" , err )
}
2014-11-30 02:26:29 -05:00
}
2016-11-10 10:16:32 -05:00
return nil
} ) ; err != nil {
2015-08-17 16:03:11 -04:00
log . Error ( 4 , "GitFsck: %v" , err )
2014-11-30 02:26:29 -05:00
}
}
2016-11-28 12:27:55 -05:00
// GitGcRepos calls 'git gc' to remove unnecessary files and optimize the local repository
2014-11-30 02:26:29 -05:00
func GitGcRepos ( ) error {
2016-08-09 20:24:32 -04:00
args := append ( [ ] string { "gc" } , setting . Git . GCArgs ... )
2016-11-10 10:16:32 -05: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
}
2017-01-17 00:58:58 -05:00
_ , stderr , err := process . GetManager ( ) . ExecDir (
2016-11-10 10:16:32 -05:00
time . Duration ( setting . Git . Timeout . GC ) * time . Second ,
RepoPath ( repo . Owner . Name , repo . Name ) , "Repository garbage collection" ,
"git" , args ... )
if err != nil {
return fmt . Errorf ( "%v: %v" , err , stderr )
}
return nil
} )
2014-11-30 02:26:29 -05:00
}
2015-09-01 11:43:53 -04:00
type repoChecker struct {
querySQL , correctSQL string
desc string
}
2015-08-17 16:03:11 -04:00
2015-09-01 11:43:53 -04:00
func repoStatsCheck ( checker * repoChecker ) {
results , err := x . Query ( checker . querySQL )
2015-08-17 16:03:11 -04:00
if err != nil {
2015-09-01 11:43:53 -04:00
log . Error ( 4 , "Select %s: %v" , checker . desc , err )
2015-08-17 16:03:11 -04:00
return
}
2015-09-01 11:43:53 -04:00
for _ , result := range results {
id := com . StrTo ( result [ "id" ] ) . MustInt64 ( )
log . Trace ( "Updating %s: %d" , checker . desc , id )
_ , err = x . Exec ( checker . correctSQL , id , id )
2015-08-17 16:03:11 -04:00
if err != nil {
2015-09-01 11:43:53 -04:00
log . Error ( 4 , "Update %s[%d]: %v" , checker . desc , id , err )
2015-08-17 16:03:11 -04:00
}
}
2015-09-01 11:43:53 -04:00
}
2015-08-17 16:03:11 -04:00
2016-11-28 12:27:55 -05:00
// CheckRepoStats checks the repository stats
2015-09-01 11:43:53 -04:00
func CheckRepoStats ( ) {
2016-11-07 11:41:28 -05:00
if taskStatusTable . IsRunning ( checkRepos ) {
2015-08-17 16:03:11 -04:00
return
2015-03-21 08:55:00 -04:00
}
2016-11-07 11:41:28 -05:00
taskStatusTable . Start ( checkRepos )
defer taskStatusTable . Stop ( checkRepos )
2015-03-21 08:55:00 -04:00
2015-09-01 11:43:53 -04:00
log . Trace ( "Doing: CheckRepoStats" )
2015-08-29 13:13:24 -04:00
2015-09-01 11:43:53 -04:00
checkers := [ ] * repoChecker {
// Repository.NumWatches
{
"SELECT repo.id FROM `repository` repo WHERE repo.num_watches!=(SELECT COUNT(*) FROM `watch` WHERE repo_id=repo.id)" ,
"UPDATE `repository` SET num_watches=(SELECT COUNT(*) FROM `watch` WHERE repo_id=?) WHERE id=?" ,
"repository count 'num_watches'" ,
} ,
// Repository.NumStars
{
"SELECT repo.id FROM `repository` repo WHERE repo.num_stars!=(SELECT COUNT(*) FROM `star` WHERE repo_id=repo.id)" ,
"UPDATE `repository` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE repo_id=?) WHERE id=?" ,
"repository count 'num_stars'" ,
} ,
// Label.NumIssues
{
"SELECT label.id FROM `label` WHERE label.num_issues!=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=label.id)" ,
"UPDATE `label` SET num_issues=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=?) WHERE id=?" ,
"label count 'num_issues'" ,
} ,
// User.NumRepos
{
"SELECT `user`.id FROM `user` WHERE `user`.num_repos!=(SELECT COUNT(*) FROM `repository` WHERE owner_id=`user`.id)" ,
"UPDATE `user` SET num_repos=(SELECT COUNT(*) FROM `repository` WHERE owner_id=?) WHERE id=?" ,
"user count 'num_repos'" ,
} ,
2015-10-29 20:40:57 -04:00
// Issue.NumComments
{
"SELECT `issue`.id FROM `issue` WHERE `issue`.num_comments!=(SELECT COUNT(*) FROM `comment` WHERE issue_id=`issue`.id AND type=0)" ,
"UPDATE `issue` SET num_comments=(SELECT COUNT(*) FROM `comment` WHERE issue_id=? AND type=0) WHERE id=?" ,
"issue count 'num_comments'" ,
} ,
2015-09-01 11:43:53 -04:00
}
for i := range checkers {
repoStatsCheck ( checkers [ i ] )
}
2016-05-27 21:23:39 -04:00
// ***** START: Repository.NumClosedIssues *****
desc := "repository count 'num_closed_issues'"
2016-06-26 13:53:30 -04:00
results , err := x . Query ( "SELECT repo.id FROM `repository` repo WHERE repo.num_closed_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)" , true , false )
2016-05-27 21:23:39 -04:00
if err != nil {
log . Error ( 4 , "Select %s: %v" , desc , err )
} else {
for _ , result := range results {
id := com . StrTo ( result [ "id" ] ) . MustInt64 ( )
log . Trace ( "Updating %s: %d" , desc , id )
2016-06-26 13:53:30 -04:00
_ , err = x . Exec ( "UPDATE `repository` SET num_closed_issues=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_closed=? AND is_pull=?) WHERE id=?" , id , true , false , id )
2016-05-27 21:23:39 -04:00
if err != nil {
log . Error ( 4 , "Update %s[%d]: %v" , desc , id , err )
}
}
}
// ***** END: Repository.NumClosedIssues *****
// FIXME: use checker when stop supporting old fork repo format.
2015-09-01 11:43:53 -04:00
// ***** START: Repository.NumForks *****
2016-05-27 21:23:39 -04:00
results , err = x . Query ( "SELECT repo.id FROM `repository` repo WHERE repo.num_forks!=(SELECT COUNT(*) FROM `repository` WHERE fork_id=repo.id)" )
2015-08-29 13:13:24 -04:00
if err != nil {
2015-09-01 11:43:53 -04:00
log . Error ( 4 , "Select repository count 'num_forks': %v" , err )
} else {
for _ , result := range results {
id := com . StrTo ( result [ "id" ] ) . MustInt64 ( )
log . Trace ( "Updating repository count 'num_forks': %d" , id )
repo , err := GetRepositoryByID ( id )
if err != nil {
log . Error ( 4 , "GetRepositoryByID[%d]: %v" , id , err )
continue
}
rawResult , err := x . Query ( "SELECT COUNT(*) FROM `repository` WHERE fork_id=?" , repo . ID )
if err != nil {
log . Error ( 4 , "Select count of forks[%d]: %v" , repo . ID , err )
continue
}
repo . NumForks = int ( parseCountResult ( rawResult ) )
if err = UpdateRepository ( repo , false ) ; err != nil {
log . Error ( 4 , "UpdateRepository[%d]: %v" , id , err )
continue
}
2015-08-29 13:13:24 -04:00
}
}
2015-09-01 11:43:53 -04:00
// ***** END: Repository.NumForks *****
2015-03-21 08:55:00 -04:00
}
2016-11-28 12:27:55 -05:00
// RepositoryList contains a list of repositories
2016-07-24 02:32:46 -04:00
type RepositoryList [ ] * Repository
func ( repos RepositoryList ) loadAttributes ( e Engine ) error {
if len ( repos ) == 0 {
return nil
}
// Load owners.
set := make ( map [ int64 ] * User )
for i := range repos {
set [ repos [ i ] . OwnerID ] = nil
}
userIDs := make ( [ ] int64 , 0 , len ( set ) )
for userID := range set {
userIDs = append ( userIDs , userID )
}
users := make ( [ ] * User , 0 , len ( userIDs ) )
2016-11-10 10:16:32 -05:00
if err := e .
Where ( "id > 0" ) .
In ( "id" , userIDs ) .
Find ( & users ) ; err != nil {
2016-07-24 02:32:46 -04:00
return fmt . Errorf ( "find users: %v" , err )
}
for i := range users {
set [ users [ i ] . ID ] = users [ i ]
}
for i := range repos {
repos [ i ] . Owner = set [ repos [ i ] . OwnerID ]
}
return nil
}
2016-11-28 12:27:55 -05:00
// LoadAttributes loads the attributes for the given RepositoryList
2016-07-24 02:32:46 -04:00
func ( repos RepositoryList ) LoadAttributes ( ) error {
return repos . loadAttributes ( x )
}
2016-11-28 12:27:55 -05:00
// MirrorRepositoryList contains the mirror repositories
2016-07-24 02:32:46 -04:00
type MirrorRepositoryList [ ] * Repository
func ( repos MirrorRepositoryList ) loadAttributes ( e Engine ) error {
if len ( repos ) == 0 {
return nil
}
// Load mirrors.
repoIDs := make ( [ ] int64 , 0 , len ( repos ) )
for i := range repos {
if ! repos [ i ] . IsMirror {
continue
}
repoIDs = append ( repoIDs , repos [ i ] . ID )
}
mirrors := make ( [ ] * Mirror , 0 , len ( repoIDs ) )
2016-11-10 10:16:32 -05:00
if err := e .
Where ( "id > 0" ) .
In ( "repo_id" , repoIDs ) .
Find ( & mirrors ) ; err != nil {
2016-07-24 02:32:46 -04:00
return fmt . Errorf ( "find mirrors: %v" , err )
}
set := make ( map [ int64 ] * Mirror )
for i := range mirrors {
set [ mirrors [ i ] . RepoID ] = mirrors [ i ]
}
for i := range repos {
repos [ i ] . Mirror = set [ repos [ i ] . ID ]
}
return nil
}
2016-11-28 12:27:55 -05:00
// LoadAttributes loads the attributes for the given MirrorRepositoryList
2016-07-24 02:32:46 -04:00
func ( repos MirrorRepositoryList ) LoadAttributes ( ) error {
return repos . loadAttributes ( x )
}
2014-09-23 15:30:04 -04:00
// __ __ __ .__
// / \ / \_____ _/ |_ ____ | |__
// \ \/\/ /\__ \\ __\/ ___\| | \
// \ / / __ \| | \ \___| Y \
// \__/\ / (____ /__| \___ >___| /
// \/ \/ \/ \/
2014-12-06 20:22:48 -05:00
// Watch is connection request for receiving repository notification.
2014-03-20 16:04:56 -04:00
type Watch struct {
2015-03-17 21:51:39 -04:00
ID int64 ` xorm:"pk autoincr" `
UserID int64 ` xorm:"UNIQUE(watch)" `
RepoID int64 ` xorm:"UNIQUE(watch)" `
2014-03-20 16:04:56 -04:00
}
2016-07-24 02:32:46 -04:00
func isWatching ( e Engine , userID , repoID int64 ) bool {
has , _ := e . Get ( & Watch { 0 , userID , repoID } )
2015-10-05 09:54:55 -04:00
return has
}
2014-10-19 01:35:24 -04:00
// IsWatching checks if user has watched given repository.
2016-07-24 02:32:46 -04:00
func IsWatching ( userID , repoID int64 ) bool {
return isWatching ( x , userID , repoID )
2014-10-19 01:35:24 -04:00
}
2016-07-24 02:32:46 -04:00
func watchRepo ( e Engine , userID , repoID int64 , watch bool ) ( err error ) {
2014-03-20 16:04:56 -04:00
if watch {
2016-07-24 02:32:46 -04:00
if isWatching ( e , userID , repoID ) {
2014-08-10 23:11:18 -04:00
return nil
}
2016-07-24 02:32:46 -04:00
if _ , err = e . Insert ( & Watch { RepoID : repoID , UserID : userID } ) ; err != nil {
2014-03-20 16:14:50 -04:00
return err
}
2016-07-24 02:32:46 -04:00
_ , err = e . Exec ( "UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?" , repoID )
2014-03-20 16:04:56 -04:00
} else {
2016-07-24 02:32:46 -04:00
if ! isWatching ( e , userID , repoID ) {
2014-08-10 23:11:18 -04:00
return nil
}
2016-07-24 02:32:46 -04:00
if _ , err = e . Delete ( & Watch { 0 , userID , repoID } ) ; err != nil {
2014-03-20 16:14:50 -04:00
return err
}
2016-07-24 02:32:46 -04:00
_ , err = e . Exec ( "UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?" , repoID )
2014-03-20 16:04:56 -04:00
}
return err
}
2016-11-28 12:27:55 -05:00
// WatchRepo watch or unwatch repository.
2016-07-24 02:32:46 -04:00
func WatchRepo ( userID , repoID int64 , watch bool ) ( err error ) {
return watchRepo ( x , userID , repoID , watch )
2014-08-10 23:11:18 -04:00
}
2015-11-16 23:28:46 -05:00
func getWatchers ( e Engine , repoID int64 ) ( [ ] * Watch , error ) {
2014-05-07 16:51:14 -04:00
watches := make ( [ ] * Watch , 0 , 10 )
2015-11-16 23:28:46 -05:00
return watches , e . Find ( & watches , & Watch { RepoID : repoID } )
2014-03-20 16:04:56 -04:00
}
2015-02-13 00:58:46 -05:00
// GetWatchers returns all watchers of given repository.
2015-11-16 23:28:46 -05:00
func GetWatchers ( repoID int64 ) ( [ ] * Watch , error ) {
return getWatchers ( x , repoID )
2015-02-13 00:58:46 -05:00
}
2016-11-28 12:27:55 -05:00
// GetWatchers returns range of users watching given repository.
2015-11-16 23:28:46 -05:00
func ( repo * Repository ) GetWatchers ( page int ) ( [ ] * User , error ) {
users := make ( [ ] * User , 0 , ItemsPerPage )
2017-01-06 22:13:02 -05:00
sess := x . Where ( "watch.repo_id=?" , repo . ID ) .
Join ( "LEFT" , "watch" , "`user`.id=`watch`.user_id" )
if page > 0 {
sess = sess . Limit ( ItemsPerPage , ( page - 1 ) * ItemsPerPage )
2015-12-14 02:40:23 -05:00
}
return users , sess . Find ( & users )
2015-10-01 09:17:27 -04:00
}
2015-02-13 00:58:46 -05:00
func notifyWatchers ( e Engine , act * Action ) error {
2014-03-25 14:04:57 -04:00
// Add feeds for user self and all watchers.
2015-03-17 21:51:39 -04:00
watches , err := getWatchers ( e , act . RepoID )
2014-03-20 01:31:24 -04:00
if err != nil {
2015-02-13 00:58:46 -05:00
return fmt . Errorf ( "get watchers: %v" , err )
2014-03-20 01:31:24 -04:00
}
2014-03-27 12:48:29 -04:00
// Add feed for actioner.
2015-03-17 21:51:39 -04:00
act . UserID = act . ActUserID
2015-02-13 00:58:46 -05:00
if _ , err = e . InsertOne ( act ) ; err != nil {
return fmt . Errorf ( "insert new actioner: %v" , err )
2014-03-27 12:48:29 -04:00
}
2014-03-20 01:31:24 -04:00
2014-03-25 14:04:57 -04:00
for i := range watches {
2015-03-17 21:51:39 -04:00
if act . ActUserID == watches [ i ] . UserID {
2014-03-27 12:48:29 -04:00
continue
2014-03-20 01:31:24 -04:00
}
2015-03-17 21:51:39 -04:00
act . ID = 0
act . UserID = watches [ i ] . UserID
2015-02-13 00:58:46 -05:00
if _ , err = e . InsertOne ( act ) ; err != nil {
return fmt . Errorf ( "insert new action: %v" , err )
2014-03-25 14:04:57 -04:00
}
2014-03-20 01:31:24 -04:00
}
2014-03-25 14:04:57 -04:00
return nil
2014-03-20 01:31:24 -04:00
}
2015-02-13 00:58:46 -05:00
// NotifyWatchers creates batch of actions for every watcher.
func NotifyWatchers ( act * Action ) error {
return notifyWatchers ( x , act )
}
2014-10-19 01:35:24 -04:00
// ___________ __
// \_ _____/__________| | __
// | __)/ _ \_ __ \ |/ /
// | \( <_> ) | \/ <
// \___ / \____/|__| |__|_ \
// \/ \/
2015-08-08 05:24:10 -04:00
// HasForkedRepo checks if given user has already forked a repository with given ID.
func HasForkedRepo ( ownerID , repoID int64 ) ( * Repository , bool ) {
repo := new ( Repository )
2016-11-10 10:16:32 -05:00
has , _ := x .
Where ( "owner_id=? AND fork_id=?" , ownerID , repoID ) .
Get ( repo )
2015-08-08 05:24:10 -04:00
return repo , has
}
2016-11-28 12:27:55 -05:00
// ForkRepository forks a repository
2015-02-24 00:27:22 -05:00
func ForkRepository ( u * User , oldRepo * Repository , name , desc string ) ( _ * Repository , err error ) {
2014-10-19 01:35:24 -04:00
repo := & Repository {
2016-07-23 13:08:22 -04:00
OwnerID : u . ID ,
2015-09-01 11:43:53 -04:00
Owner : u ,
Name : name ,
LowerName : strings . ToLower ( name ) ,
Description : desc ,
DefaultBranch : oldRepo . DefaultBranch ,
IsPrivate : oldRepo . IsPrivate ,
IsFork : true ,
ForkID : oldRepo . ID ,
2014-10-19 01:35:24 -04:00
}
2015-02-13 00:58:46 -05:00
sess := x . NewSession ( )
defer sessionRelease ( sess )
if err = sess . Begin ( ) ; err != nil {
return nil , err
}
2015-08-08 05:10:34 -04:00
if err = createRepository ( sess , u , repo ) ; err != nil {
2014-10-19 01:35:24 -04:00
return nil , err
}
2014-03-17 23:22:19 -04:00
2015-08-08 10:43:14 -04:00
if _ , err = sess . Exec ( "UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?" , oldRepo . ID ) ; err != nil {
2014-10-19 01:35:24 -04:00
return nil , err
}
repoPath := RepoPath ( u . Name , repo . Name )
2017-01-17 00:58:58 -05:00
_ , stderr , err := process . GetManager ( ) . ExecTimeout ( 10 * time . Minute ,
2014-10-19 01:35:24 -04:00
fmt . Sprintf ( "ForkRepository(git clone): %s/%s" , u . Name , repo . Name ) ,
2015-11-26 17:33:45 -05:00
"git" , "clone" , "--bare" , oldRepo . RepoPath ( ) , repoPath )
2014-10-19 01:35:24 -04:00
if err != nil {
2015-02-13 00:58:46 -05:00
return nil , fmt . Errorf ( "git clone: %v" , stderr )
2014-10-19 01:35:24 -04:00
}
2017-01-17 00:58:58 -05:00
_ , stderr , err = process . GetManager ( ) . ExecDir ( - 1 ,
2014-10-19 01:35:24 -04:00
repoPath , fmt . Sprintf ( "ForkRepository(git update-server-info): %s" , repoPath ) ,
"git" , "update-server-info" )
if err != nil {
2016-11-15 01:16:27 -05:00
return nil , fmt . Errorf ( "git update-server-info: %v" , stderr )
2014-10-19 01:35:24 -04:00
}
2015-03-12 20:18:42 -04:00
if err = createUpdateHook ( repoPath ) ; err != nil {
return nil , fmt . Errorf ( "createUpdateHook: %v" , err )
}
2016-12-25 20:16:37 -05:00
//Commit repo to get Fork ID
err = sess . Commit ( )
if err != nil {
return nil , err
}
sessionRelease ( sess )
// Copy LFS meta objects in new session
sess = x . NewSession ( )
defer sessionRelease ( sess )
if err = sess . Begin ( ) ; err != nil {
return nil , err
}
var lfsObjects [ ] * LFSMetaObject
if err = sess . Where ( "repository_id=?" , oldRepo . ID ) . Find ( & lfsObjects ) ; err != nil {
return nil , err
}
for _ , v := range lfsObjects {
v . ID = 0
v . RepositoryID = repo . ID
if _ , err = sess . Insert ( v ) ; err != nil {
return nil , err
}
}
2015-02-13 00:58:46 -05:00
return repo , sess . Commit ( )
2014-07-23 07:48:06 -04:00
}
2015-10-01 09:17:27 -04:00
2016-11-28 12:27:55 -05:00
// GetForks returns all the forks of the repository
2015-10-01 09:17:27 -04:00
func ( repo * Repository ) GetForks ( ) ( [ ] * Repository , error ) {
2015-11-16 23:33:40 -05:00
forks := make ( [ ] * Repository , 0 , repo . NumForks )
return forks , x . Find ( & forks , & Repository { ForkID : repo . ID } )
2015-10-01 09:17:27 -04:00
}
2016-08-11 08:48:08 -04:00
// __________ .__
// \______ \____________ ____ ____ | |__
// | | _/\_ __ \__ \ / \_/ ___\| | \
// | | \ | | \// __ \| | \ \___| Y \
// |______ / |__| (____ /___| /\___ >___| /
// \/ \/ \/ \/ \/
//
2016-11-28 12:27:55 -05:00
// CreateNewBranch creates a new repository branch
2016-08-11 08:48:08 -04:00
func ( repo * Repository ) CreateNewBranch ( doer * User , oldBranchName , branchName string ) ( err error ) {
repoWorkingPool . CheckIn ( com . ToStr ( repo . ID ) )
defer repoWorkingPool . CheckOut ( com . ToStr ( repo . ID ) )
2016-08-15 02:02:14 -04:00
localPath := repo . LocalCopyPath ( )
2016-08-11 08:48:08 -04:00
2016-08-15 02:02:14 -04:00
if err = discardLocalRepoBranchChanges ( localPath , oldBranchName ) ; err != nil {
2016-08-11 08:48:08 -04:00
return fmt . Errorf ( "discardLocalRepoChanges: %v" , err )
2016-08-15 02:02:14 -04:00
} else if err = repo . UpdateLocalCopyBranch ( oldBranchName ) ; err != nil {
return fmt . Errorf ( "UpdateLocalCopyBranch: %v" , err )
2016-08-11 08:48:08 -04:00
}
if err = repo . CheckoutNewBranch ( oldBranchName , branchName ) ; err != nil {
return fmt . Errorf ( "CreateNewBranch: %v" , err )
}
if err = git . Push ( localPath , "origin" , branchName ) ; err != nil {
return fmt . Errorf ( "Push: %v" , err )
}
return nil
}