A CLI-first git forge.

cli forge git self-hosted

https://gitbay.org

Commit 1790a024c2

1790a024c2586d46bfb1b57f69adc529e7f78282

parent: 27e5426f8f

Verified · cmc

cmc <hello@cleberg.net> · 2026-08-26T01:51:02Z

web: sort directories ahead of files in the tree listing

git's tree order interleaves them, but a listing is scanned by shape
before name. Stable, so each group keeps the ordering git gave it, and
scoped to the web listing: the wiki page list and the CLI's template scan
read the same call and should not shift.
e2e/design_test.go +13
@@ -57,6 +57,19 @@ func TestReadmeRelativeLinks(t *testing.T) {
5757 t.Errorf("missing %q", want)
5858 }
5959 }
60 // Directories sort ahead of files, whatever git's own tree order was:
61 // docs/ and img/ precede LICENSE and README.md despite sorting after
62 // them byte-wise.
63 for _, dir := range []string{"docs", "img"} {
64 d := strings.Index(body, `/tree/main/`+dir+`">`)
65 f := strings.Index(body, `/blob/main/README.md">`)
66 if d < 0 || f < 0 {
67 t.Fatalf("listing missing %s/ or README.md", dir)
68 }
69 if d > f {
70 t.Errorf("%s/ listed after README.md; directories should come first", dir)
71 }
72 }
6073 // Branch dropdown lists branches.
6174 if !strings.Contains(body, `class="refmenu"`) || !strings.Contains(body, ">All refs") {
6275 t.Error("branch dropdown missing")
internal/httpd/web.go +7
@@ -13,6 +13,7 @@ import (
1313 "net/http"
1414 "path"
1515 "regexp"
16 "sort"
1617 "strconv"
1718 "strings"
1819 "time"
@@ -433,6 +434,12 @@ func (s *Server) renderTree(w http.ResponseWriter, r *http.Request, p repoPage,
433434 s.notFound(w, r)
434435 return
435436 }
437 // Directories first. git's tree order interleaves them with files, but
438 // a listing is scanned by shape before name. Stable, so each group
439 // keeps the ordering git gave it.
440 sort.SliceStable(entries, func(i, j int) bool {
441 return entries[i].Type == "tree" && entries[j].Type != "tree"
442 })
436443 prefix := ""
437444 if dirPath != "" {
438445 prefix = dirPath + "/"