depp

No frills static page generator for Git repositories

git clone https://git.8pit.net/depp.git

  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}