You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
222 lines
4.3 KiB
222 lines
4.3 KiB
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) |
|
}
|
|
|