mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	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:
		@@ -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>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user