instrument git stuff with errs

This commit is contained in:
2024-05-22 12:14:22 +01:00
parent 1a3f7b1e3d
commit 0029fc4f63
4 changed files with 143 additions and 171 deletions

View File

@@ -1,8 +1,6 @@
package git
import (
"fmt"
"log"
"strings"
"github.com/bluekeyes/go-gitdiff/gitdiff"
@@ -42,44 +40,37 @@ type NiceDiff struct {
Diff []Diff
}
func (g *GitRepo) Diff() (*NiceDiff, error) {
c, err := g.r.CommitObject(g.h)
if err != nil {
return nil, fmt.Errorf("commit object: %w", err)
func (g *GitRepo) Diff() (nd *NiceDiff, err error) {
var c *object.Commit
if c, err = g.r.CommitObject(g.h); chk.E(err) {
return nil, log.E.Err("commit object: %w", err)
}
patch := &object.Patch{}
commitTree, err := c.Tree()
parent := &object.Commit{}
if err == nil {
var commitTree *object.Tree
if commitTree, err = c.Tree(); chk.E(err) {
parentTree := &object.Tree{}
if c.NumParents() != 0 {
parent, err = c.Parents().Next()
if err == nil {
parentTree, err = parent.Tree()
if err == nil {
patch, err = parentTree.Patch(commitTree)
if err != nil {
return nil, fmt.Errorf("patch: %w", err)
if parent, err = c.Parents().Next(); !chk.E(err) {
if parentTree, err = parent.Tree(); !chk.E(err) {
if patch, err = parentTree.Patch(commitTree); chk.E(err) {
return nil, log.E.Err("patch: %w", err)
}
}
}
} else {
patch, err = parentTree.Patch(commitTree)
if err != nil {
return nil, fmt.Errorf("patch: %w", err)
if patch, err = parentTree.Patch(commitTree); chk.E(err) {
return nil, log.E.Err("patch: %w", err)
}
}
}
diffs, _, err := gitdiff.Parse(strings.NewReader(patch.String()))
if err != nil {
log.Println(err)
var diffs []*gitdiff.File
if diffs, _, err = gitdiff.Parse(strings.NewReader(patch.String())); chk.E(err) {
return
}
nd := NiceDiff{}
nd = &NiceDiff{}
nd.Commit.This = c.Hash.String()
if parent.Hash.IsZero() {
nd.Commit.Parent = ""
} else {
@@ -87,17 +78,15 @@ func (g *GitRepo) Diff() (*NiceDiff, error) {
}
nd.Commit.Author = c.Author
nd.Commit.Message = c.Message
for _, d := range diffs {
ndiff := Diff{}
ndiff.Name.New = d.NewName
ndiff.Name.Old = d.OldName
ndiff.IsBinary = d.IsBinary
ndiff.IsNew = d.IsNew
ndiff.IsDelete = d.IsDelete
nDiff := Diff{}
nDiff.Name.New = d.NewName
nDiff.Name.Old = d.OldName
nDiff.IsBinary = d.IsBinary
nDiff.IsNew = d.IsNew
nDiff.IsDelete = d.IsDelete
for _, tf := range d.TextFragments {
ndiff.TextFragments = append(ndiff.TextFragments, TextFragment{
nDiff.TextFragments = append(nDiff.TextFragments, TextFragment{
Header: tf.Header(),
Lines: tf.Lines,
})
@@ -107,14 +96,13 @@ func (g *GitRepo) Diff() (*NiceDiff, error) {
nd.Stat.Insertions += 1
case gitdiff.OpDelete:
nd.Stat.Deletions += 1
default:
panic("unhandled default case")
}
}
}
nd.Diff = append(nd.Diff, ndiff)
nd.Diff = append(nd.Diff, nDiff)
}
nd.Stat.FilesChanged = len(diffs)
return &nd, nil
return
}

View File

@@ -2,13 +2,18 @@ package git
import (
"fmt"
"os"
"sort"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/storer"
"mleku.dev/git/slog"
)
var log, chk = slog.New(os.Stderr)
type GitRepo struct {
r *git.Repository
h plumbing.Hash
@@ -16,141 +21,120 @@ type GitRepo struct {
type TagList []*object.Tag
func (self TagList) Len() int {
return len(self)
}
func (t TagList) Len() int { return len(t) }
func (t TagList) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
func (t TagList) Less(i, j int) bool { return t[i].Tagger.When.After(t[j].Tagger.When) }
func (self TagList) Swap(i, j int) {
self[i], self[j] = self[j], self[i]
}
// sorting tags in reverse chronological order
func (self TagList) Less(i, j int) bool {
return self[i].Tagger.When.After(self[j].Tagger.When)
}
func Open(path string, ref string) (*GitRepo, error) {
var err error
g := GitRepo{}
g.r, err = git.PlainOpen(path)
func Open(path string, ref string) (gr *GitRepo, err error) {
gr = &GitRepo{}
gr.r, err = git.PlainOpen(path)
if err != nil {
return nil, fmt.Errorf("opening %s: %w", path, err)
}
if ref == "" {
head, err := g.r.Head()
if err != nil {
return nil, fmt.Errorf("getting head of %s: %w", path, err)
var head *plumbing.Reference
if head, err = gr.r.Head(); chk.E(err) {
err = log.E.Err("getting head of %s: %w", path, err)
return
}
g.h = head.Hash()
gr.h = head.Hash()
} else {
hash, err := g.r.ResolveRevision(plumbing.Revision(ref))
if err != nil {
return nil, fmt.Errorf("resolving rev %s for %s: %w", ref, path, err)
var h *plumbing.Hash
if h, err = gr.r.ResolveRevision(plumbing.Revision(ref)); chk.E(err) {
err = log.E.Err("resolving rev %s for %s: %w", ref, path, err)
return
}
g.h = *hash
gr.h = *h
}
return &g, nil
return
}
func (g *GitRepo) Commits() ([]*object.Commit, error) {
ci, err := g.r.Log(&git.LogOptions{From: g.h})
if err != nil {
return nil, fmt.Errorf("commits from ref: %w", err)
func (g *GitRepo) Commits() (oc []*object.Commit, err error) {
var ci object.CommitIter
if ci, err = g.r.Log(&git.LogOptions{From: g.h}); chk.E(err) {
err = log.E.Err("commits from ref: %w", err)
return
}
commits := []*object.Commit{}
ci.ForEach(func(c *object.Commit) error {
commits = append(commits, c)
return nil
})
return commits, nil
oc = []*object.Commit{}
chk.E(ci.ForEach(func(c *object.Commit) (err error) {
oc = append(oc, c)
return
}))
return
}
func (g *GitRepo) LastCommit() (*object.Commit, error) {
c, err := g.r.CommitObject(g.h)
if err != nil {
return nil, fmt.Errorf("last commit: %w", err)
}
return c, nil
func (g *GitRepo) LastCommit() (oc *object.Commit, err error) {
return g.r.CommitObject(g.h)
}
func (g *GitRepo) FileContent(path string) (string, error) {
c, err := g.r.CommitObject(g.h)
if err != nil {
return "", fmt.Errorf("commit object: %w", err)
func (g *GitRepo) FileContent(path string) (content string, err error) {
var c *object.Commit
if c, err = g.r.CommitObject(g.h); chk.E(err) {
err = log.E.Err("commit object: %w", err)
return
}
tree, err := c.Tree()
if err != nil {
return "", fmt.Errorf("file tree: %w", err)
var tree *object.Tree
if tree, err = c.Tree(); chk.E(err) {
err = log.E.Err("file tree: %w", err)
return
}
file, err := tree.File(path)
if err != nil {
return "", err
var file *object.File
if file, err = tree.File(path); chk.E(err) {
err = log.E.Err("%s: %s", err, path)
return
}
isbin, _ := file.IsBinary()
if !isbin {
return file.Contents()
isBinary, _ := file.IsBinary()
if !isBinary {
content, err = file.Contents()
} else {
return "Not displaying binary file", nil
content = "Not displaying binary file"
}
return
}
func (g *GitRepo) Tags() ([]*object.Tag, error) {
ti, err := g.r.TagObjects()
if err != nil {
return nil, fmt.Errorf("tag objects: %w", err)
func (g *GitRepo) Tags() (tags []*object.Tag, err error) {
var ti *object.TagIter
if ti, err = g.r.TagObjects(); chk.E(err) {
err = log.E.Err("tag objects: %w", err)
return
}
tags := []*object.Tag{}
_ = ti.ForEach(func(t *object.Tag) error {
chk.E(ti.ForEach(func(t *object.Tag) (err error) {
for i, existing := range tags {
if existing.Name == t.Name {
if t.Tagger.When.After(existing.Tagger.When) {
tags[i] = t
}
return nil
return
}
}
tags = append(tags, t)
return nil
})
var tagList TagList
tagList = tags
sort.Sort(tagList)
return tags, nil
return
}))
t := TagList(tags)
sort.Sort(t)
tags = t
return
}
func (g *GitRepo) Branches() ([]*plumbing.Reference, error) {
bi, err := g.r.Branches()
if err != nil {
return nil, fmt.Errorf("branchs: %w", err)
func (g *GitRepo) Branches() (branches []*plumbing.Reference, err error) {
var bi storer.ReferenceIter
if bi, err = g.r.Branches(); chk.E(err) {
err = log.E.Err("branches: %w", err)
return
}
branches := []*plumbing.Reference{}
_ = bi.ForEach(func(ref *plumbing.Reference) error {
chk.E(bi.ForEach(func(ref *plumbing.Reference) (err error) {
branches = append(branches, ref)
return nil
})
return branches, nil
return
}))
return
}
func (g *GitRepo) FindMainBranch(branches []string) (string, error) {
for _, b := range branches {
_, err := g.r.ResolveRevision(plumbing.Revision(b))
if err == nil {
return b, nil
func (g *GitRepo) FindMainBranch(branches []string) (b string, err error) {
for _, b = range branches {
if _, err = g.r.ResolveRevision(plumbing.Revision(b)); !chk.E(err) {
return
}
}
return "", fmt.Errorf("unable to find main branch")
err = log.E.Err("unable to find main branch")
return
}

View File

@@ -1,45 +1,46 @@
package git
import (
"fmt"
"os"
"github.com/go-git/go-git/v5/plumbing/object"
)
func (g *GitRepo) FileTree(path string) ([]NiceTree, error) {
c, err := g.r.CommitObject(g.h)
if err != nil {
return nil, fmt.Errorf("commit object: %w", err)
func (g *GitRepo) FileTree(path string) (files []NiceTree, err error) {
var c *object.Commit
if c, err = g.r.CommitObject(g.h); chk.E(err) {
err = log.E.Err("commit object: %w", err)
return
}
files := []NiceTree{}
tree, err := c.Tree()
if err != nil {
return nil, fmt.Errorf("file tree: %w", err)
var tree *object.Tree
if tree, err = c.Tree(); chk.E(err) {
err = log.E.Err("file tree: %w", err)
return
}
if path == "" {
files = makeNiceTree(tree)
} else {
o, err := tree.FindEntry(path)
if err != nil {
return nil, err
if files, err = makeNiceTree(tree); chk.E(err) {
return
}
} else {
var o *object.TreeEntry
if o, err = tree.FindEntry(path); chk.E(err) {
return
}
if !o.Mode.IsFile() {
subtree, err := tree.Tree(path)
if err != nil {
return nil, err
var subtree *object.Tree
if subtree, err = tree.Tree(path); chk.E(err) {
return
}
if files, err = makeNiceTree(subtree); chk.E(err) {
return
}
files = makeNiceTree(subtree)
}
}
return files, nil
return
}
// A nicer git tree representation.
type NiceTree struct {
Name string
Mode string
@@ -48,12 +49,16 @@ type NiceTree struct {
IsSubtree bool
}
func makeNiceTree(t *object.Tree) []NiceTree {
nts := []NiceTree{}
func makeNiceTree(t *object.Tree) (nts []NiceTree, err error) {
for _, e := range t.Entries {
mode, _ := e.Mode.ToOSFileMode()
sz, _ := t.Size(e.Name)
var mode os.FileMode
if mode, err = e.Mode.ToOSFileMode(); chk.E(err) {
return
}
var sz int64
if sz, err = t.Size(e.Name); chk.E(err) {
return
}
nts = append(nts, NiceTree{
Name: e.Name,
Mode: mode.String(),
@@ -61,6 +66,5 @@ func makeNiceTree(t *object.Tree) []NiceTree {
Size: sz,
})
}
return nts
return
}

4
go.sum
View File

@@ -104,8 +104,6 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -157,8 +155,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
mleku.dev/git/atomic v1.11.5 h1:Y1lFQZl1yBKRrnSKm/9jTSJ0zwQwgOp1RcyovRvLEeI=
mleku.dev/git/atomic v1.11.5/go.mod h1:Kt4gYG8niP33EVsiW+9iZJMF07a8H5ZSDIajAAOcRJE=
mleku.dev/git/atomic v1.11.6 h1:KsqRZPMD9q74kynTMendU3dAQ1loOvizCNKJoc8tQiw=
mleku.dev/git/atomic v1.11.6/go.mod h1:Kt4gYG8niP33EVsiW+9iZJMF07a8H5ZSDIajAAOcRJE=
mleku.dev/git/slog v1.0.16 h1:ibfAo21AstzrKdc9b58S8QxhNXPsSgYeGOCbNGmv60M=