1package gitweb23import (4 "errors"5 "io"6 "os"7 "path/filepath"8 "sort"910 "github.com/go-git/go-git/v5"11 "github.com/go-git/go-git/v5/plumbing/object"12)1314type RepoPage struct {15 *Repo1617 // Underlying file which is present on this page.18 CurrentFile RepoFile1920 // Tree, if the underlying file is a directory.21 tree *object.Tree22}2324type CommitInfo struct {25 Commits []*object.Commit26 Total uint27}2829var (30 ExpectedDirectory = errors.New("Expected directory")31 ExpectedSubmodule = errors.New("Expected submodule")32 ExpectedRegular = errors.New("Expected regular file")33)3435func (r *RepoPage) Files() ([]RepoFile, error) {36 if !r.CurrentFile.IsDir() {37 return nil, ExpectedDirectory38 }3940 var entries []RepoFile41 basepath := filepath.Base(r.CurrentFile.Path)4243 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 break49 } else if err != nil {50 return nil, err51 }5253 relpath := filepath.Join(basepath, name)54 file := RepoFile{55 Path: filepath.ToSlash(relpath),56 mode: f.Mode,57 }5859 entries = append(entries, file)60 }6162 sort.Sort(byType(entries))63 return entries, nil64}6566func (r *RepoPage) Commits() (*CommitInfo, error) {67 var total, numCommits uint6869 logOpts := &git.LogOptions{Order: git.LogOrderDFSPost}70 if r.CurrentFile.Path != "" {71 logOpts.PathFilter = func(fp string) bool {72 return fp == r.CurrentFile.Path73 }74 }75 iter, err := r.git.Log(logOpts)76 if err != nil {77 return nil, err78 }7980 commits := make([]*object.Commit, r.maxCommits)81 err = iter.ForEach(func(c *object.Commit) error {82 if numCommits < r.maxCommits {83 commits[numCommits] = c84 numCommits++85 }8687 total++88 return nil89 })90 if err != nil {91 return nil, err92 }9394 commits = commits[0:numCommits] // Shrink to appropriate size95 return &CommitInfo{commits, total}, nil96}9798func (r *RepoPage) Blob() (*object.File, error) {99 if r.CurrentFile.IsDir() || r.CurrentFile.IsSubmodule() {100 return nil, ExpectedRegular101 }102103 commit, err := r.Tip()104 if err != nil {105 return nil, err106 }107 return commit.File(r.CurrentFile.Path)108}109110func (r *RepoPage) Submodule(file *RepoFile) (*object.File, error) {111 if !file.IsSubmodule() {112 return nil, ExpectedSubmodule113 }114115 // git-go only seems to have very limited support for submodules116 // in bare repositories. Hence, just display .gitmodules for now.117 commit, err := r.Tip()118 if err != nil {119 return nil, err120 }121 return commit.File(".gitmodules")122}123124func (r *RepoPage) findReadme() (string, error) {125 var result string126127 walker := object.NewTreeWalker(r.tree, false, nil)128 defer walker.Close()129 for {130 _, f, err := walker.Next()131 if err == io.EOF {132 break133 } else if err != nil {134 return "", err135 }136137 if isReadme(f.Name) {138 result = f.Name139 break140 }141 }142143 if result == "" {144 return "", os.ErrNotExist145 }146147 return result, nil148}149150func (r *RepoPage) Readme() (string, error) {151 if !r.CurrentFile.IsDir() {152 return "", ExpectedDirectory153 }154155 fp, err := r.findReadme()156 if err != nil {157 return "", err158 }159 file, err := r.tree.File(fp)160 if err != nil {161 return "", err162 }163164 return file.Contents()165}