223 lines
4.3 KiB
Go
223 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/go-git/go-git/v5"
|
|
"github.com/go-git/go-git/v5/plumbing/object"
|
|
"github.com/go-git/go-git/v5/plumbing/storer"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
/*n, err := loadNotebook("/home/trevor/stick/personal/")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
log.Print(time.Unix(n.created(), 0))
|
|
log.Print(time.Unix(n.Modified(), 0))
|
|
|
|
err = n.upload()
|
|
if err != nil {
|
|
if strings.Contains(err.Error(), git.ErrNonFastForwardUpdate.Error()) {
|
|
log.Println("conflict while pushing")
|
|
} else if err != git.NoErrAlreadyUpToDate {
|
|
panic(err)
|
|
}
|
|
}
|
|
err = n.download()
|
|
if err != nil {
|
|
if strings.Contains(err.Error(), git.ErrNonFastForwardUpdate.Error()) {
|
|
log.Println("conflict while updating")
|
|
log.Println(err)
|
|
} else if err != git.NoErrAlreadyUpToDate {
|
|
panic(err)
|
|
}
|
|
}*/
|
|
|
|
const defaultSubmoduleDepth = 10
|
|
|
|
type notebook struct {
|
|
ID string
|
|
Label string
|
|
Serve map[string]int `json:"-"`
|
|
|
|
Repository *git.Repository `json:"-"`
|
|
}
|
|
|
|
func (n *notebook) getNote(id string, fetchBody bool) *note {
|
|
file := n.file(id)
|
|
if file == nil {
|
|
return nil
|
|
}
|
|
|
|
r := n.Repository
|
|
|
|
ref, err := r.Head()
|
|
if err != nil {
|
|
return nil // Empty repository
|
|
}
|
|
|
|
cIter, err := r.Log(&git.LogOptions{From: ref.Hash(), Order: git.LogOrderCommitterTime, FileName: &file.Name})
|
|
checkError(err)
|
|
|
|
note := ¬e{ID: id}
|
|
|
|
err = cIter.ForEach(func(c *object.Commit) error {
|
|
note.ModifiedAt = c.Author.When.Unix()
|
|
note.ModifiedBy = c.Author.Name
|
|
|
|
return storer.ErrStop
|
|
})
|
|
checkError(err)
|
|
|
|
note.Label = file.Name
|
|
if strings.HasSuffix(strings.ToLower(note.Label), ".md") {
|
|
note.Label = note.Label[0 : len(note.Label)-3]
|
|
}
|
|
note.Label = strings.Title(strings.Replace(note.Label, "-", " ", -1))
|
|
|
|
if fetchBody {
|
|
note.Body, err = file.Contents()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return note
|
|
}
|
|
|
|
func (n *notebook) allNotes() map[string]*note {
|
|
r := n.Repository
|
|
|
|
ref, err := r.Head()
|
|
checkError(err)
|
|
|
|
commit, err := r.CommitObject(ref.Hash())
|
|
checkError(err)
|
|
|
|
tree, err := commit.Tree()
|
|
checkError(err)
|
|
|
|
notes := make(map[string]*note)
|
|
err = tree.Files().ForEach(func(f *object.File) error {
|
|
noteID := hash(f.Name)
|
|
note := n.getNote(noteID, true)
|
|
notes[noteID] = note
|
|
return nil
|
|
})
|
|
checkError(err)
|
|
|
|
return notes
|
|
}
|
|
|
|
type servedNotebook struct {
|
|
*notebook
|
|
Notes map[string]*note
|
|
}
|
|
|
|
func newNotebook(worktree string, cloneurl string) (*notebook, error) {
|
|
var err error
|
|
|
|
n := ¬ebook{}
|
|
n.Repository, err = initializeRepository(worktree, cloneurl)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to initialize repository")
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
func loadNotebook(worktree string) (*notebook, error) {
|
|
var err error
|
|
|
|
n := ¬ebook{}
|
|
n.Repository, err = loadRepository(worktree)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed to load repository")
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
func (n *notebook) created() int64 {
|
|
head, err := n.Repository.Head()
|
|
if err != nil {
|
|
return 0 // No commits yet
|
|
}
|
|
|
|
commit, err := n.Repository.CommitObject(head.Hash())
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
|
|
for {
|
|
numParents := commit.NumParents()
|
|
if numParents == 0 {
|
|
break
|
|
}
|
|
|
|
commit, err = commit.Parent(numParents - 1)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
return commit.Author.When.Unix()
|
|
}
|
|
|
|
func (n *notebook) download() error {
|
|
w, err := n.Repository.Worktree()
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to read worktree")
|
|
}
|
|
|
|
return w.Pull(&git.PullOptions{RemoteName: "origin", RecurseSubmodules: defaultSubmoduleDepth})
|
|
}
|
|
|
|
func (n *notebook) upload() error {
|
|
return n.Repository.Push(&git.PushOptions{RemoteName: "origin"})
|
|
}
|
|
|
|
func (n *notebook) file(id string) *object.File {
|
|
var file *object.File
|
|
|
|
ref, err := n.Repository.Head()
|
|
if err != nil {
|
|
return nil // Empty repository
|
|
}
|
|
|
|
commit, err := n.Repository.CommitObject(ref.Hash())
|
|
checkError(err)
|
|
|
|
tree, err := commit.Tree()
|
|
checkError(err)
|
|
|
|
err = tree.Files().ForEach(func(f *object.File) error {
|
|
if hash(f.Name) == id {
|
|
file = f
|
|
return storer.ErrStop
|
|
}
|
|
|
|
return nil
|
|
})
|
|
checkError(err)
|
|
|
|
return file
|
|
}
|
|
|
|
func (n *notebook) compact() {
|
|
tree, err := n.Repository.Worktree()
|
|
checkError(err)
|
|
|
|
wtroot := tree.Filesystem.Root()
|
|
|
|
cmd := exec.Command("git", "-C", tree.Filesystem.Root(), "gc", "--aggressive", "--prune=now")
|
|
err = cmd.Run()
|
|
checkError(err)
|
|
|
|
n.Repository, err = loadRepository(wtroot)
|
|
checkError(err)
|
|
}
|