mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	#1021 Forked repo unavailable after deleting original repo
This commit is contained in:
		| @@ -319,6 +319,7 @@ repo_name = Repository Name | |||||||
| repo_name_helper = A good repository name is usually composed of short, memorable and unique keywords. | repo_name_helper = A good repository name is usually composed of short, memorable and unique keywords. | ||||||
| visibility = Visibility | visibility = Visibility | ||||||
| visiblity_helper = This repository is <span class="ui red text">Private</span> | visiblity_helper = This repository is <span class="ui red text">Private</span> | ||||||
|  | visiblity_fork_helper = (Change of this value will affect all forks) | ||||||
| fork_repo = Fork Repository | fork_repo = Fork Repository | ||||||
| fork_from = Fork From | fork_from = Fork From | ||||||
| fork_visiblity_helper = You cannot alter the visibility of a forked repository. | fork_visiblity_helper = You cannot alter the visibility of a forked repository. | ||||||
| @@ -508,6 +509,9 @@ settings.transfer_notices_2 = - You will conserve access if new owner is an orga | |||||||
| settings.transfer_form_title = Please enter following information to confirm your operation:  | settings.transfer_form_title = Please enter following information to confirm your operation:  | ||||||
| settings.delete_notices_1 = - This operation <strong>CANNOT</strong> be undone. | settings.delete_notices_1 = - This operation <strong>CANNOT</strong> be undone. | ||||||
| settings.delete_notices_2 = - This operation will permanently delete the everything of this repository, including Git data, issues, comments and accesses of collaborators. | settings.delete_notices_2 = - This operation will permanently delete the everything of this repository, including Git data, issues, comments and accesses of collaborators. | ||||||
|  | settings.delete_notices_fork_1 = - If this repository is public, all forks will be became independent after deletion. | ||||||
|  | settings.delete_notices_fork_2 = - If this repository is private, all forks will be removed at the same time. | ||||||
|  | settings.delete_notices_fork_3 = - If you want to keep all forks after deletion, please change visibility of this repository to public first. | ||||||
| settings.update_settings_success = Repository options has been updated successfully. | settings.update_settings_success = Repository options has been updated successfully. | ||||||
| settings.transfer_owner = New Owner | settings.transfer_owner = New Owner | ||||||
| settings.make_transfer = Make Transfer | settings.make_transfer = Make Transfer | ||||||
|   | |||||||
| @@ -158,6 +158,30 @@ func NewRepoAction(u *User, repo *Repository) (err error) { | |||||||
| 	return newRepoAction(x, u, repo) | 	return newRepoAction(x, u, repo) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func renameRepoAction(e Engine, actUser *User, oldRepoName string, repo *Repository) (err error) { | ||||||
|  | 	if err = notifyWatchers(e, &Action{ | ||||||
|  | 		ActUserID:    actUser.Id, | ||||||
|  | 		ActUserName:  actUser.Name, | ||||||
|  | 		ActEmail:     actUser.Email, | ||||||
|  | 		OpType:       RENAME_REPO, | ||||||
|  | 		RepoID:       repo.ID, | ||||||
|  | 		RepoUserName: repo.Owner.Name, | ||||||
|  | 		RepoName:     repo.Name, | ||||||
|  | 		IsPrivate:    repo.IsPrivate, | ||||||
|  | 		Content:      oldRepoName, | ||||||
|  | 	}); err != nil { | ||||||
|  | 		return fmt.Errorf("notify watchers: %v", err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Trace("action.renameRepoAction: %s/%s", actUser.Name, repo.Name) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // RenameRepoAction adds new action for renaming a repository. | ||||||
|  | func RenameRepoAction(actUser *User, oldRepoName string, repo *Repository) error { | ||||||
|  | 	return renameRepoAction(x, actUser, oldRepoName, repo) | ||||||
|  | } | ||||||
|  |  | ||||||
| // updateIssuesCommit checks if issues are manipulated by commit message. | // updateIssuesCommit checks if issues are manipulated by commit message. | ||||||
| func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string, commits []*base.PushCommit) error { | func updateIssuesCommit(u *User, repo *Repository, repoUserName, repoName string, commits []*base.PushCommit) error { | ||||||
| 	for _, c := range commits { | 	for _, c := range commits { | ||||||
| @@ -502,29 +526,6 @@ func TransferRepoAction(actUser, oldOwner, newOwner *User, repo *Repository) err | |||||||
| 	return transferRepoAction(x, actUser, oldOwner, newOwner, repo) | 	return transferRepoAction(x, actUser, oldOwner, newOwner, repo) | ||||||
| } | } | ||||||
|  |  | ||||||
| func renameRepoAction(e Engine, actUser *User, oldRepoName string, repo *Repository) (err error) { |  | ||||||
| 	if err = notifyWatchers(e, &Action{ |  | ||||||
| 		ActUserID:    actUser.Id, |  | ||||||
| 		ActUserName:  actUser.Name, |  | ||||||
| 		ActEmail:     actUser.Email, |  | ||||||
| 		OpType:       RENAME_REPO, |  | ||||||
| 		RepoUserName: repo.Owner.Name, |  | ||||||
| 		RepoName:     repo.Name, |  | ||||||
| 		IsPrivate:    repo.IsPrivate, |  | ||||||
| 		Content:      oldRepoName, |  | ||||||
| 	}); err != nil { |  | ||||||
| 		return fmt.Errorf("notify watchers: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	log.Trace("action.renameRepoAction: %s/%s", actUser.Name, repo.Name) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // RenameRepoAction adds new action for renaming a repository. |  | ||||||
| func RenameRepoAction(actUser *User, oldRepoName string, repo *Repository) error { |  | ||||||
| 	return renameRepoAction(x, actUser, oldRepoName, repo) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetFeeds returns action list of given user in given context. | // GetFeeds returns action list of given user in given context. | ||||||
| func GetFeeds(uid, offset int64, isProfile bool) ([]*Action, error) { | func GetFeeds(uid, offset int64, isProfile bool) ([]*Action, error) { | ||||||
| 	actions := make([]*Action, 0, 20) | 	actions := make([]*Action, 0, 20) | ||||||
|   | |||||||
| @@ -78,7 +78,7 @@ func init() { | |||||||
| 	tables = append(tables, | 	tables = append(tables, | ||||||
| 		new(User), new(PublicKey), new(Oauth2), new(AccessToken), | 		new(User), new(PublicKey), new(Oauth2), new(AccessToken), | ||||||
| 		new(Repository), new(DeployKey), new(Collaboration), new(Access), | 		new(Repository), new(DeployKey), new(Collaboration), new(Access), | ||||||
| 		new(Watch), new(Star), new(Follow), new(Action), | 		new(Watch), new(Star), new(ForkInfo), new(Follow), new(Action), | ||||||
| 		new(Issue), new(Comment), new(Attachment), new(IssueUser), | 		new(Issue), new(Comment), new(Attachment), new(IssueUser), | ||||||
| 		new(Label), new(IssueLabel), new(Milestone), | 		new(Label), new(IssueLabel), new(Milestone), | ||||||
| 		new(Mirror), new(Release), new(LoginSource), new(Webhook), | 		new(Mirror), new(Release), new(LoginSource), new(Webhook), | ||||||
|   | |||||||
							
								
								
									
										231
									
								
								models/repo.go
									
									
									
									
									
								
							
							
						
						
									
										231
									
								
								models/repo.go
									
									
									
									
									
								
							| @@ -160,6 +160,7 @@ type Repository struct { | |||||||
| 	IsFork   bool `xorm:"NOT NULL DEFAULT false"` | 	IsFork   bool `xorm:"NOT NULL DEFAULT false"` | ||||||
| 	ForkID   int64 | 	ForkID   int64 | ||||||
| 	BaseRepo *Repository `xorm:"-"` | 	BaseRepo *Repository `xorm:"-"` | ||||||
|  | 	ForkInfo *ForkInfo   `xorm:"-"` | ||||||
|  |  | ||||||
| 	Created time.Time `xorm:"CREATED"` | 	Created time.Time `xorm:"CREATED"` | ||||||
| 	Updated time.Time `xorm:"UPDATED"` | 	Updated time.Time `xorm:"UPDATED"` | ||||||
| @@ -167,6 +168,15 @@ type Repository struct { | |||||||
|  |  | ||||||
| func (repo *Repository) AfterSet(colName string, _ xorm.Cell) { | func (repo *Repository) AfterSet(colName string, _ xorm.Cell) { | ||||||
| 	switch colName { | 	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": | 	case "updated": | ||||||
| 		repo.Updated = regulateTimeZone(repo.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)) | 	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) { | func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err error) { | ||||||
| 	repo.LowerName = strings.ToLower(repo.Name) | 	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 { | 		if err = repo.recalculateTeamAccesses(e, 0); err != nil { | ||||||
| 			return fmt.Errorf("recalculateTeamAccesses: %v", err) | 			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 | 	return nil | ||||||
| @@ -929,7 +960,7 @@ func UpdateRepository(repo *Repository, visibilityChanged bool) (err error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // DeleteRepository deletes a repository for a user or organization. | // 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} | 	repo := &Repository{ID: repoID, OwnerID: uid} | ||||||
| 	has, err := x.Get(repo) | 	has, err := x.Get(repo) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -1015,7 +1046,9 @@ func DeleteRepository(uid, repoID int64, userName string) error { | |||||||
|  |  | ||||||
| 	if repo.IsFork { | 	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 | 			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. | 	// Remove repository files. | ||||||
| 	if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil { | 	repoPath, err := repo.RepoPath() | ||||||
| 		desc := fmt.Sprintf("delete repository files(%s/%s): %v", userName, repo.Name, err) | 	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) | 		log.Warn(desc) | ||||||
| 		if err = CreateRepositoryNotice(desc); err != nil { | 		if err = CreateRepositoryNotice(desc); err != nil { | ||||||
| 			log.Error(4, "add notice: %v", err) | 			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. | // GetRepositoryByRef returns a Repository specified by a GFM reference. | ||||||
| @@ -1275,6 +1337,27 @@ func GitGcRepos() error { | |||||||
| 		}) | 		}) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type repoChecker struct { | ||||||
|  | 	querySQL, correctSQL string | ||||||
|  | 	desc                 string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func repoStatsCheck(checker *repoChecker) { | ||||||
|  | 	results, err := x.Query(checker.querySQL) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error(4, "Select %s: %v", checker.desc, err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	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 %s[%d]: %v", checker.desc, id, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func CheckRepoStats() { | func CheckRepoStats() { | ||||||
| 	if isCheckingRepos { | 	if isCheckingRepos { | ||||||
| 		return | 		return | ||||||
| @@ -1284,69 +1367,66 @@ func CheckRepoStats() { | |||||||
|  |  | ||||||
| 	log.Trace("Doing: CheckRepoStats") | 	log.Trace("Doing: CheckRepoStats") | ||||||
|  |  | ||||||
| 	// ***** START: Repository.NumWatches ***** | 	checkers := []*repoChecker{ | ||||||
| 	results, err := x.Query("SELECT repo.id FROM `repository` repo WHERE repo.num_watches!=(SELECT COUNT(*) FROM `watch` WHERE repo_id=repo.id)") | 		// Repository.NumWatches | ||||||
| 	if err != nil { | 		{ | ||||||
| 		log.Error(4, "Select repository check 'watch': %v", err) | 			"SELECT repo.id FROM `repository` repo WHERE repo.num_watches!=(SELECT COUNT(*) FROM `watch` WHERE repo_id=repo.id)", | ||||||
| 		return | 			"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 _, watch := range results { | 	for i := range checkers { | ||||||
| 		repoID := com.StrTo(watch["id"]).MustInt64() | 		repoStatsCheck(checkers[i]) | ||||||
| 		log.Trace("Updating repository count 'watch': %d", repoID) |  | ||||||
| 		_, err = x.Exec("UPDATE `repository` SET num_watches=(SELECT COUNT(*) FROM `watch` WHERE repo_id=?) WHERE id=?", repoID, repoID) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Error(4, "Update repository check 'watch'[%d]: %v", repoID, err) |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 	// ***** END: Repository.NumWatches ***** |  | ||||||
|  |  | ||||||
| 	// ***** START: Repository.NumStars ***** | 	// FIXME: use checker when v0.8, stop supporting old fork repo format. | ||||||
| 	results, err = x.Query("SELECT repo.id FROM `repository` repo WHERE repo.num_stars!=(SELECT COUNT(*) FROM `star` WHERE repo_id=repo.id)") | 	// ***** 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 { | 	if err != nil { | ||||||
| 		log.Error(4, "Select repository check 'star': %v", err) | 		log.Error(4, "Select repository count 'num_forks': %v", err) | ||||||
| 		return | 	} else { | ||||||
| 	} | 		for _, result := range results { | ||||||
| 	for _, star := range results { | 			id := com.StrTo(result["id"]).MustInt64() | ||||||
| 		repoID := com.StrTo(star["id"]).MustInt64() | 			log.Trace("Updating repository count 'num_forks': %d", id) | ||||||
| 		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) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Error(4, "Update repository check 'star'[%d]: %v", repoID, err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	// ***** END: Repository.NumStars ***** |  | ||||||
|  |  | ||||||
| 	// ***** START: Label.NumIssues ***** | 			repo, err := GetRepositoryByID(id) | ||||||
| 	results, err = x.Query("SELECT label.id FROM `label` WHERE label.num_issues!=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=label.id)") | 			if err != nil { | ||||||
| 	if err != nil { | 				log.Error(4, "GetRepositoryByID[%d]: %v", id, err) | ||||||
| 		log.Error(4, "Select label check 'num_issues': %v", err) | 				continue | ||||||
| 		return | 			} | ||||||
| 	} |  | ||||||
| 	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) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Error(4, "Update label check 'num_issues'[%d]: %v", labelID, err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	// ***** END: Label.NumIssues ***** |  | ||||||
|  |  | ||||||
| 	// ***** START: User.NumRepos ***** | 			rawResult, err := x.Query("SELECT COUNT(*) FROM `repository` WHERE fork_id=?", repo.ID) | ||||||
| 	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 { | ||||||
| 	if err != nil { | 				log.Error(4, "Select count of forks[%d]: %v", repo.ID, err) | ||||||
| 		log.Error(4, "Select user check 'num_repos': %v", err) | 				continue | ||||||
| 		return | 			} | ||||||
| 	} | 			repo.NumForks = int(parseCountResult(rawResult)) | ||||||
| 	for _, user := range results { |  | ||||||
| 		userID := com.StrTo(user["id"]).MustInt64() | 			if err = UpdateRepository(repo, false); err != nil { | ||||||
| 		log.Trace("Updating user count 'num_repos': %d", userID) | 				log.Error(4, "UpdateRepository[%d]: %v", id, err) | ||||||
| 		_, err = x.Exec("UPDATE `user` SET num_repos=(SELECT COUNT(*) FROM `repository` WHERE owner_id=?) WHERE id=?", userID, userID) | 				continue | ||||||
| 		if err != nil { | 			} | ||||||
| 			log.Error(4, "Update user check 'num_repos'[%d]: %v", userID, err) |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	// ***** END: User.NumRepos ***** | 	// ***** 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. | // HasForkedRepo checks if given user has already forked a repository with given ID. | ||||||
| func HasForkedRepo(ownerID, repoID int64) (*Repository, bool) { | func HasForkedRepo(ownerID, repoID int64) (*Repository, bool) { | ||||||
| 	repo := new(Repository) | 	repo := new(Repository) | ||||||
| @@ -1598,14 +1685,15 @@ func HasForkedRepo(ownerID, repoID int64) (*Repository, bool) { | |||||||
|  |  | ||||||
| func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) { | func ForkRepository(u *User, oldRepo *Repository, name, desc string) (_ *Repository, err error) { | ||||||
| 	repo := &Repository{ | 	repo := &Repository{ | ||||||
| 		OwnerID:     u.Id, | 		OwnerID:       u.Id, | ||||||
| 		Owner:       u, | 		Owner:         u, | ||||||
| 		Name:        name, | 		Name:          name, | ||||||
| 		LowerName:   strings.ToLower(name), | 		LowerName:     strings.ToLower(name), | ||||||
| 		Description: desc, | 		Description:   desc, | ||||||
| 		IsPrivate:   oldRepo.IsPrivate, | 		DefaultBranch: oldRepo.DefaultBranch, | ||||||
| 		IsFork:      true, | 		IsPrivate:     oldRepo.IsPrivate, | ||||||
| 		ForkID:      oldRepo.ID, | 		IsFork:        true, | ||||||
|  | 		ForkID:        oldRepo.ID, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	sess := x.NewSession() | 	sess := x.NewSession() | ||||||
| @@ -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 { | 	if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", oldRepo.ID); err != nil { | ||||||
| 		return nil, err | 		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() | 	oldRepoPath, err := oldRepo.RepoPath() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|   | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -190,6 +190,41 @@ func RepoRef() macaron.Handler { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func RetrieveBaseRepo(ctx *Context, repo *models.Repository) { | ||||||
|  | 	// Non-fork repository will not return error in this method. | ||||||
|  | 	if err := repo.GetBaseRepo(); err != nil { | ||||||
|  | 		if models.IsErrRepoNotExist(err) { | ||||||
|  | 			repo.IsFork = false | ||||||
|  | 			repo.ForkID = 0 | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		ctx.Handle(500, "GetBaseRepo", err) | ||||||
|  | 		return | ||||||
|  | 	} else if err = repo.BaseRepo.GetOwner(); err != nil { | ||||||
|  | 		ctx.Handle(500, "BaseRepo.GetOwner", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	bsaeRepo := repo.BaseRepo | ||||||
|  | 	baseGitRepo, err := git.OpenRepository(models.RepoPath(bsaeRepo.Owner.Name, bsaeRepo.Name)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.Handle(500, "OpenRepository", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if len(bsaeRepo.DefaultBranch) > 0 && baseGitRepo.IsBranchExist(bsaeRepo.DefaultBranch) { | ||||||
|  | 		ctx.Data["BaseDefaultBranch"] = bsaeRepo.DefaultBranch | ||||||
|  | 	} else { | ||||||
|  | 		baseBranches, err := baseGitRepo.GetBranches() | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.Handle(500, "GetBranches", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if len(baseBranches) > 0 { | ||||||
|  | 			ctx.Data["BaseDefaultBranch"] = baseBranches[0] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func RepoAssignment(redirect bool, args ...bool) macaron.Handler { | func RepoAssignment(redirect bool, args ...bool) macaron.Handler { | ||||||
| 	return func(ctx *Context) { | 	return func(ctx *Context) { | ||||||
| 		var ( | 		var ( | ||||||
| @@ -292,32 +327,9 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler { | |||||||
| 		ctx.Repo.Repository.NumTags = len(tags) | 		ctx.Repo.Repository.NumTags = len(tags) | ||||||
|  |  | ||||||
| 		if repo.IsFork { | 		if repo.IsFork { | ||||||
| 			// Non-fork repository will not return error in this method. | 			RetrieveBaseRepo(ctx, repo) | ||||||
| 			if err = repo.GetBaseRepo(); err != nil { | 			if ctx.Written() { | ||||||
| 				ctx.Handle(500, "GetBaseRepo", err) |  | ||||||
| 				return | 				return | ||||||
| 			} else if repo.BaseRepo.GetOwner(); err != nil { |  | ||||||
| 				ctx.Handle(500, "BaseRepo.GetOwner", err) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			bsaeRepo := repo.BaseRepo |  | ||||||
| 			baseGitRepo, err := git.OpenRepository(models.RepoPath(bsaeRepo.Owner.Name, bsaeRepo.Name)) |  | ||||||
| 			if err != nil { |  | ||||||
| 				ctx.Handle(500, "OpenRepository", err) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			if len(bsaeRepo.DefaultBranch) > 0 && baseGitRepo.IsBranchExist(bsaeRepo.DefaultBranch) { |  | ||||||
| 				ctx.Data["BaseDefaultBranch"] = bsaeRepo.DefaultBranch |  | ||||||
| 			} else { |  | ||||||
| 				baseBranches, err := baseGitRepo.GetBranches() |  | ||||||
| 				if err != nil { |  | ||||||
| 					ctx.Handle(500, "GetBranches", err) |  | ||||||
| 					return |  | ||||||
| 				} |  | ||||||
| 				if len(baseBranches) > 0 { |  | ||||||
| 					ctx.Data["BaseDefaultBranch"] = baseBranches[0] |  | ||||||
| 				} |  | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								public/css/gogs.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								public/css/gogs.min.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -61,11 +61,13 @@ | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	&.new.repo { | 	&.new.repo { | ||||||
| 		.selection.dropdown { | 		.ui.form { | ||||||
| 			width: 50%!important; | 			.selection.dropdown:not(.owner) { | ||||||
| 		} | 				width: 50%!important; | ||||||
| 		#auto-init { | 			} | ||||||
| 			margin-left: @input-padding+15px; | 			#auto-init { | ||||||
|  | 				margin-left: @input-padding+15px; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -151,7 +151,7 @@ func createRepo(ctx *middleware.Context, owner *models.User, opt api.CreateRepoO | |||||||
| 		} else { | 		} else { | ||||||
| 			log.Error(4, "CreateRepository: %v", err) | 			log.Error(4, "CreateRepository: %v", err) | ||||||
| 			if repo != nil { | 			if repo != nil { | ||||||
| 				if err = models.DeleteRepository(ctx.User.Id, repo.ID, ctx.User.Name); err != nil { | 				if err = models.DeleteRepository(ctx.User.Id, repo.ID); err != nil { | ||||||
| 					log.Error(4, "DeleteRepository: %v", err) | 					log.Error(4, "DeleteRepository: %v", err) | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| @@ -256,7 +256,7 @@ func MigrateRepo(ctx *middleware.Context, form auth.MigrateRepoForm) { | |||||||
| 	repo, err := models.MigrateRepository(ctxUser, form.RepoName, form.Description, form.Private, form.Mirror, remoteAddr) | 	repo, err := models.MigrateRepository(ctxUser, form.RepoName, form.Description, form.Private, form.Mirror, remoteAddr) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if repo != nil { | 		if repo != nil { | ||||||
| 			if errDelete := models.DeleteRepository(ctxUser.Id, repo.ID, ctxUser.Name); errDelete != nil { | 			if errDelete := models.DeleteRepository(ctxUser.Id, repo.ID); errDelete != nil { | ||||||
| 				log.Error(4, "DeleteRepository: %v", errDelete) | 				log.Error(4, "DeleteRepository: %v", errDelete) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -126,7 +126,7 @@ func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if repo != nil { | 	if repo != nil { | ||||||
| 		if errDelete := models.DeleteRepository(ctxUser.Id, repo.ID, ctxUser.Name); errDelete != nil { | 		if errDelete := models.DeleteRepository(ctxUser.Id, repo.ID); errDelete != nil { | ||||||
| 			log.Error(4, "DeleteRepository: %v", errDelete) | 			log.Error(4, "DeleteRepository: %v", errDelete) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -192,7 +192,7 @@ func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if repo != nil { | 	if repo != nil { | ||||||
| 		if errDelete := models.DeleteRepository(ctxUser.Id, repo.ID, ctxUser.Name); errDelete != nil { | 		if errDelete := models.DeleteRepository(ctxUser.Id, repo.ID); errDelete != nil { | ||||||
| 			log.Error(4, "DeleteRepository: %v", errDelete) | 			log.Error(4, "DeleteRepository: %v", errDelete) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -53,10 +53,12 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		isNameChanged := false | ||||||
| 		oldRepoName := repo.Name | 		oldRepoName := repo.Name | ||||||
| 		newRepoName := form.RepoName | 		newRepoName := form.RepoName | ||||||
| 		// Check if repository name has been changed. | 		// Check if repository name has been changed. | ||||||
| 		if repo.LowerName != strings.ToLower(newRepoName) { | 		if repo.LowerName != strings.ToLower(newRepoName) { | ||||||
|  | 			isNameChanged = true | ||||||
| 			if err := models.ChangeRepositoryName(ctx.Repo.Owner, repo.Name, newRepoName); err != nil { | 			if err := models.ChangeRepositoryName(ctx.Repo.Owner, repo.Name, newRepoName); err != nil { | ||||||
| 				ctx.Data["Err_RepoName"] = true | 				ctx.Data["Err_RepoName"] = true | ||||||
| 				switch { | 				switch { | ||||||
| @@ -71,6 +73,7 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) { | |||||||
| 				} | 				} | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName) | 			log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName) | ||||||
| 		} | 		} | ||||||
| 		// In case it's just a case change. | 		// In case it's just a case change. | ||||||
| @@ -87,12 +90,15 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) { | |||||||
| 		if err := models.UpdateRepository(repo, visibilityChanged); err != nil { | 		if err := models.UpdateRepository(repo, visibilityChanged); err != nil { | ||||||
| 			ctx.Handle(500, "UpdateRepository", err) | 			ctx.Handle(500, "UpdateRepository", err) | ||||||
| 			return | 			return | ||||||
| 		} else if err = models.RenameRepoAction(ctx.User, oldRepoName, repo); err != nil { |  | ||||||
| 			ctx.Handle(500, "RenameRepoAction", err) |  | ||||||
| 			return |  | ||||||
| 		} | 		} | ||||||
| 		log.Trace("Repository updated: %s/%s", ctx.Repo.Owner.Name, repo.Name) | 		log.Trace("Repository updated: %s/%s", ctx.Repo.Owner.Name, repo.Name) | ||||||
|  |  | ||||||
|  | 		if isNameChanged { | ||||||
|  | 			if err := models.RenameRepoAction(ctx.User, oldRepoName, repo); err != nil { | ||||||
|  | 				log.Error(4, "RenameRepoAction: %v", err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		if repo.IsMirror { | 		if repo.IsMirror { | ||||||
| 			if form.Interval > 0 { | 			if form.Interval > 0 { | ||||||
| 				ctx.Repo.Mirror.Interval = form.Interval | 				ctx.Repo.Mirror.Interval = form.Interval | ||||||
| @@ -152,7 +158,7 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) { | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if err := models.DeleteRepository(ctx.Repo.Owner.Id, repo.ID, ctx.Repo.Owner.Name); err != nil { | 		if err := models.DeleteRepository(ctx.Repo.Owner.Id, repo.ID); err != nil { | ||||||
| 			ctx.Handle(500, "DeleteRepository", err) | 			ctx.Handle(500, "DeleteRepository", err) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|           {{template "base/alert" .}} |           {{template "base/alert" .}} | ||||||
|           <div class="inline required field {{if .Err_Owner}}error{{end}}"> |           <div class="inline required field {{if .Err_Owner}}error{{end}}"> | ||||||
|             <label>{{.i18n.Tr "repo.owner"}}</label> |             <label>{{.i18n.Tr "repo.owner"}}</label> | ||||||
|             <div class="ui selection dropdown"> |             <div class="ui selection owner dropdown"> | ||||||
|               <input type="hidden" id="uid" name="uid" value="{{.ContextUser.Id}}" required> |               <input type="hidden" id="uid" name="uid" value="{{.ContextUser.Id}}" required> | ||||||
|               <span class="text"> |               <span class="text"> | ||||||
|                 <img class="ui mini image" src="{{.ContextUser.AvatarLink}}"> |                 <img class="ui mini image" src="{{.ContextUser.AvatarLink}}"> | ||||||
|   | |||||||
| @@ -45,7 +45,7 @@ | |||||||
| 			  	</div> | 			  	</div> | ||||||
| 			  	<div class="inline field"> | 			  	<div class="inline field"> | ||||||
| 			  		<label>{{.i18n.Tr "repo.visibility"}}</label> | 			  		<label>{{.i18n.Tr "repo.visibility"}}</label> | ||||||
| 			  		<div class="ui read-only toggle checkbox"> | 			  		<div class="ui read-only checkbox"> | ||||||
| 		  		    <input type="checkbox" {{if .IsPrivate}}checked{{end}}> | 		  		    <input type="checkbox" {{if .IsPrivate}}checked{{end}}> | ||||||
| 		  		    <label>{{.i18n.Tr "repo.visiblity_helper" | Safe}}</label> | 		  		    <label>{{.i18n.Tr "repo.visiblity_helper" | Safe}}</label> | ||||||
| 		  		  </div> | 		  		  </div> | ||||||
|   | |||||||
| @@ -47,7 +47,7 @@ | |||||||
| 	            <label>{{.i18n.Tr "repo.visibility"}}</label> | 	            <label>{{.i18n.Tr "repo.visibility"}}</label> | ||||||
| 	            <div class="ui checkbox"> | 	            <div class="ui checkbox"> | ||||||
| 	              <input name="private" type="checkbox" {{if .Repository.IsPrivate}}checked{{end}}> | 	              <input name="private" type="checkbox" {{if .Repository.IsPrivate}}checked{{end}}> | ||||||
| 	              <label>{{.i18n.Tr "repo.visiblity_helper" | Safe}}</label> | 	              <label>{{.i18n.Tr "repo.visiblity_helper" | Safe}} {{if .Repository.NumForks}}<span class="text red">{{.i18n.Tr "repo.visiblity_fork_helper"}}</span>{{end}}</label> | ||||||
| 	            </div> | 	            </div> | ||||||
| 	          </div> | 	          </div> | ||||||
| 	          {{end}} | 	          {{end}} | ||||||
| @@ -138,6 +138,11 @@ | |||||||
| 		<div class="ui warning message text left"> | 		<div class="ui warning message text left"> | ||||||
| 			{{.i18n.Tr "repo.settings.delete_notices_1" | Safe}} <br> | 			{{.i18n.Tr "repo.settings.delete_notices_1" | Safe}} <br> | ||||||
| 			{{.i18n.Tr "repo.settings.delete_notices_2" | Safe}} | 			{{.i18n.Tr "repo.settings.delete_notices_2" | Safe}} | ||||||
|  | 			{{if .Repository.NumForks}}<br> | ||||||
|  | 			{{.i18n.Tr "repo.settings.delete_notices_fork_1" | Safe}} <br> | ||||||
|  | 			{{.i18n.Tr "repo.settings.delete_notices_fork_2" | Safe}} <br> | ||||||
|  | 			{{.i18n.Tr "repo.settings.delete_notices_fork_3" | Safe}} | ||||||
|  | 			{{end}} | ||||||
| 		</div> | 		</div> | ||||||
|   	<form class="ui form" action="{{.Link}}" method="post"> |   	<form class="ui form" action="{{.Link}}" method="post"> | ||||||
|       {{.CsrfTokenHtml}} |       {{.CsrfTokenHtml}} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user