cerrado @ 6079b1d963f34ada5c4b25363f2319901e283936

feat: Add error handling
diff --git a/pkg/ext/compression.go b/pkg/ext/compression.go
index 92144b82132968aa7db6115aad78f37d71c8e354..57ad49ad64f6654f9ec39ea50af972f0739d5e44 100644
--- a/pkg/ext/compression.go
+++ b/pkg/ext/compression.go
@@ -24,7 +24,7 @@ 	innerWriter    http.ResponseWriter
 	compressWriter io.Writer
 }
 
-func Compress(next func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
+func Compress(next http.HandlerFunc) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		if accept, ok := r.Header["Accept-Encoding"]; ok {
 			if compress, algo := GetCompressionWriter(u.FirstOrZero(accept), w); algo != "" {
diff --git a/pkg/ext/log.go b/pkg/ext/log.go
new file mode 100644
index 0000000000000000000000000000000000000000..a9d26a9afd92ce2309d6f27653577a97a9b76c1e
--- /dev/null
+++ b/pkg/ext/log.go
@@ -0,0 +1,53 @@
+package ext
+
+import (
+	"log/slog"
+	"net/http"
+	"time"
+)
+
+type statusWraper struct {
+	statusCode  int
+	innerWriter http.ResponseWriter
+}
+
+func (s *statusWraper) Header() http.Header {
+	return s.innerWriter.Header()
+}
+
+func (s *statusWraper) Write(b []byte) (int, error) {
+	return s.innerWriter.Write(b)
+}
+
+func (s *statusWraper) WriteHeader(statusCode int) {
+	s.statusCode = statusCode
+	s.innerWriter.WriteHeader(statusCode)
+}
+
+func (s *statusWraper) StatusCode() int {
+	if s.statusCode == 0 {
+		return 200
+	}
+	return s.statusCode
+}
+
+func wrap(w http.ResponseWriter) *statusWraper {
+	return &statusWraper{
+		innerWriter: w,
+	}
+}
+
+func Log(next http.HandlerFunc) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		t := time.Now()
+		s := wrap(w)
+		next(s, r)
+		slog.Info(
+			"Http request",
+			"method", r.Method,
+			"code", s.StatusCode(),
+			"path", r.URL,
+			"elapsed", time.Since(t),
+		)
+	}
+}
diff --git a/pkg/ext/router.go b/pkg/ext/router.go
new file mode 100644
index 0000000000000000000000000000000000000000..5d22814e9d53c5a65f4f72c6cdf67be9f929bcbb
--- /dev/null
+++ b/pkg/ext/router.go
@@ -0,0 +1,72 @@
+package ext
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+
+	"git.gabrielgio.me/cerrado/pkg/service"
+	"git.gabrielgio.me/cerrado/templates"
+)
+
+type (
+	Router struct {
+		middlewares []Middleware
+		router      *http.ServeMux
+	}
+	Middleware          func(next http.HandlerFunc) http.HandlerFunc
+	ErrorRequestHandler func(w http.ResponseWriter, r *http.Request) error
+)
+
+func NewRouter() *Router {
+	return &Router{
+		router: http.NewServeMux(),
+	}
+}
+func (r *Router) Handler() http.Handler {
+	return r.router
+}
+
+func (r *Router) AddMiddleware(middleware Middleware) {
+	r.middlewares = append(r.middlewares, middleware)
+}
+
+func wrapError(next ErrorRequestHandler) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		if err := next(w, r); err != nil {
+			if errors.Is(err, service.RepositoryNotFoundErr) {
+				NotFound(w)
+			} else {
+				InternalServerError(w, err)
+			}
+		}
+	}
+}
+
+func (r *Router) run(next ErrorRequestHandler) http.HandlerFunc {
+	return func(w http.ResponseWriter, re *http.Request) {
+		req := wrapError(next)
+		for _, r := range r.middlewares {
+			req = r(req)
+		}
+		req(w, re)
+	}
+}
+
+func (r *Router) HandleFunc(path string, handler ErrorRequestHandler) {
+	r.router.HandleFunc(path, r.run(handler))
+}
+
+func NotFound(w http.ResponseWriter) {
+	w.WriteHeader(http.StatusNotFound)
+	templates.WritePageTemplate(w, &templates.ErrorPage{
+		Message: "Not Found",
+	})
+}
+
+func InternalServerError(w http.ResponseWriter, err error) {
+	w.WriteHeader(http.StatusInternalServerError)
+	templates.WritePageTemplate(w, &templates.ErrorPage{
+		Message: fmt.Sprintf("Internal Server Error:\n%s", err.Error()),
+	})
+}
diff --git a/pkg/git/git.go b/pkg/git/git.go
index 6a7b91fcba4060e8c2462a6ff03cf9fd309218d4..ad5d3bca28fe075da5da0ea6ae72cb87bd09861a 100644
--- a/pkg/git/git.go
+++ b/pkg/git/git.go
@@ -99,9 +99,6 @@ 			return nil, err
 		}
 		commits = append(commits, c)
 	}
-	if err != nil {
-		return nil, err
-	}
 
 	return commits, nil
 }
diff --git a/pkg/handler/about/handler.go b/pkg/handler/about/handler.go
index 1acde601c7aa036550ab6e73253f7c820646e1e6..ac3d31417836e98121bd03807b2993fa2dbf27cb 100644
--- a/pkg/handler/about/handler.go
+++ b/pkg/handler/about/handler.go
@@ -2,7 +2,6 @@ package about
 
 import (
 	"io"
-	"log/slog"
 	"net/http"
 	"os"
 
@@ -27,17 +26,15 @@ func NewAboutHandler(configRepo configurationRepository) *AboutHandler {
 	return &AboutHandler{configRepo.GetRootReadme()}
 }
 
-func (g *AboutHandler) About(w http.ResponseWriter, _ *http.Request) {
+func (g *AboutHandler) About(w http.ResponseWriter, _ *http.Request) error {
 	f, err := os.Open(g.readmePath)
 	if err != nil {
-		slog.Error("Error loading readme file", "error", err)
-		return
+		return err
 	}
 
 	bs, err := io.ReadAll(f)
 	if err != nil {
-		slog.Error("Error reading readme file bytes", "error", err)
-		return
+		return err
 	}
 
 	extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock
@@ -54,4 +51,5 @@ 	gitList := &templates.AboutPage{
 		Body: bs,
 	}
 	templates.WritePageTemplate(w, gitList)
+	return nil
 }
diff --git a/pkg/handler/config/handler.go b/pkg/handler/config/handler.go
index 30f428383db9ada7b0337c2400b983a7d1a4b8c2..c43b54d621d0aed6441a8f2ba85564c0be3da6fe 100644
--- a/pkg/handler/config/handler.go
+++ b/pkg/handler/config/handler.go
@@ -3,7 +3,6 @@
 import (
 	"bytes"
 	"encoding/json"
-	"log/slog"
 	"net/http"
 
 	"github.com/alecthomas/chroma/v2/formatters/html"
@@ -11,6 +10,7 @@ 	"github.com/alecthomas/chroma/v2/lexers"
 	"github.com/alecthomas/chroma/v2/styles"
 
 	"git.gabrielgio.me/cerrado/pkg/config"
+	"git.gabrielgio.me/cerrado/pkg/ext"
 	"git.gabrielgio.me/cerrado/templates"
 )
 
@@ -21,8 +21,8 @@ 		List() []*config.GitRepositoryConfiguration
 	}
 )
 
-func ConfigFile(configRepo configurationRepository) func(http.ResponseWriter, *http.Request) {
-	return func(w http.ResponseWriter, _ *http.Request) {
+func ConfigFile(configRepo configurationRepository) ext.ErrorRequestHandler {
+	return func(w http.ResponseWriter, _ *http.Request) error {
 
 		config := struct {
 			RootReadme   string
@@ -34,8 +34,7 @@ 		}
 
 		b, err := json.MarshalIndent(config, "", "  ")
 		if err != nil {
-			slog.Error("Error parsing json", "error", err)
-			return
+			return err
 		}
 
 		lexer := lexers.Get("json")
@@ -45,15 +44,13 @@ 			html.WithLineNumbers(true),
 		)
 		iterator, err := lexer.Tokenise(nil, string(b))
 		if err != nil {
-			slog.Error("Error tokenise", "error", err)
-			return
+			return err
 		}
 
 		var code bytes.Buffer
 		err = formatter.Format(&code, style, iterator)
 		if err != nil {
-			slog.Error("Error format", "error", err)
-			return
+			return err
 		}
 
 		hello := &templates.ConfigPage{
@@ -61,5 +58,6 @@ 			Body: code.Bytes(),
 		}
 
 		templates.WritePageTemplate(w, hello)
+		return nil
 	}
 }
diff --git a/pkg/handler/git/handler.go b/pkg/handler/git/handler.go
index 7bdf3729016402aa7f8f1e462d9b34c209109835..d952fef3bc513240d3f602426e7572fa84033a9e 100644
--- a/pkg/handler/git/handler.go
+++ b/pkg/handler/git/handler.go
@@ -3,7 +3,6 @@
 import (
 	"bytes"
 	"io"
-	"log/slog"
 	"net/http"
 	"os"
 	"path/filepath"
@@ -50,23 +49,20 @@ 		readmePath: confRepo.GetRootReadme(),
 	}
 }
 
-func (g *GitHandler) List(w http.ResponseWriter, _ *http.Request) {
+func (g *GitHandler) List(w http.ResponseWriter, _ *http.Request) error {
 	repos, err := g.gitService.ListRepositories()
 	if err != nil {
-		slog.Error("Error listing repo", "error", err)
-		return
+		return err
 	}
 
 	f, err := os.Open(g.readmePath)
 	if err != nil {
-		slog.Error("Error loading readme file", "error", err)
-		return
+		return err
 	}
 
 	bs, err := io.ReadAll(f)
 	if err != nil {
-		slog.Error("Error reading readme file bytes", "error", err)
-		return
+		return err
 	}
 
 	extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock
@@ -84,15 +80,15 @@ 		Respositories: repos,
 		About:         bs,
 	}
 	templates.WritePageTemplate(w, gitList)
+	return nil
 }
 
-func (g *GitHandler) Summary(w http.ResponseWriter, r *http.Request) {
+func (g *GitHandler) Summary(w http.ResponseWriter, r *http.Request) error {
 	ext.SetHTML(w)
 	name := r.PathValue("name")
 	ref, err := g.gitService.GetHead(name)
 	if err != nil {
-		slog.Error("Error loading head", "error", err)
-		return
+		return err
 	}
 
 	gitList := &templates.GitItemPage{
@@ -101,15 +97,15 @@ 		Ref:         ref.Name().Short(),
 		GitItemBase: &templates.GitItemSummaryPage{},
 	}
 	templates.WritePageTemplate(w, gitList)
+	return nil
 }
 
-func (g *GitHandler) About(w http.ResponseWriter, r *http.Request) {
+func (g *GitHandler) About(w http.ResponseWriter, r *http.Request) error {
 	ext.SetHTML(w)
 	name := r.PathValue("name")
 	ref, err := g.gitService.GetHead(name)
 	if err != nil {
-		slog.Error("Error loading head", "error", err)
-		return
+		return err
 	}
 	gitList := &templates.GitItemPage{
 		Name:        name,
@@ -117,28 +113,26 @@ 		Ref:         ref.Name().Short(),
 		GitItemBase: &templates.GitItemAboutPage{},
 	}
 	templates.WritePageTemplate(w, gitList)
+	return nil
 }
 
-func (g *GitHandler) Refs(w http.ResponseWriter, r *http.Request) {
+func (g *GitHandler) Refs(w http.ResponseWriter, r *http.Request) error {
 	ext.SetHTML(w)
 	name := r.PathValue("name")
 
 	tags, err := g.gitService.ListTags(name)
 	if err != nil {
-		slog.Error("Error loading tags", "error", err)
-		return
+		return err
 	}
 
 	branches, err := g.gitService.ListBranches(name)
 	if err != nil {
-		slog.Error("Error loading branches", "error", err)
-		return
+		return err
 	}
 
 	ref, err := g.gitService.GetHead(name)
 	if err != nil {
-		slog.Error("Error loading head", "error", err)
-		return
+		return err
 	}
 
 	gitList := &templates.GitItemPage{
@@ -150,9 +144,10 @@ 			Branches: branches,
 		},
 	}
 	templates.WritePageTemplate(w, gitList)
+	return nil
 }
 
-func (g *GitHandler) Tree(w http.ResponseWriter, r *http.Request) {
+func (g *GitHandler) Tree(w http.ResponseWriter, r *http.Request) error {
 	ext.SetHTML(w)
 	name := r.PathValue("name")
 	ref := r.PathValue("ref")
@@ -160,8 +155,7 @@ 	rest := r.PathValue("rest")
 
 	tree, err := g.gitService.GetTree(name, ref, rest)
 	if err != nil {
-		slog.Error("Error loading tree", "error", err)
-		return
+		return err
 	}
 
 	gitList := &templates.GitItemPage{
@@ -175,9 +169,10 @@ 			Name:        name,
 		},
 	}
 	templates.WritePageTemplate(w, gitList)
+	return nil
 }
 
-func (g *GitHandler) Blob(w http.ResponseWriter, r *http.Request) {
+func (g *GitHandler) Blob(w http.ResponseWriter, r *http.Request) error {
 	ext.SetHTML(w)
 	name := r.PathValue("name")
 	ref := r.PathValue("ref")
@@ -185,8 +180,7 @@ 	rest := r.PathValue("rest")
 
 	file, err := g.gitService.GetFileContent(name, ref, rest)
 	if err != nil {
-		slog.Error("Error loading blob", "error", err)
-		return
+		return err
 	}
 
 	filename := filepath.Base(rest)
@@ -197,15 +191,13 @@ 		html.WithLineNumbers(true),
 	)
 	iterator, err := lexer.Tokenise(nil, file)
 	if err != nil {
-		slog.Error("Error tokenise", "error", err)
-		return
+		return err
 	}
 
 	var code bytes.Buffer
 	err = formatter.Format(&code, style, iterator)
 	if err != nil {
-		slog.Error("Error format", "error", err)
-		return
+		return err
 	}
 
 	gitList := &templates.GitItemPage{
@@ -217,17 +209,17 @@ 			Content: code.Bytes(),
 		},
 	}
 	templates.WritePageTemplate(w, gitList)
+	return nil
 }
 
-func (g *GitHandler) Log(w http.ResponseWriter, r *http.Request) {
+func (g *GitHandler) Log(w http.ResponseWriter, r *http.Request) error {
 	ext.SetHTML(w)
 	name := r.PathValue("name")
 	ref := r.PathValue("ref")
 
 	commits, err := g.gitService.ListCommits(name, ref)
 	if err != nil {
-		slog.Error("Error loading commits", "error", err)
-		return
+		return err
 	}
 
 	gitList := &templates.GitItemPage{
@@ -238,6 +230,7 @@ 			Commits: commits,
 		},
 	}
 	templates.WritePageTemplate(w, gitList)
+	return nil
 }
 
 func GetLexers(filename string) chroma.Lexer {
diff --git a/pkg/handler/router.go b/pkg/handler/router.go
index bf13ad5f8821c8fa48e6c932afddacd8e2f964f6..3da812feb835f6d0e5ceed41bb688d45f34baae0 100644
--- a/pkg/handler/router.go
+++ b/pkg/handler/router.go
@@ -20,9 +20,9 @@ 	gitService *service.GitService,
 	configRepo *serverconfig.ConfigurationRepository,
 ) (http.Handler, error) {
 	var (
-		gitHandler   = git.NewGitHandler(gitService, configRepo)
-		aboutHandler = about.NewAboutHandler(configRepo)
-		configHander = config.ConfigFile(configRepo)
+		gitHandler    = git.NewGitHandler(gitService, configRepo)
+		aboutHandler  = about.NewAboutHandler(configRepo)
+		configHandler = config.ConfigFile(configRepo)
 	)
 
 	staticHandler, err := static.ServeStaticHandler()
@@ -30,21 +30,19 @@ 	if err != nil {
 		return nil, err
 	}
 
-	mux := http.NewServeMux()
-
-	mux.HandleFunc("/static/{file}", m(staticHandler))
-	mux.HandleFunc("/{name}/about/{$}", m(gitHandler.About))
-	mux.HandleFunc("/{name}", m(gitHandler.Summary))
-	mux.HandleFunc("/{name}/refs/{$}", m(gitHandler.Refs))
-	mux.HandleFunc("/{name}/tree/{ref}/{rest...}", m(gitHandler.Tree))
-	mux.HandleFunc("/{name}/blob/{ref}/{rest...}", m(gitHandler.Blob))
-	mux.HandleFunc("/{name}/log/{ref}", m(gitHandler.Log))
-	mux.HandleFunc("/config", m(configHander))
-	mux.HandleFunc("/about", m(aboutHandler.About))
-	mux.HandleFunc("/", m(gitHandler.List))
-	return mux, nil
-}
+	mux := ext.NewRouter()
+	mux.AddMiddleware(ext.Compress)
+	mux.AddMiddleware(ext.Log)
 
-func m(next func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
-	return ext.Compress(next)
+	mux.HandleFunc("/static/{file}", staticHandler)
+	mux.HandleFunc("/{name}/about/{$}", gitHandler.About)
+	mux.HandleFunc("/{name}", gitHandler.Summary)
+	mux.HandleFunc("/{name}/refs/{$}", gitHandler.Refs)
+	mux.HandleFunc("/{name}/tree/{ref}/{rest...}", gitHandler.Tree)
+	mux.HandleFunc("/{name}/blob/{ref}/{rest...}", gitHandler.Blob)
+	mux.HandleFunc("/{name}/log/{ref}", gitHandler.Log)
+	mux.HandleFunc("/config", configHandler)
+	mux.HandleFunc("/about", aboutHandler.About)
+	mux.HandleFunc("/", gitHandler.List)
+	return mux.Handler(), nil
 }
diff --git a/pkg/handler/static/handler.go b/pkg/handler/static/handler.go
index 5155068306666b2303de7f129585108f32ea5dfa..0973d75ec8bf59c89111050112bb0f43368cb147 100644
--- a/pkg/handler/static/handler.go
+++ b/pkg/handler/static/handler.go
@@ -10,19 +10,21 @@ 	"git.gabrielgio.me/cerrado/pkg/ext"
 	"git.gabrielgio.me/cerrado/static"
 )
 
-func ServeStaticHandler() (func(w http.ResponseWriter, r *http.Request), error) {
+func ServeStaticHandler() (ext.ErrorRequestHandler, error) {
 	staticFs, err := fs.Sub(static.Static, ".")
 	if err != nil {
 		return nil, err
 	}
 
-	return func(w http.ResponseWriter, r *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) error {
 		var (
 			f = r.PathValue("file")
 			e = filepath.Ext(f)
 			m = mime.TypeByExtension(e)
 		)
 		ext.SetMIME(w, m)
+		w.Header().Add("Cache-Control", "immutable")
 		http.ServeFileFS(w, r, staticFs, f)
+		return nil
 	}, nil
 }
diff --git a/pkg/service/git.go b/pkg/service/git.go
index 31a1cbb47d7c74f552ce0ff097735481613a1aac..94e2adc5ab4433d9cc113f7a83d7c49aafd12787 100644
--- a/pkg/service/git.go
+++ b/pkg/service/git.go
@@ -1,6 +1,7 @@
 package service
 
 import (
+	"errors"
 	"log/slog"
 	"os"
 	"path"
@@ -29,6 +30,10 @@ 	configurationRepository interface {
 		List() []*config.GitRepositoryConfiguration
 		GetByName(name string) *config.GitRepositoryConfiguration
 	}
+)
+
+var (
+	RepositoryNotFoundErr = errors.New("Repository not found")
 )
 
 // TODO: make it configurable
@@ -84,8 +89,10 @@ 	return repos, nil
 }
 
 func (g *GitService) ListCommits(name, ref string) ([]*object.Commit, error) {
-	// TODO: handle nil
 	r := g.configRepo.GetByName(name)
+	if r == nil {
+		return nil, RepositoryNotFoundErr
+	}
 
 	repo, err := git.OpenRepository(r.Path)
 	if err != nil {
@@ -100,8 +107,10 @@ 	return repo.Commits()
 }
 
 func (g *GitService) GetTree(name, ref, path string) (*object.Tree, error) {
-	// TODO: handle nil
 	r := g.configRepo.GetByName(name)
+	if r == nil {
+		return nil, RepositoryNotFoundErr
+	}
 
 	repo, err := git.OpenRepository(r.Path)
 	if err != nil {
@@ -116,8 +125,10 @@ 	return repo.Tree(path)
 }
 
 func (g *GitService) GetFileContent(name, ref, path string) (string, error) {
-	// TODO: handle nil
 	r := g.configRepo.GetByName(name)
+	if r == nil {
+		return "", RepositoryNotFoundErr
+	}
 
 	repo, err := git.OpenRepository(r.Path)
 	if err != nil {
@@ -132,8 +143,10 @@ 	return repo.FileContent(path)
 }
 
 func (g *GitService) ListTags(name string) ([]*object.Tag, error) {
-	// TODO: handle nil
 	r := g.configRepo.GetByName(name)
+	if r == nil {
+		return nil, RepositoryNotFoundErr
+	}
 
 	repo, err := git.OpenRepository(r.Path)
 	if err != nil {
@@ -143,8 +156,10 @@ 	return repo.Tags()
 }
 
 func (g *GitService) ListBranches(name string) ([]*plumbing.Reference, error) {
-	// TODO: handle nil
 	r := g.configRepo.GetByName(name)
+	if r == nil {
+		return nil, RepositoryNotFoundErr
+	}
 
 	repo, err := git.OpenRepository(r.Path)
 	if err != nil {
@@ -154,8 +169,10 @@ 	return repo.Branches()
 }
 
 func (g *GitService) GetHead(name string) (*plumbing.Reference, error) {
-	// TODO: handle nil
 	r := g.configRepo.GetByName(name)
+	if r == nil {
+		return nil, RepositoryNotFoundErr
+	}
 
 	repo, err := git.OpenRepository(r.Path)
 	if err != nil {
diff --git a/templates/error.qtpl b/templates/error.qtpl
new file mode 100644
index 0000000000000000000000000000000000000000..771d5333f95ee509b2e1680de168003bf24b2b32
--- /dev/null
+++ b/templates/error.qtpl
@@ -0,0 +1,16 @@
+{% code
+type ErrorPage struct {
+    Message string
+}
+%}
+
+{% func (p *ErrorPage) Title() %}Error{% endfunc %}
+
+{% func (p *ErrorPage) Navbar() %}{%= Navbar(Git) %}{% endfunc %}
+
+{% func (p *ErrorPage) Content() %}
+{%s p.Message %}
+{% endfunc %}
+
+{% func (p *ErrorPage) Script() %}
+{% endfunc %}
diff --git a/templates/error.qtpl.go b/templates/error.qtpl.go
new file mode 100644
index 0000000000000000000000000000000000000000..099395f076a3fea1744d060bf5d96617e3c3bc21
--- /dev/null
+++ b/templates/error.qtpl.go
@@ -0,0 +1,162 @@
+// Code generated by qtc from "error.qtpl". DO NOT EDIT.
+// See https://github.com/valyala/quicktemplate for details.
+
+//line error.qtpl:1
+package templates
+
+//line error.qtpl:1
+import (
+	qtio422016 "io"
+
+	qt422016 "github.com/valyala/quicktemplate"
+)
+
+//line error.qtpl:1
+var (
+	_ = qtio422016.Copy
+	_ = qt422016.AcquireByteBuffer
+)
+
+//line error.qtpl:2
+type ErrorPage struct {
+	Message string
+}
+
+//line error.qtpl:7
+func (p *ErrorPage) StreamTitle(qw422016 *qt422016.Writer) {
+//line error.qtpl:7
+	qw422016.N().S(`Error`)
+//line error.qtpl:7
+}
+
+//line error.qtpl:7
+func (p *ErrorPage) WriteTitle(qq422016 qtio422016.Writer) {
+//line error.qtpl:7
+	qw422016 := qt422016.AcquireWriter(qq422016)
+//line error.qtpl:7
+	p.StreamTitle(qw422016)
+//line error.qtpl:7
+	qt422016.ReleaseWriter(qw422016)
+//line error.qtpl:7
+}
+
+//line error.qtpl:7
+func (p *ErrorPage) Title() string {
+//line error.qtpl:7
+	qb422016 := qt422016.AcquireByteBuffer()
+//line error.qtpl:7
+	p.WriteTitle(qb422016)
+//line error.qtpl:7
+	qs422016 := string(qb422016.B)
+//line error.qtpl:7
+	qt422016.ReleaseByteBuffer(qb422016)
+//line error.qtpl:7
+	return qs422016
+//line error.qtpl:7
+}
+
+//line error.qtpl:9
+func (p *ErrorPage) StreamNavbar(qw422016 *qt422016.Writer) {
+//line error.qtpl:9
+	StreamNavbar(qw422016, Git)
+//line error.qtpl:9
+}
+
+//line error.qtpl:9
+func (p *ErrorPage) WriteNavbar(qq422016 qtio422016.Writer) {
+//line error.qtpl:9
+	qw422016 := qt422016.AcquireWriter(qq422016)
+//line error.qtpl:9
+	p.StreamNavbar(qw422016)
+//line error.qtpl:9
+	qt422016.ReleaseWriter(qw422016)
+//line error.qtpl:9
+}
+
+//line error.qtpl:9
+func (p *ErrorPage) Navbar() string {
+//line error.qtpl:9
+	qb422016 := qt422016.AcquireByteBuffer()
+//line error.qtpl:9
+	p.WriteNavbar(qb422016)
+//line error.qtpl:9
+	qs422016 := string(qb422016.B)
+//line error.qtpl:9
+	qt422016.ReleaseByteBuffer(qb422016)
+//line error.qtpl:9
+	return qs422016
+//line error.qtpl:9
+}
+
+//line error.qtpl:11
+func (p *ErrorPage) StreamContent(qw422016 *qt422016.Writer) {
+//line error.qtpl:11
+	qw422016.N().S(`
+`)
+//line error.qtpl:12
+	qw422016.E().S(p.Message)
+//line error.qtpl:12
+	qw422016.N().S(`
+`)
+//line error.qtpl:13
+}
+
+//line error.qtpl:13
+func (p *ErrorPage) WriteContent(qq422016 qtio422016.Writer) {
+//line error.qtpl:13
+	qw422016 := qt422016.AcquireWriter(qq422016)
+//line error.qtpl:13
+	p.StreamContent(qw422016)
+//line error.qtpl:13
+	qt422016.ReleaseWriter(qw422016)
+//line error.qtpl:13
+}
+
+//line error.qtpl:13
+func (p *ErrorPage) Content() string {
+//line error.qtpl:13
+	qb422016 := qt422016.AcquireByteBuffer()
+//line error.qtpl:13
+	p.WriteContent(qb422016)
+//line error.qtpl:13
+	qs422016 := string(qb422016.B)
+//line error.qtpl:13
+	qt422016.ReleaseByteBuffer(qb422016)
+//line error.qtpl:13
+	return qs422016
+//line error.qtpl:13
+}
+
+//line error.qtpl:15
+func (p *ErrorPage) StreamScript(qw422016 *qt422016.Writer) {
+//line error.qtpl:15
+	qw422016.N().S(`
+`)
+//line error.qtpl:16
+}
+
+//line error.qtpl:16
+func (p *ErrorPage) WriteScript(qq422016 qtio422016.Writer) {
+//line error.qtpl:16
+	qw422016 := qt422016.AcquireWriter(qq422016)
+//line error.qtpl:16
+	p.StreamScript(qw422016)
+//line error.qtpl:16
+	qt422016.ReleaseWriter(qw422016)
+//line error.qtpl:16
+}
+
+//line error.qtpl:16
+func (p *ErrorPage) Script() string {
+//line error.qtpl:16
+	qb422016 := qt422016.AcquireByteBuffer()
+//line error.qtpl:16
+	p.WriteScript(qb422016)
+//line error.qtpl:16
+	qs422016 := string(qb422016.B)
+//line error.qtpl:16
+	qt422016.ReleaseByteBuffer(qb422016)
+//line error.qtpl:16
+	return qs422016
+//line error.qtpl:16
+}
diff --git a/templates/gititem.qtpl b/templates/gititem.qtpl
index 3e2dd4e543265ca86fbe8832a7001b392edb0b7b..d6957820865ed335415da36d5fc0d240039d0350 100644
--- a/templates/gititem.qtpl
+++ b/templates/gititem.qtpl
@@ -13,7 +13,7 @@     GitItemBase
 }
 %}
 
-{% func (p *GitItemPage) Title() %}Git | List{% endfunc %}
+{% func (p *GitItemPage) Title() %}Git | {%s p.Name %}{% endfunc %}
 
 {% func (p *GitItemPage) Navbar() %}{%= Navbar(Git) %}{% endfunc %}
 
diff --git a/templates/gititem.qtpl.go b/templates/gititem.qtpl.go
index 2c4610465d702fd2ceab108977a1dff00576ab68..a7ed65941e2355817c95d8429afe3a34d0a91fe9 100644
--- a/templates/gititem.qtpl.go
+++ b/templates/gititem.qtpl.go
@@ -44,7 +44,9 @@
 //line gititem.qtpl:16
 func (p *GitItemPage) StreamTitle(qw422016 *qt422016.Writer) {
 //line gititem.qtpl:16
-	qw422016.N().S(`Git | List`)
+	qw422016.N().S(`Git | `)
+//line gititem.qtpl:16
+	qw422016.E().S(p.Name)
 //line gititem.qtpl:16
 }