depp

No frills static page generator for Git repositories

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

  1package gitweb
  2
  3import (
  4	"errors"
  5	"io"
  6	"os"
  7	"path/filepath"
  8	"sort"
  9
 10	"github.com/go-git/go-git/v5"
 11	"github.com/go-git/go-git/v5/plumbing/object"
 12)
 13
 14type RepoPage struct {
 15	*Repo
 16
 17	// Underlying file which is present on this page.
 18	CurrentFile RepoFile
 19
 20	// Tree, if the underlying file is a directory.
 21	tree *object.Tree
 22}
 23
 24type CommitInfo struct {
 25	Commits []*object.Commit
 26	Total   uint
 27}
 28
 29var (
 30	ExpectedDirectory = errors.New("Expected directory")
 31	ExpectedSubmodule = errors.New("Expected submodule")
 32	ExpectedRegular   = errors.New("Expected regular file")
 33)
 34
 35func (r *RepoPage) Files() ([]RepoFile, error) {
 36	if !r.CurrentFile.IsDir() {
 37		return nil, ExpectedDirectory
 38	}
 39
 40	var entries []RepoFile
 41	basepath := filepath.Base(r.CurrentFile.Path)
 42
 43	walker := object.NewTreeWalker(r.tree, false, nil)
 44	defer walker.Close()
 45	for {
 46		name, f, err := walker.Next()
 47		if err == io.EOF {
 48			break
 49		} else if err != nil {
 50			return nil, err
 51		}
 52
 53		relpath := filepath.Join(basepath, name)
 54		file := RepoFile{
 55			Path: filepath.ToSlash(relpath),
 56			mode: f.Mode,
 57		}
 58
 59		entries = append(entries, file)
 60	}
 61
 62	sort.Sort(byType(entries))
 63	return entries, nil
 64}
 65
 66func (r *RepoPage) Commits() (*CommitInfo, error) {
 67	var total, numCommits uint
 68
 69	logOpts := &git.LogOptions{Order: git.LogOrderDFSPost}
 70	if r.CurrentFile.Path != "" {
 71		logOpts.PathFilter = func(fp string) bool {
 72			return fp == r.CurrentFile.Path
 73		}
 74	}
 75	iter, err := r.git.Log(logOpts)
 76	if err != nil {
 77		return nil, err
 78	}
 79
 80	commits := make([]*object.Commit, r.maxCommits)
 81	err = iter.ForEach(func(c *object.Commit) error {
 82		if numCommits < r.maxCommits {
 83			commits[numCommits] = c
 84			numCommits++
 85		}
 86
 87		total++
 88		return nil
 89	})
 90	if err != nil {
 91		return nil, err
 92	}
 93
 94	commits = commits[0:numCommits] // Shrink to appropriate size
 95	return &CommitInfo{commits, total}, nil
 96}
 97
 98func (r *RepoPage) Blob() (*object.File, error) {
 99	if r.CurrentFile.IsDir() || r.CurrentFile.IsSubmodule() {
100		return nil, ExpectedRegular
101	}
102
103	commit, err := r.Tip()
104	if err != nil {
105		return nil, err
106	}
107	return commit.File(r.CurrentFile.Path)
108}
109
110func (r *RepoPage) Submodule(file *RepoFile) (*object.File, error) {
111	if !file.IsSubmodule() {
112		return nil, ExpectedSubmodule
113	}
114
115	// git-go only seems to have very limited support for submodules
116	// in bare repositories. Hence, just display .gitmodules for now.
117	commit, err := r.Tip()
118	if err != nil {
119		return nil, err
120	}
121	return commit.File(".gitmodules")
122}
123
124func (r *RepoPage) findReadme() (string, error) {
125	var result string
126
127	walker := object.NewTreeWalker(r.tree, false, nil)
128	defer walker.Close()
129	for {
130		_, f, err := walker.Next()
131		if err == io.EOF {
132			break
133		} else if err != nil {
134			return "", err
135		}
136
137		if isReadme(f.Name) {
138			result = f.Name
139			break
140		}
141	}
142
143	if result == "" {
144		return "", os.ErrNotExist
145	}
146
147	return result, nil
148}
149
150func (r *RepoPage) Readme() (string, error) {
151	if !r.CurrentFile.IsDir() {
152		return "", ExpectedDirectory
153	}
154
155	fp, err := r.findReadme()
156	if err != nil {
157		return "", err
158	}
159	file, err := r.tree.File(fp)
160	if err != nil {
161		return "", err
162	}
163
164	return file.Contents()
165}