Add pager to the branches page (#14202)
* Add pager to the branches page * override pageSize if bigger than max * Make branches commit range configurable Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
		
							parent
							
								
									8d0e331c0a
								
							
						
					
					
						commit
						0c0445c97a
					
				
					 6 changed files with 160 additions and 88 deletions
				
			
		|  | @ -1098,6 +1098,10 @@ MAX_GIT_DIFF_LINES = 1000 | |||
| MAX_GIT_DIFF_LINE_CHARACTERS = 5000 | ||||
| ; Max number of files shown in diff view | ||||
| MAX_GIT_DIFF_FILES = 100 | ||||
| ; Set the default commits range size | ||||
| COMMITS_RANGE_SIZE = 50 | ||||
| ; Set the default branches range size | ||||
| BRANCHES_RANGE_SIZE = 20 | ||||
| ; Arguments for command 'git gc', e.g. "--aggressive --auto" | ||||
| ; see more on http://git-scm.com/docs/git-gc/ | ||||
| GC_ARGS = | ||||
|  |  | |||
|  | @ -752,6 +752,8 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false` | |||
| - `MAX_GIT_DIFF_LINES`: **100**: Max number of lines allowed of a single file in diff view. | ||||
| - `MAX_GIT_DIFF_LINE_CHARACTERS`: **5000**: Max character count per line highlighted in diff view. | ||||
| - `MAX_GIT_DIFF_FILES`: **100**: Max number of files shown in diff view. | ||||
| - `COMMITS_RANGE_SIZE`: **50**: Set the default commits range size | ||||
| - `BRANCHES_RANGE_SIZE`: **20**: Set the default branches range size | ||||
| - `GC_ARGS`: **\<empty\>**: Arguments for command `git gc`, e.g. `--aggressive --auto`. See more on http://git-scm.com/docs/git-gc/ | ||||
| - `ENABLE_AUTO_GIT_WIRE_PROTOCOL`: **true**: If use git wire protocol version 2 when git version >= 2.18, default is true, set to false when you always want git wire protocol version 1 | ||||
| - `PULL_REQUEST_PUSH_MESSAGE`: **true**: Respond to pushes to a non-default branch with a URL for creating a Pull Request (if the repository has them enabled) | ||||
|  |  | |||
|  | @ -110,6 +110,9 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) { | |||
| // CommitsRangeSize the default commits range size
 | ||||
| var CommitsRangeSize = 50 | ||||
| 
 | ||||
| // BranchesRangeSize the default branches range size
 | ||||
| var BranchesRangeSize = 20 | ||||
| 
 | ||||
| func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) (*list.List, error) { | ||||
| 	stdout, err := NewCommand("log", id.String(), "--skip="+strconv.Itoa((page-1)*pageSize), | ||||
| 		"--max-count="+strconv.Itoa(pageSize), prettyLogFormat).RunInDirBytes(repo.Path) | ||||
|  |  | |||
|  | @ -19,6 +19,8 @@ var ( | |||
| 		MaxGitDiffLines           int | ||||
| 		MaxGitDiffLineCharacters  int | ||||
| 		MaxGitDiffFiles           int | ||||
| 		CommitsRangeSize          int | ||||
| 		BranchesRangeSize         int | ||||
| 		VerbosePush               bool | ||||
| 		VerbosePushDelay          time.Duration | ||||
| 		GCArgs                    []string `ini:"GC_ARGS" delim:" "` | ||||
|  | @ -37,6 +39,8 @@ var ( | |||
| 		MaxGitDiffLines:           1000, | ||||
| 		MaxGitDiffLineCharacters:  5000, | ||||
| 		MaxGitDiffFiles:           100, | ||||
| 		CommitsRangeSize:          50, | ||||
| 		BranchesRangeSize:         20, | ||||
| 		VerbosePush:               true, | ||||
| 		VerbosePushDelay:          5 * time.Second, | ||||
| 		GCArgs:                    []string{}, | ||||
|  | @ -91,5 +95,8 @@ func newGit() { | |||
| 		args = append(args, "Version 2") // for focus color
 | ||||
| 	} | ||||
| 
 | ||||
| 	git.CommitsRangeSize = Git.CommitsRangeSize | ||||
| 	git.BranchesRangeSize = Git.BranchesRangeSize | ||||
| 
 | ||||
| 	log.Info(format, args...) | ||||
| } | ||||
|  |  | |||
|  | @ -52,7 +52,25 @@ func Branches(ctx *context.Context) { | |||
| 	ctx.Data["PageIsViewCode"] = true | ||||
| 	ctx.Data["PageIsBranches"] = true | ||||
| 
 | ||||
| 	ctx.Data["Branches"] = loadBranches(ctx) | ||||
| 	page := ctx.QueryInt("page") | ||||
| 	if page <= 1 { | ||||
| 		page = 1 | ||||
| 	} | ||||
| 
 | ||||
| 	pageSize := ctx.QueryInt("limit") | ||||
| 	if pageSize <= 0 || pageSize > git.BranchesRangeSize { | ||||
| 		pageSize = git.BranchesRangeSize | ||||
| 	} | ||||
| 
 | ||||
| 	branches, branchesCount := loadBranches(ctx, page, pageSize) | ||||
| 	if ctx.Written() { | ||||
| 		return | ||||
| 	} | ||||
| 	ctx.Data["Branches"] = branches | ||||
| 	pager := context.NewPagination(int(branchesCount), git.BranchesRangeSize, page, 5) | ||||
| 	pager.SetDefaultParams(ctx) | ||||
| 	ctx.Data["Page"] = pager | ||||
| 
 | ||||
| 	ctx.HTML(200, tplBranch) | ||||
| } | ||||
| 
 | ||||
|  | @ -176,17 +194,25 @@ func deleteBranch(ctx *context.Context, branchName string) error { | |||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func loadBranches(ctx *context.Context) []*Branch { | ||||
| // loadBranches loads branches from the repository limited by page & pageSize.
 | ||||
| // NOTE: May write to context on error. page & pageSize must be > 0
 | ||||
| func loadBranches(ctx *context.Context, page, pageSize int) ([]*Branch, int) { | ||||
| 	defaultBranch, err := repo_module.GetBranch(ctx.Repo.Repository, ctx.Repo.Repository.DefaultBranch) | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("GetDefaultBranch", err) | ||||
| 		return nil, 0 | ||||
| 	} | ||||
| 
 | ||||
| 	rawBranches, err := repo_module.GetBranches(ctx.Repo.Repository) | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("GetBranches", err) | ||||
| 		return nil | ||||
| 		return nil, 0 | ||||
| 	} | ||||
| 
 | ||||
| 	protectedBranches, err := ctx.Repo.Repository.GetProtectedBranches() | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("GetProtectedBranches", err) | ||||
| 		return nil | ||||
| 		return nil, 0 | ||||
| 	} | ||||
| 
 | ||||
| 	repoIDToRepo := map[int64]*models.Repository{} | ||||
|  | @ -195,100 +221,129 @@ func loadBranches(ctx *context.Context) []*Branch { | |||
| 	repoIDToGitRepo := map[int64]*git.Repository{} | ||||
| 	repoIDToGitRepo[ctx.Repo.Repository.ID] = ctx.Repo.GitRepo | ||||
| 
 | ||||
| 	branches := make([]*Branch, len(rawBranches)) | ||||
| 	for i := range rawBranches { | ||||
| 		commit, err := rawBranches[i].GetCommit() | ||||
| 		if err != nil { | ||||
| 			ctx.ServerError("GetCommit", err) | ||||
| 			return nil | ||||
| 		} | ||||
| 
 | ||||
| 		var isProtected bool | ||||
| 		branchName := rawBranches[i].Name | ||||
| 		for _, b := range protectedBranches { | ||||
| 			if b.BranchName == branchName { | ||||
| 				isProtected = true | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		divergence, divergenceError := repofiles.CountDivergingCommits(ctx.Repo.Repository, git.BranchPrefix+branchName) | ||||
| 		if divergenceError != nil { | ||||
| 			ctx.ServerError("CountDivergingCommits", divergenceError) | ||||
| 			return nil | ||||
| 		} | ||||
| 
 | ||||
| 		pr, err := models.GetLatestPullRequestByHeadInfo(ctx.Repo.Repository.ID, branchName) | ||||
| 		if err != nil { | ||||
| 			ctx.ServerError("GetLatestPullRequestByHeadInfo", err) | ||||
| 			return nil | ||||
| 		} | ||||
| 		headCommit := commit.ID.String() | ||||
| 
 | ||||
| 		mergeMovedOn := false | ||||
| 		if pr != nil { | ||||
| 			pr.HeadRepo = ctx.Repo.Repository | ||||
| 			if err := pr.LoadIssue(); err != nil { | ||||
| 				ctx.ServerError("pr.LoadIssue", err) | ||||
| 				return nil | ||||
| 			} | ||||
| 			if repo, ok := repoIDToRepo[pr.BaseRepoID]; ok { | ||||
| 				pr.BaseRepo = repo | ||||
| 			} else if err := pr.LoadBaseRepo(); err != nil { | ||||
| 				ctx.ServerError("pr.LoadBaseRepo", err) | ||||
| 				return nil | ||||
| 			} else { | ||||
| 				repoIDToRepo[pr.BaseRepoID] = pr.BaseRepo | ||||
| 			} | ||||
| 			pr.Issue.Repo = pr.BaseRepo | ||||
| 
 | ||||
| 			if pr.HasMerged { | ||||
| 				baseGitRepo, ok := repoIDToGitRepo[pr.BaseRepoID] | ||||
| 				if !ok { | ||||
| 					baseGitRepo, err = git.OpenRepository(pr.BaseRepo.RepoPath()) | ||||
| 					if err != nil { | ||||
| 						ctx.ServerError("OpenRepository", err) | ||||
| 						return nil | ||||
| 					} | ||||
| 					defer baseGitRepo.Close() | ||||
| 					repoIDToGitRepo[pr.BaseRepoID] = baseGitRepo | ||||
| 				} | ||||
| 				pullCommit, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName()) | ||||
| 				if err != nil && !git.IsErrNotExist(err) { | ||||
| 					ctx.ServerError("GetBranchCommitID", err) | ||||
| 					return nil | ||||
| 				} | ||||
| 				if err == nil && headCommit != pullCommit { | ||||
| 					// the head has moved on from the merge - we shouldn't delete
 | ||||
| 					mergeMovedOn = true | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		isIncluded := divergence.Ahead == 0 && ctx.Repo.Repository.DefaultBranch != branchName | ||||
| 
 | ||||
| 		branches[i] = &Branch{ | ||||
| 			Name:              branchName, | ||||
| 			Commit:            commit, | ||||
| 			IsProtected:       isProtected, | ||||
| 			IsIncluded:        isIncluded, | ||||
| 			CommitsAhead:      divergence.Ahead, | ||||
| 			CommitsBehind:     divergence.Behind, | ||||
| 			LatestPullRequest: pr, | ||||
| 			MergeMovedOn:      mergeMovedOn, | ||||
| 		} | ||||
| 	var totalNumOfBranches = len(rawBranches) | ||||
| 	var startIndex = (page - 1) * pageSize | ||||
| 	if startIndex > totalNumOfBranches { | ||||
| 		startIndex = totalNumOfBranches - 1 | ||||
| 	} | ||||
| 	var endIndex = startIndex + pageSize | ||||
| 	if endIndex > totalNumOfBranches { | ||||
| 		endIndex = totalNumOfBranches - 1 | ||||
| 	} | ||||
| 
 | ||||
| 	var branches []*Branch | ||||
| 	for i := startIndex; i < endIndex; i++ { | ||||
| 		var branch = loadOneBranch(ctx, rawBranches[i], protectedBranches, repoIDToRepo, repoIDToGitRepo) | ||||
| 		if branch == nil { | ||||
| 			return nil, 0 | ||||
| 		} | ||||
| 
 | ||||
| 		if branch.Name == ctx.Repo.Repository.DefaultBranch { | ||||
| 			// Skip default branch
 | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		branches = append(branches, branch) | ||||
| 	} | ||||
| 
 | ||||
| 	// Always add the default branch
 | ||||
| 	branches = append(branches, loadOneBranch(ctx, defaultBranch, protectedBranches, repoIDToRepo, repoIDToGitRepo)) | ||||
| 
 | ||||
| 	if ctx.Repo.CanWrite(models.UnitTypeCode) { | ||||
| 		deletedBranches, err := getDeletedBranches(ctx) | ||||
| 		if err != nil { | ||||
| 			ctx.ServerError("getDeletedBranches", err) | ||||
| 			return nil | ||||
| 			return nil, 0 | ||||
| 		} | ||||
| 		branches = append(branches, deletedBranches...) | ||||
| 	} | ||||
| 
 | ||||
| 	return branches | ||||
| 	return branches, len(rawBranches) - 1 | ||||
| } | ||||
| 
 | ||||
| func loadOneBranch(ctx *context.Context, rawBranch *git.Branch, protectedBranches []*models.ProtectedBranch, | ||||
| 	repoIDToRepo map[int64]*models.Repository, | ||||
| 	repoIDToGitRepo map[int64]*git.Repository) *Branch { | ||||
| 
 | ||||
| 	commit, err := rawBranch.GetCommit() | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("GetCommit", err) | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	branchName := rawBranch.Name | ||||
| 	var isProtected bool | ||||
| 	for _, b := range protectedBranches { | ||||
| 		if b.BranchName == branchName { | ||||
| 			isProtected = true | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	divergence, divergenceError := repofiles.CountDivergingCommits(ctx.Repo.Repository, git.BranchPrefix+branchName) | ||||
| 	if divergenceError != nil { | ||||
| 		ctx.ServerError("CountDivergingCommits", divergenceError) | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	pr, err := models.GetLatestPullRequestByHeadInfo(ctx.Repo.Repository.ID, branchName) | ||||
| 	if err != nil { | ||||
| 		ctx.ServerError("GetLatestPullRequestByHeadInfo", err) | ||||
| 		return nil | ||||
| 	} | ||||
| 	headCommit := commit.ID.String() | ||||
| 
 | ||||
| 	mergeMovedOn := false | ||||
| 	if pr != nil { | ||||
| 		pr.HeadRepo = ctx.Repo.Repository | ||||
| 		if err := pr.LoadIssue(); err != nil { | ||||
| 			ctx.ServerError("pr.LoadIssue", err) | ||||
| 			return nil | ||||
| 		} | ||||
| 		if repo, ok := repoIDToRepo[pr.BaseRepoID]; ok { | ||||
| 			pr.BaseRepo = repo | ||||
| 		} else if err := pr.LoadBaseRepo(); err != nil { | ||||
| 			ctx.ServerError("pr.LoadBaseRepo", err) | ||||
| 			return nil | ||||
| 		} else { | ||||
| 			repoIDToRepo[pr.BaseRepoID] = pr.BaseRepo | ||||
| 		} | ||||
| 		pr.Issue.Repo = pr.BaseRepo | ||||
| 
 | ||||
| 		if pr.HasMerged { | ||||
| 			baseGitRepo, ok := repoIDToGitRepo[pr.BaseRepoID] | ||||
| 			if !ok { | ||||
| 				baseGitRepo, err = git.OpenRepository(pr.BaseRepo.RepoPath()) | ||||
| 				if err != nil { | ||||
| 					ctx.ServerError("OpenRepository", err) | ||||
| 					return nil | ||||
| 				} | ||||
| 				defer baseGitRepo.Close() | ||||
| 				repoIDToGitRepo[pr.BaseRepoID] = baseGitRepo | ||||
| 			} | ||||
| 			pullCommit, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName()) | ||||
| 			if err != nil && !git.IsErrNotExist(err) { | ||||
| 				ctx.ServerError("GetBranchCommitID", err) | ||||
| 				return nil | ||||
| 			} | ||||
| 			if err == nil && headCommit != pullCommit { | ||||
| 				// the head has moved on from the merge - we shouldn't delete
 | ||||
| 				mergeMovedOn = true | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	isIncluded := divergence.Ahead == 0 && ctx.Repo.Repository.DefaultBranch != branchName | ||||
| 	return &Branch{ | ||||
| 		Name:              branchName, | ||||
| 		Commit:            commit, | ||||
| 		IsProtected:       isProtected, | ||||
| 		IsIncluded:        isIncluded, | ||||
| 		CommitsAhead:      divergence.Ahead, | ||||
| 		CommitsBehind:     divergence.Behind, | ||||
| 		LatestPullRequest: pr, | ||||
| 		MergeMovedOn:      mergeMovedOn, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func getDeletedBranches(ctx *context.Context) ([]*Branch, error) { | ||||
|  |  | |||
|  | @ -127,6 +127,7 @@ | |||
| 					</tbody> | ||||
| 				</table> | ||||
| 			</div> | ||||
| 			{{template "base/paginate" .}} | ||||
| 		{{end}} | ||||
| 	</div> | ||||
| </div> | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue