1package web
2
3import (
4 "math"
5 "net/http"
6 "strconv"
7
8 "github.com/charmbracelet/log/v2"
9 "github.com/charmbracelet/soft-serve/git"
10 "github.com/charmbracelet/soft-serve/pkg/config"
11 "github.com/charmbracelet/soft-serve/pkg/proto"
12)
13
14// BranchInfo contains branch reference and its latest commit.
15type BranchInfo struct {
16 Ref *git.Reference
17 Commit *git.Commit
18}
19
20// BranchesData contains data for rendering branches listing.
21type BranchesData struct {
22 RepoBaseData
23 PaginationData
24 Branches []BranchInfo
25 TotalBranches int
26}
27
28// repoBranches handles branches listing page.
29func repoBranches(w http.ResponseWriter, r *http.Request) {
30 ctx := r.Context()
31 logger := log.FromContext(ctx)
32 cfg := config.FromContext(ctx)
33 repo := proto.RepositoryFromContext(ctx)
34
35 gr, err := openRepository(repo)
36 if err != nil {
37 logger.Debug("failed to open repository", "repo", repo.Name(), "err", err)
38 renderInternalServerError(w, r)
39 return
40 }
41
42 defaultBranch := getDefaultBranch(gr)
43
44 page := 1
45 if pageStr := r.URL.Query().Get("page"); pageStr != "" {
46 if p, err := strconv.Atoi(pageStr); err == nil && p > 0 {
47 page = p
48 }
49 }
50
51 // First fetch to get total count and calculate pages
52 _, totalBranches, err := FetchRefsPaginated(gr, RefTypeBranch, 0, 1, defaultBranch)
53 if err != nil {
54 logger.Debug("failed to fetch branches", "repo", repo.Name(), "err", err)
55 renderInternalServerError(w, r)
56 return
57 }
58
59 totalPages := int(math.Ceil(float64(totalBranches) / float64(defaultBranchesPerPage)))
60 if totalPages < 1 {
61 totalPages = 1
62 }
63
64 // Clamp page before computing offset
65 if page > totalPages {
66 page = totalPages
67 }
68 if page < 1 {
69 page = 1
70 }
71
72 // Calculate offset for pagination
73 offset := (page - 1) * defaultBranchesPerPage
74
75 // Fetch only the branches we need for this page, pre-sorted
76 paginatedRefItems, _, err := FetchRefsPaginated(gr, RefTypeBranch, offset, defaultBranchesPerPage, defaultBranch)
77 if err != nil {
78 logger.Debug("failed to fetch branches", "repo", repo.Name(), "err", err)
79 renderInternalServerError(w, r)
80 return
81 }
82
83 var paginatedBranches []BranchInfo
84 for _, refItem := range paginatedRefItems {
85 paginatedBranches = append(paginatedBranches, BranchInfo{
86 Ref: refItem.Reference,
87 Commit: refItem.Commit,
88 })
89 }
90
91 repoDisplayName := repo.ProjectName()
92 if repoDisplayName == "" {
93 repoDisplayName = repo.Name()
94 }
95
96 data := BranchesData{
97 RepoBaseData: RepoBaseData{
98 BaseData: BaseData{
99 ServerName: cfg.Name,
100 ActiveTab: "branches",
101 Title: "Branches | " + repoDisplayName,
102 },
103 Repo: repo,
104 DefaultBranch: defaultBranch,
105 },
106 PaginationData: PaginationData{
107 Page: page,
108 TotalPages: totalPages,
109 HasPrevPage: page > 1,
110 HasNextPage: page < totalPages,
111 },
112 Branches: paginatedBranches,
113 TotalBranches: totalBranches,
114 }
115
116 renderHTML(w, "branches.html", data)
117}