webui_branches.go

  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	description := getRepoDescriptionOrFallback(repo, "Branches in "+repoDisplayName)
 97
 98	data := BranchesData{
 99		RepoBaseData: RepoBaseData{
100			BaseData: BaseData{
101				ServerName:  cfg.Name,
102				ActiveTab:   "branches",
103				Title:       "Branches | " + repoDisplayName,
104				Description: description,
105			},
106			Repo:          repo,
107			DefaultBranch: defaultBranch,
108		},
109		PaginationData: PaginationData{
110			Page:        page,
111			TotalPages:  totalPages,
112			HasPrevPage: page > 1,
113			HasNextPage: page < totalPages,
114		},
115		Branches:      paginatedBranches,
116		TotalBranches: totalBranches,
117	}
118
119	renderHTML(w, "branches.html", data)
120}