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 Repo proto.Repository
23 DefaultBranch string
24 Branches []BranchInfo
25 ActiveTab string
26 Page int
27 TotalPages int
28 TotalBranches int
29 HasPrevPage bool
30 HasNextPage bool
31 ServerName string
32}
33
34// repoBranches handles branches listing page.
35func repoBranches(w http.ResponseWriter, r *http.Request) {
36 ctx := r.Context()
37 logger := log.FromContext(ctx)
38 cfg := config.FromContext(ctx)
39 repo := proto.RepositoryFromContext(ctx)
40
41 gr, err := openRepository(repo)
42 if err != nil {
43 logger.Debug("failed to open repository", "repo", repo.Name(), "err", err)
44 renderInternalServerError(w, r)
45 return
46 }
47
48 defaultBranch := getDefaultBranch(gr)
49
50 page := 1
51 if pageStr := r.URL.Query().Get("page"); pageStr != "" {
52 if p, err := strconv.Atoi(pageStr); err == nil && p > 0 {
53 page = p
54 }
55 }
56
57 // First fetch to get total count and calculate pages
58 paginatedRefItems, totalBranches, err := FetchRefsPaginated(gr, RefTypeBranch, 0, 1, defaultBranch)
59 if err != nil {
60 logger.Debug("failed to fetch branches", "repo", repo.Name(), "err", err)
61 renderInternalServerError(w, r)
62 return
63 }
64
65 totalPages := int(math.Ceil(float64(totalBranches) / float64(defaultBranchesPerPage)))
66 if totalPages < 1 {
67 totalPages = 1
68 }
69
70 // Clamp page before computing offset
71 if page > totalPages {
72 page = totalPages
73 }
74 if page < 1 {
75 page = 1
76 }
77
78 // Calculate offset for pagination
79 offset := (page - 1) * defaultBranchesPerPage
80
81 // Fetch only the branches we need for this page, pre-sorted
82 paginatedRefItems, totalBranches, err = FetchRefsPaginated(gr, RefTypeBranch, offset, defaultBranchesPerPage, defaultBranch)
83 if err != nil {
84 logger.Debug("failed to fetch branches", "repo", repo.Name(), "err", err)
85 renderInternalServerError(w, r)
86 return
87 }
88
89 var paginatedBranches []BranchInfo
90 for _, refItem := range paginatedRefItems {
91 paginatedBranches = append(paginatedBranches, BranchInfo{
92 Ref: refItem.Reference,
93 Commit: refItem.Commit,
94 })
95 }
96
97 data := BranchesData{
98 Repo: repo,
99 DefaultBranch: defaultBranch,
100 Branches: paginatedBranches,
101 ActiveTab: "branches",
102 Page: page,
103 TotalPages: totalPages,
104 TotalBranches: totalBranches,
105 HasPrevPage: page > 1,
106 HasNextPage: page < totalPages,
107 ServerName: cfg.Name,
108 }
109
110 renderHTML(w, "branches.html", data)
111}