@ -160,6 +160,7 @@ type Repository struct {
IsFork bool ` xorm:"NOT NULL DEFAULT false" `
ForkID int64
BaseRepo * Repository ` xorm:"-" `
ForkInfo * ForkInfo ` xorm:"-" `
Created time . Time ` xorm:"CREATED" `
Updated time . Time ` xorm:"UPDATED" `
@ -167,6 +168,15 @@ type Repository struct {
func ( repo * Repository ) AfterSet ( colName string , _ xorm . Cell ) {
switch colName {
case "is_fork" :
forkInfo := new ( ForkInfo )
has , err := x . Where ( "repo_id=?" , repo . ID ) . Get ( forkInfo )
if err != nil {
log . Error ( 3 , "get fork in[%d]: %v" , repo . ID , err )
return
} else if has {
repo . ForkInfo = forkInfo
}
case "updated" :
repo . Updated = regulateTimeZone ( repo . Updated )
}
@ -883,6 +893,16 @@ func ChangeRepositoryName(u *User, oldRepoName, newRepoName string) (err error)
return os . Rename ( RepoPath ( u . LowerName , oldRepoName ) , RepoPath ( u . LowerName , newRepoName ) )
}
func getRepositoriesByForkID ( e Engine , forkID int64 ) ( [ ] * Repository , error ) {
repos := make ( [ ] * Repository , 0 , 10 )
return repos , e . Where ( "fork_id=?" , forkID ) . Find ( & repos )
}
// GetRepositoriesByForkID returns all repositories with given fork ID.
func GetRepositoriesByForkID ( forkID int64 ) ( [ ] * Repository , error ) {
return getRepositoriesByForkID ( x , forkID )
}
func updateRepository ( e Engine , repo * Repository , visibilityChanged bool ) ( err error ) {
repo . LowerName = strings . ToLower ( repo . Name )
@ -909,6 +929,17 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
if err = repo . recalculateTeamAccesses ( e , 0 ) ; err != nil {
return fmt . Errorf ( "recalculateTeamAccesses: %v" , err )
}
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 )
}
}
}
return nil
@ -929,7 +960,7 @@ func UpdateRepository(repo *Repository, visibilityChanged bool) (err error) {
}
// DeleteRepository deletes a repository for a user or organization.
func DeleteRepository ( uid , repoID int64 , userName string ) error {
func DeleteRepository ( uid , repoID int64 ) error {
repo := & Repository { ID : repoID , OwnerID : uid }
has , err := x . Get ( repo )
if err != nil {
@ -1015,7 +1046,9 @@ func DeleteRepository(uid, repoID int64, userName string) error {
if repo . IsFork {
if _ , err = sess . Exec ( "UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?" , repo . ForkID ) ; err != nil {
return err
return fmt . Errorf ( "decrease fork count: %v" , err )
} else if _ , err = sess . Delete ( & ForkInfo { RepoID : repo . ID } ) ; err != nil {
return fmt . Errorf ( "delete fork info: %v" , err )
}
}
@ -1024,8 +1057,12 @@ func DeleteRepository(uid, repoID int64, userName string) error {
}
// Remove repository files.
if err = os . RemoveAll ( RepoPath ( userName , repo . Name ) ) ; err != nil {
desc := fmt . Sprintf ( "delete repository files(%s/%s): %v" , userName , repo . Name , err )
repoPath , err := repo . RepoPath ( )
if err != nil {
return fmt . Errorf ( "RepoPath: %v" , err )
}
if err = os . RemoveAll ( repoPath ) ; err != nil {
desc := fmt . Sprintf ( "delete repository files[%s]: %v" , repoPath , err )
log . Warn ( desc )
if err = CreateRepositoryNotice ( desc ) ; err != nil {
log . Error ( 4 , "add notice: %v" , err )
@ -1039,7 +1076,32 @@ func DeleteRepository(uid, repoID int64, userName string) error {
}
}
return sess . Commit ( )
if err = sess . Commit ( ) ; err != nil {
return fmt . Errorf ( "Commit: %v" , err )
}
if repo . NumForks > 0 {
if repo . IsPrivate {
forkRepos , err := GetRepositoriesByForkID ( repo . ID )
if err != nil {
return fmt . Errorf ( "getRepositoriesByForkID: %v" , err )
}
for i := range forkRepos {
if err = DeleteRepository ( forkRepos [ i ] . OwnerID , forkRepos [ i ] . ID ) ; err != nil {
log . Error ( 4 , "updateRepository[%d]: %v" , forkRepos [ i ] . ID , err )
}
}
} else {
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 )
}
if _ , err = x . Delete ( & ForkInfo { ForkID : repo . ID } ) ; err != nil {
log . Error ( 4 , "clear fork infos: %v" , err )
}
}
}
return nil
}
// GetRepositoryByRef returns a Repository specified by a GFM reference.
@ -1275,78 +1337,96 @@ func GitGcRepos() error {
} )
}
func CheckRepoStats ( ) {
if isCheckingRepos {
return
}
isCheckingRepos = true
defer func ( ) { isCheckingRepos = false } ( )
log . Trace ( "Doing: CheckRepoStats" )
type repoChecker struct {
querySQL , correctSQL string
desc string
}
// ***** START: Repository.NumWatches *****
results , err := x . Query ( "SELECT repo.id FROM `repository` repo WHERE repo.num_watches!=(SELECT COUNT(*) FROM `watch` WHERE repo_id=repo.id)" )
func repoStatsCheck ( checker * repoChecker ) {
results , err := x . Query ( checker . querySQL )
if err != nil {
log . Error ( 4 , "Select repository check 'watch': %v" , err )
log . Error ( 4 , "Select %s: %v" , checker . desc , err )
return
}
for _ , watch := range results {
repoID := com . StrTo ( watch [ "id" ] ) . MustInt64 ( )
log . Trace ( "Updating repository count 'watch': %d" , repoID )
_ , err = x . Exec ( "UPDATE `repository` SET num_watches=(SELECT COUNT(*) FROM `watch` WHERE repo_id=?) WHERE id=?" , repoID , repoID )
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 )
if err != nil {
log . Error ( 4 , "Update repository check 'watch'[%d]: %v" , repoID , err )
log . Error ( 4 , "Update %s[%d]: %v" , checker . desc , id , err )
}
}
// ***** END: Repository.NumWatches *****
}
// ***** START: Repository.NumStars *****
results , err = x . Query ( "SELECT repo.id FROM `repository` repo WHERE repo.num_stars!=(SELECT COUNT(*) FROM `star` WHERE repo_id=repo.id)" )
if err != nil {
log . Error ( 4 , "Select repository check 'star': %v" , err )
func CheckRepoStats ( ) {
if isCheckingRepos {
return
}
for _ , star := range results {
repoID := com . StrTo ( star [ "id" ] ) . MustInt64 ( )
log . Trace ( "Updating repository count 'star': %d" , repoID )
_ , err = x . Exec ( "UPDATE `repository` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE repo_id=?) WHERE id=?" , repoID , repoID )
isCheckingRepos = true
defer func ( ) { isCheckingRepos = false } ( )
log . Trace ( "Doing: CheckRepoStats" )
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'" ,
} ,
}
for i := range checkers {
repoStatsCheck ( checkers [ i ] )
}
// FIXME: use checker when v0.8, stop supporting old fork repo format.
// ***** START: Repository.NumForks *****
results , err := x . Query ( "SELECT repo.id FROM `repository` repo WHERE repo.num_forks!=(SELECT COUNT(*) FROM `repository` WHERE fork_id=repo.id)" )
if err != nil {
log . Error ( 4 , "Update repository check 'star'[%d]: %v" , repoID , err )
}
}
// ***** END: Repository.NumStars *****
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 )
// ***** START: Label.NumIssues *****
results , err = x . Query ( "SELECT label.id FROM `label` WHERE label.num_issues!=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=label.id)" )
repo , err := GetRepositoryByID ( id )
if err != nil {
log . Error ( 4 , "Select label check 'num_issues': %v" , err )
return
log . Error ( 4 , "GetRepositoryByID[%d]: %v" , id , err )
continue
}
for _ , label := range results {
labelID := com . StrTo ( label [ "id" ] ) . MustInt64 ( )
log . Trace ( "Updating label count 'num_issues': %d" , labelID )
_ , err = x . Exec ( "UPDATE `label` SET num_issues=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=?) WHERE id=?" , labelID , labelID )
rawResult , err := x . Query ( "SELECT COUNT(*) FROM `repository` WHERE fork_id=?" , repo . ID )
if err != nil {
log . Error ( 4 , "Update label check 'num_issues'[%d]: %v" , labelID , err )
}
log . Error ( 4 , "Select count of forks[%d]: %v" , repo . ID , err )
continue
}
// ***** END: Label.NumIssues *****
repo . NumForks = int ( parseCountResult ( rawResult ) )
// ***** START: User.NumRepos *****
results , err = x . Query ( "SELECT `user`.id FROM `user` WHERE `user`.num_repos!=(SELECT COUNT(*) FROM `repository` WHERE owner_id=`user`.id)" )
if err != nil {
log . Error ( 4 , "Select user check 'num_repos': %v" , err )
return
if err = UpdateRepository ( repo , false ) ; err != nil {
log . Error ( 4 , "UpdateRepository[%d]: %v" , id , err )
continue
}
for _ , user := range results {
userID := com . StrTo ( user [ "id" ] ) . MustInt64 ( )
log . Trace ( "Updating user count 'num_repos': %d" , userID )
_ , err = x . Exec ( "UPDATE `user` SET num_repos=(SELECT COUNT(*) FROM `repository` WHERE owner_id=?) WHERE id=?" , userID , userID )
if err != nil {
log . Error ( 4 , "Update user check 'num_repos'[%d]: %v" , userID , err )
}
}
// ***** END: User.NumRepo s *****
// ***** END: Repository.NumForks *****
}
// _________ .__ .__ ___. __ .__
@ -1589,6 +1669,13 @@ func IsStaring(uid, repoId int64) bool {
// \___ / \____/|__| |__|_ \
// \/ \/
type ForkInfo struct {
ID int64 ` xorm:"pk autoincr" `
ForkID int64
RepoID int64 ` xorm:"UNIQUE" `
StartCommitID string ` xorm:"VARCHAR(40)" `
}
// HasForkedRepo checks if given user has already forked a repository with given ID.
func HasForkedRepo ( ownerID , repoID int64 ) ( * Repository , bool ) {
repo := new ( Repository )
@ -1603,6 +1690,7 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit
Name : name ,
LowerName : strings . ToLower ( name ) ,
Description : desc ,
DefaultBranch : oldRepo . DefaultBranch ,
IsPrivate : oldRepo . IsPrivate ,
IsFork : true ,
ForkID : oldRepo . ID ,
@ -1621,6 +1709,13 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Reposit
if _ , err = sess . Exec ( "UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?" , oldRepo . ID ) ; err != nil {
return nil , err
}
// else if _, err = sess.Insert(&ForkInfo{
// ForkID: oldRepo.ID,
// RepoID: repo.ID,
// StartCommitID: "",
// }); err != nil {
// return nil, fmt.Errorf("insert fork info: %v", err)
// }
oldRepoPath , err := oldRepo . RepoPath ( )
if err != nil {