@cryptotaxi247 / kubo / commits / bf7da8522

basic implementation of object diff

License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>

Jeromy committed Apr 14, 2016 at 13:06 UTC bf7da8522cf05df21c77a6b3d2c5e0c5ad560069
4 files changed +192 -15
core/commands/object/diff.go new
+85
@@ -0,0 +1,85 @@
1 +package objectcmd
2 +
3 +import (
4 + "bytes"
5 + "fmt"
6 + "io"
7 +
8 + cmds "github.com/ipfs/go-ipfs/commands"
9 + dagutils "github.com/ipfs/go-ipfs/merkledag/utils"
10 + path "github.com/ipfs/go-ipfs/path"
11 +)
12 +
13 +var ObjectDiffCmd = &cmds.Command{
14 + Helptext: cmds.HelpText{
15 + Tagline: "takes a diff of the two given objects",
16 + ShortDescription: `
17 +`,
18 + },
19 + Arguments: []cmds.Argument{
20 + cmds.StringArg("obj_a", true, false, "object to diff against"),
21 + cmds.StringArg("obj_b", true, false, "object to diff"),
22 + },
23 + Run: func(req cmds.Request, res cmds.Response) {
24 + node, err := req.InvocContext().GetNode()
25 + if err != nil {
26 + res.SetError(err, cmds.ErrNormal)
27 + return
28 + }
29 +
30 + a := req.Arguments()[0]
31 + b := req.Arguments()[1]
32 +
33 + pa, err := path.ParsePath(a)
34 + if err != nil {
35 + res.SetError(err, cmds.ErrNormal)
36 + return
37 + }
38 +
39 + pb, err := path.ParsePath(b)
40 + if err != nil {
41 + res.SetError(err, cmds.ErrNormal)
42 + return
43 + }
44 +
45 + ctx := req.Context()
46 +
47 + obj_a, err := node.Resolver.ResolvePath(ctx, pa)
48 + if err != nil {
49 + res.SetError(err, cmds.ErrNormal)
50 + return
51 + }
52 +
53 + obj_b, err := node.Resolver.ResolvePath(ctx, pb)
54 + if err != nil {
55 + res.SetError(err, cmds.ErrNormal)
56 + return
57 + }
58 +
59 + changes, err := dagutils.Diff(ctx, node.DAG, obj_a, obj_b)
60 + if err != nil {
61 + res.SetError(err, cmds.ErrNormal)
62 + return
63 + }
64 +
65 + res.SetOutput(changes)
66 + },
67 + Type: []*dagutils.Change{},
68 + Marshalers: cmds.MarshalerMap{
69 + cmds.Text: func(res cmds.Response) (io.Reader, error) {
70 + changes := res.Output().([]*dagutils.Change)
71 + buf := new(bytes.Buffer)
72 + for _, change := range changes {
73 + switch change.Type {
74 + case dagutils.Add:
75 + fmt.Fprintf(buf, "added new link %q pointing to %s\n", change.Path, change.After)
76 + case dagutils.Mod:
77 + fmt.Fprintf(buf, "changed %q from %s to %s\n", change.Path, change.Before, change.After)
78 + case dagutils.Remove:
79 + fmt.Fprintf(buf, "removed link %q (was %s)\n", change.Path, change.Before)
80 + }
81 + }
82 + return buf, nil
83 + },
84 + },
85 +}
core/commands/object/object.go
+9 -7
@@ -48,13 +48,14 @@ var ObjectCmd = &cmds.Command{
48 'ipfs object' is a plumbing command used to manipulate DAG objects
49 directly.`,
50 Synopsis: `
51 -ipfs object data <key> - Outputs raw bytes in an object
52 -ipfs object get <key> - Get the DAG node named by <key>
53 -ipfs object links <key> - Outputs links pointed to by object
54 -ipfs object new <template> - Create new ipfs objects
55 -ipfs object patch <args> - Create new object from old ones
56 -ipfs object put <data> - Stores input, outputs its key
57 -ipfs object stat <key> - Outputs statistics of object
51 +ipfs object data <key> - Outputs raw bytes in an object
52 +ipfs object get <key> - Get the DAG node named by <key>
53 +ipfs object links <key> - Outputs links pointed to by object
54 +ipfs object new <template> - Create new ipfs objects
55 +ipfs object patch <args> - Create new object from old ones
56 +ipfs object put <data> - Stores input, outputs its key
57 +ipfs object stat <key> - Outputs statistics of object
58 +ipfs object diff <key1> <key2> - Diffs two given objects
59 `,
60 },
61
@@ -66,6 +67,7 @@ ipfs object stat <key> - Outputs statistics of object
67 "patch": ObjectPatchCmd,
68 "put": ObjectPutCmd,
69 "stat": ObjectStatCmd,
70 + "diff": ObjectDiffCmd,
71 },
72 }
73
merkledag/utils/diff.go
+27 -8
@@ -75,17 +75,25 @@ func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Cha
75 return e.Finalize(ds)
76 }
77
78 -func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) []*Change {
78 +func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) ([]*Change, error) {
79 if len(a.Links) == 0 && len(b.Links) == 0 {
80 - ak, _ := a.Key()
81 - bk, _ := b.Key()
80 + ak, err := a.Key()
81 + if err != nil {
82 + return nil, err
83 + }
84 +
85 + bk, err := b.Key()
86 + if err != nil {
87 + return nil, err
88 + }
89 +
90 return []*Change{
91 &Change{
92 Type: Mod,
93 Before: ak,
94 After: bk,
95 },
88 - }
96 + }, nil
97 }
98
99 var out []*Change
@@ -99,9 +107,20 @@ func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) []*Change {
107 if bytes.Equal(l.Hash, lnk.Hash) {
108 // no change... ignore it
109 } else {
102 - anode, _ := lnk.GetNode(ctx, ds)
103 - bnode, _ := l.GetNode(ctx, ds)
104 - sub := Diff(ctx, ds, anode, bnode)
110 + anode, err := lnk.GetNode(ctx, ds)
111 + if err != nil {
112 + return nil, err
113 + }
114 +
115 + bnode, err := l.GetNode(ctx, ds)
116 + if err != nil {
117 + return nil, err
118 + }
119 +
120 + sub, err := Diff(ctx, ds, anode, bnode)
121 + if err != nil {
122 + return nil, err
123 + }
124
125 for _, subc := range sub {
126 subc.Path = path.Join(lnk.Name, subc.Path)
@@ -128,7 +147,7 @@ func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) []*Change {
147 })
148 }
149
131 - return out
150 + return out, nil
151 }
152
153 type Conflict struct {
test/sharness/t0052-object-diff.sh new
+71
@@ -0,0 +1,71 @@
1 +#!/bin/sh
2 +#
3 +# Copyright (c) 2016 Jeromy Johnson
4 +# MIT Licensed; see the LICENSE file in this repository.
5 +#
6 +
7 +test_description="Test object diff command"
8 +
9 +. lib/test-lib.sh
10 +
11 +test_init_ipfs
12 +
13 +test_expect_success "create some objects for testing diffs" '
14 + mkdir foo &&
15 + echo "stuff" > foo/bar &&
16 + mkdir foo/baz &&
17 + A=$(ipfs add -r -q foo | tail -n1) &&
18 + echo "more things" > foo/cat &&
19 + B=$(ipfs add -r -q foo | tail -n1) &&
20 + echo "nested" > foo/baz/dog &&
21 + C=$(ipfs add -r -q foo | tail -n1)
22 + echo "changed" > foo/bar &&
23 + D=$(ipfs add -r -q foo | tail -n1)
24 +'
25 +
26 +test_expect_success "diff against self is empty" '
27 + ipfs object diff $A $A > diff_out
28 +'
29 +
30 +test_expect_success "identity diff output looks good" '
31 + printf "" > diff_exp &&
32 + test_cmp diff_exp diff_out
33 +'
34 +
35 +test_expect_success "diff added link works" '
36 + ipfs object diff $A $B > diff_out
37 +'
38 +
39 +test_expect_success "diff added link looks right" '
40 + echo added new link \"cat\" pointing to QmUSvcqzhdfYM1KLDbM76eLPdS9ANFtkJvFuPYeZt73d7A > diff_exp &&
41 + test_cmp diff_exp diff_out
42 +'
43 +
44 +test_expect_success "diff removed link works" '
45 + ipfs object diff $B $A > diff_out
46 +'
47 +
48 +test_expect_success "diff removed link looks right" '
49 + echo removed link \"cat\" \(was QmUSvcqzhdfYM1KLDbM76eLPdS9ANFtkJvFuPYeZt73d7A\) > diff_exp &&
50 + test_cmp diff_exp diff_out
51 +'
52 +
53 +test_expect_success "diff nested add works" '
54 + ipfs object diff $B $C > diff_out
55 +'
56 +
57 +test_expect_success "diff looks right" '
58 + echo added new link \"baz/dog\" pointing to QmdNJQUTZuDpsUcec7YDuCfRfvw1w4J13DCm7YcU4VMZdS > diff_exp &&
59 + test_cmp diff_exp diff_out
60 +'
61 +
62 +test_expect_success "diff changed link works" '
63 + ipfs object diff $C $D > diff_out
64 +'
65 +
66 +test_expect_success "diff looks right" '
67 + echo changed \"bar\" from QmNgd5cz2jNftnAHBhcRUGdtiaMzb5Rhjqd4etondHHST8 to QmRfFVsjSXkhFxrfWnLpMae2M4GBVsry6VAuYYcji5MiZb > diff_exp &&
68 + test_cmp diff_exp diff_out
69 +'
70 +
71 +test_done