parent:
27221edbb187c64e1acc43018bf0e09ee1ac1193
nmyk <nick@nmyk.io>
2026-02-16T12:18:28-05:00
add refs page
diff --git a/main.go b/main.go
index 324555e0c08b19a42c7eb98c65160bc629b863fb..964d71f48ad6a6c9be499a03a3ae68dd94670ebb 100644
--- a/main.go
+++ b/main.go
@@ -37,6 +37,7 @@ func router() *http.ServeMux {
mux := http.NewServeMux()
mux.HandleFunc("GET /", listRepos)
mux.HandleFunc("GET /{name}", repoIndex)
+ mux.HandleFunc("GET /{name}/refs", refs)
mux.HandleFunc("GET /{name}/tree/{ref}", repoTree)
mux.HandleFunc("GET /{name}/tree/{ref}/{rest...}", repoTree)
mux.HandleFunc("GET /{name}/blob/{ref}/{rest...}", blob)
@@ -94,6 +95,45 @@ reader.Read(content)
return string(content), nil
}
+type BranchMeta struct {
+ Name string
+}
+
+type RefsData struct {
+ Repo string
+ Branches []BranchMeta
+}
+
+func refs(w http.ResponseWriter, r *http.Request) {
+ repoName := r.PathValue("name")
+ if _, err := os.Stat(repoName + DOT_GIT); os.IsNotExist(err) {
+ http.NotFound(w, r)
+ return
+ }
+ repo, err := git.PlainOpen(repoName + DOT_GIT)
+ if err != nil {
+ http.Error(w, err.Error(), 500)
+ return
+ }
+ iter, err := repo.Branches()
+ if err != nil {
+ http.Error(w, err.Error(), 500)
+ }
+ var branches []BranchMeta
+ err = iter.ForEach(func(ref *plumbing.Reference) error {
+ branches = append(branches, BranchMeta{ref.Name().Short()})
+ return nil
+ })
+ if err != nil {
+ http.Error(w, err.Error(), 500)
+ }
+ data := RefsData{
+ Repo: repoName,
+ Branches: branches,
+ }
+ serve(w, "refs", data)
+}
+
func blob(w http.ResponseWriter, r *http.Request) {
repoName := r.PathValue("name")
if _, err := os.Stat(repoName + DOT_GIT); os.IsNotExist(err) {
@@ -151,6 +191,7 @@
type TreeData struct {
Repo string
Ref string
+ HeadRef string
Base string
Crumbs []Crumb
Entries []EntryMeta
@@ -159,9 +200,7 @@ Readme string
}
type EntryMeta struct {
- Repo string
Name string
- Ref string
Path string
IsDir bool
}
@@ -178,6 +217,16 @@ http.Error(w, err.Error(), 500)
return
}
refName := r.PathValue("ref")
+ head, err := repo.Head()
+ if err != nil {
+ http.Error(w, "Cannot resolve HEAD state", 500)
+ }
+ headRef := head.Name().Short()
+ treePath := r.PathValue("rest")
+ if refName == headRef && treePath == "" {
+ repoIndex(w, r)
+ return
+ }
branch := path.Join("refs", "heads", refName)
ref, err := repo.Reference(plumbing.ReferenceName(branch), true)
if err != nil {
@@ -194,7 +243,6 @@ if err != nil {
http.Error(w, err.Error(), 500)
return
}
- treePath := r.PathValue("rest")
var tree *object.Tree
if treePath != "" {
treeEntry, err := rootTree.FindEntry(treePath)
@@ -213,9 +261,7 @@ var entries []EntryMeta
if treePath != "" {
entries = []EntryMeta{
{
- Repo: repoName,
Name: "..",
- Ref: refName,
Path: path.Dir(treePath),
IsDir: true,
},
@@ -225,9 +271,7 @@ var readme, readmeName string
for _, e := range tree.Entries {
fullPath := path.Join(treePath, e.Name)
em := EntryMeta{
- Repo: repoName,
Name: e.Name,
- Ref: refName,
Path: fullPath,
IsDir: e.Mode == filemode.Dir,
}
@@ -244,6 +288,7 @@ }
data := TreeData{
Repo: repoName,
Ref: refName,
+ HeadRef: headRef,
Base: treePath,
Crumbs: breadcrumbs(repoName, refName, treePath),
Entries: entries,
@@ -315,9 +360,7 @@ var entries []EntryMeta
var readme, readmeName string
for _, e := range tree.Entries {
em := EntryMeta{
- Repo: repoName,
Name: e.Name,
- Ref: refName,
Path: e.Name,
IsDir: e.Mode == filemode.Dir,
}
@@ -338,5 +381,5 @@ Entries: entries,
ReadmeName: readmeName,
Readme: readme,
}
- serve(w, "tree", data)
+ serve(w, "repo", data)
}
diff --git a/templates/refs.tmpl b/templates/refs.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..69cce1f4c73c8d5c7fcec8b65f7bc774312af99c
--- /dev/null
+++ b/templates/refs.tmpl
@@ -0,0 +1,14 @@
+{{define "title"}}{{.Repo}}{{end}}
+{{define "content"}}
+<h1><a href="/{{.Repo}}">{{.Repo}}</a></h1>
+<h2>branches</h2>
+<ul>
+{{range .Branches}}
+<li><a href="/{{$.Repo}}/tree/{{.Name}}">{{.Name}}</a></li>
+{{end}}
+</ul>
+{{end}}
+
+{{define "refs"}}
+{{template "base" .}}
+{{end}}
\ No newline at end of file
diff --git a/templates/repo.tmpl b/templates/repo.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..1768d39091f2ff5a2f9830dab58e806846c10df9
--- /dev/null
+++ b/templates/repo.tmpl
@@ -0,0 +1,28 @@
+{{define "title"}}{{.Repo}}/{{.Base}}{{end}}
+{{define "content"}}
+<h1>{{.Repo}}</h1>
+<p>@ {{.Ref}}</p>
+<p><a href="/"><-- back</a> | <a href="/{{.Repo}}/refs">refs</a></p>
+<ul>
+{{range .Entries}}
+<li>
+ {{- if .IsDir -}}
+ 📁 <a href="/{{$.Repo}}/tree/{{$.Ref}}/{{.Path}}">{{.Name}}</a>
+ {{- else -}}
+ 📄 <a href="/{{$.Repo}}/blob/{{$.Ref}}/{{.Path}}">{{.Name}}</a>
+ {{- end -}}
+</li>
+{{end}}
+</ul>
+{{- if ne .Readme "" -}}
+<hr>
+<h2>{{.ReadmeName}}</h2>
+<pre>
+{{.Readme}}
+</pre>
+{{- end -}}
+{{end}}
+
+{{define "repo"}}
+{{template "base" .}}
+{{end}}
\ No newline at end of file
diff --git a/templates/tree.tmpl b/templates/tree.tmpl
index e492f157d050594093357fdc0a8a1fc32905804c..74bf3fd312b0bb754d610f28df06fcdcbbdb6640 100644
--- a/templates/tree.tmpl
+++ b/templates/tree.tmpl
@@ -1,6 +1,9 @@
{{define "title"}}{{.Repo}}/{{.Base}}{{end}}
{{define "content"}}
-<h1>{{if eq .Base ""}}{{.Repo}}{{else}}<a href="/{{.Repo}}/tree/{{.Ref}}">{{.Repo}}</a> /{{end}}
+<h1>{{if ne .Base ""}}<a href="/{{.Repo}}/tree/{{.Ref}}">{{.Repo}}</a> /
+{{else}}
+<a href="/{{.Repo}}/tree/{{.HeadRef}}">{{.Repo}}</a> /
+{{end}}
{{range $i, $c := .Crumbs}}
{{if $i}} / {{end}}
{{if $c.IsLast}}
@@ -9,14 +12,14 @@ {{else}}
<a href="/{{$c.URL}}">{{$c.Name}}</a>
{{end}}
{{end}}</h1>
-{{if eq .Base ""}}<p><a href="/"><-- back</a></p>{{end}}
+<p>@ {{.Ref}}</p>
<ul>
{{range .Entries}}
<li>
{{- if .IsDir -}}
- 📁 <a href="/{{.Repo}}/tree/{{.Ref}}/{{.Path}}">{{.Name}}</a>
+ 📁 <a href="/{{$.Repo}}/tree/{{$.Ref}}/{{.Path}}">{{.Name}}</a>
{{- else -}}
- 📄 <a href="/{{.Repo}}/blob/{{.Ref}}/{{.Path}}">{{.Name}}</a>
+ 📄 <a href="/{{$.Repo}}/blob/{{$.Ref}}/{{.Path}}">{{.Name}}</a>
{{- end -}}
</li>
{{end}}