@ 1d4c973b9311fd7523b2414d8464db45ba2907cb | history
package core
import (
"errors"
"os"
"path"
"strings"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
)
const DOT_GIT = ".git"
func isBareRepo(e os.DirEntry) bool {
return (e.IsDir() && strings.HasSuffix(e.Name(), DOT_GIT) && e.Name() != DOT_GIT)
}
func List(dir string) ([]string, error) {
entries, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
var repos []string
for _, e := range entries {
if isBareRepo(e) {
repos = append(repos, strings.TrimSuffix(e.Name(), DOT_GIT))
}
}
if len(repos) == 0 {
return nil, errors.New("No repos found in " + dir)
}
return repos, nil
}
type Repo struct {
*git.Repository
}
func Open(repoName string) (*Repo, error) {
if _, err := os.Stat(repoName + DOT_GIT); os.IsNotExist(err) {
return nil, err
}
repo, err := git.PlainOpen(repoName + DOT_GIT)
if err != nil {
return nil, err
}
return &Repo{repo}, nil
}
func (repo *Repo) Load(entry *object.TreeEntry) (string, error) {
file, err := repo.BlobObject(entry.Hash)
if err != nil {
return "", err
}
reader, err := file.Reader()
if err != nil {
return "", err
}
defer reader.Close()
content := make([]byte, file.Size)
reader.Read(content)
return string(content), nil
}
type Commit struct {
*object.Commit
Hash plumbing.Hash
RefName string
}
func (repo *Repo) ParseRoute(route string) (*Commit, string) {
if route == "" {
return nil, ""
}
candidate := strings.TrimPrefix(route, "/")
for {
ref, err := repo.resolveRef(candidate)
var relPath string
if err == nil { // candidate is a tag or a branch name
if len(candidate) < len(route) {
relPath = strings.TrimPrefix(route[len(candidate):], "/")
}
refName := ref.Name().Short()
commit, _ := repo.resolveCommit(refName)
return &Commit{commit, commit.Hash, refName}, relPath
}
commit, err := repo.resolveCommit(candidate)
if err == nil { // candidate is a commit hash
if len(candidate) < len(route) {
relPath = strings.TrimPrefix(route[len(candidate):], "/")
}
return &Commit{commit, commit.Hash, ""}, relPath
}
parent := path.Dir(candidate)
if parent == "." || parent == candidate {
break
}
candidate = parent
}
return nil, ""
}
func (repo *Repo) resolveCommit(name string) (*object.Commit, error) {
hash, err := repo.ResolveRevision(plumbing.Revision(name))
if err != nil {
return nil, err
}
return repo.CommitObject(*hash)
}
func (repo *Repo) resolveRef(name string) (*plumbing.Reference, error) {
// first check for branches with the given name
branchRef := plumbing.ReferenceName(path.Join("refs", "heads", name))
ref, err := repo.Reference(branchRef, true)
if err == nil {
return ref, nil
}
// then tags
tagRef := plumbing.ReferenceName(path.Join("refs", "tags", name))
ref, err = repo.Reference(tagRef, true)
if err != nil {
return nil, err
}
obj, err := repo.Object(plumbing.AnyObject, ref.Hash())
if err != nil {
return nil, err
}
// if it's an annotated tag, extract the target
if tagObj, ok := obj.(*object.Tag); ok {
ref = plumbing.NewHashReference(ref.Name(), tagObj.Target)
}
return ref, nil
}