cerrado @ e3705f35c642e578625ce4574d189fa0b0869403

feat: Add ref support

Now log and tree can be loaded using a given reference.
diff --git a/pkg/git/git.go b/pkg/git/git.go
index 7ef23f74b726b769ed7f197bd3ec25b30c2363a7..ce72465a84f2b8f0c65411b176b7a075ca031609 100644
--- a/pkg/git/git.go
+++ b/pkg/git/git.go
@@ -13,19 +13,50 @@
 var ()
 
 var (
-	MissingHeadErr = errors.New("Head not found")
+	MissingRefErr = errors.New("Reference not found")
 )
 
 type (
 	GitRepository struct {
-		path string
+		path       string
+		repository *git.Repository
+
+		ref plumbing.Hash
+		// this is setRef when ref is setRef
+		setRef bool
 	}
 )
 
-func NewGitRepository(dir string) *GitRepository {
-	return &GitRepository{
+func OpenRepository(dir string) (*GitRepository, error) {
+	g := &GitRepository{
 		path: dir,
 	}
+
+	repo, err := git.PlainOpen(dir)
+	if err != nil {
+		return nil, err
+	}
+	g.repository = repo
+
+	return g, nil
+}
+
+func (g *GitRepository) SetRef(ref string) error {
+	if ref == "" {
+		head, err := g.repository.Head()
+		if err != nil {
+			return errors.Join(MissingRefErr, err)
+		}
+		g.ref = head.Hash()
+	} else {
+		hash, err := g.repository.ResolveRevision(plumbing.Revision(ref))
+		if err != nil {
+			return errors.Join(MissingRefErr, err)
+		}
+		g.ref = *hash
+	}
+	g.setRef = true
+	return nil
 }
 
 func (g *GitRepository) Path() string {
@@ -33,17 +64,12 @@ 	return g.path
 }
 
 func (g *GitRepository) LastCommit() (*object.Commit, error) {
-	repo, err := git.PlainOpen(g.path)
+	err := g.validateRef()
 	if err != nil {
 		return nil, err
 	}
 
-	ref, err := repo.Head()
-	if err != nil {
-		return nil, errors.Join(MissingHeadErr, err)
-	}
-
-	c, err := repo.CommitObject(ref.Hash())
+	c, err := g.repository.CommitObject(g.ref)
 	if err != nil {
 		return nil, err
 	}
@@ -51,17 +77,12 @@ 	return c, nil
 }
 
 func (g *GitRepository) Commits() ([]*object.Commit, error) {
-	repo, err := git.PlainOpen(g.path)
+	err := g.validateRef()
 	if err != nil {
 		return nil, err
 	}
 
-	ref, err := repo.Head()
-	if err != nil {
-		return nil, errors.Join(MissingHeadErr, err)
-	}
-
-	ci, err := repo.Log(&git.LogOptions{From: ref.Hash()})
+	ci, err := g.repository.Log(&git.LogOptions{From: g.ref})
 	if err != nil {
 		return nil, fmt.Errorf("commits from ref: %w", err)
 	}
@@ -84,13 +105,12 @@
 	return commits, nil
 }
 
+func (g *GitRepository) Head() (*plumbing.Reference, error) {
+	return g.repository.Head()
+}
+
 func (g *GitRepository) Tags() ([]*object.Tag, error) {
-	repo, err := git.PlainOpen(g.path)
-	if err != nil {
-		return nil, err
-	}
-
-	ti, err := repo.TagObjects()
+	ti, err := g.repository.TagObjects()
 	if err != nil {
 		return nil, err
 	}
@@ -108,12 +128,7 @@ 	return tags, nil
 }
 
 func (g *GitRepository) Branches() ([]*plumbing.Reference, error) {
-	repo, err := git.PlainOpen(g.path)
-	if err != nil {
-		return nil, err
-	}
-
-	bs, err := repo.Branches()
+	bs, err := g.repository.Branches()
 	if err != nil {
 		return nil, err
 	}
@@ -129,3 +144,10 @@ 	}
 
 	return branches, nil
 }
+
+func (g *GitRepository) validateRef() error {
+	if !g.setRef {
+		return g.SetRef("")
+	}
+	return nil
+}
diff --git a/pkg/handler/git/handler.go b/pkg/handler/git/handler.go
index d090f2269b5c0057caf828048fe161028afcfacc..b77bcfc0e35d58f47b6fd5a08e74a23fa4d3735f 100644
--- a/pkg/handler/git/handler.go
+++ b/pkg/handler/git/handler.go
@@ -18,9 +18,10 @@ 	}
 
 	gitService interface {
 		ListRepositories() ([]*service.Repository, error)
-		ListCommits(string) ([]*object.Commit, error)
-		ListTags(string) ([]*object.Tag, error)
-		ListBranches(string) ([]*plumbing.Reference, error)
+		ListCommits(name string, ref string) ([]*object.Commit, error)
+		GetHead(name string) (*plumbing.Reference, error)
+		ListTags(name string) ([]*object.Tag, error)
+		ListBranches(name string) ([]*plumbing.Reference, error)
 	}
 )
 
@@ -43,8 +44,15 @@ }
 
 func (g *GitHandler) Summary(w http.ResponseWriter, r *http.Request) {
 	name := mux.Vars(r)["name"]
+	ref, err := g.gitService.GetHead(name)
+	if err != nil {
+		slog.Error("Error loading head", "error", err)
+		return
+	}
+
 	gitList := &templates.GitItemPage{
 		Name:        name,
+		Ref:         ref.Name().Short(),
 		GitItemBase: &templates.GitItemSummaryPage{},
 	}
 	templates.WritePageTemplate(w, gitList)
@@ -52,8 +60,14 @@ }
 
 func (g *GitHandler) About(w http.ResponseWriter, r *http.Request) {
 	name := mux.Vars(r)["name"]
+	ref, err := g.gitService.GetHead(name)
+	if err != nil {
+		slog.Error("Error loading head", "error", err)
+		return
+	}
 	gitList := &templates.GitItemPage{
 		Name:        name,
+		Ref:         ref.Name().Short(),
 		GitItemBase: &templates.GitItemAboutPage{},
 	}
 	templates.WritePageTemplate(w, gitList)
@@ -74,8 +88,15 @@ 		slog.Error("Error loading branches", "error", err)
 		return
 	}
 
+	ref, err := g.gitService.GetHead(name)
+	if err != nil {
+		slog.Error("Error loading head", "error", err)
+		return
+	}
+
 	gitList := &templates.GitItemPage{
 		Name: name,
+		Ref:  ref.Name().Short(),
 		GitItemBase: &templates.GitItemRefsPage{
 			Tags:     tags,
 			Branches: branches,
@@ -86,8 +107,10 @@ }
 
 func (g *GitHandler) Tree(w http.ResponseWriter, r *http.Request) {
 	name := mux.Vars(r)["name"]
+	ref := mux.Vars(r)["ref"]
 	gitList := &templates.GitItemPage{
 		Name:        name,
+		Ref:         ref,
 		GitItemBase: &templates.GitItemTreePage{},
 	}
 	templates.WritePageTemplate(w, gitList)
@@ -95,8 +118,9 @@ }
 
 func (g *GitHandler) Log(w http.ResponseWriter, r *http.Request) {
 	name := mux.Vars(r)["name"]
+	ref := mux.Vars(r)["ref"]
 
-	commits, err := g.gitService.ListCommits(name)
+	commits, err := g.gitService.ListCommits(name, ref)
 	if err != nil {
 		slog.Error("Error loading commits", "error", err)
 		return
@@ -104,6 +128,7 @@ 	}
 
 	gitList := &templates.GitItemPage{
 		Name: name,
+		Ref:  ref,
 		GitItemBase: &templates.GitItemLogPage{
 			Commits: commits,
 		},
diff --git a/pkg/handler/router.go b/pkg/handler/router.go
index f73e9fba6d0e2ce0127d6798fc26ebf017969c0d..79f70f11e263dbac66bf9660a97a4514b0dfd5fb 100644
--- a/pkg/handler/router.go
+++ b/pkg/handler/router.go
@@ -34,10 +34,10 @@ 	mux := mux.NewRouter()
 
 	mux.PathPrefix("/static").Handler(staticHandler)
 	mux.HandleFunc("/{name}/about", gitHandler.About)
-	mux.HandleFunc("/{name}/summary", gitHandler.Summary)
+	mux.HandleFunc("/{name}", gitHandler.Summary)
 	mux.HandleFunc("/{name}/refs", gitHandler.Refs)
-	mux.HandleFunc("/{name}/tree", gitHandler.Tree)
-	mux.HandleFunc("/{name}/log", gitHandler.Log)
+	mux.HandleFunc("/{name}/tree/{ref}", gitHandler.Tree)
+	mux.HandleFunc("/{name}/log/{ref}", gitHandler.Log)
 	mux.HandleFunc("/config", configHander)
 	mux.HandleFunc("/about", aboutHandler.About)
 	mux.HandleFunc("/", gitHandler.List)
diff --git a/pkg/service/git.go b/pkg/service/git.go
index 57b9b6e7327aa0901955e95bc0c2958d9185d408..9bf11f4e79cb77ae692820236f59db9bc9ea0453 100644
--- a/pkg/service/git.go
+++ b/pkg/service/git.go
@@ -15,6 +15,7 @@ 		Name              string
 		Title             string
 		LastCommitMessage string
 		LastCommitDate    string
+		Ref               string
 	}
 
 	GitService struct {
@@ -41,29 +42,50 @@ 	rs := g.configRepo.List()
 
 	repos := make([]*Repository, len(rs))
 	for i, r := range rs {
-		repo := git.NewGitRepository(r.Path)
+		repo, err := git.OpenRepository(r.Path)
+		if err != nil {
+			return nil, err
+		}
+		if err != nil {
+			return nil, err
+		}
+
 		obj, err := repo.LastCommit()
 		if err != nil {
 			return nil, err
 		}
 
+		head, err := repo.Head()
+		if err != nil {
+			return nil, err
+		}
+
 		baseName := path.Base(r.Path)
 		repos[i] = &Repository{
 			Name:              baseName,
 			Title:             baseName,
 			LastCommitMessage: obj.Message,
 			LastCommitDate:    obj.Author.When.Format(timeFormat),
+			Ref:               head.Name().Short(),
 		}
 	}
 
 	return repos, nil
 }
 
-func (g *GitService) ListCommits(name string) ([]*object.Commit, error) {
+func (g *GitService) ListCommits(name, ref string) ([]*object.Commit, error) {
 	// TODO: handle nil
 	r := g.configRepo.GetByName(name)
 
-	repo := git.NewGitRepository(r.Path)
+	repo, err := git.OpenRepository(r.Path)
+	if err != nil {
+		return nil, err
+	}
+
+	err = repo.SetRef(ref)
+	if err != nil {
+		return nil, err
+	}
 	return repo.Commits()
 }
 
@@ -71,7 +93,10 @@ func (g *GitService) ListTags(name string) ([]*object.Tag, error) {
 	// TODO: handle nil
 	r := g.configRepo.GetByName(name)
 
-	repo := git.NewGitRepository(r.Path)
+	repo, err := git.OpenRepository(r.Path)
+	if err != nil {
+		return nil, err
+	}
 	return repo.Tags()
 }
 
@@ -79,6 +104,21 @@ func (g *GitService) ListBranches(name string) ([]*plumbing.Reference, error) {
 	// TODO: handle nil
 	r := g.configRepo.GetByName(name)
 
-	repo := git.NewGitRepository(r.Path)
+	repo, err := git.OpenRepository(r.Path)
+	if err != nil {
+		return nil, err
+	}
 	return repo.Branches()
 }
+
+func (g *GitService) GetHead(name string) (*plumbing.Reference, error) {
+	// TODO: handle nil
+	r := g.configRepo.GetByName(name)
+
+	repo, err := git.OpenRepository(r.Path)
+	if err != nil {
+		return nil, err
+	}
+
+	return repo.Head()
+}
diff --git a/templates/gititem.qtpl b/templates/gititem.qtpl
index d2fcea7fcbac20ca753c668f71b7937a7c61e7c5..3e2dd4e543265ca86fbe8832a7001b392edb0b7b 100644
--- a/templates/gititem.qtpl
+++ b/templates/gititem.qtpl
@@ -1,6 +1,6 @@
 {% interface 
 GitItemBase {
-   Nav(name string)
+   Nav(name, ref string)
    GitContent()
 }
 %}
@@ -8,6 +8,7 @@
 {% code
 type GitItemPage struct {
     Name string
+    Ref string
     GitItemBase
 }
 %}
@@ -17,12 +18,7 @@
 {% func (p *GitItemPage) Navbar() %}{%= Navbar(Git) %}{% endfunc %}
 
 {% func (p *GitItemPage) Content() %}
-<div class="row">
-    <h3>{%s p.Name %}</h3>
-</div>
-<div class="row">
-{%= p.Nav(p.Name) %}
-</div>
+{%= p.Nav(p.Name, p.Ref) %}
 <div class="container">
 {%= p.GitContent() %}
 </div>
diff --git a/templates/gititem.qtpl.go b/templates/gititem.qtpl.go
index 9709a43ec697fd27687af9e2ac9b424ff8e981a1..2c4610465d702fd2ceab108977a1dff00576ab68 100644
--- a/templates/gititem.qtpl.go
+++ b/templates/gititem.qtpl.go
@@ -20,11 +20,11 @@
 //line gititem.qtpl:2
 type GitItemBase interface {
 //line gititem.qtpl:2
-	Nav(name string) string
+	Nav(name, ref string) string
 //line gititem.qtpl:2
-	StreamNav(qw422016 *qt422016.Writer, name string)
+	StreamNav(qw422016 *qt422016.Writer, name, ref string)
 //line gititem.qtpl:2
-	WriteNav(qq422016 qtio422016.Writer, name string)
+	WriteNav(qq422016 qtio422016.Writer, name, ref string)
 //line gititem.qtpl:2
 	GitContent() string
 //line gititem.qtpl:2
@@ -37,160 +37,152 @@
 //line gititem.qtpl:9
 type GitItemPage struct {
 	Name string
+	Ref  string
 	GitItemBase
 }
 
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 func (p *GitItemPage) StreamTitle(qw422016 *qt422016.Writer) {
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 	qw422016.N().S(`Git | List`)
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 }
 
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 func (p *GitItemPage) WriteTitle(qq422016 qtio422016.Writer) {
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 	qw422016 := qt422016.AcquireWriter(qq422016)
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 	p.StreamTitle(qw422016)
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 	qt422016.ReleaseWriter(qw422016)
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 }
 
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 func (p *GitItemPage) Title() string {
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 	qb422016 := qt422016.AcquireByteBuffer()
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 	p.WriteTitle(qb422016)
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 	qs422016 := string(qb422016.B)
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 	qt422016.ReleaseByteBuffer(qb422016)
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 	return qs422016
-//line gititem.qtpl:15
+//line gititem.qtpl:16
 }
 
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 func (p *GitItemPage) StreamNavbar(qw422016 *qt422016.Writer) {
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 	StreamNavbar(qw422016, Git)
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 }
 
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 func (p *GitItemPage) WriteNavbar(qq422016 qtio422016.Writer) {
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 	qw422016 := qt422016.AcquireWriter(qq422016)
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 	p.StreamNavbar(qw422016)
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 	qt422016.ReleaseWriter(qw422016)
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 }
 
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 func (p *GitItemPage) Navbar() string {
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 	qb422016 := qt422016.AcquireByteBuffer()
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 	p.WriteNavbar(qb422016)
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 	qs422016 := string(qb422016.B)
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 	qt422016.ReleaseByteBuffer(qb422016)
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 	return qs422016
-//line gititem.qtpl:17
+//line gititem.qtpl:18
 }
 
-//line gititem.qtpl:19
+//line gititem.qtpl:20
 func (p *GitItemPage) StreamContent(qw422016 *qt422016.Writer) {
-//line gititem.qtpl:19
+//line gititem.qtpl:20
 	qw422016.N().S(`
-<div class="row">
-    <h3>`)
+`)
 //line gititem.qtpl:21
-	qw422016.E().S(p.Name)
+	p.StreamNav(qw422016, p.Name, p.Ref)
 //line gititem.qtpl:21
-	qw422016.N().S(`</h3>
-</div>
-<div class="row">
-`)
-//line gititem.qtpl:24
-	p.StreamNav(qw422016, p.Name)
-//line gititem.qtpl:24
 	qw422016.N().S(`
-</div>
 <div class="container">
 `)
-//line gititem.qtpl:27
+//line gititem.qtpl:23
 	p.StreamGitContent(qw422016)
-//line gititem.qtpl:27
+//line gititem.qtpl:23
 	qw422016.N().S(`
 </div>
 `)
-//line gititem.qtpl:29
+//line gititem.qtpl:25
 }
 
-//line gititem.qtpl:29
+//line gititem.qtpl:25
 func (p *GitItemPage) WriteContent(qq422016 qtio422016.Writer) {
-//line gititem.qtpl:29
+//line gititem.qtpl:25
 	qw422016 := qt422016.AcquireWriter(qq422016)
-//line gititem.qtpl:29
+//line gititem.qtpl:25
 	p.StreamContent(qw422016)
-//line gititem.qtpl:29
+//line gititem.qtpl:25
 	qt422016.ReleaseWriter(qw422016)
-//line gititem.qtpl:29
+//line gititem.qtpl:25
 }
 
-//line gititem.qtpl:29
+//line gititem.qtpl:25
 func (p *GitItemPage) Content() string {
-//line gititem.qtpl:29
+//line gititem.qtpl:25
 	qb422016 := qt422016.AcquireByteBuffer()
-//line gititem.qtpl:29
+//line gititem.qtpl:25
 	p.WriteContent(qb422016)
-//line gititem.qtpl:29
+//line gititem.qtpl:25
 	qs422016 := string(qb422016.B)
-//line gititem.qtpl:29
+//line gititem.qtpl:25
 	qt422016.ReleaseByteBuffer(qb422016)
-//line gititem.qtpl:29
+//line gititem.qtpl:25
 	return qs422016
-//line gititem.qtpl:29
+//line gititem.qtpl:25
 }
 
-//line gititem.qtpl:31
+//line gititem.qtpl:27
 func (p *GitItemPage) StreamScript(qw422016 *qt422016.Writer) {
-//line gititem.qtpl:31
+//line gititem.qtpl:27
 	qw422016.N().S(`
 `)
-//line gititem.qtpl:32
+//line gititem.qtpl:28
 }
 
-//line gititem.qtpl:32
+//line gititem.qtpl:28
 func (p *GitItemPage) WriteScript(qq422016 qtio422016.Writer) {
-//line gititem.qtpl:32
+//line gititem.qtpl:28
 	qw422016 := qt422016.AcquireWriter(qq422016)
-//line gititem.qtpl:32
+//line gititem.qtpl:28
 	p.StreamScript(qw422016)
-//line gititem.qtpl:32
+//line gititem.qtpl:28
 	qt422016.ReleaseWriter(qw422016)
-//line gititem.qtpl:32
+//line gititem.qtpl:28
 }
 
-//line gititem.qtpl:32
+//line gititem.qtpl:28
 func (p *GitItemPage) Script() string {
-//line gititem.qtpl:32
+//line gititem.qtpl:28
 	qb422016 := qt422016.AcquireByteBuffer()
-//line gititem.qtpl:32
+//line gititem.qtpl:28
 	p.WriteScript(qb422016)
-//line gititem.qtpl:32
+//line gititem.qtpl:28
 	qs422016 := string(qb422016.B)
-//line gititem.qtpl:32
+//line gititem.qtpl:28
 	qt422016.ReleaseByteBuffer(qb422016)
-//line gititem.qtpl:32
+//line gititem.qtpl:28
 	return qs422016
-//line gititem.qtpl:32
+//line gititem.qtpl:28
 }
diff --git a/templates/gititemabout.qtpl b/templates/gititemabout.qtpl
index 67d43f1249399e49aa1a240812427a3f72e8077c..e0fa9c3f184837a96d3194d5baacdcdfd866a56b 100644
--- a/templates/gititemabout.qtpl
+++ b/templates/gititemabout.qtpl
@@ -3,7 +3,7 @@ type GitItemAboutPage struct {
 }
 %}
 
-{% func (g *GitItemAboutPage) Nav(name string) %}{%= GitItemNav(name, Readme) %}{% endfunc %}
+{% func (g *GitItemAboutPage) Nav(name, ref string) %}{%= GitItemNav(name, ref, Readme) %}{% endfunc %}
 
 {% func (g *GitItemAboutPage) GitContent() %}
 <h4>About</h4>
diff --git a/templates/gititemabout.qtpl.go b/templates/gititemabout.qtpl.go
index 7b772e5bf41536602a120b48ed78b20de81e56d2..0827fbe114bd75cf7b2b8d8c7b67a29c5194ffb0 100644
--- a/templates/gititemabout.qtpl.go
+++ b/templates/gititemabout.qtpl.go
@@ -22,29 +22,29 @@ type GitItemAboutPage struct {
 }
 
 //line gititemabout.qtpl:6
-func (g *GitItemAboutPage) StreamNav(qw422016 *qt422016.Writer, name string) {
+func (g *GitItemAboutPage) StreamNav(qw422016 *qt422016.Writer, name, ref string) {
 //line gititemabout.qtpl:6
-	StreamGitItemNav(qw422016, name, Readme)
+	StreamGitItemNav(qw422016, name, ref, Readme)
 //line gititemabout.qtpl:6
 }
 
 //line gititemabout.qtpl:6
-func (g *GitItemAboutPage) WriteNav(qq422016 qtio422016.Writer, name string) {
+func (g *GitItemAboutPage) WriteNav(qq422016 qtio422016.Writer, name, ref string) {
 //line gititemabout.qtpl:6
 	qw422016 := qt422016.AcquireWriter(qq422016)
 //line gititemabout.qtpl:6
-	g.StreamNav(qw422016, name)
+	g.StreamNav(qw422016, name, ref)
 //line gititemabout.qtpl:6
 	qt422016.ReleaseWriter(qw422016)
 //line gititemabout.qtpl:6
 }
 
 //line gititemabout.qtpl:6
-func (g *GitItemAboutPage) Nav(name string) string {
+func (g *GitItemAboutPage) Nav(name, ref string) string {
 //line gititemabout.qtpl:6
 	qb422016 := qt422016.AcquireByteBuffer()
 //line gititemabout.qtpl:6
-	g.WriteNav(qb422016, name)
+	g.WriteNav(qb422016, name, ref)
 //line gititemabout.qtpl:6
 	qs422016 := string(qb422016.B)
 //line gititemabout.qtpl:6
diff --git a/templates/gititemlog.qtpl b/templates/gititemlog.qtpl
index 436c1d274193ee35357d087d94ce89d99ff30d7a..e037c52718fa440432f3e361b61625729fed8edb 100644
--- a/templates/gititemlog.qtpl
+++ b/templates/gititemlog.qtpl
@@ -6,7 +6,7 @@     Commits []*object.Commit
 }
 %}
 
-{% func (g *GitItemLogPage) Nav(name string) %}{%= GitItemNav(name, Log) %}{% endfunc %}
+{% func (g *GitItemLogPage) Nav(name, ref string) %}{%= GitItemNav(name, ref, Log) %}{% endfunc %}
 
 {% func (g *GitItemLogPage) GitContent() %}
 <div class="logs">
diff --git a/templates/gititemlog.qtpl.go b/templates/gititemlog.qtpl.go
index e63c8712559c77e4efff48789b7ab17c4bd4ce41..47e700d343dbf7be77ef68410f4d907ba0bd29fc 100644
--- a/templates/gititemlog.qtpl.go
+++ b/templates/gititemlog.qtpl.go
@@ -26,29 +26,29 @@ 	Commits []*object.Commit
 }
 
 //line gititemlog.qtpl:9
-func (g *GitItemLogPage) StreamNav(qw422016 *qt422016.Writer, name string) {
+func (g *GitItemLogPage) StreamNav(qw422016 *qt422016.Writer, name, ref string) {
 //line gititemlog.qtpl:9
-	StreamGitItemNav(qw422016, name, Log)
+	StreamGitItemNav(qw422016, name, ref, Log)
 //line gititemlog.qtpl:9
 }
 
 //line gititemlog.qtpl:9
-func (g *GitItemLogPage) WriteNav(qq422016 qtio422016.Writer, name string) {
+func (g *GitItemLogPage) WriteNav(qq422016 qtio422016.Writer, name, ref string) {
 //line gititemlog.qtpl:9
 	qw422016 := qt422016.AcquireWriter(qq422016)
 //line gititemlog.qtpl:9
-	g.StreamNav(qw422016, name)
+	g.StreamNav(qw422016, name, ref)
 //line gititemlog.qtpl:9
 	qt422016.ReleaseWriter(qw422016)
 //line gititemlog.qtpl:9
 }
 
 //line gititemlog.qtpl:9
-func (g *GitItemLogPage) Nav(name string) string {
+func (g *GitItemLogPage) Nav(name, ref string) string {
 //line gititemlog.qtpl:9
 	qb422016 := qt422016.AcquireByteBuffer()
 //line gititemlog.qtpl:9
-	g.WriteNav(qb422016, name)
+	g.WriteNav(qb422016, name, ref)
 //line gititemlog.qtpl:9
 	qs422016 := string(qb422016.B)
 //line gititemlog.qtpl:9
diff --git a/templates/gititemrefs.qtpl b/templates/gititemrefs.qtpl
index 9c588630a087401485881be712c413e026664f07..56f6c2bf557e1afc0a2dc12a5f918f6f347353d2 100644
--- a/templates/gititemrefs.qtpl
+++ b/templates/gititemrefs.qtpl
@@ -8,7 +8,7 @@     Branches []*plumbing.Reference
 }
 %}
 
-{% func (g *GitItemRefsPage) Nav(name string) %}{%= GitItemNav(name ,Refs) %}{% endfunc %}
+{% func (g *GitItemRefsPage) Nav(name, ref string) %}{%= GitItemNav(name, ref, Refs) %}{% endfunc %}
 
 {% func (g *GitItemRefsPage) GitContent() %}
 <div class="row">
@@ -36,7 +36,7 @@     <div class="logs">
       {% for _, b := range g.Branches %}
       <div class="row">
           <div class="col-xxl">
-           {%s b.String() %}
+           {%s b.Name().Short() %}
           </div>
       </div>
       {% endfor %}
diff --git a/templates/gititemrefs.qtpl.go b/templates/gititemrefs.qtpl.go
index f2d2b6fcec5540caa852103cb2513ce0bcb6b518..d2a362e5bacc6b8e2fcf10bb9da486ef48fcecda 100644
--- a/templates/gititemrefs.qtpl.go
+++ b/templates/gititemrefs.qtpl.go
@@ -30,29 +30,29 @@ 	Branches []*plumbing.Reference
 }
 
 //line gititemrefs.qtpl:11
-func (g *GitItemRefsPage) StreamNav(qw422016 *qt422016.Writer, name string) {
+func (g *GitItemRefsPage) StreamNav(qw422016 *qt422016.Writer, name, ref string) {
 //line gititemrefs.qtpl:11
-	StreamGitItemNav(qw422016, name, Refs)
+	StreamGitItemNav(qw422016, name, ref, Refs)
 //line gititemrefs.qtpl:11
 }
 
 //line gititemrefs.qtpl:11
-func (g *GitItemRefsPage) WriteNav(qq422016 qtio422016.Writer, name string) {
+func (g *GitItemRefsPage) WriteNav(qq422016 qtio422016.Writer, name, ref string) {
 //line gititemrefs.qtpl:11
 	qw422016 := qt422016.AcquireWriter(qq422016)
 //line gititemrefs.qtpl:11
-	g.StreamNav(qw422016, name)
+	g.StreamNav(qw422016, name, ref)
 //line gititemrefs.qtpl:11
 	qt422016.ReleaseWriter(qw422016)
 //line gititemrefs.qtpl:11
 }
 
 //line gititemrefs.qtpl:11
-func (g *GitItemRefsPage) Nav(name string) string {
+func (g *GitItemRefsPage) Nav(name, ref string) string {
 //line gititemrefs.qtpl:11
 	qb422016 := qt422016.AcquireByteBuffer()
 //line gititemrefs.qtpl:11
-	g.WriteNav(qb422016, name)
+	g.WriteNav(qb422016, name, ref)
 //line gititemrefs.qtpl:11
 	qs422016 := string(qb422016.B)
 //line gititemrefs.qtpl:11
@@ -117,7 +117,7 @@       <div class="row">
           <div class="col-xxl">
            `)
 //line gititemrefs.qtpl:39
-		qw422016.E().S(b.String())
+		qw422016.E().S(b.Name().Short())
 //line gititemrefs.qtpl:39
 		qw422016.N().S(`
           </div>
diff --git a/templates/gititemsummary.qtpl b/templates/gititemsummary.qtpl
index f4b0dd6ed86aa1d3f65b86798453e662db5c3c8b..5756ea51e1182941cb268f30836a0fc0902f324b 100644
--- a/templates/gititemsummary.qtpl
+++ b/templates/gititemsummary.qtpl
@@ -3,7 +3,7 @@ type GitItemSummaryPage struct {
 }
 %}
 
-{% func (g *GitItemSummaryPage) Nav(name string) %}{%= GitItemNav(name, Summary) %}{% endfunc %}
+{% func (g *GitItemSummaryPage) Nav(name, ref string) %}{%= GitItemNav(name, ref, Summary) %}{% endfunc %}
 
 {% func (g *GitItemSummaryPage) GitContent() %}
 <h4>Summary</h4>
diff --git a/templates/gititemsummary.qtpl.go b/templates/gititemsummary.qtpl.go
index aa41a17e5c03cc55cc9fa2a560cf7169f330ae8e..99cb984846d93b0e576a56e13479174083e0d4f5 100644
--- a/templates/gititemsummary.qtpl.go
+++ b/templates/gititemsummary.qtpl.go
@@ -22,29 +22,29 @@ type GitItemSummaryPage struct {
 }
 
 //line gititemsummary.qtpl:6
-func (g *GitItemSummaryPage) StreamNav(qw422016 *qt422016.Writer, name string) {
+func (g *GitItemSummaryPage) StreamNav(qw422016 *qt422016.Writer, name, ref string) {
 //line gititemsummary.qtpl:6
-	StreamGitItemNav(qw422016, name, Summary)
+	StreamGitItemNav(qw422016, name, ref, Summary)
 //line gititemsummary.qtpl:6
 }
 
 //line gititemsummary.qtpl:6
-func (g *GitItemSummaryPage) WriteNav(qq422016 qtio422016.Writer, name string) {
+func (g *GitItemSummaryPage) WriteNav(qq422016 qtio422016.Writer, name, ref string) {
 //line gititemsummary.qtpl:6
 	qw422016 := qt422016.AcquireWriter(qq422016)
 //line gititemsummary.qtpl:6
-	g.StreamNav(qw422016, name)
+	g.StreamNav(qw422016, name, ref)
 //line gititemsummary.qtpl:6
 	qt422016.ReleaseWriter(qw422016)
 //line gititemsummary.qtpl:6
 }
 
 //line gititemsummary.qtpl:6
-func (g *GitItemSummaryPage) Nav(name string) string {
+func (g *GitItemSummaryPage) Nav(name, ref string) string {
 //line gititemsummary.qtpl:6
 	qb422016 := qt422016.AcquireByteBuffer()
 //line gititemsummary.qtpl:6
-	g.WriteNav(qb422016, name)
+	g.WriteNav(qb422016, name, ref)
 //line gititemsummary.qtpl:6
 	qs422016 := string(qb422016.B)
 //line gititemsummary.qtpl:6
diff --git a/templates/gititemtree.qtpl b/templates/gititemtree.qtpl
index 778cc004ad00863b1b10e95b19a50e4b9a6dec8e..5ace7b0ea7262684dcbb60ef67ce6f4249ff15e3 100644
--- a/templates/gititemtree.qtpl
+++ b/templates/gititemtree.qtpl
@@ -3,7 +3,7 @@ type GitItemTreePage struct {
 }
 %}
 
-{% func (g *GitItemTreePage) Nav(name string) %}{%= GitItemNav(name, Tree) %}{% endfunc %}
+{% func (g *GitItemTreePage) Nav(name, ref string) %}{%= GitItemNav(name, ref, Tree) %}{% endfunc %}
 
 {% func (g *GitItemTreePage) GitContent() %}
 <h4>Tree</h4>
diff --git a/templates/gititemtree.qtpl.go b/templates/gititemtree.qtpl.go
index 04861d1539348a8bad52ac42f30228b356496c99..d8beb0e09acb90fabbe5c2bb75de765952ca11a3 100644
--- a/templates/gititemtree.qtpl.go
+++ b/templates/gititemtree.qtpl.go
@@ -22,29 +22,29 @@ type GitItemTreePage struct {
 }
 
 //line gititemtree.qtpl:6
-func (g *GitItemTreePage) StreamNav(qw422016 *qt422016.Writer, name string) {
+func (g *GitItemTreePage) StreamNav(qw422016 *qt422016.Writer, name, ref string) {
 //line gititemtree.qtpl:6
-	StreamGitItemNav(qw422016, name, Tree)
+	StreamGitItemNav(qw422016, name, ref, Tree)
 //line gititemtree.qtpl:6
 }
 
 //line gititemtree.qtpl:6
-func (g *GitItemTreePage) WriteNav(qq422016 qtio422016.Writer, name string) {
+func (g *GitItemTreePage) WriteNav(qq422016 qtio422016.Writer, name, ref string) {
 //line gititemtree.qtpl:6
 	qw422016 := qt422016.AcquireWriter(qq422016)
 //line gititemtree.qtpl:6
-	g.StreamNav(qw422016, name)
+	g.StreamNav(qw422016, name, ref)
 //line gititemtree.qtpl:6
 	qt422016.ReleaseWriter(qw422016)
 //line gititemtree.qtpl:6
 }
 
 //line gititemtree.qtpl:6
-func (g *GitItemTreePage) Nav(name string) string {
+func (g *GitItemTreePage) Nav(name, ref string) string {
 //line gititemtree.qtpl:6
 	qb422016 := qt422016.AcquireByteBuffer()
 //line gititemtree.qtpl:6
-	g.WriteNav(qb422016, name)
+	g.WriteNav(qb422016, name, ref)
 //line gititemtree.qtpl:6
 	qs422016 := string(qb422016.B)
 //line gititemtree.qtpl:6
diff --git a/templates/gitlist.qtpl b/templates/gitlist.qtpl
index b7beca58d127482042da362b30d569f03ff67efb..3d7ef822b9bae8b6debebe51796799f2417513ed 100644
--- a/templates/gitlist.qtpl
+++ b/templates/gitlist.qtpl
@@ -17,15 +17,15 @@     <div class="event-list">
       {% for _, r := range p.Respositories %}
       <div class="event">
         <h4>
-          <a href="/{%s r.Name %}/summary">{%s r.Name %}</a>
+          <a href="/{%s r.Name %}">{%s r.Name %}</a>
         </h4>
         </hr>
         <p>{%s r.LastCommitMessage %}</p>
         <p><small>{%s r.LastCommitDate %}</small></p>
         <p>
-          <a href="/{%s r.Name %}/summary">summary</a>
-          <a href="/{%s r.Name %}/log">log</a>
-          <a href="/{%s r.Name %}/tree">tree</a>
+          <a href="/{%s r.Name %}/log/{%s r.Ref %}">log</a>
+          <a href="/{%s r.Name %}/tree/{%s r.Ref %}">tree</a>
+          <a href="/{%s r.Name %}/refs">refs</a>
         </p>
       </div>
       {% endfor %}
diff --git a/templates/gitlist.qtpl.go b/templates/gitlist.qtpl.go
index 73f887ab91d2df83f2be1e6241a0dfd57cd8427c..d9f7ec1ec45568e18d549fa56015753b5511221a 100644
--- a/templates/gitlist.qtpl.go
+++ b/templates/gitlist.qtpl.go
@@ -109,7 +109,7 @@           <a href="/`)
 //line gitlist.qtpl:20
 		qw422016.E().S(r.Name)
 //line gitlist.qtpl:20
-		qw422016.N().S(`/summary">`)
+		qw422016.N().S(`">`)
 //line gitlist.qtpl:20
 		qw422016.E().S(r.Name)
 //line gitlist.qtpl:20
@@ -131,17 +131,25 @@           <a href="/`)
 //line gitlist.qtpl:26
 		qw422016.E().S(r.Name)
 //line gitlist.qtpl:26
-		qw422016.N().S(`/summary">summary</a>
+		qw422016.N().S(`/log/`)
+//line gitlist.qtpl:26
+		qw422016.E().S(r.Ref)
+//line gitlist.qtpl:26
+		qw422016.N().S(`">log</a>
           <a href="/`)
 //line gitlist.qtpl:27
 		qw422016.E().S(r.Name)
 //line gitlist.qtpl:27
-		qw422016.N().S(`/log">log</a>
+		qw422016.N().S(`/tree/`)
+//line gitlist.qtpl:27
+		qw422016.E().S(r.Ref)
+//line gitlist.qtpl:27
+		qw422016.N().S(`">tree</a>
           <a href="/`)
 //line gitlist.qtpl:28
 		qw422016.E().S(r.Name)
 //line gitlist.qtpl:28
-		qw422016.N().S(`/tree">tree</a>
+		qw422016.N().S(`/refs">refs</a>
         </p>
       </div>
       `)
diff --git a/templates/navbar.qtpl b/templates/navbar.qtpl
index 8b0799d412ad6da105a309a921cfd8a14a51f99b..775f496a4b52da8eef28656dedee80fe9995871d 100644
--- a/templates/navbar.qtpl
+++ b/templates/navbar.qtpl
@@ -32,22 +32,27 @@           </div>
         </nav>
 {% endfunc %}
 
-{% func GitItemNav (name string, s GitSelection) %}
+{% func GitItemNav (name, ref string, s GitSelection) %}
+<div class="row">
+    <h3>{%s name %} {% if ref != "" && (s == Log || s == Tree)  %}@ {%s ref %}{% endif %}</h3>
+</div>
+<div class="row">
   <ul class="nav">
     <li class="nav-item">
       <a class="nav-link{%= insertIfEqual(s, Readme) %}" aria-current="page" href="/{%s name %}/about">about</a>
     </li>
     <li class="nav-item">
-      <a class="nav-link{%= insertIfEqual(s, Log) %}" aria-current="page" href="/{%s name %}/log">log</a>
+      <a class="nav-link{%= insertIfEqual(s, Log) %}" aria-current="page" href="/{%s name %}/log/{%s ref %}">log</a>
     </li>
     <li class="nav-item">
-      <a class="nav-link{%= insertIfEqual(s, Summary) %}" aria-current="page" href="/{%s name %}/summary">summary</a>
+      <a class="nav-link{%= insertIfEqual(s, Summary) %}" aria-current="page" href="/{%s name %}">summary</a>
     </li>
     <li class="nav-item">
       <a class="nav-link{%= insertIfEqual(s, Refs) %}" aria-current="page" href="/{%s name %}/refs">refs</a>
     </li>
     <li class="nav-item">
-      <a class="nav-link{%= insertIfEqual(s, Tree) %}" aria-current="page" href="/{%s name %}/tree">tree</a>
+      <a class="nav-link{%= insertIfEqual(s, Tree) %}" aria-current="page" href="/{%s name %}/tree/{%s ref %}">tree</a>
     </li>
   </ul>
+</div>
 {% endfunc %}
diff --git a/templates/navbar.qtpl.go b/templates/navbar.qtpl.go
index acf21b4a0b6c952c2df4a5567b7c34229db8da82..cddc6a6de17621590eb849a2ef2a27b84c661715 100644
--- a/templates/navbar.qtpl.go
+++ b/templates/navbar.qtpl.go
@@ -134,92 +134,119 @@ //line navbar.qtpl:33
 }
 
 //line navbar.qtpl:35
-func StreamGitItemNav(qw422016 *qt422016.Writer, name string, s GitSelection) {
+func StreamGitItemNav(qw422016 *qt422016.Writer, name, ref string, s GitSelection) {
 //line navbar.qtpl:35
 	qw422016.N().S(`
+<div class="row">
+    <h3>`)
+//line navbar.qtpl:37
+	qw422016.E().S(name)
+//line navbar.qtpl:37
+	qw422016.N().S(` `)
+//line navbar.qtpl:37
+	if ref != "" && (s == Log || s == Tree) {
+//line navbar.qtpl:37
+		qw422016.N().S(`@ `)
+//line navbar.qtpl:37
+		qw422016.E().S(ref)
+//line navbar.qtpl:37
+	}
+//line navbar.qtpl:37
+	qw422016.N().S(`</h3>
+</div>
+<div class="row">
   <ul class="nav">
     <li class="nav-item">
       <a class="nav-link`)
-//line navbar.qtpl:38
+//line navbar.qtpl:42
 	streaminsertIfEqual(qw422016, s, Readme)
-//line navbar.qtpl:38
+//line navbar.qtpl:42
 	qw422016.N().S(`" aria-current="page" href="/`)
-//line navbar.qtpl:38
+//line navbar.qtpl:42
 	qw422016.E().S(name)
-//line navbar.qtpl:38
+//line navbar.qtpl:42
 	qw422016.N().S(`/about">about</a>
     </li>
     <li class="nav-item">
       <a class="nav-link`)
-//line navbar.qtpl:41
+//line navbar.qtpl:45
 	streaminsertIfEqual(qw422016, s, Log)
-//line navbar.qtpl:41
+//line navbar.qtpl:45
 	qw422016.N().S(`" aria-current="page" href="/`)
-//line navbar.qtpl:41
+//line navbar.qtpl:45
 	qw422016.E().S(name)
-//line navbar.qtpl:41
-	qw422016.N().S(`/log">log</a>
+//line navbar.qtpl:45
+	qw422016.N().S(`/log/`)
+//line navbar.qtpl:45
+	qw422016.E().S(ref)
+//line navbar.qtpl:45
+	qw422016.N().S(`">log</a>
     </li>
     <li class="nav-item">
       <a class="nav-link`)
-//line navbar.qtpl:44
+//line navbar.qtpl:48
 	streaminsertIfEqual(qw422016, s, Summary)
-//line navbar.qtpl:44
+//line navbar.qtpl:48
 	qw422016.N().S(`" aria-current="page" href="/`)
-//line navbar.qtpl:44
+//line navbar.qtpl:48
 	qw422016.E().S(name)
-//line navbar.qtpl:44
-	qw422016.N().S(`/summary">summary</a>
+//line navbar.qtpl:48
+	qw422016.N().S(`">summary</a>
     </li>
     <li class="nav-item">
       <a class="nav-link`)
-//line navbar.qtpl:47
+//line navbar.qtpl:51
 	streaminsertIfEqual(qw422016, s, Refs)
-//line navbar.qtpl:47
+//line navbar.qtpl:51
 	qw422016.N().S(`" aria-current="page" href="/`)
-//line navbar.qtpl:47
+//line navbar.qtpl:51
 	qw422016.E().S(name)
-//line navbar.qtpl:47
+//line navbar.qtpl:51
 	qw422016.N().S(`/refs">refs</a>
     </li>
     <li class="nav-item">
       <a class="nav-link`)
-//line navbar.qtpl:50
+//line navbar.qtpl:54
 	streaminsertIfEqual(qw422016, s, Tree)
-//line navbar.qtpl:50
+//line navbar.qtpl:54
 	qw422016.N().S(`" aria-current="page" href="/`)
-//line navbar.qtpl:50
+//line navbar.qtpl:54
 	qw422016.E().S(name)
-//line navbar.qtpl:50
-	qw422016.N().S(`/tree">tree</a>
+//line navbar.qtpl:54
+	qw422016.N().S(`/tree/`)
+//line navbar.qtpl:54
+	qw422016.E().S(ref)
+//line navbar.qtpl:54
+	qw422016.N().S(`">tree</a>
     </li>
   </ul>
+</div>
 `)
-//line navbar.qtpl:53
+//line navbar.qtpl:58
 }
 
-//line navbar.qtpl:53
-func WriteGitItemNav(qq422016 qtio422016.Writer, name string, s GitSelection) {
-//line navbar.qtpl:53
+//line navbar.qtpl:58
+func WriteGitItemNav(qq422016 qtio422016.Writer, name, ref string, s GitSelection) {
+//line navbar.qtpl:58
 	qw422016 := qt422016.AcquireWriter(qq422016)
-//line navbar.qtpl:53
-	StreamGitItemNav(qw422016, name, s)
-//line navbar.qtpl:53
+//line navbar.qtpl:58
+	StreamGitItemNav(qw422016, name, ref, s)
+//line navbar.qtpl:58
 	qt422016.ReleaseWriter(qw422016)
-//line navbar.qtpl:53
+//line navbar.qtpl:58
 }
 
-//line navbar.qtpl:53
-func GitItemNav(name string, s GitSelection) string {
-//line navbar.qtpl:53
+//line navbar.qtpl:58
+func GitItemNav(name, ref string, s GitSelection) string {
+//line navbar.qtpl:58
 	qb422016 := qt422016.AcquireByteBuffer()
-//line navbar.qtpl:53
-	WriteGitItemNav(qb422016, name, s)
-//line navbar.qtpl:53
+//line navbar.qtpl:58
+	WriteGitItemNav(qb422016, name, ref, s)
+//line navbar.qtpl:58
 	qs422016 := string(qb422016.B)
-//line navbar.qtpl:53
+//line navbar.qtpl:58
 	qt422016.ReleaseByteBuffer(qb422016)
-//line navbar.qtpl:53
+//line navbar.qtpl:58
 	return qs422016
-//line navbar.qtpl:53
+//line navbar.qtpl:58
 }