From 76f89047183fc70a077ac37b4168d90539f12e09 Mon Sep 17 00:00:00 2001
From: Peter Smit <peter@smitmail.eu>
Date: Fri, 23 Jan 2015 09:54:16 +0200
Subject: Introducing Collaboration Struct

---
 routers/repo/setting.go | 57 ++++++++++---------------------------------------
 1 file changed, 11 insertions(+), 46 deletions(-)

(limited to 'routers/repo/setting.go')

diff --git a/routers/repo/setting.go b/routers/repo/setting.go
index 33bf1eab28..a6f50d3068 100644
--- a/routers/repo/setting.go
+++ b/routers/repo/setting.go
@@ -10,7 +10,6 @@ import (
 	"fmt"
 	"strings"
 	"time"
-	"path"
 
 	"github.com/Unknwon/com"
 
@@ -170,22 +169,12 @@ func SettingsCollaboration(ctx *middleware.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.settings")
 	ctx.Data["PageIsSettingsCollaboration"] = true
 
-	repoLink := path.Join(ctx.Repo.Owner.LowerName, ctx.Repo.Repository.LowerName)
-
 	if ctx.Req.Method == "POST" {
 		name := strings.ToLower(ctx.Query("collaborator"))
 		if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
 			ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path)
 			return
 		}
-		has, err := models.HasAccess(name, repoLink, models.WRITABLE)
-		if err != nil {
-			ctx.Handle(500, "HasAccess", err)
-			return
-		} else if has {
-			ctx.Redirect(setting.AppSubUrl + ctx.Req.URL.Path)
-			return
-		}
 
 		u, err := models.GetUserByName(name)
 		if err != nil {
@@ -205,9 +194,8 @@ func SettingsCollaboration(ctx *middleware.Context) {
 			return
 		}
 
-		if err = models.AddAccess(&models.Access{UserName: name, RepoName: repoLink,
-			Mode: models.WRITABLE}); err != nil {
-			ctx.Handle(500, "AddAccess", err)
+		if err = ctx.Repo.Repository.AddCollaborator(u); err != nil {
+			ctx.Handle(500, "AddCollaborator", err)
 			return
 		}
 
@@ -226,50 +214,27 @@ func SettingsCollaboration(ctx *middleware.Context) {
 	// Delete collaborator.
 	remove := strings.ToLower(ctx.Query("remove"))
 	if len(remove) > 0 && remove != ctx.Repo.Owner.LowerName {
-		needDelete := true
-		if ctx.User.IsOrganization() {
-			// Check if user belongs to a team that has access to this repository.
-			auth, err := models.GetHighestAuthorize(ctx.Repo.Owner.Id, ctx.User.Id, ctx.Repo.Repository.Id, 0)
-			if err != nil {
-				ctx.Handle(500, "GetHighestAuthorize", err)
-				return
-			}
-			if auth > 0 {
-				needDelete = false
-			}
+		u, err := models.GetUserByName(remove)
+		if err != nil {
+			ctx.Handle(500, "GetUserByName", err)
+			return
 		}
-
-		if needDelete {
-			if err := models.DeleteAccess(&models.Access{UserName: remove, RepoName: repoLink}); err != nil {
-				ctx.Handle(500, "DeleteAccess", err)
-				return
-			}
+		if err := ctx.Repo.Repository.DeleteCollaborator(u); err != nil {
+			ctx.Handle(500, "DeleteCollaborator", err)
+			return
 		}
 		ctx.Flash.Success(ctx.Tr("repo.settings.remove_collaborator_success"))
 		ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration")
 		return
 	}
 
-	names, err := models.GetCollaboratorNames(repoLink)
+	users, err := ctx.Repo.Repository.GetCollaborators()
 	if err != nil {
 		ctx.Handle(500, "GetCollaborators", err)
 		return
 	}
 
-	collaborators := make([]*models.User, 0, len(names))
-	for _, name := range names {
-		u, err := models.GetUserByName(name)
-		if err != nil {
-			ctx.Handle(500, "GetUserByName", err)
-			return
-		}
-		// Does not show organization members.
-		if ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgMember(u.Id) {
-			continue
-		}
-		collaborators = append(collaborators, u)
-	}
-	ctx.Data["Collaborators"] = collaborators
+	ctx.Data["Collaborators"] = users
 	ctx.HTML(200, COLLABORATION)
 }
 
-- 
cgit v1.2.3


From ee68a826a55c6a4305e7f609db57501a54a5bc47 Mon Sep 17 00:00:00 2001
From: Unknwon <u@gogs.io>
Date: Mon, 23 Feb 2015 02:15:53 -0500
Subject: v4 migration, merge 'dev', clean code and mirror fix

---
 gogs.go                         |   2 +-
 models/access.go                |  37 +++--
 models/action.go                |  20 +--
 models/migrations/migrations.go |  76 +++++++++--
 models/models.go                |   5 +-
 models/org.go                   | 296 ++++++++++++++++++++++++----------------
 models/repo.go                  | 132 ++++++++----------
 models/user.go                  |   4 +-
 routers/org/teams.go            |   2 +-
 routers/repo/setting.go         |   1 -
 templates/.VERSION              |   2 +-
 11 files changed, 343 insertions(+), 234 deletions(-)

(limited to 'routers/repo/setting.go')

diff --git a/gogs.go b/gogs.go
index b2366b6341..58cbb1a05c 100644
--- a/gogs.go
+++ b/gogs.go
@@ -17,7 +17,7 @@ import (
 	"github.com/gogits/gogs/modules/setting"
 )
 
-const APP_VER = "0.5.14.0222 Beta"
+const APP_VER = "0.5.15.0223 Beta"
 
 func init() {
 	runtime.GOMAXPROCS(runtime.NumCPU())
diff --git a/models/access.go b/models/access.go
index 1e1c0114ed..29d1a730b7 100644
--- a/models/access.go
+++ b/models/access.go
@@ -24,21 +24,19 @@ type Access struct {
 	Mode   AccessMode
 }
 
-// Return the Access a user has to a repository. Will return NoneAccess if the
-// user does not have access. User can be nil!
-func AccessLevel(u *User, r *Repository) (AccessMode, error) {
+func accessLevel(e Engine, u *User, repo *Repository) (AccessMode, error) {
 	mode := ACCESS_MODE_NONE
-	if !r.IsPrivate {
+	if !repo.IsPrivate {
 		mode = ACCESS_MODE_READ
 	}
 
 	if u != nil {
-		if u.Id == r.OwnerId {
+		if u.Id == repo.OwnerId {
 			return ACCESS_MODE_OWNER, nil
 		}
 
-		a := &Access{UserID: u.Id, RepoID: r.Id}
-		if has, err := x.Get(a); !has || err != nil {
+		a := &Access{UserID: u.Id, RepoID: repo.Id}
+		if has, err := e.Get(a); !has || err != nil {
 			return mode, err
 		}
 		return a.Mode, nil
@@ -47,12 +45,22 @@ func AccessLevel(u *User, r *Repository) (AccessMode, error) {
 	return mode, nil
 }
 
-// HasAccess returns true if someone has the request access level. User can be nil!
-func HasAccess(u *User, r *Repository, testMode AccessMode) (bool, error) {
-	mode, err := AccessLevel(u, r)
+// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
+// user does not have access. User can be nil!
+func AccessLevel(u *User, repo *Repository) (AccessMode, error) {
+	return accessLevel(x, u, repo)
+}
+
+func hasAccess(e Engine, u *User, repo *Repository, testMode AccessMode) (bool, error) {
+	mode, err := accessLevel(e, u, repo)
 	return testMode <= mode, err
 }
 
+// HasAccess returns true if someone has the request access level. User can be nil!
+func HasAccess(u *User, repo *Repository, testMode AccessMode) (bool, error) {
+	return hasAccess(x, u, repo, testMode)
+}
+
 // GetAccessibleRepositories finds all repositories where a user has access to,
 // besides his own.
 func (u *User) GetAccessibleRepositories() (map[*Repository]AccessMode, error) {
@@ -96,7 +104,8 @@ func (repo *Repository) recalculateTeamAccesses(e Engine, mode AccessMode) error
 func (repo *Repository) recalculateAccesses(e Engine) error {
 	accessMap := make(map[int64]AccessMode, 20)
 
-	// Give all collaborators write access
+	// FIXME: should be able to have read-only access.
+	// Give all collaborators write access.
 	collaborators, err := repo.getCollaborators(e)
 	if err != nil {
 		return err
@@ -114,9 +123,7 @@ func (repo *Repository) recalculateAccesses(e Engine) error {
 		}
 
 		for _, team := range repo.Owner.Teams {
-			if !(team.IsOwnerTeam() || team.HasRepository(repo)) {
-				continue
-			} else if team.IsOwnerTeam() {
+			if team.IsOwnerTeam() {
 				team.Authorize = ACCESS_MODE_OWNER
 			}
 
@@ -129,6 +136,8 @@ func (repo *Repository) recalculateAccesses(e Engine) error {
 		}
 	}
 
+	// FIXME: do corss-comparison so reduce deletions and additions to the minimum?
+
 	minMode := ACCESS_MODE_READ
 	if !repo.IsPrivate {
 		minMode = ACCESS_MODE_WRITE
diff --git a/models/action.go b/models/action.go
index 9ba661f1e2..fee5e5e676 100644
--- a/models/action.go
+++ b/models/action.go
@@ -434,20 +434,20 @@ func NewRepoAction(u *User, repo *Repository) (err error) {
 	return newRepoAction(x, u, repo)
 }
 
-func transferRepoAction(e Engine, u, newUser *User, repo *Repository) (err error) {
+func transferRepoAction(e Engine, actUser, oldOwner, newOwner *User, repo *Repository) (err error) {
 	action := &Action{
-		ActUserId:    u.Id,
-		ActUserName:  u.Name,
-		ActEmail:     u.Email,
+		ActUserId:    actUser.Id,
+		ActUserName:  actUser.Name,
+		ActEmail:     actUser.Email,
 		OpType:       TRANSFER_REPO,
 		RepoId:       repo.Id,
-		RepoUserName: newUser.Name,
+		RepoUserName: newOwner.Name,
 		RepoName:     repo.Name,
 		IsPrivate:    repo.IsPrivate,
-		Content:      path.Join(repo.Owner.LowerName, repo.LowerName),
+		Content:      path.Join(oldOwner.LowerName, repo.LowerName),
 	}
 	if err = notifyWatchers(e, action); err != nil {
-		return fmt.Errorf("notify watchers '%d/%s'", u.Id, repo.Id)
+		return fmt.Errorf("notify watchers '%d/%s'", actUser.Id, repo.Id)
 	}
 
 	// Remove watch for organization.
@@ -457,13 +457,13 @@ func transferRepoAction(e Engine, u, newUser *User, repo *Repository) (err error
 		}
 	}
 
-	log.Trace("action.TransferRepoAction: %s/%s", u.Name, repo.Name)
+	log.Trace("action.TransferRepoAction: %s/%s", actUser.Name, repo.Name)
 	return nil
 }
 
 // TransferRepoAction adds new action for transferring repository.
-func TransferRepoAction(u, newUser *User, repo *Repository) (err error) {
-	return transferRepoAction(x, u, newUser, repo)
+func TransferRepoAction(actUser, oldOwner, newOwner *User, repo *Repository) (err error) {
+	return transferRepoAction(x, actUser, oldOwner, newOwner, repo)
 }
 
 // GetFeeds returns action list of given user in given context.
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index f6ef513207..25c130a926 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -53,6 +53,7 @@ var migrations = []Migration{
 	NewMigration("generate collaboration from access", accessToCollaboration), // V0 -> V1
 	NewMigration("make authorize 4 if team is owners", ownerTeamUpdate),       // V1 -> V2
 	NewMigration("refactor access table to use id's", accessRefactor),         // V2 -> V3
+	NewMigration("generate team-repo from team", teamToTeamRepo),              // V3 -> V4
 }
 
 // Migrate database to current version
@@ -214,8 +215,8 @@ func accessToCollaboration(x *xorm.Engine) (err error) {
 }
 
 func ownerTeamUpdate(x *xorm.Engine) (err error) {
-	if _, err := x.Exec("UPDATE team SET authorize=4 WHERE lower_name=?", "owners"); err != nil {
-		return fmt.Errorf("drop table: %v", err)
+	if _, err := x.Exec("UPDATE `team` SET authorize=4 WHERE lower_name=?", "owners"); err != nil {
+		return fmt.Errorf("update owner team table: %v", err)
 	}
 	return nil
 }
@@ -239,9 +240,9 @@ func accessRefactor(x *xorm.Engine) (err error) {
 
 	accessMap := make(map[UserRepo]AccessMode, 50)
 
-	results, err := x.Query("SELECT r.id as `repo_id`, r.is_private as `is_private`, r.owner_id as `owner_id`, u.type as `owner_type` FROM `repository` r LEFT JOIN user u ON r.owner_id=u.id")
+	results, err := x.Query("SELECT r.id AS `repo_id`, r.is_private AS `is_private`, r.owner_id AS `owner_id`, u.type AS `owner_type` FROM `repository` r LEFT JOIN `user` u ON r.owner_id=u.id")
 	if err != nil {
-		return err
+		return fmt.Errorf("select repositories: %v", err)
 	}
 	for _, repo := range results {
 		repoID := com.StrTo(repo["repo_id"]).MustInt64()
@@ -249,9 +250,9 @@ func accessRefactor(x *xorm.Engine) (err error) {
 		ownerID := com.StrTo(repo["owner_id"]).MustInt64()
 		ownerIsOrganization := com.StrTo(repo["owner_type"]).MustInt() > 0
 
-		results, err := x.Query("SELECT user_id FROM collaboration WHERE repo_id=?", repoID)
+		results, err := x.Query("SELECT `user_id` FROM `collaboration` WHERE repo_id=?", repoID)
 		if err != nil {
-			return fmt.Errorf("select repos: %v", err)
+			return fmt.Errorf("select collaborators: %v", err)
 		}
 		for _, user := range results {
 			userID := com.StrTo(user["user_id"]).MustInt64()
@@ -262,6 +263,8 @@ func accessRefactor(x *xorm.Engine) (err error) {
 			continue
 		}
 
+		// The minimum level to add a new access record,
+		// because public repository has implicit open access.
 		minAccessLevel := AccessMode(0)
 		if !isPrivate {
 			minAccessLevel = 1
@@ -269,7 +272,7 @@ func accessRefactor(x *xorm.Engine) (err error) {
 
 		repoString := "$" + string(repo["repo_id"]) + "|"
 
-		results, err = x.Query("SELECT id, authorize, repo_ids FROM team WHERE org_id=? AND authorize > ? ORDER BY authorize ASC", ownerID, int(minAccessLevel))
+		results, err = x.Query("SELECT `id`,`authorize`,`repo_ids` FROM `team` WHERE org_id=? AND authorize>? ORDER BY `authorize` ASC", ownerID, int(minAccessLevel))
 		if err != nil {
 			return fmt.Errorf("select teams from org: %v", err)
 		}
@@ -281,20 +284,20 @@ func accessRefactor(x *xorm.Engine) (err error) {
 			teamID := com.StrTo(team["id"]).MustInt64()
 			mode := AccessMode(com.StrTo(team["authorize"]).MustInt())
 
-			results, err := x.Query("SELECT uid FROM team_user WHERE team_id=?", teamID)
+			results, err := x.Query("SELECT `uid` FROM `team_user` WHERE team_id=?", teamID)
 			if err != nil {
 				return fmt.Errorf("select users from team: %v", err)
 			}
 			for _, user := range results {
-				userID := com.StrTo(user["uid"]).MustInt64()
+				userID := com.StrTo(user["user_id"]).MustInt64()
 				accessMap[UserRepo{userID, repoID}] = mode
 			}
 		}
 	}
 
 	// Drop table can't be in a session (at least not in sqlite)
-	if _, err = x.Exec("DROP TABLE access"); err != nil {
-		return fmt.Errorf("drop table: %v", err)
+	if _, err = x.Exec("DROP TABLE `access`"); err != nil {
+		return fmt.Errorf("drop access table: %v", err)
 	}
 
 	// Now we start writing so we make a session
@@ -313,7 +316,56 @@ func accessRefactor(x *xorm.Engine) (err error) {
 		accesses = append(accesses, &Access{UserID: ur.UserID, RepoID: ur.RepoID, Mode: mode})
 	}
 
-	_, err = sess.Insert(accesses)
+	if _, err = sess.Insert(accesses); err != nil {
+		return fmt.Errorf("insert accesses: %v", err)
+	}
+
+	return sess.Commit()
+}
+
+func teamToTeamRepo(x *xorm.Engine) error {
+	type TeamRepo struct {
+		ID     int64 `xorm:"pk autoincr"`
+		OrgID  int64 `xorm:"INDEX"`
+		TeamID int64 `xorm:"UNIQUE(s)"`
+		RepoID int64 `xorm:"UNIQUE(s)"`
+	}
+
+	teamRepos := make([]*TeamRepo, 0, 50)
+
+	results, err := x.Query("SELECT `id`,`org_id`,`repo_ids` FROM `team`")
+	if err != nil {
+		return fmt.Errorf("select teams: %v", err)
+	}
+	for _, team := range results {
+		orgID := com.StrTo(team["org_id"]).MustInt64()
+		teamID := com.StrTo(team["id"]).MustInt64()
+
+		for _, idStr := range strings.Split(string(team["repo_ids"]), "|") {
+			repoID := com.StrTo(strings.TrimPrefix(idStr, "$")).MustInt64()
+			if repoID == 0 {
+				continue
+			}
+
+			teamRepos = append(teamRepos, &TeamRepo{
+				OrgID:  orgID,
+				TeamID: teamID,
+				RepoID: repoID,
+			})
+		}
+	}
+
+	sess := x.NewSession()
+	defer sessionRelease(sess)
+	if err = sess.Begin(); err != nil {
+		return err
+	}
+
+	if err = sess.Sync2(new(TeamRepo)); err != nil {
+		return fmt.Errorf("sync: %v", err)
+	} else if _, err = sess.Insert(teamRepos); err != nil {
+		return fmt.Errorf("insert team-repos: %v", err)
+	}
 
 	return sess.Commit()
 }
diff --git a/models/models.go b/models/models.go
index b0c3b8978f..a9436fca85 100644
--- a/models/models.go
+++ b/models/models.go
@@ -16,7 +16,7 @@ import (
 	"github.com/go-xorm/xorm"
 	_ "github.com/lib/pq"
 
-	// "github.com/gogits/gogs/models/migrations"
+	"github.com/gogits/gogs/models/migrations"
 	"github.com/gogits/gogs/modules/setting"
 )
 
@@ -59,7 +59,8 @@ func init() {
 		new(Watch), new(Star), new(Follow), new(Action),
 		new(Issue), new(Comment), new(Attachment), new(IssueUser), new(Label), new(Milestone),
 		new(Mirror), new(Release), new(LoginSource), new(Webhook),
-		new(UpdateTask), new(HookTask), new(Team), new(OrgUser), new(TeamUser),
+		new(UpdateTask), new(HookTask),
+		new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
 		new(Notice), new(EmailAddress))
 }
 
diff --git a/models/org.go b/models/org.go
index 8cb17b6054..629e9345c3 100644
--- a/models/org.go
+++ b/models/org.go
@@ -6,11 +6,10 @@ package models
 
 import (
 	"errors"
+	"fmt"
 	"os"
 	"strings"
 
-	"github.com/Unknwon/com"
-
 	"github.com/gogits/gogs/modules/base"
 )
 
@@ -126,53 +125,48 @@ func CreateOrganization(org, owner *User) (*User, error) {
 	org.NumMembers = 1
 
 	sess := x.NewSession()
-	defer sess.Close()
+	defer sessionRelease(sess)
 	if err = sess.Begin(); err != nil {
 		return nil, err
 	}
 
 	if _, err = sess.Insert(org); err != nil {
-		sess.Rollback()
-		return nil, err
-	}
-
-	if err = os.MkdirAll(UserPath(org.Name), os.ModePerm); err != nil {
-		sess.Rollback()
 		return nil, err
 	}
 
 	// Create default owner team.
 	t := &Team{
-		OrgId:      org.Id,
+		OrgID:      org.Id,
 		LowerName:  strings.ToLower(OWNER_TEAM),
 		Name:       OWNER_TEAM,
 		Authorize:  ACCESS_MODE_OWNER,
 		NumMembers: 1,
 	}
 	if _, err = sess.Insert(t); err != nil {
-		sess.Rollback()
 		return nil, err
 	}
 
 	// Add initial creator to organization and owner team.
 	ou := &OrgUser{
 		Uid:      owner.Id,
-		OrgId:    org.Id,
+		OrgID:    org.Id,
 		IsOwner:  true,
 		NumTeams: 1,
 	}
 	if _, err = sess.Insert(ou); err != nil {
-		sess.Rollback()
 		return nil, err
 	}
 
 	tu := &TeamUser{
 		Uid:    owner.Id,
-		OrgId:  org.Id,
-		TeamId: t.Id,
+		OrgID:  org.Id,
+		TeamID: t.ID,
 	}
 	if _, err = sess.Insert(tu); err != nil {
-		sess.Rollback()
+		return nil, err
+	}
+
+	if err = os.MkdirAll(UserPath(org.Name), os.ModePerm); err != nil {
 		return nil, err
 	}
 
@@ -223,15 +217,15 @@ func DeleteOrganization(org *User) (err error) {
 		return err
 	}
 
-	if _, err = sess.Delete(&Team{OrgId: org.Id}); err != nil {
+	if _, err = sess.Delete(&Team{OrgID: org.Id}); err != nil {
 		sess.Rollback()
 		return err
 	}
-	if _, err = sess.Delete(&OrgUser{OrgId: org.Id}); err != nil {
+	if _, err = sess.Delete(&OrgUser{OrgID: org.Id}); err != nil {
 		sess.Rollback()
 		return err
 	}
-	if _, err = sess.Delete(&TeamUser{OrgId: org.Id}); err != nil {
+	if _, err = sess.Delete(&TeamUser{OrgID: org.Id}); err != nil {
 		sess.Rollback()
 		return err
 	}
@@ -247,9 +241,9 @@ func DeleteOrganization(org *User) (err error) {
 
 // OrgUser represents an organization-user relation.
 type OrgUser struct {
-	Id       int64
+	ID       int64 `xorm:"pk autoincr"`
 	Uid      int64 `xorm:"INDEX UNIQUE(s)"`
-	OrgId    int64 `xorm:"INDEX UNIQUE(s)"`
+	OrgID    int64 `xorm:"INDEX UNIQUE(s)"`
 	IsPublic bool
 	IsOwner  bool
 	NumTeams int
@@ -298,7 +292,7 @@ func ChangeOrgUserStatus(orgId, uid int64, public bool) error {
 	}
 
 	ou.IsPublic = public
-	_, err = x.Id(ou.Id).AllCols().Update(ou)
+	_, err = x.Id(ou.ID).AllCols().Update(ou)
 	return err
 }
 
@@ -316,7 +310,7 @@ func AddOrgUser(orgId, uid int64) error {
 
 	ou := &OrgUser{
 		Uid:   uid,
-		OrgId: orgId,
+		OrgID: orgId,
 	}
 
 	if _, err := sess.Insert(ou); err != nil {
@@ -367,7 +361,7 @@ func RemoveOrgUser(orgId, uid int64) error {
 		return err
 	}
 
-	if _, err := sess.Id(ou.Id).Delete(ou); err != nil {
+	if _, err := sess.Id(ou.ID).Delete(ou); err != nil {
 		sess.Rollback()
 		return err
 	} else if _, err = sess.Exec("UPDATE `user` SET num_members = num_members - 1 WHERE id = ?", orgId); err != nil {
@@ -400,7 +394,7 @@ func RemoveOrgUser(orgId, uid int64) error {
 		return err
 	}
 	for _, t := range ts {
-		if err = removeTeamMember(sess, org.Id, t.Id, u.Id); err != nil {
+		if err = removeTeamMember(sess, org.Id, t.ID, u.Id); err != nil {
 			return err
 		}
 	}
@@ -419,13 +413,12 @@ const OWNER_TEAM = "Owners"
 
 // Team represents a organization team.
 type Team struct {
-	Id          int64
-	OrgId       int64 `xorm:"INDEX"`
+	ID          int64 `xorm:"pk autoincr"`
+	OrgID       int64 `xorm:"INDEX"`
 	LowerName   string
 	Name        string
 	Description string
 	Authorize   AccessMode
-	RepoIds     string        `xorm:"TEXT"`
 	Repos       []*Repository `xorm:"-"`
 	Members     []*User       `xorm:"-"`
 	NumRepos    int
@@ -439,23 +432,20 @@ func (t *Team) IsOwnerTeam() bool {
 
 // IsTeamMember returns true if given user is a member of team.
 func (t *Team) IsMember(uid int64) bool {
-	return IsTeamMember(t.OrgId, t.Id, uid)
+	return IsTeamMember(t.OrgID, t.ID, uid)
 }
 
-func (t *Team) getRepositories(e Engine) error {
-	idStrs := strings.Split(t.RepoIds, "|")
-	t.Repos = make([]*Repository, 0, len(idStrs))
-	for _, str := range idStrs {
-		if len(str) == 0 {
-			continue
-		}
-		id := com.StrTo(str[1:]).MustInt64()
-		if id == 0 {
-			continue
-		}
-		repo, err := getRepositoryById(e, id)
+func (t *Team) getRepositories(e Engine) (err error) {
+	teamRepos := make([]*TeamRepo, 0, t.NumRepos)
+	if err = x.Where("team_id=?", t.ID).Find(&teamRepos); err != nil {
+		return fmt.Errorf("get team-repos: %v", err)
+	}
+
+	t.Repos = make([]*Repository, 0, len(teamRepos))
+	for i := range teamRepos {
+		repo, err := getRepositoryById(e, teamRepos[i].RepoID)
 		if err != nil {
-			return err
+			return fmt.Errorf("getRepositoryById(%d): %v", teamRepos[i].RepoID, err)
 		}
 		t.Repos = append(t.Repos, repo)
 	}
@@ -468,7 +458,7 @@ func (t *Team) GetRepositories() error {
 }
 
 func (t *Team) getMembers(e Engine) (err error) {
-	t.Members, err = getTeamMembers(e, t.Id)
+	t.Members, err = getTeamMembers(e, t.ID)
 	return err
 }
 
@@ -479,96 +469,121 @@ func (t *Team) GetMembers() (err error) {
 
 // AddMember adds new member to team of organization.
 func (t *Team) AddMember(uid int64) error {
-	return AddTeamMember(t.OrgId, t.Id, uid)
+	return AddTeamMember(t.OrgID, t.ID, uid)
 }
 
 // RemoveMember removes member from team of organization.
 func (t *Team) RemoveMember(uid int64) error {
-	return RemoveTeamMember(t.OrgId, t.Id, uid)
+	return RemoveTeamMember(t.OrgID, t.ID, uid)
 }
 
-// AddRepository adds new repository to team of organization.
-func (t *Team) AddRepository(repo *Repository) (err error) {
-	idStr := "$" + com.ToStr(repo.Id) + "|"
-	if repo.OwnerId != t.OrgId {
-		return errors.New("Repository not belong to organization")
-	} else if strings.Contains(t.RepoIds, idStr) {
-		return nil
-	}
+func (t *Team) hasRepository(e Engine, repoID int64) bool {
+	return hasTeamRepo(e, t.OrgID, t.ID, repoID)
+}
 
-	if err = repo.GetOwner(); err != nil {
-		return err
-	} else if err = t.GetMembers(); err != nil {
-		return err
-	}
+// HasRepository returns true if given repository belong to team.
+func (t *Team) HasRepository(repoID int64) bool {
+	return HasTeamRepo(t.OrgID, t.ID, repoID)
+}
 
-	sess := x.NewSession()
-	defer sessionRelease(sess)
-	if err = sess.Begin(); err != nil {
+func (t *Team) addRepository(e Engine, repo *Repository) (err error) {
+	if err = addTeamRepo(e, t.OrgID, t.ID, repo.Id); err != nil {
 		return err
 	}
 
 	t.NumRepos++
-	t.RepoIds += idStr
-	if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
+	if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
 		return err
 	}
 
-	if err = repo.recalculateAccesses(sess); err != nil {
+	if err = repo.recalculateAccesses(e); err != nil {
 		return err
 	}
 
+	if err = t.getMembers(e); err != nil {
+		return fmt.Errorf("get team members: %v", err)
+	}
 	for _, u := range t.Members {
-		if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
+		if err = watchRepo(e, u.Id, repo.Id, true); err != nil {
 			return err
 		}
 	}
-	return sess.Commit()
-}
-
-func (t *Team) HasRepository(repo *Repository) bool {
-	idStr := "$" + com.ToStr(repo.Id) + "|"
-	return strings.Contains(t.RepoIds, idStr)
+	return nil
 }
 
-// RemoveRepository removes repository from team of organization.
-func (t *Team) RemoveRepository(repoId int64) error {
-	idStr := "$" + com.ToStr(repoId) + "|"
-	if !strings.Contains(t.RepoIds, idStr) {
+// AddRepository adds new repository to team of organization.
+func (t *Team) AddRepository(repo *Repository) (err error) {
+	if repo.OwnerId != t.OrgID {
+		return errors.New("Repository does not belong to organization")
+	} else if t.HasRepository(repo.Id) {
 		return nil
 	}
 
-	repo, err := GetRepositoryById(repoId)
-	if err != nil {
+	sess := x.NewSession()
+	defer sessionRelease(sess)
+	if err = sess.Begin(); err != nil {
 		return err
 	}
 
-	if err = repo.GetOwner(); err != nil {
-		return err
-	} else if err = t.GetMembers(); err != nil {
+	if err = t.addRepository(sess, repo); err != nil {
 		return err
 	}
 
-	sess := x.NewSession()
-	defer sessionRelease(sess)
-	if err = sess.Begin(); err != nil {
+	return sess.Commit()
+}
+
+func (t *Team) removeRepository(e Engine, repo *Repository) (err error) {
+	if err = removeTeamRepo(e, t.ID, repo.Id); err != nil {
 		return err
 	}
 
 	t.NumRepos--
-	t.RepoIds = strings.Replace(t.RepoIds, idStr, "", 1)
-	if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
+	if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
 		return err
 	}
 
-	if err = repo.recalculateAccesses(sess); err != nil {
+	if err = repo.recalculateAccesses(e); err != nil {
 		return err
 	}
 
+	if err = t.getMembers(e); err != nil {
+		return fmt.Errorf("get team members: %v", err)
+	}
 	for _, u := range t.Members {
-		if err = watchRepo(sess, u.Id, repo.Id, false); err != nil {
+		has, err := hasAccess(e, u, repo, ACCESS_MODE_READ)
+		if err != nil {
 			return err
+		} else if has {
+			continue
 		}
+
+		if err = watchRepo(e, u.Id, repo.Id, false); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// RemoveRepository removes repository from team of organization.
+func (t *Team) RemoveRepository(repoID int64) error {
+	if !t.HasRepository(repoID) {
+		return nil
+	}
+
+	repo, err := GetRepositoryById(repoID)
+	if err != nil {
+		return err
+	}
+
+	sess := x.NewSession()
+	defer sessionRelease(sess)
+	if err = sess.Begin(); err != nil {
+		return err
+	}
+
+	if err = t.removeRepository(sess, repo); err != nil {
+		return err
 	}
 
 	return sess.Commit()
@@ -581,7 +596,7 @@ func NewTeam(t *Team) error {
 		return ErrTeamNameIllegal
 	}
 
-	has, err := x.Id(t.OrgId).Get(new(User))
+	has, err := x.Id(t.OrgID).Get(new(User))
 	if err != nil {
 		return err
 	} else if !has {
@@ -589,7 +604,7 @@ func NewTeam(t *Team) error {
 	}
 
 	t.LowerName = strings.ToLower(t.Name)
-	has, err = x.Where("org_id=?", t.OrgId).And("lower_name=?", t.LowerName).Get(new(Team))
+	has, err = x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).Get(new(Team))
 	if err != nil {
 		return err
 	} else if has {
@@ -608,7 +623,7 @@ func NewTeam(t *Team) error {
 	}
 
 	// Update organization number of teams.
-	if _, err = sess.Exec("UPDATE `user` SET num_teams = num_teams + 1 WHERE id = ?", t.OrgId); err != nil {
+	if _, err = sess.Exec("UPDATE `user` SET num_teams = num_teams + 1 WHERE id = ?", t.OrgID); err != nil {
 		sess.Rollback()
 		return err
 	}
@@ -617,7 +632,7 @@ func NewTeam(t *Team) error {
 
 func getTeam(e Engine, orgId int64, name string) (*Team, error) {
 	t := &Team{
-		OrgId:     orgId,
+		OrgID:     orgId,
 		LowerName: strings.ToLower(name),
 	}
 	has, err := e.Get(t)
@@ -667,7 +682,7 @@ func UpdateTeam(t *Team, authChanged bool) (err error) {
 	}
 
 	t.LowerName = strings.ToLower(t.Name)
-	if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
+	if _, err = sess.Id(t.ID).AllCols().Update(t); err != nil {
 		return err
 	}
 
@@ -697,7 +712,7 @@ func DeleteTeam(t *Team) error {
 	}
 
 	// Get organization.
-	org, err := GetUserById(t.OrgId)
+	org, err := GetUserById(t.OrgID)
 	if err != nil {
 		return err
 	}
@@ -716,16 +731,16 @@ func DeleteTeam(t *Team) error {
 	}
 
 	// Delete team-user.
-	if _, err = sess.Where("org_id=?", org.Id).Where("team_id=?", t.Id).Delete(new(TeamUser)); err != nil {
+	if _, err = sess.Where("org_id=?", org.Id).Where("team_id=?", t.ID).Delete(new(TeamUser)); err != nil {
 		return err
 	}
 
 	// Delete team.
-	if _, err = sess.Id(t.Id).Delete(new(Team)); err != nil {
+	if _, err = sess.Id(t.ID).Delete(new(Team)); err != nil {
 		return err
 	}
 	// Update organization number of teams.
-	if _, err = sess.Exec("UPDATE `user` SET num_teams = num_teams - 1 WHERE id = ?", t.OrgId); err != nil {
+	if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
 		return err
 	}
 
@@ -741,20 +756,20 @@ func DeleteTeam(t *Team) error {
 
 // TeamUser represents an team-user relation.
 type TeamUser struct {
-	Id     int64
-	Uid    int64
-	OrgId  int64 `xorm:"INDEX"`
-	TeamId int64
+	ID     int64 `xorm:"pk autoincr"`
+	OrgID  int64 `xorm:"INDEX"`
+	TeamID int64 `xorm:"UNIQUE(s)"`
+	Uid    int64 `xorm:"UNIQUE(s)"`
 }
 
-func isTeamMember(e Engine, orgId, teamId, uid int64) bool {
-	has, _ := e.Where("uid=?", uid).And("org_id=?", orgId).And("team_id=?", teamId).Get(new(TeamUser))
+func isTeamMember(e Engine, orgID, teamID, uid int64) bool {
+	has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("uid=?", uid).Get(new(TeamUser))
 	return has
 }
 
 // IsTeamMember returns true if given user is a member of team.
-func IsTeamMember(orgId, teamId, uid int64) bool {
-	return isTeamMember(x, orgId, teamId, uid)
+func IsTeamMember(orgID, teamID, uid int64) bool {
+	return isTeamMember(x, orgID, teamID, uid)
 }
 
 func getTeamMembers(e Engine, teamID int64) ([]*User, error) {
@@ -777,7 +792,7 @@ func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) {
 	ts := make([]*Team, len(tus))
 	for i, tu := range tus {
 		t := new(Team)
-		has, err := e.Id(tu.TeamId).Get(t)
+		has, err := e.Id(tu.TeamID).Get(t)
 		if err != nil {
 			return nil, err
 		} else if !has {
@@ -822,13 +837,13 @@ func AddTeamMember(orgId, teamId, uid int64) error {
 
 	tu := &TeamUser{
 		Uid:    uid,
-		OrgId:  orgId,
-		TeamId: teamId,
+		OrgID:  orgId,
+		TeamID: teamId,
 	}
 
 	if _, err = sess.Insert(tu); err != nil {
 		return err
-	} else if _, err = sess.Id(t.Id).Update(t); err != nil {
+	} else if _, err = sess.Id(t.ID).Update(t); err != nil {
 		return err
 	}
 
@@ -848,7 +863,7 @@ func AddTeamMember(orgId, teamId, uid int64) error {
 	if t.IsOwnerTeam() {
 		ou.IsOwner = true
 	}
-	if _, err = sess.Id(ou.Id).AllCols().Update(ou); err != nil {
+	if _, err = sess.Id(ou.ID).AllCols().Update(ou); err != nil {
 		return err
 	}
 
@@ -885,13 +900,13 @@ func removeTeamMember(e Engine, orgId, teamId, uid int64) error {
 
 	tu := &TeamUser{
 		Uid:    uid,
-		OrgId:  orgId,
-		TeamId: teamId,
+		OrgID:  orgId,
+		TeamID: teamId,
 	}
 
 	if _, err := e.Delete(tu); err != nil {
 		return err
-	} else if _, err = e.Id(t.Id).AllCols().Update(t); err != nil {
+	} else if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
 		return err
 	}
 
@@ -912,7 +927,7 @@ func removeTeamMember(e Engine, orgId, teamId, uid int64) error {
 	if t.IsOwnerTeam() {
 		ou.IsOwner = false
 	}
-	if _, err = e.Id(ou.Id).AllCols().Update(ou); err != nil {
+	if _, err = e.Id(ou.ID).AllCols().Update(ou); err != nil {
 		return err
 	}
 	return nil
@@ -921,13 +936,64 @@ func removeTeamMember(e Engine, orgId, teamId, uid int64) error {
 // RemoveTeamMember removes member from given team of given organization.
 func RemoveTeamMember(orgId, teamId, uid int64) error {
 	sess := x.NewSession()
-	defer sess.Close()
+	defer sessionRelease(sess)
 	if err := sess.Begin(); err != nil {
 		return err
 	}
 	if err := removeTeamMember(sess, orgId, teamId, uid); err != nil {
-		sess.Rollback()
 		return err
 	}
 	return sess.Commit()
 }
+
+// ___________                  __________
+// \__    ___/___ _____    _____\______   \ ____ ______   ____
+//   |    |_/ __ \\__  \  /     \|       _// __ \\____ \ /  _ \
+//   |    |\  ___/ / __ \|  Y Y  \    |   \  ___/|  |_> >  <_> )
+//   |____| \___  >____  /__|_|  /____|_  /\___  >   __/ \____/
+//              \/     \/      \/       \/     \/|__|
+
+// TeamRepo represents an team-repository relation.
+type TeamRepo struct {
+	ID     int64 `xorm:"pk autoincr"`
+	OrgID  int64 `xorm:"INDEX"`
+	TeamID int64 `xorm:"UNIQUE(s)"`
+	RepoID int64 `xorm:"UNIQUE(s)"`
+}
+
+func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool {
+	has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("repo_id=?", repoID).Get(new(TeamRepo))
+	return has
+}
+
+// HasTeamRepo returns true if given repository belongs to team.
+func HasTeamRepo(orgID, teamID, repoID int64) bool {
+	return hasTeamRepo(x, orgID, teamID, repoID)
+}
+
+func addTeamRepo(e Engine, orgID, teamID, repoID int64) error {
+	_, err := e.InsertOne(&TeamRepo{
+		OrgID:  orgID,
+		TeamID: teamID,
+		RepoID: repoID,
+	})
+	return err
+}
+
+// AddTeamRepo adds new repository relation to team.
+func AddTeamRepo(orgID, teamID, repoID int64) error {
+	return addTeamRepo(x, orgID, teamID, repoID)
+}
+
+func removeTeamRepo(e Engine, teamID, repoID int64) error {
+	_, err := e.Delete(&TeamRepo{
+		TeamID: teamID,
+		RepoID: repoID,
+	})
+	return err
+}
+
+// RemoveTeamRepo deletes repository relation to team.
+func RemoveTeamRepo(teamID, repoID int64) error {
+	return removeTeamRepo(x, teamID, repoID)
+}
diff --git a/models/repo.go b/models/repo.go
index bfa5925c35..7f87bbb2a7 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -514,7 +514,7 @@ func initRepository(e Engine, f string, u *User, repo *Repository, initReadme bo
 }
 
 // CreateRepository creates a repository for given user or organization.
-func CreateRepository(u *User, name, desc, lang, license string, private, mirror, initReadme bool) (*Repository, error) {
+func CreateRepository(u *User, name, desc, lang, license string, isPrivate, isMirror, initReadme bool) (*Repository, error) {
 	if !IsLegalName(name) {
 		return nil, ErrRepoNameIllegal
 	}
@@ -532,7 +532,7 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror
 		Name:        name,
 		LowerName:   strings.ToLower(name),
 		Description: desc,
-		IsPrivate:   private,
+		IsPrivate:   isPrivate,
 	}
 
 	sess := x.NewSession()
@@ -558,34 +558,22 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror
 		// Update owner team info and count.
 		t, err := u.getOwnerTeam(sess)
 		if err != nil {
-			return nil, fmt.Errorf("get owner team: %v", err)
-		} else if err = t.getMembers(sess); err != nil {
-			return nil, fmt.Errorf("get team members: %v", err)
-		}
-
-		for _, u := range t.Members {
-			if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
-				return nil, fmt.Errorf("watch repository: %v", err)
-			}
-		}
-
-		t.RepoIds += "$" + com.ToStr(repo.Id) + "|"
-		t.NumRepos++
-		if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
-			return nil, err
+			return nil, fmt.Errorf("getOwnerTeam: %v", err)
+		} else if err = t.addRepository(sess, repo); err != nil {
+			return nil, fmt.Errorf("addRepository: %v", err)
 		}
 	} else {
 		if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
-			return nil, fmt.Errorf("watch repository 2: %v", err)
+			return nil, fmt.Errorf("watchRepo: %v", err)
 		}
 	}
 
 	if err = newRepoAction(sess, u, repo); err != nil {
-		return nil, fmt.Errorf("new repository action: %v", err)
+		return nil, fmt.Errorf("newRepoAction: %v", err)
 	}
 
 	// No need for init mirror.
-	if !mirror {
+	if !isMirror {
 		repoPath := RepoPath(u.Name, repo.Name)
 		if err = initRepository(sess, repoPath, u, repo, initReadme, lang, license); err != nil {
 			if err2 := os.RemoveAll(repoPath); err2 != nil {
@@ -640,14 +628,14 @@ func RepoPath(userName, repoName string) string {
 }
 
 // TransferOwnership transfers all corresponding setting from old user to new one.
-func TransferOwnership(u *User, newOwner string, repo *Repository) error {
-	newUser, err := GetUserByName(newOwner)
+func TransferOwnership(u *User, newOwnerName string, repo *Repository) error {
+	newOwner, err := GetUserByName(newOwnerName)
 	if err != nil {
-		return fmt.Errorf("fail to get new owner(%s): %v", newOwner, err)
+		return fmt.Errorf("get new owner(%s): %v", newOwnerName, err)
 	}
 
 	// Check if new owner has repository with same name.
-	has, err := IsRepositoryExist(newUser, repo.Name)
+	has, err := IsRepositoryExist(newOwner, repo.Name)
 	if err != nil {
 		return err
 	} else if has {
@@ -663,8 +651,8 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error {
 	owner := repo.Owner
 
 	// Update repository.
-	repo.OwnerId = newUser.Id
-	repo.Owner = newUser
+	repo.OwnerId = newOwner.Id
+	repo.Owner = newOwner
 	if _, err := sess.Id(repo.Id).Update(repo); err != nil {
 		return err
 	}
@@ -675,28 +663,38 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error {
 		return err
 	}
 	for _, c := range collaborators {
-		if c.Id == newUser.Id || newUser.IsOrgMember(c.Id) {
+		if c.Id == newOwner.Id || newOwner.IsOrgMember(c.Id) {
 			if _, err = sess.Delete(&Collaboration{RepoID: repo.Id, UserID: c.Id}); err != nil {
 				return err
 			}
 		}
 	}
 
+	if newOwner.IsOrganization() {
+		// Update owner team info and count.
+		t, err := newOwner.GetOwnerTeam()
+		if err != nil {
+			return err
+		} else if err = t.addRepository(sess, repo); err != nil {
+			return err
+		}
+	}
+
 	// Update user repository number.
-	if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", newUser.Id); err != nil {
+	if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", newOwner.Id); err != nil {
 		return err
 	} else if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", owner.Id); err != nil {
 		return err
 	} else if err = repo.recalculateAccesses(sess); err != nil {
 		return err
-	} else if err = watchRepo(sess, newUser.Id, repo.Id, true); err != nil {
+	} else if err = watchRepo(sess, newOwner.Id, repo.Id, true); err != nil {
 		return err
-	} else if err = transferRepoAction(sess, u, newUser, repo); err != nil {
+	} else if err = transferRepoAction(sess, u, owner, newOwner, repo); err != nil {
 		return err
 	}
 
 	// Change repository directory name.
-	if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newUser.Name, repo.Name)); err != nil {
+	if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil {
 		return err
 	}
 
@@ -734,8 +732,8 @@ func UpdateRepository(repo *Repository) error {
 }
 
 // DeleteRepository deletes a repository for a user or organization.
-func DeleteRepository(uid, repoId int64, userName string) error {
-	repo := &Repository{Id: repoId, OwnerId: uid}
+func DeleteRepository(uid, repoID int64, userName string) error {
+	repo := &Repository{Id: repoID, OwnerId: uid}
 	has, err := x.Get(repo)
 	if err != nil {
 		return err
@@ -761,70 +759,66 @@ func DeleteRepository(uid, repoId int64, userName string) error {
 	}
 
 	if org.IsOrganization() {
-		idStr := "$" + com.ToStr(repoId) + "|"
 		for _, t := range org.Teams {
-			if !strings.Contains(t.RepoIds, idStr) {
+			if !t.hasRepository(sess, repoID) {
 				continue
-			}
-			t.NumRepos--
-			t.RepoIds = strings.Replace(t.RepoIds, idStr, "", 1)
-			if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
+			} else if err = t.removeRepository(sess, repo); err != nil {
 				return err
 			}
 		}
 	}
 
-	if _, err = sess.Delete(&Repository{Id: repoId}); err != nil {
+	if _, err = sess.Delete(&Repository{Id: repoID}); err != nil {
 		return err
 	} else if _, err := sess.Delete(&Access{RepoID: repo.Id}); err != nil {
 		return err
 	} else if _, err := sess.Delete(&Action{RepoId: repo.Id}); err != nil {
 		return err
-	} else if _, err = sess.Delete(&Watch{RepoId: repoId}); err != nil {
+	} else if _, err = sess.Delete(&Watch{RepoId: repoID}); err != nil {
 		return err
-	} else if _, err = sess.Delete(&Mirror{RepoId: repoId}); err != nil {
+	} else if _, err = sess.Delete(&Mirror{RepoId: repoID}); err != nil {
 		return err
-	} else if _, err = sess.Delete(&IssueUser{RepoId: repoId}); err != nil {
+	} else if _, err = sess.Delete(&IssueUser{RepoId: repoID}); err != nil {
 		return err
-	} else if _, err = sess.Delete(&Milestone{RepoId: repoId}); err != nil {
+	} else if _, err = sess.Delete(&Milestone{RepoId: repoID}); err != nil {
 		return err
-	} else if _, err = sess.Delete(&Release{RepoId: repoId}); err != nil {
+	} else if _, err = sess.Delete(&Release{RepoId: repoID}); err != nil {
 		return err
-	} else if _, err = sess.Delete(&Collaboration{RepoID: repoId}); err != nil {
+	} else if _, err = sess.Delete(&Collaboration{RepoID: repoID}); err != nil {
 		return err
 	}
 
 	// Delete comments.
-	if err = x.Iterate(&Issue{RepoId: repoId}, func(idx int, bean interface{}) error {
-		issue := bean.(*Issue)
-		if _, err = sess.Delete(&Comment{IssueId: issue.Id}); err != nil {
+	issues := make([]*Issue, 0, 25)
+	if err = sess.Where("repo_id=?", repoID).Find(&issues); err != nil {
+		return err
+	}
+	for i := range issues {
+		if _, err = sess.Delete(&Comment{IssueId: issues[i].Id}); err != nil {
 			return err
 		}
-		return nil
-	}); err != nil {
-		return err
 	}
 
-	if _, err = sess.Delete(&Issue{RepoId: repoId}); err != nil {
+	if _, err = sess.Delete(&Issue{RepoId: repoID}); err != nil {
 		return err
 	}
 
 	if repo.IsFork {
-		if _, err = sess.Exec("UPDATE `repository` SET num_forks = num_forks - 1 WHERE id = ?", repo.ForkId); err != nil {
+		if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repo.ForkId); err != nil {
 			return err
 		}
 	}
 
-	if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", uid); err != nil {
+	if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", uid); err != nil {
 		return err
 	}
 
 	// Remove repository files.
 	if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil {
-		desc := fmt.Sprintf("Fail to delete repository files(%s/%s): %v", userName, repo.Name, err)
+		desc := fmt.Sprintf("delete repository files(%s/%s): %v", userName, repo.Name, err)
 		log.Warn(desc)
 		if err = CreateRepositoryNotice(desc); err != nil {
-			log.Error(4, "Fail to add notice: %v", err)
+			log.Error(4, "add notice: %v", err)
 		}
 	}
 
@@ -1328,33 +1322,21 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repositor
 		// Update owner team info and count.
 		t, err := u.getOwnerTeam(sess)
 		if err != nil {
-			return nil, fmt.Errorf("get owner team: %v", err)
-		} else if err = t.getMembers(sess); err != nil {
-			return nil, fmt.Errorf("get team members: %v", err)
-		}
-
-		for _, u := range t.Members {
-			if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
-				return nil, fmt.Errorf("watch repository: %v", err)
-			}
-		}
-
-		t.RepoIds += "$" + com.ToStr(repo.Id) + "|"
-		t.NumRepos++
-		if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
-			return nil, err
+			return nil, fmt.Errorf("getOwnerTeam: %v", err)
+		} else if err = t.addRepository(sess, repo); err != nil {
+			return nil, fmt.Errorf("addRepository: %v", err)
 		}
 	} else {
 		if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
-			return nil, fmt.Errorf("watch repository 2: %v", err)
+			return nil, fmt.Errorf("watchRepo: %v", err)
 		}
 	}
 
 	if err = newRepoAction(sess, u, repo); err != nil {
-		return nil, fmt.Errorf("new repository action: %v", err)
+		return nil, fmt.Errorf("newRepoAction: %v", err)
 	}
 
-	if _, err = sess.Exec("UPDATE `repository` SET num_forks = num_forks + 1 WHERE id = ?", oldRepo.Id); err != nil {
+	if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", oldRepo.Id); err != nil {
 		return nil, err
 	}
 
diff --git a/models/user.go b/models/user.go
index 387ab6555b..ea5041bdff 100644
--- a/models/user.go
+++ b/models/user.go
@@ -231,7 +231,7 @@ func (u *User) GetOrganizations() error {
 
 	u.Orgs = make([]*User, len(ous))
 	for i, ou := range ous {
-		u.Orgs[i], err = GetUserById(ou.OrgId)
+		u.Orgs[i], err = GetUserById(ou.OrgID)
 		if err != nil {
 			return err
 		}
@@ -860,7 +860,7 @@ func UpdateMentions(userNames []string, issueId int64) error {
 		}
 
 		for _, orgUser := range orgUsers {
-			tempIds = append(tempIds, orgUser.Id)
+			tempIds = append(tempIds, orgUser.ID)
 		}
 
 		ids = append(ids, tempIds...)
diff --git a/routers/org/teams.go b/routers/org/teams.go
index 2fbb1480d1..69f2734c64 100644
--- a/routers/org/teams.go
+++ b/routers/org/teams.go
@@ -181,7 +181,7 @@ func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) {
 	org := ctx.Org.Organization
 
 	t := &models.Team{
-		OrgId:       org.Id,
+		OrgID:       org.Id,
 		Name:        form.TeamName,
 		Description: form.Description,
 		Authorize:   auth,
diff --git a/routers/repo/setting.go b/routers/repo/setting.go
index 0f8acfa5ba..e2b8968375 100644
--- a/routers/repo/setting.go
+++ b/routers/repo/setting.go
@@ -8,7 +8,6 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"path"
 	"strings"
 	"time"
 
diff --git a/templates/.VERSION b/templates/.VERSION
index 056f3141af..4758bb670f 100644
--- a/templates/.VERSION
+++ b/templates/.VERSION
@@ -1 +1 @@
-0.5.14.0222 Beta
+0.5.15.0223 Beta
\ No newline at end of file
-- 
cgit v1.2.3


From 8896c82d34ec5d86af1eeafb8c55a06f81bd3af1 Mon Sep 17 00:00:00 2001
From: Unknwon <u@gogs.io>
Date: Tue, 24 Feb 2015 00:27:22 -0500
Subject: models: fix issue with transfer repository

README: fix typo
---
 README.md               |  6 ++--
 README_ZH.md            |  6 ++--
 gogs.go                 |  2 +-
 models/access.go        | 10 ++++--
 models/org.go           | 28 ++++++++++-----
 models/repo.go          | 90 ++++++++++++++++++++++++-------------------------
 routers/repo/setting.go |  8 ++---
 templates/.VERSION      |  2 +-
 8 files changed, 80 insertions(+), 72 deletions(-)

(limited to 'routers/repo/setting.go')

diff --git a/README.md b/README.md
index 150aa64de8..ff4edd0935 100644
--- a/README.md
+++ b/README.md
@@ -7,13 +7,13 @@ Gogs (Go Git Service) is a painless self-hosted Git service written in Go.
 
 ![Demo](http://gogs.qiniudn.com/gogs_demo.gif)
 
-##### Current version: 0.5.13 Beta
+##### Current version: 0.5.15 Beta
 
 ### NOTICES
 
 - Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) has been reset in **Jan 28, 2015** and will reset multiple times after. Please do **NOT** put your important data on the site.
 - The demo site [try.gogs.io](https://try.gogs.io) is running under `dev` branch.
-- You **MUST** read [CONTRIBUTING.md](CONTRIBUTING.md) before you start filing a issue or making a Pull Request.
+- You **MUST** read [CONTRIBUTING.md](CONTRIBUTING.md) before you start filing an issue or making a Pull Request.
 - If you think there are vulnerabilities in the project, please talk privately to **u@gogs.io**.  Thanks!
 
 #### Other language version
@@ -57,7 +57,7 @@ The goal of this project is to make the easiest, fastest, and most painless way
 ## System Requirements
 
 - A cheap Raspberry Pi is powerful enough for basic functionality.
-- At least 4 CPU cores and 1GB RAM would be the baseline for teamwork.
+- At least 2 CPU cores and 1GB RAM would be the baseline for teamwork.
 
 ## Installation
 
diff --git a/README_ZH.md b/README_ZH.md
index b6d74e7b14..ffbbf0dd71 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。
 
 ![Demo](http://gogs.qiniudn.com/gogs_demo.gif)
 
-##### 当前版本:0.5.13 Beta
+##### 当前版本:0.5.15 Beta
 
 ## 开发目的
 
@@ -44,7 +44,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
 ## 系统要求
 
 - 最低的系统硬件要求为一个廉价的树莓派
-- 如果用于团队项目,建议使用 4 核 CPU 及 1GB 内存
+- 如果用于团队项目,建议使用 2 核 CPU 及 1GB 内存
 
 ## 安装部署
 
@@ -75,4 +75,4 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
 
 ## 授权许可
 
-本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) 文件中。
\ No newline at end of file
+本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) 文件中。
diff --git a/gogs.go b/gogs.go
index 58cbb1a05c..930203afa3 100644
--- a/gogs.go
+++ b/gogs.go
@@ -17,7 +17,7 @@ import (
 	"github.com/gogits/gogs/modules/setting"
 )
 
-const APP_VER = "0.5.15.0223 Beta"
+const APP_VER = "0.5.15.0224 Beta"
 
 func init() {
 	runtime.GOMAXPROCS(runtime.NumCPU())
diff --git a/models/access.go b/models/access.go
index 29d1a730b7..683282f336 100644
--- a/models/access.go
+++ b/models/access.go
@@ -4,6 +4,10 @@
 
 package models
 
+import (
+	"fmt"
+)
+
 type AccessMode int
 
 const (
@@ -128,7 +132,7 @@ func (repo *Repository) recalculateAccesses(e Engine) error {
 			}
 
 			if err = team.getMembers(e); err != nil {
-				return err
+				return fmt.Errorf("getMembers '%d': %v", team.ID, err)
 			}
 			for _, u := range team.Members {
 				accessMap[u.Id] = maxAccessMode(accessMap[u.Id], team.Authorize)
@@ -157,9 +161,9 @@ func (repo *Repository) recalculateAccesses(e Engine) error {
 
 	// Delete old accesses and insert new ones for repository.
 	if _, err = e.Delete(&Access{RepoID: repo.Id}); err != nil {
-		return err
+		return fmt.Errorf("delete old accesses: %v", err)
 	} else if _, err = e.Insert(newAccesses); err != nil {
-		return err
+		return fmt.Errorf("insert new accesses: %v", err)
 	}
 
 	return nil
diff --git a/models/org.go b/models/org.go
index 629e9345c3..b47fbced5f 100644
--- a/models/org.go
+++ b/models/org.go
@@ -28,7 +28,7 @@ func (org *User) IsOwnedBy(uid int64) bool {
 
 // IsOrgMember returns true if given user is member of organization.
 func (org *User) IsOrgMember(uid int64) bool {
-	return IsOrganizationMember(org.Id, uid)
+	return org.IsOrganization() && IsOrganizationMember(org.Id, uid)
 }
 
 func (org *User) getTeam(e Engine, name string) (*Team, error) {
@@ -493,19 +493,19 @@ func (t *Team) addRepository(e Engine, repo *Repository) (err error) {
 
 	t.NumRepos++
 	if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
-		return err
+		return fmt.Errorf("update team: %v", err)
 	}
 
 	if err = repo.recalculateAccesses(e); err != nil {
-		return err
+		return fmt.Errorf("recalculateAccesses: %v", err)
 	}
 
 	if err = t.getMembers(e); err != nil {
-		return fmt.Errorf("get team members: %v", err)
+		return fmt.Errorf("getMembers: %v", err)
 	}
 	for _, u := range t.Members {
 		if err = watchRepo(e, u.Id, repo.Id, true); err != nil {
-			return err
+			return fmt.Errorf("watchRepo: %v", err)
 		}
 	}
 	return nil
@@ -772,10 +772,20 @@ func IsTeamMember(orgID, teamID, uid int64) bool {
 	return isTeamMember(x, orgID, teamID, uid)
 }
 
-func getTeamMembers(e Engine, teamID int64) ([]*User, error) {
-	us := make([]*User, 0, 10)
-	err := e.Sql("SELECT * FROM `user` JOIN `team_user` ON `team_user`.`team_id` = ? AND `team_user`.`uid` = `user`.`id`", teamID).Find(&us)
-	return us, err
+func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) {
+	teamUsers := make([]*TeamUser, 0, 10)
+	if err = e.Where("team_id=?", teamID).Find(&teamUsers); err != nil {
+		return nil, fmt.Errorf("get team-users: %v", err)
+	}
+	members := make([]*User, 0, len(teamUsers))
+	for i := range teamUsers {
+		member := new(User)
+		if _, err = e.Id(teamUsers[i].Uid).Get(member); err != nil {
+			return nil, fmt.Errorf("get user '%d': %v", teamUsers[i].Uid, err)
+		}
+		members = append(members, member)
+	}
+	return members, nil
 }
 
 // GetTeamMembers returns all members in given team of organization.
diff --git a/models/repo.go b/models/repo.go
index 7f87bbb2a7..0f75029096 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -223,16 +223,12 @@ func (repo *Repository) DescriptionHtml() template.HTML {
 }
 
 // IsRepositoryExist returns true if the repository with given name under user has already existed.
-func IsRepositoryExist(u *User, repoName string) (bool, error) {
-	repo := Repository{OwnerId: u.Id}
-	has, err := x.Where("lower_name = ?", strings.ToLower(repoName)).Get(&repo)
-	if err != nil {
-		return has, err
-	} else if !has {
-		return false, nil
-	}
-
-	return com.IsDir(RepoPath(u.Name, repoName)), nil
+func IsRepositoryExist(u *User, repoName string) bool {
+	has, _ := x.Get(&Repository{
+		OwnerId:   u.Id,
+		LowerName: strings.ToLower(repoName),
+	})
+	return has && com.IsDir(RepoPath(u.Name, repoName))
 }
 
 // CloneLink represents different types of clone URLs of repository.
@@ -514,15 +510,12 @@ func initRepository(e Engine, f string, u *User, repo *Repository, initReadme bo
 }
 
 // CreateRepository creates a repository for given user or organization.
-func CreateRepository(u *User, name, desc, lang, license string, isPrivate, isMirror, initReadme bool) (*Repository, error) {
+func CreateRepository(u *User, name, desc, lang, license string, isPrivate, isMirror, initReadme bool) (_ *Repository, err error) {
 	if !IsLegalName(name) {
 		return nil, ErrRepoNameIllegal
 	}
 
-	isExist, err := IsRepositoryExist(u, name)
-	if err != nil {
-		return nil, err
-	} else if isExist {
+	if IsRepositoryExist(u, name) {
 		return nil, ErrRepoAlreadyExist
 	}
 
@@ -631,71 +624,79 @@ func RepoPath(userName, repoName string) string {
 func TransferOwnership(u *User, newOwnerName string, repo *Repository) error {
 	newOwner, err := GetUserByName(newOwnerName)
 	if err != nil {
-		return fmt.Errorf("get new owner(%s): %v", newOwnerName, err)
+		return fmt.Errorf("get new owner '%s': %v", newOwnerName, err)
 	}
 
 	// Check if new owner has repository with same name.
-	has, err := IsRepositoryExist(newOwner, repo.Name)
-	if err != nil {
-		return err
-	} else if has {
+	if IsRepositoryExist(newOwner, repo.Name) {
 		return ErrRepoAlreadyExist
 	}
 
 	sess := x.NewSession()
 	defer sessionRelease(sess)
 	if err = sess.Begin(); err != nil {
-		return err
+		return fmt.Errorf("sess.Begin: %v", err)
 	}
 
 	owner := repo.Owner
 
-	// Update repository.
+	// Note: we have to set value here to make sure recalculate accesses is based on
+	//	new owner.
 	repo.OwnerId = newOwner.Id
 	repo.Owner = newOwner
+
+	// Update repository.
 	if _, err := sess.Id(repo.Id).Update(repo); err != nil {
-		return err
+		return fmt.Errorf("update owner: %v", err)
 	}
 
-	// Remove redundant collaborators
+	// Remove redundant collaborators.
 	collaborators, err := repo.GetCollaborators()
 	if err != nil {
-		return err
+		return fmt.Errorf("GetCollaborators: %v", err)
 	}
+
+	// Dummy object.
+	collaboration := &Collaboration{RepoID: repo.Id}
 	for _, c := range collaborators {
+		collaboration.UserID = c.Id
 		if c.Id == newOwner.Id || newOwner.IsOrgMember(c.Id) {
-			if _, err = sess.Delete(&Collaboration{RepoID: repo.Id, UserID: c.Id}); err != nil {
-				return err
+			if _, err = sess.Delete(collaboration); err != nil {
+				return fmt.Errorf("remove collaborator '%d': %v", c.Id, err)
 			}
 		}
 	}
 
 	if newOwner.IsOrganization() {
-		// Update owner team info and count.
 		t, err := newOwner.GetOwnerTeam()
 		if err != nil {
-			return err
+			return fmt.Errorf("GetOwnerTeam: %v", err)
 		} else if err = t.addRepository(sess, repo); err != nil {
-			return err
+			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)
 		}
 	}
 
-	// Update user repository number.
-	if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", newOwner.Id); err != nil {
-		return err
-	} else if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", owner.Id); err != nil {
-		return err
-	} else if err = repo.recalculateAccesses(sess); err != nil {
-		return err
-	} else if err = watchRepo(sess, newOwner.Id, repo.Id, true); err != nil {
-		return err
+	// Update repository count.
+	if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos+1 WHERE id=?", newOwner.Id); err != nil {
+		return fmt.Errorf("increase new owner repository count: %v", err)
+	} else if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", owner.Id); err != nil {
+		return fmt.Errorf("decrease old owner repository count: %v", err)
+	}
+
+	if err = watchRepo(sess, newOwner.Id, repo.Id, true); err != nil {
+		return fmt.Errorf("watchRepo: %v", err)
 	} else if err = transferRepoAction(sess, u, owner, newOwner, repo); err != nil {
-		return err
+		return fmt.Errorf("transferRepoAction: %v", err)
 	}
 
 	// Change repository directory name.
 	if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil {
-		return err
+		return fmt.Errorf("rename directory: %v", err)
 	}
 
 	return sess.Commit()
@@ -1275,11 +1276,8 @@ func IsStaring(uid, repoId int64) bool {
 //  \___  / \____/|__|  |__|_ \
 //      \/                   \/
 
-func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repository, error) {
-	isExist, err := IsRepositoryExist(u, name)
-	if err != nil {
-		return nil, err
-	} else if isExist {
+func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) {
+	if IsRepositoryExist(u, name) {
 		return nil, ErrRepoAlreadyExist
 	}
 
diff --git a/routers/repo/setting.go b/routers/repo/setting.go
index e2b8968375..5cd39ada2c 100644
--- a/routers/repo/setting.go
+++ b/routers/repo/setting.go
@@ -53,15 +53,11 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
 		newRepoName := form.RepoName
 		// Check if repository name has been changed.
 		if ctx.Repo.Repository.Name != newRepoName {
-			isExist, err := models.IsRepositoryExist(ctx.Repo.Owner, newRepoName)
-			if err != nil {
-				ctx.Handle(500, "IsRepositoryExist", err)
-				return
-			} else if isExist {
+			if models.IsRepositoryExist(ctx.Repo.Owner, newRepoName) {
 				ctx.Data["Err_RepoName"] = true
 				ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), SETTINGS_OPTIONS, nil)
 				return
-			} else if err = models.ChangeRepositoryName(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName); err != nil {
+			} else if err := models.ChangeRepositoryName(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name, newRepoName); err != nil {
 				if err == models.ErrRepoNameIllegal {
 					ctx.Data["Err_RepoName"] = true
 					ctx.RenderWithErr(ctx.Tr("form.illegal_repo_name"), SETTINGS_OPTIONS, nil)
diff --git a/templates/.VERSION b/templates/.VERSION
index 4758bb670f..64dc6626d5 100644
--- a/templates/.VERSION
+++ b/templates/.VERSION
@@ -1 +1 @@
-0.5.15.0223 Beta
\ No newline at end of file
+0.5.15.0224 Beta
\ No newline at end of file
-- 
cgit v1.2.3