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_GIT_DIFF_LINE_CHARACTERS = 5000 | ||||||
| ; Max number of files shown in diff view | ; Max number of files shown in diff view | ||||||
| MAX_GIT_DIFF_FILES = 100 | 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" | ; Arguments for command 'git gc', e.g. "--aggressive --auto" | ||||||
| ; see more on http://git-scm.com/docs/git-gc/ | ; see more on http://git-scm.com/docs/git-gc/ | ||||||
| GC_ARGS = | 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_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_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. | - `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/ | - `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 | - `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) | - `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
 | // CommitsRangeSize the default commits range size
 | ||||||
| var CommitsRangeSize = 50 | var CommitsRangeSize = 50 | ||||||
| 
 | 
 | ||||||
|  | // BranchesRangeSize the default branches range size
 | ||||||
|  | var BranchesRangeSize = 20 | ||||||
|  | 
 | ||||||
| func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) (*list.List, error) { | func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) (*list.List, error) { | ||||||
| 	stdout, err := NewCommand("log", id.String(), "--skip="+strconv.Itoa((page-1)*pageSize), | 	stdout, err := NewCommand("log", id.String(), "--skip="+strconv.Itoa((page-1)*pageSize), | ||||||
| 		"--max-count="+strconv.Itoa(pageSize), prettyLogFormat).RunInDirBytes(repo.Path) | 		"--max-count="+strconv.Itoa(pageSize), prettyLogFormat).RunInDirBytes(repo.Path) | ||||||
|  |  | ||||||
|  | @ -19,6 +19,8 @@ var ( | ||||||
| 		MaxGitDiffLines           int | 		MaxGitDiffLines           int | ||||||
| 		MaxGitDiffLineCharacters  int | 		MaxGitDiffLineCharacters  int | ||||||
| 		MaxGitDiffFiles           int | 		MaxGitDiffFiles           int | ||||||
|  | 		CommitsRangeSize          int | ||||||
|  | 		BranchesRangeSize         int | ||||||
| 		VerbosePush               bool | 		VerbosePush               bool | ||||||
| 		VerbosePushDelay          time.Duration | 		VerbosePushDelay          time.Duration | ||||||
| 		GCArgs                    []string `ini:"GC_ARGS" delim:" "` | 		GCArgs                    []string `ini:"GC_ARGS" delim:" "` | ||||||
|  | @ -37,6 +39,8 @@ var ( | ||||||
| 		MaxGitDiffLines:           1000, | 		MaxGitDiffLines:           1000, | ||||||
| 		MaxGitDiffLineCharacters:  5000, | 		MaxGitDiffLineCharacters:  5000, | ||||||
| 		MaxGitDiffFiles:           100, | 		MaxGitDiffFiles:           100, | ||||||
|  | 		CommitsRangeSize:          50, | ||||||
|  | 		BranchesRangeSize:         20, | ||||||
| 		VerbosePush:               true, | 		VerbosePush:               true, | ||||||
| 		VerbosePushDelay:          5 * time.Second, | 		VerbosePushDelay:          5 * time.Second, | ||||||
| 		GCArgs:                    []string{}, | 		GCArgs:                    []string{}, | ||||||
|  | @ -91,5 +95,8 @@ func newGit() { | ||||||
| 		args = append(args, "Version 2") // for focus color
 | 		args = append(args, "Version 2") // for focus color
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	git.CommitsRangeSize = Git.CommitsRangeSize | ||||||
|  | 	git.BranchesRangeSize = Git.BranchesRangeSize | ||||||
|  | 
 | ||||||
| 	log.Info(format, args...) | 	log.Info(format, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -52,7 +52,25 @@ func Branches(ctx *context.Context) { | ||||||
| 	ctx.Data["PageIsViewCode"] = true | 	ctx.Data["PageIsViewCode"] = true | ||||||
| 	ctx.Data["PageIsBranches"] = 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) | 	ctx.HTML(200, tplBranch) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -176,17 +194,25 @@ func deleteBranch(ctx *context.Context, branchName string) error { | ||||||
| 	return nil | 	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) | 	rawBranches, err := repo_module.GetBranches(ctx.Repo.Repository) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("GetBranches", err) | 		ctx.ServerError("GetBranches", err) | ||||||
| 		return nil | 		return nil, 0 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	protectedBranches, err := ctx.Repo.Repository.GetProtectedBranches() | 	protectedBranches, err := ctx.Repo.Repository.GetProtectedBranches() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ctx.ServerError("GetProtectedBranches", err) | 		ctx.ServerError("GetProtectedBranches", err) | ||||||
| 		return nil | 		return nil, 0 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	repoIDToRepo := map[int64]*models.Repository{} | 	repoIDToRepo := map[int64]*models.Repository{} | ||||||
|  | @ -195,100 +221,129 @@ func loadBranches(ctx *context.Context) []*Branch { | ||||||
| 	repoIDToGitRepo := map[int64]*git.Repository{} | 	repoIDToGitRepo := map[int64]*git.Repository{} | ||||||
| 	repoIDToGitRepo[ctx.Repo.Repository.ID] = ctx.Repo.GitRepo | 	repoIDToGitRepo[ctx.Repo.Repository.ID] = ctx.Repo.GitRepo | ||||||
| 
 | 
 | ||||||
| 	branches := make([]*Branch, len(rawBranches)) | 	var totalNumOfBranches = len(rawBranches) | ||||||
| 	for i := range rawBranches { | 	var startIndex = (page - 1) * pageSize | ||||||
| 		commit, err := rawBranches[i].GetCommit() | 	if startIndex > totalNumOfBranches { | ||||||
| 		if err != nil { | 		startIndex = totalNumOfBranches - 1 | ||||||
| 			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 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) { | 	if ctx.Repo.CanWrite(models.UnitTypeCode) { | ||||||
| 		deletedBranches, err := getDeletedBranches(ctx) | 		deletedBranches, err := getDeletedBranches(ctx) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ctx.ServerError("getDeletedBranches", err) | 			ctx.ServerError("getDeletedBranches", err) | ||||||
| 			return nil | 			return nil, 0 | ||||||
| 		} | 		} | ||||||
| 		branches = append(branches, deletedBranches...) | 		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) { | func getDeletedBranches(ctx *context.Context) ([]*Branch, error) { | ||||||
|  |  | ||||||
|  | @ -127,6 +127,7 @@ | ||||||
| 					</tbody> | 					</tbody> | ||||||
| 				</table> | 				</table> | ||||||
| 			</div> | 			</div> | ||||||
|  | 			{{template "base/paginate" .}} | ||||||
| 		{{end}} | 		{{end}} | ||||||
| 	</div> | 	</div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue