diff --git a/core/commands/object/diff.go b/core/commands/object/diff.go new file mode 100644 index 000000000..75dadc341 --- /dev/null +++ b/core/commands/object/diff.go @@ -0,0 +1,121 @@ +package objectcmd + +import ( + "bytes" + "fmt" + "io" + + cmds "github.com/ipfs/go-ipfs/commands" + dagutils "github.com/ipfs/go-ipfs/merkledag/utils" + path "github.com/ipfs/go-ipfs/path" +) + +var ObjectDiffCmd = &cmds.Command{ + Helptext: cmds.HelpText{ + Tagline: "takes a diff of the two given objects", + ShortDescription: ` +ipfs object diff is a command used to show the differences between +two ipfs objects.`, + LongDescription: ` +ipfs object diff is a command used to show the differences between +two ipfs objects. + +Example: + + $ ls foo + bar baz/ giraffe + $ ipfs add -r foo + ... + added QmegHcnrPgMwC7tBiMxChD54fgQMBUecNw9nE9UUU4x1bz foo + $ OBJ_A=QmegHcnrPgMwC7tBiMxChD54fgQMBUecNw9nE9UUU4x1bz + $ echo "different content" > foo/bar + $ ipfs add -r foo + ... + added QmcmRptkSPWhptCttgHg27QNDmnV33wAJyUkCnAvqD3eCD foo + $ OBJ_B=QmcmRptkSPWhptCttgHg27QNDmnV33wAJyUkCnAvqD3eCD + $ ipfs object diff -v $OBJ_A $OBJ_B + changed "bar" from QmNgd5cz2jNftnAHBhcRUGdtiaMzb5Rhjqd4etondHHST8 to QmRfFVsjSXkhFxrfWnLpMae2M4GBVsry6VAuYYcji5MiZb +`, + }, + Arguments: []cmds.Argument{ + cmds.StringArg("obj_a", true, false, "object to diff against"), + cmds.StringArg("obj_b", true, false, "object to diff"), + }, + Options: []cmds.Option{ + cmds.BoolOption("verbose", "v", "Produce verbose output"), + }, + Run: func(req cmds.Request, res cmds.Response) { + node, err := req.InvocContext().GetNode() + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + a := req.Arguments()[0] + b := req.Arguments()[1] + + pa, err := path.ParsePath(a) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + pb, err := path.ParsePath(b) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + ctx := req.Context() + + obj_a, err := node.Resolver.ResolvePath(ctx, pa) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + obj_b, err := node.Resolver.ResolvePath(ctx, pb) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + changes, err := dagutils.Diff(ctx, node.DAG, obj_a, obj_b) + if err != nil { + res.SetError(err, cmds.ErrNormal) + return + } + + res.SetOutput(changes) + }, + Type: []*dagutils.Change{}, + Marshalers: cmds.MarshalerMap{ + cmds.Text: func(res cmds.Response) (io.Reader, error) { + verbose, _, _ := res.Request().Option("v").Bool() + changes := res.Output().([]*dagutils.Change) + buf := new(bytes.Buffer) + for _, change := range changes { + if verbose { + switch change.Type { + case dagutils.Add: + fmt.Fprintf(buf, "added new link %q pointing to %s\n", change.Path, change.After) + case dagutils.Mod: + fmt.Fprintf(buf, "changed %q from %s to %s\n", change.Path, change.Before, change.After) + case dagutils.Remove: + fmt.Fprintf(buf, "removed link %q (was %s)\n", change.Path, change.Before) + } + } else { + switch change.Type { + case dagutils.Add: + fmt.Fprintf(buf, "+ %s %q\n", change.After, change.Path) + case dagutils.Mod: + fmt.Fprintf(buf, "~ %s %s %q\n", change.Before, change.After, change.Path) + case dagutils.Remove: + fmt.Fprintf(buf, "- %s %q\n", change.Before, change.Path) + } + } + } + return buf, nil + }, + }, +} diff --git a/core/commands/object/object.go b/core/commands/object/object.go index 68869119d..7ab8bf949 100644 --- a/core/commands/object/object.go +++ b/core/commands/object/object.go @@ -48,18 +48,20 @@ var ObjectCmd = &cmds.Command{ 'ipfs object' is a plumbing command used to manipulate DAG objects directly.`, Synopsis: ` -ipfs object data - Outputs raw bytes in an object -ipfs object get - Get the DAG node named by -ipfs object links - Outputs links pointed to by object -ipfs object new