cerrado @ cb9036aab96895ddf03cab68f75d3356f5227968

feat: Add log pagination

Now it is possible to navigate through all commit from a given repository.
It implements a simple navigation which will plenty for now.
diff --git a/pkg/git/git.go b/pkg/git/git.go
index 6221e33dc5d8e7aca9682f8d22c830225b1e306b..b33afa7bf1f8a453ddc4d2da48273b199f821b96 100644
--- a/pkg/git/git.go
+++ b/pkg/git/git.go
@@ -16,8 +16,6 @@ 	"github.com/go-git/go-git/v5/plumbing"
 	"github.com/go-git/go-git/v5/plumbing/object"
 )
 
-var ()
-
 var (
 	MissingRefErr  = errors.New("Reference not found")
 	TreeForFileErr = errors.New("Trying to get tree of a file")
@@ -96,30 +94,46 @@ 	}
 	return c, nil
 }
 
-func (g *GitRepository) Commits(count int) ([]*object.Commit, error) {
+func (g *GitRepository) Commits(count int, from string) ([]*object.Commit, *object.Commit, error) {
 	err := g.validateRef()
 	if err != nil {
-		return nil, err
+		return nil, nil, err
+	}
+
+	opts := &git.LogOptions{Order: git.LogOrderCommitterTime}
+
+	if from != "" {
+		hash, err := g.repository.ResolveRevision(plumbing.Revision(from))
+		if err != nil {
+			return nil, nil, errors.Join(MissingRefErr, err)
+		}
+		opts.From = *hash
 	}
 
-	ci, err := g.repository.Log(&git.LogOptions{From: g.ref})
+	ci, err := g.repository.Log(opts)
 	if err != nil {
-		return nil, fmt.Errorf("commits from ref: %w", err)
+		return nil, nil, fmt.Errorf("commits from ref: %w", err)
 	}
 
 	commits := []*object.Commit{}
-	// TODO: for now only load first 1000
-	for x := 0; x < count; x++ {
+	var next *object.Commit
+
+	// iterate one more item so we can fetch the next commit
+	for x := 0; x < (count + 1); x++ {
 		c, err := ci.Next()
 		if err != nil && errors.Is(err, io.EOF) {
 			break
 		} else if err != nil {
-			return nil, err
+			return nil, nil, err
+		}
+		if x == count {
+			next = c
+		} else {
+			commits = append(commits, c)
 		}
-		commits = append(commits, c)
 	}
 
-	return commits, nil
+	return commits, next, nil
 }
 
 func (g *GitRepository) Head() (*plumbing.Reference, error) {
@@ -438,7 +452,6 @@ 	if t.tag != nil {
 		return t.tag.Message
 	}
 	return ""
-
 }
 
 func (self *tagList) Len() int {
diff --git a/pkg/handler/git/handler.go b/pkg/handler/git/handler.go
index 6225b1a444f98701f9049c856c2b63e5c248adc6..436d36430c4c952af21c7fb95c9af8018cdd414f 100644
--- a/pkg/handler/git/handler.go
+++ b/pkg/handler/git/handler.go
@@ -130,7 +130,7 @@ 	if err != nil {
 		return err
 	}
 
-	commits, err := g.gitService.ListCommits(name, "", 10)
+	commits, _, err := g.gitService.ListCommits(name, "", "", 10)
 	if err != nil {
 		return err
 	}
@@ -324,8 +324,9 @@ func (g *GitHandler) Log(w http.ResponseWriter, r *http.Request) error {
 	ext.SetHTML(w)
 	name := r.PathValue("name")
 	ref := r.PathValue("ref")
+	from := r.URL.Query().Get("from")
 
-	commits, err := g.gitService.ListCommits(name, ref, 1000)
+	commits, next, err := g.gitService.ListCommits(name, ref, from, 100)
 	if err != nil {
 		return err
 	}
@@ -335,6 +336,7 @@ 		Name: name,
 		Ref:  ref,
 		GitItemBase: &templates.GitItemLogPage{
 			Commits: commits,
+			Next:    next,
 		},
 	}
 	templates.WritePageTemplate(w, gitList, r.Context())
diff --git a/pkg/service/git.go b/pkg/service/git.go
index 12d238e9dc388f2b25e7dc4fb417c3c4d7579de8..2d00715f57ab9553c54a3d02a88bb8feb34cb817 100644
--- a/pkg/service/git.go
+++ b/pkg/service/git.go
@@ -81,22 +81,22 @@
 	return repos, nil
 }
 
-func (g *GitService) ListCommits(name, ref string, count int) ([]*object.Commit, error) {
+func (g *GitService) ListCommits(name, ref, from string, count int) ([]*object.Commit, *object.Commit, error) {
 	r := g.configRepo.GetByName(name)
 	if r == nil {
-		return nil, ErrRepositoryNotFound
+		return nil, nil, ErrRepositoryNotFound
 	}
 
 	repo, err := git.OpenRepository(r.Path)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 
 	err = repo.SetRef(ref)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
-	return repo.Commits(count)
+	return repo.Commits(count, from)
 }
 
 func (g *GitService) LastCommit(name, ref string) (*object.Commit, error) {
diff --git a/templates/gititemlog.qtpl b/templates/gititemlog.qtpl
index e5bfc1b7f2e76c0fa7e666fd480f03e20bb7bad6..b0c8cec1f2ee8ea9f8cae4dfca45e51e12b1c51e 100644
--- a/templates/gititemlog.qtpl
+++ b/templates/gititemlog.qtpl
@@ -3,6 +3,7 @@
 {% code
 type GitItemLogPage struct {
     Commits []*object.Commit
+    Next *object.Commit
 }
 %}
 
@@ -13,5 +14,9 @@ <div class="event-list">
   {% for _, c := range g.Commits %}
   {%= Commit(name, c, false) %}
   {% endfor %}
+  {% if g.Next != nil %}
+  <a href="/{%s name %}/log/{%s ref %}/?from={%s g.Next.Hash.String() %}"  class="btn btn-primary">Next</a>
+  {% endif %}
+
 </div>
 {% endfunc %}
diff --git a/templates/gititemlog.qtpl.go b/templates/gititemlog.qtpl.go
index 20fc1f54a9f6ce2ea4a7a14cbb6dc6e6d60b5d6e..719b71f9bb4f74125415288c3438eca26a55624c 100644
--- a/templates/gititemlog.qtpl.go
+++ b/templates/gititemlog.qtpl.go
@@ -23,88 +23,113 @@
 //line templates/gititemlog.qtpl:4
 type GitItemLogPage struct {
 	Commits []*object.Commit
+	Next    *object.Commit
 }
 
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 func (g *GitItemLogPage) StreamNav(qw422016 *qt422016.Writer, name, ref string) {
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 	StreamGitItemNav(qw422016, name, ref, Log)
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 }
 
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 func (g *GitItemLogPage) WriteNav(qq422016 qtio422016.Writer, name, ref string) {
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 	qw422016 := qt422016.AcquireWriter(qq422016)
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 	g.StreamNav(qw422016, name, ref)
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 	qt422016.ReleaseWriter(qw422016)
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 }
 
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 func (g *GitItemLogPage) Nav(name, ref string) string {
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 	qb422016 := qt422016.AcquireByteBuffer()
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 	g.WriteNav(qb422016, name, ref)
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 	qs422016 := string(qb422016.B)
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 	qt422016.ReleaseByteBuffer(qb422016)
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 	return qs422016
-//line templates/gititemlog.qtpl:9
+//line templates/gititemlog.qtpl:10
 }
 
-//line templates/gititemlog.qtpl:11
+//line templates/gititemlog.qtpl:12
 func (g *GitItemLogPage) StreamGitContent(qw422016 *qt422016.Writer, name, ref string) {
-//line templates/gititemlog.qtpl:11
+//line templates/gititemlog.qtpl:12
 	qw422016.N().S(`
 <div class="event-list">
   `)
-//line templates/gititemlog.qtpl:13
+//line templates/gititemlog.qtpl:14
 	for _, c := range g.Commits {
-//line templates/gititemlog.qtpl:13
+//line templates/gititemlog.qtpl:14
 		qw422016.N().S(`
   `)
-//line templates/gititemlog.qtpl:14
+//line templates/gititemlog.qtpl:15
 		StreamCommit(qw422016, name, c, false)
-//line templates/gititemlog.qtpl:14
+//line templates/gititemlog.qtpl:15
+		qw422016.N().S(`
+  `)
+//line templates/gititemlog.qtpl:16
+	}
+//line templates/gititemlog.qtpl:16
+	qw422016.N().S(`
+  `)
+//line templates/gititemlog.qtpl:17
+	if g.Next != nil {
+//line templates/gititemlog.qtpl:17
 		qw422016.N().S(`
+  <a href="/`)
+//line templates/gititemlog.qtpl:18
+		qw422016.E().S(name)
+//line templates/gititemlog.qtpl:18
+		qw422016.N().S(`/log/`)
+//line templates/gititemlog.qtpl:18
+		qw422016.E().S(ref)
+//line templates/gititemlog.qtpl:18
+		qw422016.N().S(`/?from=`)
+//line templates/gititemlog.qtpl:18
+		qw422016.E().S(g.Next.Hash.String())
+//line templates/gititemlog.qtpl:18
+		qw422016.N().S(`"  class="btn btn-primary">Next</a>
   `)
-//line templates/gititemlog.qtpl:15
+//line templates/gititemlog.qtpl:19
 	}
-//line templates/gititemlog.qtpl:15
+//line templates/gititemlog.qtpl:19
 	qw422016.N().S(`
+
 </div>
 `)
-//line templates/gititemlog.qtpl:17
+//line templates/gititemlog.qtpl:22
 }
 
-//line templates/gititemlog.qtpl:17
+//line templates/gititemlog.qtpl:22
 func (g *GitItemLogPage) WriteGitContent(qq422016 qtio422016.Writer, name, ref string) {
-//line templates/gititemlog.qtpl:17
+//line templates/gititemlog.qtpl:22
 	qw422016 := qt422016.AcquireWriter(qq422016)
-//line templates/gititemlog.qtpl:17
+//line templates/gititemlog.qtpl:22
 	g.StreamGitContent(qw422016, name, ref)
-//line templates/gititemlog.qtpl:17
+//line templates/gititemlog.qtpl:22
 	qt422016.ReleaseWriter(qw422016)
-//line templates/gititemlog.qtpl:17
+//line templates/gititemlog.qtpl:22
 }
 
-//line templates/gititemlog.qtpl:17
+//line templates/gititemlog.qtpl:22
 func (g *GitItemLogPage) GitContent(name, ref string) string {
-//line templates/gititemlog.qtpl:17
+//line templates/gititemlog.qtpl:22
 	qb422016 := qt422016.AcquireByteBuffer()
-//line templates/gititemlog.qtpl:17
+//line templates/gititemlog.qtpl:22
 	g.WriteGitContent(qb422016, name, ref)
-//line templates/gititemlog.qtpl:17
+//line templates/gititemlog.qtpl:22
 	qs422016 := string(qb422016.B)
-//line templates/gititemlog.qtpl:17
+//line templates/gititemlog.qtpl:22
 	qt422016.ReleaseByteBuffer(qb422016)
-//line templates/gititemlog.qtpl:17
+//line templates/gititemlog.qtpl:22
 	return qs422016
-//line templates/gititemlog.qtpl:17
+//line templates/gititemlog.qtpl:22
 }