3308f43a6e40266947abfc54223603240d5ab26a
- Author
- Ayman Bagabas <ayman.bagabas@gmail.com>
- Committer
- Ayman Bagabas <ayman.bagabas@gmail.com>
- Date
Message
Diff
This diff is truncated to protect this page.
1diff --git a/internal/git/git.go b/internal/git/git.go
2index 16931421dbddafa113dff031539d5b14fc901f1a..fbfa102503fbe978c07756f0bb9d7dd35d22ab1b 100644
3--- a/internal/git/git.go
4+++ b/internal/git/git.go
5@@ -9,8 +9,10 @@ import (
6 "sync"
7 "time"
8
9+ gitypes "github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/types"
10 "github.com/go-git/go-billy/v5/memfs"
11 "github.com/go-git/go-git/v5"
12+ "github.com/go-git/go-git/v5/plumbing"
13 "github.com/go-git/go-git/v5/plumbing/object"
14 "github.com/go-git/go-git/v5/plumbing/transport"
15 "github.com/go-git/go-git/v5/storage/memory"
16@@ -25,22 +27,147 @@ type Repo struct {
17 Repository *git.Repository
18 Readme string
19 LastUpdated *time.Time
20- commits CommitLog
21+ refCommits map[plumbing.Hash]gitypes.Commits
22+ ref *plumbing.Reference
23 }
24
25-// RepoCommit contains metadata for a Git commit.
26-type RepoCommit struct {
27- Name string
28- Commit *object.Commit
29+// GetName returns the name of the repository.
30+func (r *Repo) GetName() string {
31+ return r.Name
32 }
33
34-// CommitLog is a series of Git commits.
35-type CommitLog []RepoCommit
36+// GetReference returns the reference for a repository.
37+func (r *Repo) GetReference() *plumbing.Reference {
38+ return r.ref
39+}
40+
41+// SetReference sets the repository head reference.
42+func (r *Repo) SetReference(ref *plumbing.Reference) error {
43+ r.ref = ref
44+ return nil
45+}
46+
47+// GetRepository returns the underlying go-git repository object.
48+func (r *Repo) GetRepository() *git.Repository {
49+ return r.Repository
50+}
51+
52+// Tree returns the git tree for a given path.
53+func (r *Repo) Tree(ref *plumbing.Reference, path string) (*object.Tree, error) {
54+ path = filepath.Clean(path)
55+ hash, err := r.targetHash(ref)
56+ if err != nil {
57+ return nil, err
58+ }
59+ c, err := r.Repository.CommitObject(hash)
60+ if err != nil {
61+ return nil, err
62+ }
63+ t, err := c.Tree()
64+ if err != nil {
65+ return nil, err
66+ }
67+ if path == "." {
68+ return t, nil
69+ }
70+ return t.Tree(path)
71+}
72
73-func (cl CommitLog) Len() int { return len(cl) }
74-func (cl CommitLog) Swap(i, j int) { cl[i], cl[j] = cl[j], cl[i] }
75-func (cl CommitLog) Less(i, j int) bool {
76- return cl[i].Commit.Author.When.After(cl[j].Commit.Author.When)
77+// GetCommits returns the commits for a repository.
78+func (r *Repo) GetCommits(ref *plumbing.Reference) (gitypes.Commits, error) {
79+ hash, err := r.targetHash(ref)
80+ if err != nil {
81+ return nil, err
82+ }
83+ // return cached commits if available
84+ commits, ok := r.refCommits[hash]
85+ if ok {
86+ return commits, nil
87+ }
88+ log.Printf("caching commits for %s/%s: %s", r.Name, ref.Name(), ref.Hash())
89+ commits = gitypes.Commits{}
90+ co, err := r.Repository.CommitObject(hash)
91+ if err != nil {
92+ return nil, err
93+ }
94+ // traverse the commit tree to get all commits
95+ commits = append(commits, &gitypes.Commit{Commit: co})
96+ for {
97+ co, err = co.Parent(0)
98+ if err != nil {
99+ if err == object.ErrParentNotFound {
100+ err = nil
101+ }
102+ break
103+ }
104+ commits = append(commits, &gitypes.Commit{Commit: co})
105diff --git a/internal/tui/bubbles/git/about/bubble.go b/internal/tui/bubbles/git/about/bubble.go
106index 9b3e84f86012b05acb569299a0665a0aeb4a9340..17ad0dbdc1903786faad95e8e6761b6d0d9a8798 100644
107--- a/internal/tui/bubbles/git/about/bubble.go
108+++ b/internal/tui/bubbles/git/about/bubble.go
109@@ -3,9 +3,11 @@ package about
110 import (
111 "github.com/charmbracelet/bubbles/viewport"
112 tea "github.com/charmbracelet/bubbletea"
113+ "github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/refs"
114 "github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/types"
115 vp "github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/viewport"
116 "github.com/charmbracelet/soft-serve/internal/tui/style"
117+ "github.com/go-git/go-git/v5/plumbing"
118 )
119
120 type Bubble struct {
121@@ -16,6 +18,7 @@ type Bubble struct {
122 heightMargin int
123 width int
124 widthMargin int
125+ ref *plumbing.Reference
126 }
127
128 func NewBubble(repo types.Repo, styles *style.Styles, width, wm, height, hm int) *Bubble {
129@@ -27,6 +30,7 @@ func NewBubble(repo types.Repo, styles *style.Styles, width, wm, height, hm int)
130 styles: styles,
131 widthMargin: wm,
132 heightMargin: hm,
133+ ref: repo.GetReference(),
134 }
135 b.SetSize(width, height)
136 return b
137@@ -53,6 +57,9 @@ func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
138 case "R":
139 b.GotoTop()
140 }
141+ case refs.RefMsg:
142+ b.ref = msg
143+ return b, b.setupCmd
144 }
145 rv, cmd := b.readmeViewport.Update(msg)
146 b.readmeViewport = rv.(*vp.ViewportBubble)
147@@ -87,7 +94,7 @@ func (b *Bubble) glamourize() (string, error) {
148 func (b *Bubble) setupCmd() tea.Msg {
149 md, err := b.glamourize()
150 if err != nil {
151- return types.ErrMsg{err}
152+ return types.ErrMsg{Err: err}
153 }
154 b.readmeViewport.Viewport.SetContent(md)
155 b.GotoTop()
156diff --git a/internal/tui/bubbles/git/bubble.go b/internal/tui/bubbles/git/bubble.go
157index 9e26e4567fcbe1782dae844ce4aabfd6184e6d2b..8bec715b8feb344db683de02d1c75494d8088cd0 100644
158--- a/internal/tui/bubbles/git/bubble.go
159+++ b/internal/tui/bubbles/git/bubble.go
160@@ -34,6 +34,7 @@ type Bubble struct {
161 widthMargin int
162 style *style.Styles
163 boxes []tea.Model
164+ ref *plumbing.Reference
165 }
166
167 func NewBubble(repo types.Repo, styles *style.Styles, width, wm, height, hm int) *Bubble {
168@@ -46,6 +47,7 @@ func NewBubble(repo types.Repo, styles *style.Styles, width, wm, height, hm int)
169 heightMargin: hm,
170 style: styles,
171 boxes: make([]tea.Model, 4),
172+ ref: repo.GetReference(),
173 }
174 heightMargin := hm + lipgloss.Height(b.headerView())
175 b.boxes[aboutPage] = about.NewBubble(repo, b.style, b.width, wm, b.height, heightMargin)
176@@ -63,7 +65,7 @@ func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
177 cmds := make([]tea.Cmd, 0)
178 switch msg := msg.(type) {
179 case tea.KeyMsg:
180- if b.repo.Name() != "config" {
181+ if b.repo.GetName() != "config" {
182 switch msg.String() {
183 case "R":
184 b.state = aboutPage
185@@ -87,6 +89,14 @@ func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
186 }
187 case refs.RefMsg:
188 b.state = treePage
189+ b.ref = msg
190+ for i, bx := range b.boxes {
191+ m, cmd := bx.Update(msg)
192+ b.boxes[i] = m
193+ if cmd != nil {
194+ cmds = append(cmds, cmd)
195+ }
196+ }
197 }
198 m, cmd := b.boxes[b.state].Update(msg)
199 b.boxes[b.state] = m
200@@ -99,7 +109,7 @@ func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
201 func (b *Bubble) Help() []types.HelpEntry {
202 h := []types.HelpEntry{}
203 h = append(h, b.boxes[b.state].(types.BubbleHelper).Help()...)
204- if b.repo.Name() != "config" {
205+ if b.repo.GetName() != "config" {
206 h = append(h, types.HelpEntry{"R", "readme"})
207 h = append(h, types.HelpEntry{"F", "files"})
208 h = append(h, types.HelpEntry{"C", "commits"})
209@@ -109,7 +119,7 @@ func (b *Bubble) Help() []types.HelpEntry {
210 }
211
212 func (b *Bubble) Reference() plumbing.ReferenceName {
213- return b.repo.GetReference().Name()
214+ return b.ref.Name()
215 }
216
217 func (b *Bubble) headerView() string {
218diff --git a/internal/tui/bubbles/git/log/bubble.go b/internal/tui/bubbles/git/log/bubble.go
219index 3299ca211d9a0f226028f08895cbbeb91505cc9a..d6928889d76fcbeea298f066f285e3b8fcbdda21 100644
220--- a/internal/tui/bubbles/git/log/bubble.go
221+++ b/internal/tui/bubbles/git/log/bubble.go
222@@ -12,10 +12,12 @@ import (
223 "github.com/charmbracelet/bubbles/viewport"
224 tea "github.com/charmbracelet/bubbletea"
225 gansi "github.com/charmbracelet/glamour/ansi"
226+ "github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/refs"
227 "github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/types"
228 vp "github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/viewport"
229 "github.com/charmbracelet/soft-serve/internal/tui/style"
230 "github.com/dustin/go-humanize/english"
231+ "github.com/go-git/go-git/v5/plumbing"
232 "github.com/go-git/go-git/v5/plumbing/object"
233 )
234
235@@ -91,6 +93,7 @@ type Bubble struct {
236 list list.Model
237 state sessionState
238 commitViewport *vp.ViewportBubble
239+ ref *plumbing.Reference
240 style *style.Styles
241 width int
242 widthMargin int
243@@ -122,16 +125,23 @@ func NewBubble(repo types.Repo, styles *style.Styles, width, widthMargin, height
244 height: height,
245 heightMargin: heightMargin,
246 list: l,
247+ ref: repo.GetReference(),
248 }
249 b.SetSize(width, height)
250 return b
251 }
252
253+func (b *Bubble) reset() tea.Cmd {
254+ b.state = logState
255+ b.list.Select(0)
256+ return b.updateItems()
257+}
258+
259 func (b *Bubble) updateItems() tea.Cmd {
260 items := make([]list.Item, 0)
261- cc, err := b.repo.GetCommits(0)
262+ cc, err := b.repo.GetCommits(b.ref)
263 if err != nil {
264- return func() tea.Msg { return types.ErrMsg{err} }
265+ return func() tea.Msg { return types.ErrMsg{Err: err} }
266 }
267 for _, c := range cc {
268 items = append(items, item{c})
269@@ -148,7 +158,7 @@ func (b *Bubble) GotoTop() {
270 }
271
272 func (b *Bubble) Init() tea.Cmd {
273- return b.updateItems()
274+ return b.reset()
275 }
276
277 func (b *Bubble) SetSize(width, height int) {
278@@ -168,9 +178,7 @@ func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
279 case tea.KeyMsg:
280 switch msg.String() {
281 case "C":
282- b.state = logState
283- b.list.Select(0)
284- cmds = append(cmds, b.updateItems())
285+ return b, b.reset()
286 case "enter", "right", "l":
287 if b.state == logState {
288 cmds = append(cmds, b.loadCommit())
289@@ -189,6 +197,8 @@ func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
290 b.state = commitState
291 b.commitViewport.Viewport.SetContent(content)
292 b.GotoTop()
293+ case refs.RefMsg:
294+ b.ref = msg
295 }
296
297 switch b.state {
298diff --git a/internal/tui/bubbles/git/refs/bubble.go b/internal/tui/bubbles/git/refs/bubble.go
299index bd4b2f8e83fd713a8d2dae394b325ef547a8e249..ac9cf285ec351b2952809dd930c0af00351c446d 100644
300--- a/internal/tui/bubbles/git/refs/bubble.go
301+++ b/internal/tui/bubbles/git/refs/bubble.go
302@@ -73,6 +73,7 @@ type Bubble struct {
303 widthMargin int
304 height int
305 heightMargin int
306+ ref *plumbing.Reference
307 }
308
309 func NewBubble(repo types.Repo, styles *style.Styles, width, widthMargin, height, heightMargin int) *Bubble {
310@@ -92,14 +93,15 @@ func NewBubble(repo types.Repo, styles *style.Styles, width, widthMargin, height
311 widthMargin: widthMargin,
312 heightMargin: heightMargin,
313 list: l,
314+ ref: repo.GetReference(),
315 }
316 b.SetSize(width, height)
317 return b
318 }
319
320 func (b *Bubble) SetBranch(ref *plumbing.Reference) (tea.Model, tea.Cmd) {
321+ b.ref = ref
322 return b, func() tea.Msg {
323- b.repo.SetReference(ref)
324 return RefMsg(ref)
325 }
326 }
327@@ -121,7 +123,7 @@ func (b *Bubble) Help() []types.HelpEntry {
328 func (b *Bubble) updateItems() tea.Cmd {
329 its := make(items, 0)
330 tags := make(items, 0)
331- ri, err := b.repo.Repository().References()
332+ ri, err := b.repo.GetRepository().References()
333 if err != nil {
334 return nil
335 }
336diff --git a/internal/tui/bubbles/git/tree/bubble.go b/internal/tui/bubbles/git/tree/bubble.go
337index 926ff77cc9c4057136fa42b7ec9908f6e7a4edd4..e8d4e0924eb8276c4046fb5ba3fa0c2a233fcef0 100644
338--- a/internal/tui/bubbles/git/tree/bubble.go
339+++ b/internal/tui/bubbles/git/tree/bubble.go
340@@ -128,6 +128,7 @@ type Bubble struct {
341 error types.ErrMsg
342 fileViewport *vp.ViewportBubble
343 lastSelected []int
344+ ref *plumbing.Reference
345 }
346
347 func NewBubble(repo types.Repo, styles *style.Styles, width, widthMargin, height, heightMargin int) *Bubble {
348@@ -153,6 +154,7 @@ func NewBubble(repo types.Repo, styles *style.Styles, width, widthMargin, height
349 heightMargin: heightMargin,
350 list: l,
351 state: treeState,
352+ ref: repo.GetReference(),
353 }
354 b.SetSize(width, height)
355 return b
356@@ -183,7 +185,7 @@ func (b *Bubble) Help() []types.HelpEntry {
357
358 func (b *Bubble) updateItems() tea.Cmd {
359 its := make(items, 0)
360- t, err := b.repo.Tree(b.path)
361+ t, err := b.repo.Tree(b.ref, b.path)
362 if err != nil {
363 return func() tea.Msg { return types.ErrMsg{err} }
364 }
365@@ -219,6 +221,14 @@ func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
366 b.SetSize(msg.Width, msg.Height)
367
368 case tea.KeyMsg:
369+ if b.state == errorState {
370+ ref := b.repo.GetReference()
371+ b.ref = ref
372+ return b, tea.Batch(b.reset(), func() tea.Msg {
373+ return ref
374+ })
375+ }
376+
377 switch msg.String() {
378 case "F":
379 return b, b.reset()
380@@ -252,6 +262,7 @@ func (b *Bubble) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
381 }
382
383 case refs.RefMsg:
384+ b.ref = msg
385 return b, b.reset()
386
387 case types.ErrMsg:
388diff --git a/internal/tui/bubbles/git/types/git.go b/internal/tui/bubbles/git/types/git.go
389index 8e0213871b9bc7e868e15dfa9fd87b5a44fb88e7..9cff00a5b2ef8a00891b2cdd9126b53dd2f439f1 100644
390--- a/internal/tui/bubbles/git/types/git.go
391+++ b/internal/tui/bubbles/git/types/git.go
392@@ -7,13 +7,13 @@ import (
393 )
394
395 type Repo interface {
396- Name() string
397+ GetName() string
398 GetReference() *plumbing.Reference
399 SetReference(*plumbing.Reference) error
400 GetReadme() string
401- GetCommits(limit int) (Commits, error)
402- Repository() *git.Repository
403- Tree(path string) (*object.Tree, error)
404+ GetCommits(*plumbing.Reference) (Commits, error)
405+ GetRepository() *git.Repository
406+ Tree(*plumbing.Reference, string) (*object.Tree, error)
407 }
408
409 type Commit struct {
410diff --git a/internal/tui/bubbles/git/types/reset.go b/internal/tui/bubbles/git/types/reset.go
411new file mode 100644
412index 0000000000000000000000000000000000000000..919ab3d896bcc1dfa7d677f7e5df94d9a6ae6a4e
413--- /dev/null
414+++ b/internal/tui/bubbles/git/types/reset.go
415@@ -0,0 +1,7 @@
416+package types
417+
418+import tea "github.com/charmbracelet/bubbletea"
419+
420+type BubbleReset interface {
421+ Reset() tea.Msg
422+}
423diff --git a/internal/tui/commands.go b/internal/tui/commands.go
424index d8879e0873903f25884d9d6a1f33a66b191deeeb..4a6f34507b22c3141a6035951e90f5c133cacb8c 100644
425--- a/internal/tui/commands.go
426+++ b/internal/tui/commands.go
427@@ -96,9 +96,6 @@ func (b *Bubble) menuEntriesFromSource() ([]MenuEntry, error) {
428 }
429
430 func (b *Bubble) newMenuEntry(name string, rn string) (MenuEntry, error) {
431- gr := &Repo{
432- name: rn,
433- }
434 me := MenuEntry{Name: name, Repo: rn}
435 r, err := b.config.Source.GetRepo(rn)
436 if err != nil {
437@@ -111,19 +108,13 @@ func (b *Bubble) newMenuEntry(name string, rn string) (MenuEntry, error) {
438 }
439 r.Readme = md
440 }
441- gr.repo = r.Repository
442- gr.readme = r.Readme
443- gr.ref, err = r.Repository.Head()
444- if err != nil {
445- return me, err
446- }
447 boxLeftWidth := b.styles.Menu.GetWidth() + b.styles.Menu.GetHorizontalFrameSize()
448 // TODO: also send this along with a tea.WindowSizeMsg
449 var heightMargin = lipgloss.Height(b.headerView()) +
450 lipgloss.Height(b.footerView()) +
451 b.styles.RepoBody.GetVerticalFrameSize() +
452 b.styles.App.GetVerticalMargins()
453- rb := repo.NewBubble(rn, b.config.Host, b.config.Port, gr, b.styles, b.width, boxLeftWidth, b.height, heightMargin)
454+ rb := repo.NewBubble(rn, b.config.Host, b.config.Port, r, b.styles, b.width, boxLeftWidth, b.height, heightMargin)
455 initCmd := rb.Init()
456 msg := initCmd()
457 switch msg := msg.(type) {
458diff --git a/internal/tui/git.go b/internal/tui/git.go
459deleted file mode 100644
460index f8105a695317d734ddb4502ee04cb34dd3253968..0000000000000000000000000000000000000000
461--- a/internal/tui/git.go
462+++ /dev/null
463@@ -1,105 +0,0 @@
464-package tui
465-
466-import (
467- "path/filepath"
468-
469- gitypes "github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/types"
470- "github.com/go-git/go-git/v5"
471- "github.com/go-git/go-git/v5/plumbing"
472- "github.com/go-git/go-git/v5/plumbing/object"
473-)
474-
475-type Repo struct {
476- name string
477- repo *git.Repository
478- readme string
479- ref *plumbing.Reference
480-}
481-
482-func (r *Repo) Name() string {
483- return r.name
484-}
485-
486-func (r *Repo) GetReference() *plumbing.Reference {
487- return r.ref
488-}
489-
490-func (r *Repo) SetReference(ref *plumbing.Reference) error {
491- r.ref = ref
492- return nil
493-}
494-
495-func (r *Repo) Repository() *git.Repository {
496- return r.repo
497-}
498-
499-func (r *Repo) Tree(path string) (*object.Tree, error) {
500- path = filepath.Clean(path)
501- c, err := r.repo.CommitObject(r.ref.Hash())
502- if err != nil {
503- return nil, err
504- }
505- t, err := c.Tree()
506- if err != nil {
507- return nil, err
508- }
509- if path == "." {
510- return t, nil
511- }
512- return t.Tree(path)
513-}
514-
515-func (r *Repo) GetCommits(limit int) (gitypes.Commits, error) {
516- commits := gitypes.Commits{}
517- l, err := r.repo.Log(&git.LogOptions{
518- Order: git.LogOrderCommitterTime,
519- From: r.ref.Hash(),
520- })
521- if err != nil {
522- return nil, err
523- }
524- err = l.ForEach(func(c *object.Commit) error {
525- commits = append(commits, &gitypes.Commit{c})
526- return nil
527- })
528- if err != nil {
529- return nil, err
530- }
531- if limit <= 0 || limit > len(commits) {
532- limit = len(commits)
533- }
534- return commits[:limit], nil
535-}
536-
537-func (r *Repo) GetReadme() string {
538- if r.readme != "" {
539- return r.readme
540- }
541- md, err := r.readFile("README.md")
542- if err != nil {
543- return ""
544- }
545- return md
546-}
547-
548-func (r *Repo) readFile(path string) (string, error) {
549- lg, err := r.repo.Log(&git.LogOptions{
550- From: r.ref.Hash(),
551- })
552- if err != nil {
553- return "", err
554- }
555- c, err := lg.Next()
556- if err != nil {
557- return "", err
558- }
559- f, err := c.File(path)
560- if err != nil {
561- return "", err
562- }