1package gitweb
2
3import (
4 git "github.com/libgit2/git2go/v34"
5
6 "errors"
7 "fmt"
8 "os"
9 "path/filepath"
10 "regexp"
11 "sort"
12)
13
14// RepoPage represents information required per reference.
15type RepoPage struct {
16 Repo
17
18 tree *git.Tree
19 commit *git.Commit
20
21 CurrentFile RepoFile
22}
23
24type CommitInfo struct {
25 Commits []*git.Commit
26 Total uint
27}
28
29var readmeRegex = regexp.MustCompile(`README|(README\.[a-zA-Z0-9]+)`)
30
31func (r *RepoPage) Files() ([]RepoFile, error) {
32 var entries []RepoFile
33 err := r.tree.Walk(func(root string, e *git.TreeEntry) error {
34 if root != "" {
35 return git.TreeWalkSkip // Skip passed entry
36 }
37
38 basepath := filepath.Base(r.CurrentFile.Path)
39 relpath := filepath.Join(basepath, e.Name)
40
41 file := RepoFile{
42 Path: filepath.ToSlash(relpath),
43 Type: e.Type,
44 }
45
46 entries = append(entries, file)
47 return nil
48 })
49 if err != nil {
50 return nil, err
51 }
52
53 sort.Sort(byType(entries))
54 return entries, nil
55}
56
57func (r *RepoPage) Commits() (*CommitInfo, error) {
58 var oid git.Oid
59 var total, numCommits uint
60
61 walker, err := r.git.Walk()
62 if err != nil {
63 return nil, err
64 }
65 defer walker.Free()
66 walker.Push(r.commit.AsObject().Id())
67
68 commits := make([]*git.Commit, r.maxCommits)
69 for walker.Next(&oid) == nil {
70 commit, err := r.git.LookupCommit(&oid)
71 if err != nil {
72 return nil, err
73 }
74
75 if numCommits < r.maxCommits {
76 commits[numCommits] = commit
77 numCommits++
78 }
79
80 total++
81 }
82
83 commits = commits[0:numCommits] // Shrink to appropriate size
84 return &CommitInfo{commits, total}, nil
85}
86
87func (r *RepoPage) Blob(file *RepoFile) ([]byte, error) {
88 if file.Type != git.ObjectBlob {
89 return []byte{}, errors.New("given RepoFile is not a blob")
90 }
91 fp := file.FilePath()
92
93 entry, err := r.tree.EntryByPath(fp)
94 if err != nil {
95 return []byte{}, err
96 }
97
98 oid := entry.Id
99 blob, err := r.git.LookupBlob(oid)
100 if err != nil {
101 return []byte{}, err
102 }
103
104 return blob.Contents(), nil
105}
106
107func (r *RepoPage) Submodule(file *RepoFile) ([]byte, error) {
108 if !file.IsSubmodule() {
109 return []byte{}, errors.New("given RepoFile is not a submodule")
110 }
111 fp := file.FilePath()
112
113 submodule, err := r.git.Submodules.Lookup(fp)
114 if git.IsErrorClass(err, git.ErrorClassSubmodule) {
115 // TODO: Submodules.Lookup does not work in bare repositories.
116 // See: https://github.com/libgit2/libgit2/commit/477b3e047426d7ccddb6028416ff0fcc2541a0fd
117 gitmodules := &RepoFile{".gitmodules", git.ObjectBlob}
118 return r.Blob(gitmodules)
119 }
120
121 out := fmt.Sprintf("%v @ %v", submodule.Url(), submodule.IndexId())
122 return []byte(out), nil
123}
124
125func (r *RepoPage) matchFile(reg *regexp.Regexp) *git.TreeEntry {
126 var result *git.TreeEntry
127 r.tree.Walk(func(root string, e *git.TreeEntry) error {
128 if root != "" {
129 return git.TreeWalkSkip // Different directory
130 }
131
132 if e.Type == git.ObjectBlob && reg.MatchString(e.Name) {
133 result = e
134 return errors.New("found match") // Stop the walk
135 }
136
137 return nil
138 })
139
140 return result
141}
142
143func (r *RepoPage) Readme() (string, error) {
144 entry := r.matchFile(readmeRegex)
145 if entry == nil {
146 return "", os.ErrNotExist
147 }
148
149 blob, err := r.git.LookupBlob(entry.Id)
150 if err != nil {
151 return "", err
152 }
153
154 return string(blob.Contents()), nil
155}