cerrado @ c51e2ae8af53a544cb6aa0bdc4713cb9571ba304

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.
  1diff --git a/pkg/git/git.go b/pkg/git/git.go
  2index 6221e33dc5d8e7aca9682f8d22c830225b1e306b..b33afa7bf1f8a453ddc4d2da48273b199f821b96 100644
  3--- a/pkg/git/git.go
  4+++ b/pkg/git/git.go
  5@@ -16,8 +16,6 @@ 	"github.com/go-git/go-git/v5/plumbing"
  6 	"github.com/go-git/go-git/v5/plumbing/object"
  7 )
  8 
  9-var ()
 10-
 11 var (
 12 	MissingRefErr  = errors.New("Reference not found")
 13 	TreeForFileErr = errors.New("Trying to get tree of a file")
 14@@ -96,30 +94,46 @@ 	}
 15 	return c, nil
 16 }
 17 
 18-func (g *GitRepository) Commits(count int) ([]*object.Commit, error) {
 19+func (g *GitRepository) Commits(count int, from string) ([]*object.Commit, *object.Commit, error) {
 20 	err := g.validateRef()
 21 	if err != nil {
 22-		return nil, err
 23+		return nil, nil, err
 24+	}
 25+
 26+	opts := &git.LogOptions{Order: git.LogOrderCommitterTime}
 27+
 28+	if from != "" {
 29+		hash, err := g.repository.ResolveRevision(plumbing.Revision(from))
 30+		if err != nil {
 31+			return nil, nil, errors.Join(MissingRefErr, err)
 32+		}
 33+		opts.From = *hash
 34 	}
 35 
 36-	ci, err := g.repository.Log(&git.LogOptions{From: g.ref})
 37+	ci, err := g.repository.Log(opts)
 38 	if err != nil {
 39-		return nil, fmt.Errorf("commits from ref: %w", err)
 40+		return nil, nil, fmt.Errorf("commits from ref: %w", err)
 41 	}
 42 
 43 	commits := []*object.Commit{}
 44-	// TODO: for now only load first 1000
 45-	for x := 0; x < count; x++ {
 46+	var next *object.Commit
 47+
 48+	// iterate one more item so we can fetch the next commit
 49+	for x := 0; x < (count + 1); x++ {
 50 		c, err := ci.Next()
 51 		if err != nil && errors.Is(err, io.EOF) {
 52 			break
 53 		} else if err != nil {
 54-			return nil, err
 55+			return nil, nil, err
 56+		}
 57+		if x == count {
 58+			next = c
 59+		} else {
 60+			commits = append(commits, c)
 61 		}
 62-		commits = append(commits, c)
 63 	}
 64 
 65-	return commits, nil
 66+	return commits, next, nil
 67 }
 68 
 69 func (g *GitRepository) Head() (*plumbing.Reference, error) {
 70@@ -438,7 +452,6 @@ 	if t.tag != nil {
 71 		return t.tag.Message
 72 	}
 73 	return ""
 74-
 75 }
 76 
 77 func (self *tagList) Len() int {
 78diff --git a/pkg/handler/git/handler.go b/pkg/handler/git/handler.go
 79index 6225b1a444f98701f9049c856c2b63e5c248adc6..436d36430c4c952af21c7fb95c9af8018cdd414f 100644
 80--- a/pkg/handler/git/handler.go
 81+++ b/pkg/handler/git/handler.go
 82@@ -130,7 +130,7 @@ 	if err != nil {
 83 		return err
 84 	}
 85 
 86-	commits, err := g.gitService.ListCommits(name, "", 10)
 87+	commits, _, err := g.gitService.ListCommits(name, "", "", 10)
 88 	if err != nil {
 89 		return err
 90 	}
 91@@ -324,8 +324,9 @@ func (g *GitHandler) Log(w http.ResponseWriter, r *http.Request) error {
 92 	ext.SetHTML(w)
 93 	name := r.PathValue("name")
 94 	ref := r.PathValue("ref")
 95+	from := r.URL.Query().Get("from")
 96 
 97-	commits, err := g.gitService.ListCommits(name, ref, 1000)
 98+	commits, next, err := g.gitService.ListCommits(name, ref, from, 100)
 99 	if err != nil {
100 		return err
101 	}
102@@ -335,6 +336,7 @@ 		Name: name,
103 		Ref:  ref,
104 		GitItemBase: &templates.GitItemLogPage{
105 			Commits: commits,
106+			Next:    next,
107 		},
108 	}
109 	templates.WritePageTemplate(w, gitList, r.Context())
110diff --git a/pkg/service/git.go b/pkg/service/git.go
111index 12d238e9dc388f2b25e7dc4fb417c3c4d7579de8..2d00715f57ab9553c54a3d02a88bb8feb34cb817 100644
112--- a/pkg/service/git.go
113+++ b/pkg/service/git.go
114@@ -81,22 +81,22 @@
115 	return repos, nil
116 }
117 
118-func (g *GitService) ListCommits(name, ref string, count int) ([]*object.Commit, error) {
119+func (g *GitService) ListCommits(name, ref, from string, count int) ([]*object.Commit, *object.Commit, error) {
120 	r := g.configRepo.GetByName(name)
121 	if r == nil {
122-		return nil, ErrRepositoryNotFound
123+		return nil, nil, ErrRepositoryNotFound
124 	}
125 
126 	repo, err := git.OpenRepository(r.Path)
127 	if err != nil {
128-		return nil, err
129+		return nil, nil, err
130 	}
131 
132 	err = repo.SetRef(ref)
133 	if err != nil {
134-		return nil, err
135+		return nil, nil, err
136 	}
137-	return repo.Commits(count)
138+	return repo.Commits(count, from)
139 }
140 
141 func (g *GitService) LastCommit(name, ref string) (*object.Commit, error) {
142diff --git a/templates/gititemlog.qtpl b/templates/gititemlog.qtpl
143index e5bfc1b7f2e76c0fa7e666fd480f03e20bb7bad6..b0c8cec1f2ee8ea9f8cae4dfca45e51e12b1c51e 100644
144--- a/templates/gititemlog.qtpl
145+++ b/templates/gititemlog.qtpl
146@@ -3,6 +3,7 @@
147 {% code
148 type GitItemLogPage struct {
149     Commits []*object.Commit
150+    Next *object.Commit
151 }
152 %}
153 
154@@ -13,5 +14,9 @@ <div class="event-list">
155   {% for _, c := range g.Commits %}
156   {%= Commit(name, c, false) %}
157   {% endfor %}
158+  {% if g.Next != nil %}
159+  <a href="/{%s name %}/log/{%s ref %}/?from={%s g.Next.Hash.String() %}"  class="btn btn-primary">Next</a>
160+  {% endif %}
161+
162 </div>
163 {% endfunc %}
164diff --git a/templates/gititemlog.qtpl.go b/templates/gititemlog.qtpl.go
165index 20fc1f54a9f6ce2ea4a7a14cbb6dc6e6d60b5d6e..719b71f9bb4f74125415288c3438eca26a55624c 100644
166--- a/templates/gititemlog.qtpl.go
167+++ b/templates/gititemlog.qtpl.go
168@@ -23,88 +23,113 @@
169 //line templates/gititemlog.qtpl:4
170 type GitItemLogPage struct {
171 	Commits []*object.Commit
172+	Next    *object.Commit
173 }
174 
175-//line templates/gititemlog.qtpl:9
176+//line templates/gititemlog.qtpl:10
177 func (g *GitItemLogPage) StreamNav(qw422016 *qt422016.Writer, name, ref string) {
178-//line templates/gititemlog.qtpl:9
179+//line templates/gititemlog.qtpl:10
180 	StreamGitItemNav(qw422016, name, ref, Log)
181-//line templates/gititemlog.qtpl:9
182+//line templates/gititemlog.qtpl:10
183 }
184 
185-//line templates/gititemlog.qtpl:9
186+//line templates/gititemlog.qtpl:10
187 func (g *GitItemLogPage) WriteNav(qq422016 qtio422016.Writer, name, ref string) {
188-//line templates/gititemlog.qtpl:9
189+//line templates/gititemlog.qtpl:10
190 	qw422016 := qt422016.AcquireWriter(qq422016)
191-//line templates/gititemlog.qtpl:9
192+//line templates/gititemlog.qtpl:10
193 	g.StreamNav(qw422016, name, ref)
194-//line templates/gititemlog.qtpl:9
195+//line templates/gititemlog.qtpl:10
196 	qt422016.ReleaseWriter(qw422016)
197-//line templates/gititemlog.qtpl:9
198+//line templates/gititemlog.qtpl:10
199 }
200 
201-//line templates/gititemlog.qtpl:9
202+//line templates/gititemlog.qtpl:10
203 func (g *GitItemLogPage) Nav(name, ref string) string {
204-//line templates/gititemlog.qtpl:9
205+//line templates/gititemlog.qtpl:10
206 	qb422016 := qt422016.AcquireByteBuffer()
207-//line templates/gititemlog.qtpl:9
208+//line templates/gititemlog.qtpl:10
209 	g.WriteNav(qb422016, name, ref)
210-//line templates/gititemlog.qtpl:9
211+//line templates/gititemlog.qtpl:10
212 	qs422016 := string(qb422016.B)
213-//line templates/gititemlog.qtpl:9
214+//line templates/gititemlog.qtpl:10
215 	qt422016.ReleaseByteBuffer(qb422016)
216-//line templates/gititemlog.qtpl:9
217+//line templates/gititemlog.qtpl:10
218 	return qs422016
219-//line templates/gititemlog.qtpl:9
220+//line templates/gititemlog.qtpl:10
221 }
222 
223-//line templates/gititemlog.qtpl:11
224+//line templates/gititemlog.qtpl:12
225 func (g *GitItemLogPage) StreamGitContent(qw422016 *qt422016.Writer, name, ref string) {
226-//line templates/gititemlog.qtpl:11
227+//line templates/gititemlog.qtpl:12
228 	qw422016.N().S(`
229 <div class="event-list">
230   `)
231-//line templates/gititemlog.qtpl:13
232+//line templates/gititemlog.qtpl:14
233 	for _, c := range g.Commits {
234-//line templates/gititemlog.qtpl:13
235+//line templates/gititemlog.qtpl:14
236 		qw422016.N().S(`
237   `)
238-//line templates/gititemlog.qtpl:14
239+//line templates/gititemlog.qtpl:15
240 		StreamCommit(qw422016, name, c, false)
241-//line templates/gititemlog.qtpl:14
242+//line templates/gititemlog.qtpl:15
243+		qw422016.N().S(`
244+  `)
245+//line templates/gititemlog.qtpl:16
246+	}
247+//line templates/gititemlog.qtpl:16
248+	qw422016.N().S(`
249+  `)
250+//line templates/gititemlog.qtpl:17
251+	if g.Next != nil {
252+//line templates/gititemlog.qtpl:17
253 		qw422016.N().S(`
254+  <a href="/`)
255+//line templates/gititemlog.qtpl:18
256+		qw422016.E().S(name)
257+//line templates/gititemlog.qtpl:18
258+		qw422016.N().S(`/log/`)
259+//line templates/gititemlog.qtpl:18
260+		qw422016.E().S(ref)
261+//line templates/gititemlog.qtpl:18
262+		qw422016.N().S(`/?from=`)
263+//line templates/gititemlog.qtpl:18
264+		qw422016.E().S(g.Next.Hash.String())
265+//line templates/gititemlog.qtpl:18
266+		qw422016.N().S(`"  class="btn btn-primary">Next</a>
267   `)
268-//line templates/gititemlog.qtpl:15
269+//line templates/gititemlog.qtpl:19
270 	}
271-//line templates/gititemlog.qtpl:15
272+//line templates/gititemlog.qtpl:19
273 	qw422016.N().S(`
274+
275 </div>
276 `)
277-//line templates/gititemlog.qtpl:17
278+//line templates/gititemlog.qtpl:22
279 }
280 
281-//line templates/gititemlog.qtpl:17
282+//line templates/gititemlog.qtpl:22
283 func (g *GitItemLogPage) WriteGitContent(qq422016 qtio422016.Writer, name, ref string) {
284-//line templates/gititemlog.qtpl:17
285+//line templates/gititemlog.qtpl:22
286 	qw422016 := qt422016.AcquireWriter(qq422016)
287-//line templates/gititemlog.qtpl:17
288+//line templates/gititemlog.qtpl:22
289 	g.StreamGitContent(qw422016, name, ref)
290-//line templates/gititemlog.qtpl:17
291+//line templates/gititemlog.qtpl:22
292 	qt422016.ReleaseWriter(qw422016)
293-//line templates/gititemlog.qtpl:17
294+//line templates/gititemlog.qtpl:22
295 }
296 
297-//line templates/gititemlog.qtpl:17
298+//line templates/gititemlog.qtpl:22
299 func (g *GitItemLogPage) GitContent(name, ref string) string {
300-//line templates/gititemlog.qtpl:17
301+//line templates/gititemlog.qtpl:22
302 	qb422016 := qt422016.AcquireByteBuffer()
303-//line templates/gititemlog.qtpl:17
304+//line templates/gititemlog.qtpl:22
305 	g.WriteGitContent(qb422016, name, ref)
306-//line templates/gititemlog.qtpl:17
307+//line templates/gititemlog.qtpl:22
308 	qs422016 := string(qb422016.B)
309-//line templates/gititemlog.qtpl:17
310+//line templates/gititemlog.qtpl:22
311 	qt422016.ReleaseByteBuffer(qb422016)
312-//line templates/gititemlog.qtpl:17
313+//line templates/gititemlog.qtpl:22
314 	return qs422016
315-//line templates/gititemlog.qtpl:17
316+//line templates/gititemlog.qtpl:22
317 }