1package gitweb
2
3import (
4 git "github.com/libgit2/git2go/v34"
5
6 "errors"
7 "net/url"
8 "os"
9 "path/filepath"
10 "strings"
11)
12
13// Repo represents information required per repository.
14type Repo struct {
15 git *git.Repository
16 maxCommits uint
17
18 Path string
19 Title string
20 URL string
21}
22
23// File name of the git description file.
24const descFn = "description"
25
26func NewRepo(fp string, gitServer *url.URL, commits uint) (*Repo, error) {
27 var err error
28
29 absFp, err := filepath.Abs(fp)
30 if err != nil {
31 return nil, err
32 }
33 r := &Repo{Path: absFp}
34
35 r.git, err = git.OpenRepository(absFp)
36 if err != nil {
37 return nil, err
38 }
39
40 r.Title = filepath.Base(absFp)
41 if gitServer != nil {
42 r.URL = gitServer.String()
43 }
44
45 ext := strings.LastIndex(r.Title, ".git")
46 if ext > 0 {
47 r.Title = r.Title[0:ext]
48 }
49
50 r.maxCommits = commits
51 return r, nil
52}
53
54func (r *Repo) Tip() (*git.Commit, error) {
55 head, err := r.git.Head()
56 if err != nil {
57 return nil, err
58 }
59
60 oid := head.Target()
61 commit, err := r.git.LookupCommit(oid)
62 if err != nil {
63 return nil, err
64 }
65
66 return commit, nil
67}
68
69func (r *Repo) Walk(fn func(*RepoPage) error) error {
70 head, err := r.git.Head()
71 if err != nil {
72 return err
73 }
74
75 indexPage, err := r.Page(head, "")
76 if err != nil {
77 return err
78 }
79
80 // . is not included by tree.Walk()
81 err = fn(indexPage)
82 if err != nil {
83 return err
84 }
85
86 return indexPage.tree.Walk(func(root string, e *git.TreeEntry) error {
87 fp := filepath.Join(root, e.Name)
88 page, err := r.Page(head, fp)
89 if err != nil {
90 return err
91 }
92
93 err = fn(page)
94 if err != nil {
95 return err
96 }
97
98 return nil
99 })
100}
101
102func (r *Repo) Page(ref *git.Reference, fp string) (*RepoPage, error) {
103 var err error
104 page := &RepoPage{Repo: *r}
105
106 oid := ref.Target()
107 page.commit, err = r.git.LookupCommit(oid)
108 if err != nil {
109 return nil, err
110 }
111
112 page.tree, err = page.commit.Tree()
113 if err != nil {
114 return nil, err
115 }
116
117 // TODO: Find out how to retrieve the TreeEntry for /
118 page.CurrentFile = RepoFile{Path: filepath.ToSlash(fp)}
119 if page.CurrentFile.Path != "" {
120 entry, err := page.tree.EntryByPath(fp)
121 if err != nil {
122 return nil, err
123 }
124 page.CurrentFile.Type = entry.Type
125
126 if page.CurrentFile.IsDir() {
127 page.tree, err = r.git.LookupTree(entry.Id)
128 if err != nil {
129 panic(err)
130 return nil, err
131 }
132 }
133 } else {
134 page.CurrentFile.Type = git.ObjectTree
135 }
136
137 return page, nil
138}
139
140func (r *Repo) Description() (string, error) {
141 fp := filepath.Join(r.Path, descFn)
142
143 desc, err := os.ReadFile(fp)
144 if errors.Is(err, os.ErrNotExist) {
145 return "", nil
146 } else if err != nil {
147 return "", err
148 }
149
150 descText := string(desc)
151 return strings.TrimSpace(descText), nil
152}