forked from cerc-io/ipld-eth-server
Update vendor directory and make necessary code changes
Fixes for new geth version
This commit is contained in:
+32
@@ -0,0 +1,32 @@
|
||||
os:
|
||||
- linux
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
|
||||
env:
|
||||
global:
|
||||
- GOTFLAGS="-race"
|
||||
matrix:
|
||||
- BUILD_DEPTYPE=gx
|
||||
- BUILD_DEPTYPE=gomod
|
||||
|
||||
|
||||
# disable travis install
|
||||
install:
|
||||
- true
|
||||
|
||||
script:
|
||||
- bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh)
|
||||
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $GOPATH/src/gx
|
||||
- $GOPATH/pkg/mod
|
||||
- $HOME/.cache/go-build
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
Git ipld format
|
||||
==================
|
||||
|
||||
[](http://ipn.io)
|
||||
[](http://ipfs.io/)
|
||||
[](http://webchat.freenode.net/?channels=%23ipfs)
|
||||
[](https://codecov.io/gh/ipfs/go-ipld-git/branch/master)
|
||||
[](https://travis-ci.org/ipfs/go-ipld-git)
|
||||
|
||||
> An ipld codec for git objects allowing path traversals across the git graph!
|
||||
|
||||
Note: This is WIP and may not be an entirely correct parser.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Install](#install)
|
||||
- [About](#about)
|
||||
- [Contribute](#contribute)
|
||||
- [License](#license)
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
go get github.com/ipfs/go-ipld-git
|
||||
```
|
||||
|
||||
## About
|
||||
This is an IPLD codec which handles git objects. Objects are transformed
|
||||
into IPLD graph in the following way:
|
||||
|
||||
* Commit:
|
||||
```json
|
||||
{
|
||||
"author": {
|
||||
"date": "1503667703 +0200",
|
||||
"email": "author@mail",
|
||||
"name": "Author Name"
|
||||
},
|
||||
"committer": {
|
||||
"date": "1503667703 +0200",
|
||||
"email": "author@mail",
|
||||
"name": "Author Name"
|
||||
},
|
||||
"message": "Commit Message\n",
|
||||
"parents": [
|
||||
<LINK>, <LINK>, ...
|
||||
],
|
||||
"tree": <LINK>
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
* Tag:
|
||||
```json
|
||||
{
|
||||
"message": "message\n",
|
||||
"object": {
|
||||
"/": "baf4bcfg3mbz3yj3njqyr3ifdaqyfv3prei6h6bq"
|
||||
},
|
||||
"tag": "tagname",
|
||||
"tagger": {
|
||||
"date": "1503667703 +0200",
|
||||
"email": "author@mail",
|
||||
"name": "Author Name"
|
||||
},
|
||||
"type": "commit"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
* Tree:
|
||||
```json
|
||||
{
|
||||
"file.name": {
|
||||
"mode": "100664",
|
||||
"hash": <LINK>
|
||||
},
|
||||
"directoryname": {
|
||||
"mode": "40000",
|
||||
"hash": <LINK>
|
||||
},
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
* Blob:
|
||||
```json
|
||||
"<base64 of 'blob <size>\0<data>'>"
|
||||
```
|
||||
## Contribute
|
||||
|
||||
PRs are welcome!
|
||||
|
||||
Small note: If editing the Readme, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification.
|
||||
|
||||
## License
|
||||
|
||||
MIT © Jeromy Johnson
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package ipldgit
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
node "github.com/ipfs/go-ipld-format"
|
||||
)
|
||||
|
||||
type Blob struct {
|
||||
rawData []byte
|
||||
cid cid.Cid
|
||||
}
|
||||
|
||||
func (b *Blob) Cid() cid.Cid {
|
||||
return b.cid
|
||||
}
|
||||
|
||||
func (b *Blob) Copy() node.Node {
|
||||
nb := *b
|
||||
return &nb
|
||||
}
|
||||
|
||||
func (b *Blob) Links() []*node.Link {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Blob) Resolve(_ []string) (interface{}, []string, error) {
|
||||
return nil, nil, errors.New("no such link")
|
||||
}
|
||||
|
||||
func (b *Blob) ResolveLink(_ []string) (*node.Link, []string, error) {
|
||||
return nil, nil, errors.New("no such link")
|
||||
}
|
||||
|
||||
func (b *Blob) Loggable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"type": "git_blob",
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Blob) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(b.rawData)
|
||||
}
|
||||
|
||||
func (b *Blob) RawData() []byte {
|
||||
return []byte(b.rawData)
|
||||
}
|
||||
|
||||
func (b *Blob) Size() (uint64, error) {
|
||||
return uint64(len(b.rawData)), nil
|
||||
}
|
||||
|
||||
func (b *Blob) Stat() (*node.NodeStat, error) {
|
||||
return &node.NodeStat{}, nil
|
||||
}
|
||||
|
||||
func (b *Blob) String() string {
|
||||
return "[git blob]"
|
||||
}
|
||||
|
||||
func (b *Blob) Tree(p string, depth int) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Blob) GitSha() []byte {
|
||||
return cidToSha(b.Cid())
|
||||
}
|
||||
|
||||
var _ node.Node = (*Blob)(nil)
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
coverage:
|
||||
range: "50...100"
|
||||
comment: off
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
package ipldgit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
node "github.com/ipfs/go-ipld-format"
|
||||
)
|
||||
|
||||
type Commit struct {
|
||||
DataSize string `json:"-"`
|
||||
GitTree cid.Cid `json:"tree"`
|
||||
Parents []cid.Cid `json:"parents"`
|
||||
Message string `json:"message"`
|
||||
Author *PersonInfo `json:"author"`
|
||||
Committer *PersonInfo `json:"committer"`
|
||||
Encoding string `json:"encoding,omitempty"`
|
||||
Sig *GpgSig `json:"signature,omitempty"`
|
||||
MergeTag []*MergeTag `json:"mergetag,omitempty"`
|
||||
|
||||
// Other contains all the non-standard headers, such as 'HG:extra'
|
||||
Other []string `json:"other,omitempty"`
|
||||
|
||||
cid cid.Cid
|
||||
|
||||
rawData []byte
|
||||
rawDataOnce sync.Once
|
||||
}
|
||||
|
||||
type PersonInfo struct {
|
||||
Name string
|
||||
Email string
|
||||
Date string
|
||||
Timezone string
|
||||
}
|
||||
|
||||
func (pi *PersonInfo) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(map[string]string{
|
||||
"name": pi.Name,
|
||||
"email": pi.Email,
|
||||
"date": pi.Date + " " + pi.Timezone,
|
||||
})
|
||||
}
|
||||
|
||||
func (pi *PersonInfo) String() string {
|
||||
f := "%s <%s>"
|
||||
arg := []interface{}{pi.Name, pi.Email}
|
||||
if pi.Date != "" {
|
||||
f = f + " %s"
|
||||
arg = append(arg, pi.Date)
|
||||
}
|
||||
|
||||
if pi.Timezone != "" {
|
||||
f = f + " %s"
|
||||
arg = append(arg, pi.Timezone)
|
||||
}
|
||||
return fmt.Sprintf(f, arg...)
|
||||
}
|
||||
|
||||
func (pi *PersonInfo) tree(name string, depth int) []string {
|
||||
if depth == 1 {
|
||||
return []string{name}
|
||||
}
|
||||
return []string{name + "/name", name + "/email", name + "/date"}
|
||||
}
|
||||
|
||||
func (pi *PersonInfo) resolve(p []string) (interface{}, []string, error) {
|
||||
switch p[0] {
|
||||
case "name":
|
||||
return pi.Name, p[1:], nil
|
||||
case "email":
|
||||
return pi.Email, p[1:], nil
|
||||
case "date":
|
||||
return pi.Date + " " + pi.Timezone, p[1:], nil
|
||||
default:
|
||||
return nil, nil, errors.New("no such link")
|
||||
}
|
||||
}
|
||||
|
||||
type MergeTag struct {
|
||||
Object cid.Cid `json:"object"`
|
||||
Type string `json:"type"`
|
||||
Tag string `json:"tag"`
|
||||
Tagger *PersonInfo `json:"tagger"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
type GpgSig struct {
|
||||
Text string
|
||||
}
|
||||
|
||||
func (c *Commit) Cid() cid.Cid {
|
||||
return c.cid
|
||||
}
|
||||
|
||||
func (c *Commit) Copy() node.Node {
|
||||
nc := *c
|
||||
return &nc
|
||||
}
|
||||
|
||||
func (c *Commit) Links() []*node.Link {
|
||||
out := []*node.Link{
|
||||
{Cid: c.GitTree},
|
||||
}
|
||||
|
||||
for _, p := range c.Parents {
|
||||
out = append(out, &node.Link{Cid: p})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (c *Commit) Loggable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"type": "git_commit",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commit) RawData() []byte {
|
||||
c.rawDataOnce.Do(func() {
|
||||
buf := new(bytes.Buffer)
|
||||
fmt.Fprintf(buf, "commit %s\x00", c.DataSize)
|
||||
fmt.Fprintf(buf, "tree %s\n", hex.EncodeToString(cidToSha(c.GitTree)))
|
||||
for _, p := range c.Parents {
|
||||
fmt.Fprintf(buf, "parent %s\n", hex.EncodeToString(cidToSha(p)))
|
||||
}
|
||||
fmt.Fprintf(buf, "author %s\n", c.Author.String())
|
||||
fmt.Fprintf(buf, "committer %s\n", c.Committer.String())
|
||||
if len(c.Encoding) > 0 {
|
||||
fmt.Fprintf(buf, "encoding %s\n", c.Encoding)
|
||||
}
|
||||
for _, mtag := range c.MergeTag {
|
||||
fmt.Fprintf(buf, "mergetag object %s\n", hex.EncodeToString(cidToSha(mtag.Object)))
|
||||
fmt.Fprintf(buf, " type %s\n", mtag.Type)
|
||||
fmt.Fprintf(buf, " tag %s\n", mtag.Tag)
|
||||
fmt.Fprintf(buf, " tagger %s\n \n", mtag.Tagger.String())
|
||||
fmt.Fprintf(buf, "%s", mtag.Text)
|
||||
}
|
||||
if c.Sig != nil {
|
||||
fmt.Fprintln(buf, "gpgsig -----BEGIN PGP SIGNATURE-----")
|
||||
fmt.Fprint(buf, c.Sig.Text)
|
||||
fmt.Fprintln(buf, " -----END PGP SIGNATURE-----")
|
||||
}
|
||||
for _, line := range c.Other {
|
||||
fmt.Fprintln(buf, line)
|
||||
}
|
||||
fmt.Fprintf(buf, "\n%s", c.Message)
|
||||
c.rawData = buf.Bytes()
|
||||
})
|
||||
|
||||
return c.rawData
|
||||
}
|
||||
|
||||
func (c *Commit) Resolve(path []string) (interface{}, []string, error) {
|
||||
if len(path) == 0 {
|
||||
return nil, nil, fmt.Errorf("zero length path")
|
||||
}
|
||||
|
||||
switch path[0] {
|
||||
case "parents":
|
||||
if len(path) == 1 {
|
||||
return c.Parents, nil, nil
|
||||
}
|
||||
|
||||
i, err := strconv.Atoi(path[1])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if i < 0 || i >= len(c.Parents) {
|
||||
return nil, nil, fmt.Errorf("index out of range")
|
||||
}
|
||||
|
||||
return &node.Link{Cid: c.Parents[i]}, path[2:], nil
|
||||
case "author":
|
||||
if len(path) == 1 {
|
||||
return c.Author, nil, nil
|
||||
}
|
||||
return c.Author.resolve(path[1:])
|
||||
case "committer":
|
||||
if len(path) == 1 {
|
||||
return c.Committer, nil, nil
|
||||
}
|
||||
return c.Committer.resolve(path[1:])
|
||||
case "signature":
|
||||
return c.Sig.Text, path[1:], nil
|
||||
case "message":
|
||||
return c.Message, path[1:], nil
|
||||
case "tree":
|
||||
return &node.Link{Cid: c.GitTree}, path[1:], nil
|
||||
case "mergetag":
|
||||
if len(path) == 1 {
|
||||
return c.MergeTag, nil, nil
|
||||
}
|
||||
|
||||
i, err := strconv.Atoi(path[1])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if i < 0 || i >= len(c.MergeTag) {
|
||||
return nil, nil, fmt.Errorf("index out of range")
|
||||
}
|
||||
|
||||
if len(path) == 2 {
|
||||
return c.MergeTag[i], nil, nil
|
||||
}
|
||||
return c.MergeTag[i].resolve(path[2:])
|
||||
default:
|
||||
return nil, nil, errors.New("no such link")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commit) ResolveLink(path []string) (*node.Link, []string, error) {
|
||||
out, rest, err := c.Resolve(path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
lnk, ok := out.(*node.Link)
|
||||
if !ok {
|
||||
return nil, nil, errors.New("not a link")
|
||||
}
|
||||
|
||||
return lnk, rest, nil
|
||||
}
|
||||
|
||||
func (c *Commit) Size() (uint64, error) {
|
||||
return uint64(len(c.RawData())), nil
|
||||
}
|
||||
|
||||
func (c *Commit) Stat() (*node.NodeStat, error) {
|
||||
return &node.NodeStat{}, nil
|
||||
}
|
||||
|
||||
func (c *Commit) String() string {
|
||||
return "[git commit object]"
|
||||
}
|
||||
|
||||
func (c *Commit) Tree(p string, depth int) []string {
|
||||
if depth != -1 {
|
||||
panic("proper tree not yet implemented")
|
||||
}
|
||||
tree := []string{"tree", "parents", "message", "gpgsig"}
|
||||
tree = append(tree, c.Author.tree("author", depth)...)
|
||||
tree = append(tree, c.Committer.tree("committer", depth)...)
|
||||
for i := range c.Parents {
|
||||
tree = append(tree, fmt.Sprintf("parents/%d", i))
|
||||
}
|
||||
return tree
|
||||
}
|
||||
|
||||
func (c *Commit) GitSha() []byte {
|
||||
return cidToSha(c.Cid())
|
||||
}
|
||||
|
||||
func (t *MergeTag) resolve(path []string) (interface{}, []string, error) {
|
||||
if len(path) == 0 {
|
||||
return nil, nil, fmt.Errorf("zero length path")
|
||||
}
|
||||
|
||||
switch path[0] {
|
||||
case "object":
|
||||
return &node.Link{Cid: t.Object}, path[1:], nil
|
||||
case "tag":
|
||||
return t.Tag, path[1:], nil
|
||||
case "tagger":
|
||||
if len(path) == 1 {
|
||||
return t.Tagger, nil, nil
|
||||
}
|
||||
return t.Tagger.resolve(path[1:])
|
||||
case "text":
|
||||
return t.Text, path[1:], nil
|
||||
case "type":
|
||||
return t.Type, path[1:], nil
|
||||
default:
|
||||
return nil, nil, errors.New("no such link")
|
||||
}
|
||||
}
|
||||
|
||||
var _ node.Node = (*Commit)(nil)
|
||||
+478
@@ -0,0 +1,478 @@
|
||||
package ipldgit
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
blocks "github.com/ipfs/go-block-format"
|
||||
cid "github.com/ipfs/go-cid"
|
||||
node "github.com/ipfs/go-ipld-format"
|
||||
mh "github.com/multiformats/go-multihash"
|
||||
)
|
||||
|
||||
func DecodeBlock(block blocks.Block) (node.Node, error) {
|
||||
prefix := block.Cid().Prefix()
|
||||
|
||||
if prefix.Codec != cid.GitRaw || prefix.MhType != mh.SHA1 || prefix.MhLength != mh.DefaultLengths[mh.SHA1] {
|
||||
return nil, errors.New("invalid CID prefix")
|
||||
}
|
||||
|
||||
return ParseObjectFromBuffer(block.RawData())
|
||||
}
|
||||
|
||||
var _ node.DecodeBlockFunc = DecodeBlock
|
||||
|
||||
func ParseObjectFromBuffer(b []byte) (node.Node, error) {
|
||||
return ParseObject(bytes.NewReader(b))
|
||||
}
|
||||
|
||||
func ParseCompressedObject(r io.Reader) (node.Node, error) {
|
||||
rc, err := zlib.NewReader(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
return ParseObject(rc)
|
||||
}
|
||||
|
||||
func ParseObject(r io.Reader) (node.Node, error) {
|
||||
rd := bufio.NewReader(r)
|
||||
|
||||
typ, err := rd.ReadString(' ')
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typ = typ[:len(typ)-1]
|
||||
|
||||
switch typ {
|
||||
case "tree":
|
||||
return ReadTree(rd)
|
||||
case "commit":
|
||||
return ReadCommit(rd)
|
||||
case "blob":
|
||||
return ReadBlob(rd)
|
||||
case "tag":
|
||||
return ReadTag(rd)
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized object type: %s", typ)
|
||||
}
|
||||
}
|
||||
|
||||
func ReadBlob(rd *bufio.Reader) (*Blob, error) {
|
||||
size, err := rd.ReadString(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sizen, err := strconv.Atoi(size[:len(size)-1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
fmt.Fprintf(buf, "blob %d\x00", sizen)
|
||||
|
||||
n, err := io.Copy(buf, rd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if n != int64(sizen) {
|
||||
return nil, fmt.Errorf("blob size was not accurate")
|
||||
}
|
||||
|
||||
out := &Blob{}
|
||||
out.rawData = buf.Bytes()
|
||||
out.cid = hashObject(out.RawData())
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func ReadCommit(rd *bufio.Reader) (*Commit, error) {
|
||||
size, err := rd.ReadString(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := &Commit{
|
||||
DataSize: size[:len(size)-1],
|
||||
}
|
||||
|
||||
for {
|
||||
line, _, err := rd.ReadLine()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = parseCommitLine(out, line, rd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
out.cid = hashObject(out.RawData())
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func parseCommitLine(out *Commit, line []byte, rd *bufio.Reader) error {
|
||||
switch {
|
||||
case bytes.HasPrefix(line, []byte("tree ")):
|
||||
sha, err := hex.DecodeString(string(line[5:]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out.GitTree = shaToCid(sha)
|
||||
case bytes.HasPrefix(line, []byte("parent ")):
|
||||
psha, err := hex.DecodeString(string(line[7:]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out.Parents = append(out.Parents, shaToCid(psha))
|
||||
case bytes.HasPrefix(line, []byte("author ")):
|
||||
a, err := parsePersonInfo(line)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out.Author = a
|
||||
case bytes.HasPrefix(line, []byte("committer ")):
|
||||
c, err := parsePersonInfo(line)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out.Committer = c
|
||||
case bytes.HasPrefix(line, []byte("encoding ")):
|
||||
out.Encoding = string(line[9:])
|
||||
case bytes.HasPrefix(line, []byte("mergetag object ")):
|
||||
sha, err := hex.DecodeString(string(line)[16:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mt, rest, err := ReadMergeTag(sha, rd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out.MergeTag = append(out.MergeTag, mt)
|
||||
|
||||
if rest != nil {
|
||||
err = parseCommitLine(out, rest, rd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case bytes.HasPrefix(line, []byte("gpgsig ")):
|
||||
sig, err := ReadGpgSig(rd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Sig = sig
|
||||
case len(line) == 0:
|
||||
rest, err := ioutil.ReadAll(rd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out.Message = string(rest)
|
||||
default:
|
||||
out.Other = append(out.Other, string(line))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReadTag(rd *bufio.Reader) (*Tag, error) {
|
||||
size, err := rd.ReadString(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := &Tag{
|
||||
dataSize: size[:len(size)-1],
|
||||
}
|
||||
|
||||
for {
|
||||
line, _, err := rd.ReadLine()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case bytes.HasPrefix(line, []byte("object ")):
|
||||
sha, err := hex.DecodeString(string(line[7:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out.Object = shaToCid(sha)
|
||||
case bytes.HasPrefix(line, []byte("tag ")):
|
||||
out.Tag = string(line[4:])
|
||||
case bytes.HasPrefix(line, []byte("tagger ")):
|
||||
c, err := parsePersonInfo(line)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out.Tagger = c
|
||||
case bytes.HasPrefix(line, []byte("type ")):
|
||||
out.Type = string(line[5:])
|
||||
case len(line) == 0:
|
||||
rest, err := ioutil.ReadAll(rd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out.Message = string(rest)
|
||||
default:
|
||||
fmt.Println("unhandled line: ", string(line))
|
||||
}
|
||||
}
|
||||
|
||||
out.cid = hashObject(out.RawData())
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func hashObject(data []byte) cid.Cid {
|
||||
c, err := cid.Prefix{
|
||||
MhType: mh.SHA1,
|
||||
MhLength: -1,
|
||||
Codec: cid.GitRaw,
|
||||
Version: 1,
|
||||
}.Sum(data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func ReadMergeTag(hash []byte, rd *bufio.Reader) (*MergeTag, []byte, error) {
|
||||
out := new(MergeTag)
|
||||
|
||||
out.Object = shaToCid(hash)
|
||||
for {
|
||||
line, _, err := rd.ReadLine()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case bytes.HasPrefix(line, []byte(" type ")):
|
||||
out.Type = string(line[6:])
|
||||
case bytes.HasPrefix(line, []byte(" tag ")):
|
||||
out.Tag = string(line[5:])
|
||||
case bytes.HasPrefix(line, []byte(" tagger ")):
|
||||
tagger, err := parsePersonInfo(line[1:])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
out.Tagger = tagger
|
||||
case string(line) == " ":
|
||||
for {
|
||||
line, _, err := rd.ReadLine()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if !bytes.HasPrefix(line, []byte(" ")) {
|
||||
return out, line, nil
|
||||
}
|
||||
|
||||
out.Text += string(line) + "\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, nil, nil
|
||||
}
|
||||
|
||||
func ReadGpgSig(rd *bufio.Reader) (*GpgSig, error) {
|
||||
line, _, err := rd.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := new(GpgSig)
|
||||
|
||||
if string(line) != " " {
|
||||
if strings.HasPrefix(string(line), " Version: ") || strings.HasPrefix(string(line), " Comment: ") {
|
||||
out.Text += string(line) + "\n"
|
||||
} else {
|
||||
return nil, fmt.Errorf("expected first line of sig to be a single space or version")
|
||||
}
|
||||
} else {
|
||||
out.Text += " \n"
|
||||
}
|
||||
|
||||
for {
|
||||
line, _, err := rd.ReadLine()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bytes.Equal(line, []byte(" -----END PGP SIGNATURE-----")) {
|
||||
break
|
||||
}
|
||||
|
||||
out.Text += string(line) + "\n"
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func parsePersonInfo(line []byte) (*PersonInfo, error) {
|
||||
parts := bytes.Split(line, []byte{' '})
|
||||
if len(parts) < 3 {
|
||||
fmt.Println(string(line))
|
||||
return nil, fmt.Errorf("incorrectly formatted person info line")
|
||||
}
|
||||
|
||||
//TODO: just use regex?
|
||||
//skip prefix
|
||||
at := 1
|
||||
|
||||
var pi PersonInfo
|
||||
var name string
|
||||
|
||||
for {
|
||||
if at == len(parts) {
|
||||
return nil, fmt.Errorf("invalid personInfo: %s\n", line)
|
||||
}
|
||||
part := parts[at]
|
||||
if len(part) != 0 {
|
||||
if part[0] == '<' {
|
||||
break
|
||||
}
|
||||
name += string(part) + " "
|
||||
} else if len(name) > 0 {
|
||||
name += " "
|
||||
}
|
||||
at++
|
||||
}
|
||||
if len(name) != 0 {
|
||||
pi.Name = name[:len(name)-1]
|
||||
}
|
||||
|
||||
var email string
|
||||
for {
|
||||
if at == len(parts) {
|
||||
return nil, fmt.Errorf("invalid personInfo: %s\n", line)
|
||||
}
|
||||
part := parts[at]
|
||||
if part[0] == '<' {
|
||||
part = part[1:]
|
||||
}
|
||||
|
||||
at++
|
||||
if part[len(part)-1] == '>' {
|
||||
email += string(part[:len(part)-1])
|
||||
break
|
||||
}
|
||||
email += string(part) + " "
|
||||
}
|
||||
pi.Email = email
|
||||
|
||||
if at == len(parts) {
|
||||
return &pi, nil
|
||||
}
|
||||
pi.Date = string(parts[at])
|
||||
|
||||
at++
|
||||
if at == len(parts) {
|
||||
return &pi, nil
|
||||
}
|
||||
pi.Timezone = string(parts[at])
|
||||
return &pi, nil
|
||||
}
|
||||
|
||||
func ReadTree(rd *bufio.Reader) (*Tree, error) {
|
||||
lstr, err := rd.ReadString(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lstr = lstr[:len(lstr)-1]
|
||||
|
||||
n, err := strconv.Atoi(lstr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := &Tree{
|
||||
entries: make(map[string]*TreeEntry),
|
||||
size: n,
|
||||
}
|
||||
var order []string
|
||||
for {
|
||||
e, err := ReadEntry(rd)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
order = append(order, e.name)
|
||||
t.entries[e.name] = e
|
||||
}
|
||||
t.order = order
|
||||
t.cid = hashObject(t.RawData())
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func cidToSha(c cid.Cid) []byte {
|
||||
h := c.Hash()
|
||||
return h[len(h)-20:]
|
||||
}
|
||||
|
||||
func shaToCid(sha []byte) cid.Cid {
|
||||
h, _ := mh.Encode(sha, mh.SHA1)
|
||||
return cid.NewCidV1(cid.GitRaw, h)
|
||||
}
|
||||
|
||||
func ReadEntry(r *bufio.Reader) (*TreeEntry, error) {
|
||||
data, err := r.ReadString(' ')
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = data[:len(data)-1]
|
||||
|
||||
name, err := r.ReadString(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name = name[:len(name)-1]
|
||||
|
||||
sha := make([]byte, 20)
|
||||
_, err = io.ReadFull(r, sha)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &TreeEntry{
|
||||
name: name,
|
||||
Mode: data,
|
||||
Hash: shaToCid(sha),
|
||||
}, nil
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
module github.com/ipfs/go-ipld-git
|
||||
|
||||
require (
|
||||
github.com/ipfs/go-block-format v0.0.2
|
||||
github.com/ipfs/go-cid v0.0.2
|
||||
github.com/ipfs/go-ipld-format v0.0.1
|
||||
github.com/multiformats/go-multihash v0.0.1
|
||||
)
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
github.com/gxed/hashland/keccakpg v0.0.1 h1:wrk3uMNaMxbXiHibbPO4S0ymqJMm41WiudyFSs7UnsU=
|
||||
github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
|
||||
github.com/gxed/hashland/murmur3 v0.0.1 h1:SheiaIt0sda5K+8FLz952/1iWS9zrnKsEJaOJu4ZbSc=
|
||||
github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48=
|
||||
github.com/ipfs/go-block-format v0.0.2 h1:qPDvcP19izTjU8rgo6p7gTXZlkMkF5bz5G3fqIsSCPE=
|
||||
github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY=
|
||||
github.com/ipfs/go-cid v0.0.1 h1:GBjWPktLnNyX0JiQCNFpUuUSoMw5KMyqrsejHYlILBE=
|
||||
github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
|
||||
github.com/ipfs/go-cid v0.0.2 h1:tuuKaZPU1M6HcejsO3AcYWW8sZ8MTvyxfc4uqB4eFE8=
|
||||
github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
|
||||
github.com/ipfs/go-ipfs-util v0.0.1 h1:Wz9bL2wB2YBJqggkA4dD7oSmqB4cAnpNbGrlHJulv50=
|
||||
github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc=
|
||||
github.com/ipfs/go-ipld-format v0.0.1 h1:HCu4eB/Gh+KD/Q0M8u888RFkorTWNIL3da4oc5dwc80=
|
||||
github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
|
||||
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16 h1:5W7KhL8HVF3XCFOweFD3BNESdnO8ewyYTFT2R+/b8FQ=
|
||||
github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U=
|
||||
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
|
||||
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
|
||||
github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
|
||||
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
|
||||
github.com/multiformats/go-multibase v0.0.1 h1:PN9/v21eLywrFWdFNsFKaU04kLJzuYzmrJR+ubhT9qA=
|
||||
github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs=
|
||||
github.com/multiformats/go-multihash v0.0.1 h1:HHwN1K12I+XllBCrqKnhX949Orn4oawPkegHMu2vDqQ=
|
||||
github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE=
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/sys v0.0.0-20190219092855-153ac476189d h1:Z0Ahzd7HltpJtjAHHxX8QFP3j1yYgiuvjbjRzDj/KH0=
|
||||
golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
CUR_DIR=$(pwd)
|
||||
TEST_DIR=$(mktemp -d)
|
||||
cd ${TEST_DIR}
|
||||
|
||||
git init
|
||||
|
||||
# Test generic commit/blob
|
||||
|
||||
git config user.name "John Doe"
|
||||
git config user.email johndoe@example.com
|
||||
|
||||
echo "Hello world" > file
|
||||
git add file
|
||||
|
||||
git commit -m "Init"
|
||||
|
||||
# Test generic commit/tree/blob, weird person info
|
||||
|
||||
mkdir dir
|
||||
mkdir dir/subdir
|
||||
mkdir dir2
|
||||
|
||||
echo "qwerty" > dir/f1
|
||||
echo "123456" > dir/subdir/f2
|
||||
echo "',.pyf" > dir2/f3
|
||||
|
||||
git add .
|
||||
|
||||
git config user.name "John Doe & John Other"
|
||||
git config user.email "johndoe@example.com, johnother@example.com"
|
||||
git commit -m "Commit 2"
|
||||
|
||||
# Test merge-tag
|
||||
git config user.name "John Doe"
|
||||
git config user.email johndoe@example.com
|
||||
|
||||
git branch dev
|
||||
git checkout dev
|
||||
|
||||
echo ";qjkxb" > dir/f4
|
||||
|
||||
git add dir/f4
|
||||
git commit -m "Release"
|
||||
git tag -a v1 -m "Some version"
|
||||
git checkout master
|
||||
|
||||
## defer eyes.Open()
|
||||
## eyes.Close()
|
||||
|
||||
git cat-file tag $(cat .git/refs/tags/v1) | head -n4 | sed 's/v1/v1sig/g' > sigobj
|
||||
cat >>sigobj <<EOF
|
||||
|
||||
Some signed version
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
NotReallyABase64Signature
|
||||
ButItsGoodEnough
|
||||
-----END PGP SIGNATURE-----
|
||||
EOF
|
||||
|
||||
cat <(printf "tag %d\0" $(wc -c sigobj | cut -d' ' -f1); cat sigobj) > sigtag
|
||||
FILE=.git/objects/$(sha1sum sigtag | cut -d' ' -f1 | sed 's/../\0\//')
|
||||
mkdir -p $(dirname ${FILE})
|
||||
cat sigtag | zlib-flate -compress > ${FILE}
|
||||
echo $(sha1sum sigtag | cut -d' ' -f1) > .git/refs/tags/v1sig
|
||||
|
||||
git merge v1sig --no-ff -m "Merge tag v1"
|
||||
|
||||
# Test encoding
|
||||
git config i18n.commitencoding "ISO-8859-1"
|
||||
echo "fgcrl" > f6
|
||||
git add f6
|
||||
git commit -m "Encoded"
|
||||
|
||||
# Test iplBlob/tree tags
|
||||
git tag -a v1-file -m "Some file" 933b7583b7767b07ea4cf242c1be29162eb8bb85
|
||||
git tag -a v1-tree -m "Some tree" 672ef117424f54b71e5e058d1184de6a07450d0e
|
||||
|
||||
# Create test 'signed' objects
|
||||
|
||||
git cat-file commit $(cat .git/refs/heads/master) | head -n4 > sigobj
|
||||
echo "gpgsig -----BEGIN PGP SIGNATURE-----" >> sigobj
|
||||
echo " " >> sigobj
|
||||
echo " NotReallyABase64Signature" >> sigobj
|
||||
echo " ButItsGoodEnough" >> sigobj
|
||||
echo " -----END PGP SIGNATURE-----" >> sigobj
|
||||
echo "" >> sigobj
|
||||
echo "Encoded" >> sigobj
|
||||
|
||||
cat <(printf "commit %d\0" $(wc -c sigobj | cut -d' ' -f1); cat sigobj) > sigcommit
|
||||
FILE=.git/objects/$(sha1sum sigcommit | cut -d' ' -f1 | sed 's/../\0\//')
|
||||
mkdir -p $(dirname ${FILE})
|
||||
cat sigcommit | zlib-flate -compress > ${FILE}
|
||||
|
||||
git cat-file commit $(cat .git/refs/heads/master) | head -n4 > sigobj
|
||||
echo "gpgsig -----BEGIN PGP SIGNATURE-----" >> sigobj
|
||||
echo " Version: 0.1.2" >> sigobj
|
||||
echo " " >> sigobj
|
||||
echo " NotReallyABase64Signature" >> sigobj
|
||||
echo " ButItsGoodEnough" >> sigobj
|
||||
echo " -----END PGP SIGNATURE-----" >> sigobj
|
||||
echo " " >> sigobj
|
||||
echo "" >> sigobj
|
||||
echo "Encoded" >> sigobj
|
||||
|
||||
cat <(printf "commit %d\0" $(wc -c sigobj | cut -d' ' -f1); cat sigobj) > sigcommit
|
||||
FILE=.git/objects/$(sha1sum sigcommit | cut -d' ' -f1 | sed 's/../\0\//')
|
||||
mkdir -p $(dirname ${FILE})
|
||||
cat sigcommit | zlib-flate -compress >> ${FILE}
|
||||
rm sigobj sigcommit
|
||||
|
||||
# Create test archive, clean up
|
||||
|
||||
tar czf git.tar.gz .git
|
||||
mv git.tar.gz ${CUR_DIR}/testdata.tar.gz
|
||||
cd ${CUR_DIR}
|
||||
rm -rf ${TEST_DIR}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"author": "whyrusleeping",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ipfs/go-ipld-git"
|
||||
},
|
||||
"gx": {
|
||||
"dvcsimport": "github.com/ipfs/go-ipld-git"
|
||||
},
|
||||
"gxDependencies": [
|
||||
{
|
||||
"author": "whyrusleeping",
|
||||
"hash": "QmZ6nzCLwGLVfRzYLpD7pW6UNuBDKEcA2imJtVpbEx2rxy",
|
||||
"name": "go-ipld-format",
|
||||
"version": "0.8.1"
|
||||
},
|
||||
{
|
||||
"author": "whyrusleeping",
|
||||
"hash": "QmTbxNB1NwDesLmKTscr4udL2tVP7MaxvXnD1D9yX7g3PN",
|
||||
"name": "go-cid",
|
||||
"version": "0.9.3"
|
||||
},
|
||||
{
|
||||
"author": "stebalien",
|
||||
"hash": "QmYYLnAzR28nAQ4U5MFniLprnktu6eTFKibeNt96V21EZK",
|
||||
"name": "go-block-format",
|
||||
"version": "0.2.2"
|
||||
},
|
||||
{
|
||||
"author": "multiformats",
|
||||
"hash": "QmerPMzPk1mJVowm8KgmoknWa4yCYvvugMPsgWmDNUvDLW",
|
||||
"name": "go-multihash",
|
||||
"version": "1.0.9"
|
||||
}
|
||||
],
|
||||
"gxVersion": "0.10.0",
|
||||
"language": "go",
|
||||
"license": "",
|
||||
"name": "go-ipld-git",
|
||||
"releaseCmd": "git commit -a -m \"gx publish $VERSION\"",
|
||||
"version": "0.3.6"
|
||||
}
|
||||
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
package ipldgit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
node "github.com/ipfs/go-ipld-format"
|
||||
)
|
||||
|
||||
type Tag struct {
|
||||
Object cid.Cid `json:"object"`
|
||||
Type string `json:"type"`
|
||||
Tag string `json:"tag"`
|
||||
Tagger *PersonInfo `json:"tagger"`
|
||||
Message string `json:"message"`
|
||||
dataSize string
|
||||
|
||||
cid cid.Cid
|
||||
|
||||
rawData []byte
|
||||
rawDataOnce sync.Once
|
||||
}
|
||||
|
||||
func (t *Tag) Cid() cid.Cid {
|
||||
return t.cid
|
||||
}
|
||||
|
||||
func (t *Tag) Copy() node.Node {
|
||||
nt := *t
|
||||
return &nt
|
||||
}
|
||||
|
||||
func (t *Tag) Links() []*node.Link {
|
||||
return []*node.Link{{Cid: t.Object}}
|
||||
}
|
||||
|
||||
func (t *Tag) Loggable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"type": "git_tag",
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tag) RawData() []byte {
|
||||
t.rawDataOnce.Do(func() {
|
||||
buf := new(bytes.Buffer)
|
||||
fmt.Fprintf(buf, "tag %s\x00", t.dataSize)
|
||||
fmt.Fprintf(buf, "object %s\n", hex.EncodeToString(cidToSha(t.Object)))
|
||||
fmt.Fprintf(buf, "type %s\n", t.Type)
|
||||
fmt.Fprintf(buf, "tag %s\n", t.Tag)
|
||||
if t.Tagger != nil {
|
||||
fmt.Fprintf(buf, "tagger %s\n", t.Tagger.String())
|
||||
}
|
||||
if t.Message != "" {
|
||||
fmt.Fprintf(buf, "\n%s", t.Message)
|
||||
}
|
||||
t.rawData = buf.Bytes()
|
||||
})
|
||||
|
||||
return t.rawData
|
||||
}
|
||||
|
||||
func (t *Tag) Resolve(path []string) (interface{}, []string, error) {
|
||||
if len(path) == 0 {
|
||||
return nil, nil, fmt.Errorf("zero length path")
|
||||
}
|
||||
|
||||
switch path[0] {
|
||||
case "object":
|
||||
return &node.Link{Cid: t.Object}, path[1:], nil
|
||||
case "type":
|
||||
return t.Type, path[1:], nil
|
||||
case "tagger":
|
||||
if len(path) == 1 {
|
||||
return t.Tagger, nil, nil
|
||||
}
|
||||
return t.Tagger.resolve(path[1:])
|
||||
case "message":
|
||||
return t.Message, path[1:], nil
|
||||
case "tag":
|
||||
return t.Tag, path[1:], nil
|
||||
default:
|
||||
return nil, nil, errors.New("no such link")
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tag) ResolveLink(path []string) (*node.Link, []string, error) {
|
||||
out, rest, err := t.Resolve(path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
lnk, ok := out.(*node.Link)
|
||||
if !ok {
|
||||
return nil, nil, errors.New("not a link")
|
||||
}
|
||||
|
||||
return lnk, rest, nil
|
||||
}
|
||||
|
||||
func (t *Tag) Size() (uint64, error) {
|
||||
return uint64(len(t.RawData())), nil
|
||||
}
|
||||
|
||||
func (t *Tag) Stat() (*node.NodeStat, error) {
|
||||
return &node.NodeStat{}, nil
|
||||
}
|
||||
|
||||
func (t *Tag) String() string {
|
||||
return "[git tag object]"
|
||||
}
|
||||
|
||||
func (t *Tag) Tree(p string, depth int) []string {
|
||||
if p != "" {
|
||||
if p == "tagger" {
|
||||
return []string{"name", "email", "date"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if depth == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
tree := []string{"object", "type", "tag", "message"}
|
||||
tree = append(tree, t.Tagger.tree("tagger", depth)...)
|
||||
return tree
|
||||
}
|
||||
|
||||
func (t *Tag) GitSha() []byte {
|
||||
return cidToSha(t.Cid())
|
||||
}
|
||||
|
||||
var _ node.Node = (*Tag)(nil)
|
||||
BIN
Binary file not shown.
+170
@@ -0,0 +1,170 @@
|
||||
package ipldgit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
cid "github.com/ipfs/go-cid"
|
||||
node "github.com/ipfs/go-ipld-format"
|
||||
)
|
||||
|
||||
type Tree struct {
|
||||
entries map[string]*TreeEntry
|
||||
size int
|
||||
order []string
|
||||
cid cid.Cid
|
||||
rawData []byte
|
||||
rawDataOnce sync.Once
|
||||
}
|
||||
|
||||
type TreeEntry struct {
|
||||
name string
|
||||
Mode string `json:"mode"`
|
||||
Hash cid.Cid `json:"hash"`
|
||||
}
|
||||
|
||||
func (t *Tree) Cid() cid.Cid {
|
||||
return t.cid
|
||||
}
|
||||
|
||||
func (t *Tree) String() string {
|
||||
return "[git tree object]"
|
||||
}
|
||||
|
||||
func (t *Tree) GitSha() []byte {
|
||||
return cidToSha(t.cid)
|
||||
}
|
||||
|
||||
func (t *Tree) Copy() node.Node {
|
||||
out := &Tree{
|
||||
entries: make(map[string]*TreeEntry),
|
||||
cid: t.cid,
|
||||
size: t.size,
|
||||
order: t.order, // TODO: make a deep copy of this
|
||||
}
|
||||
|
||||
for k, v := range t.entries {
|
||||
nv := *v
|
||||
out.entries[k] = &nv
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (t *Tree) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(t.entries)
|
||||
}
|
||||
|
||||
func (t *Tree) Tree(p string, depth int) []string {
|
||||
if p != "" {
|
||||
_, ok := t.entries[p]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return []string{"mode", "type", "hash"}
|
||||
}
|
||||
|
||||
if depth == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if depth == 1 {
|
||||
return t.order
|
||||
}
|
||||
|
||||
var out []string
|
||||
for k, _ := range t.entries {
|
||||
out = append(out, k, k+"/mode", k+"/type", k+"/hash")
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (t *Tree) Links() []*node.Link {
|
||||
var out []*node.Link
|
||||
for _, v := range t.entries {
|
||||
out = append(out, &node.Link{Cid: v.Hash})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (t *Tree) Loggable() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"type": "git tree object",
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tree) RawData() []byte {
|
||||
t.rawDataOnce.Do(func() {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
fmt.Fprintf(buf, "tree %d\x00", t.size)
|
||||
for _, s := range t.order {
|
||||
t.entries[s].WriteTo(buf)
|
||||
}
|
||||
t.rawData = buf.Bytes()
|
||||
})
|
||||
|
||||
return t.rawData
|
||||
}
|
||||
|
||||
func (t *Tree) Resolve(p []string) (interface{}, []string, error) {
|
||||
e, ok := t.entries[p[0]]
|
||||
if !ok {
|
||||
return nil, nil, errors.New("no such link")
|
||||
}
|
||||
|
||||
if len(p) == 1 {
|
||||
return e, nil, nil
|
||||
}
|
||||
|
||||
switch p[1] {
|
||||
case "hash":
|
||||
return &node.Link{Cid: e.Hash}, p[2:], nil
|
||||
case "mode":
|
||||
return e.Mode, p[2:], nil
|
||||
default:
|
||||
return nil, nil, errors.New("no such link")
|
||||
}
|
||||
}
|
||||
|
||||
func (t Tree) ResolveLink(path []string) (*node.Link, []string, error) {
|
||||
out, rest, err := t.Resolve(path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
lnk, ok := out.(*node.Link)
|
||||
if !ok {
|
||||
return nil, nil, errors.New("not a link")
|
||||
}
|
||||
|
||||
return lnk, rest, nil
|
||||
}
|
||||
|
||||
func (t *Tree) Size() (uint64, error) {
|
||||
return uint64(len(t.RawData())), nil
|
||||
}
|
||||
|
||||
func (t *Tree) Stat() (*node.NodeStat, error) {
|
||||
return &node.NodeStat{}, nil
|
||||
}
|
||||
|
||||
func (te *TreeEntry) WriteTo(w io.Writer) (int, error) {
|
||||
n, err := fmt.Fprintf(w, "%s %s\x00", te.Mode, te.name)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
nn, err := w.Write(cidToSha(te.Hash))
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
return n + nn, nil
|
||||
}
|
||||
|
||||
var _ node.Node = (*Tree)(nil)
|
||||
Reference in New Issue
Block a user