refactor object patch command to work more betterer
License: MIT Signed-off-by: Jeromy <jeromyj@gmail.com>
Jeromy committed
Jan 3, 2016 at 11:34 UTC
d4ffc9454fcf9820d69e880c4c3976c2bd962d1a
4 files changed
+330
-254
core/commands/object/object.go
renamed
+14
-247
@@ -1,4 +1,4 @@
1
-package commands
1
+package objectcmd
2
3
import (
4
"bytes"
@@ -13,14 +13,11 @@ import (
13
14
mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
15
16
- key "github.com/ipfs/go-ipfs/blocks/key"
16
cmds "github.com/ipfs/go-ipfs/commands"
17
core "github.com/ipfs/go-ipfs/core"
18
dag "github.com/ipfs/go-ipfs/merkledag"
20
- dagutils "github.com/ipfs/go-ipfs/merkledag/utils"
19
path "github.com/ipfs/go-ipfs/path"
20
ft "github.com/ipfs/go-ipfs/unixfs"
23
- u "github.com/ipfs/go-ipfs/util"
21
)
22
23
// ErrObjectTooLarge is returned when too much data was read from stdin. current limit 512k
@@ -61,17 +58,17 @@ ipfs object patch <args> - Create new object from old ones
58
},
59
60
Subcommands: map[string]*cmds.Command{
64
- "data": objectDataCmd,
65
- "links": objectLinksCmd,
66
- "get": objectGetCmd,
67
- "put": objectPutCmd,
68
- "stat": objectStatCmd,
69
- "new": objectNewCmd,
70
- "patch": objectPatchCmd,
61
+ "data": ObjectDataCmd,
62
+ "links": ObjectLinksCmd,
63
+ "get": ObjectGetCmd,
64
+ "put": ObjectPutCmd,
65
+ "stat": ObjectStatCmd,
66
+ "new": ObjectNewCmd,
67
+ "patch": ObjectPatchCmd,
68
},
69
}
70
74
-var objectDataCmd = &cmds.Command{
71
+var ObjectDataCmd = &cmds.Command{
72
Helptext: cmds.HelpText{
73
Tagline: "Outputs the raw bytes in an IPFS object",
74
ShortDescription: `
@@ -109,7 +106,7 @@ output is the raw data of the object.
106
},
107
}
108
112
-var objectLinksCmd = &cmds.Command{
109
+var ObjectLinksCmd = &cmds.Command{
110
Helptext: cmds.HelpText{
111
Tagline: "Outputs the links pointed to by the specified object",
112
ShortDescription: `
@@ -158,7 +155,7 @@ multihash.
155
Type: Object{},
156
}
157
161
-var objectGetCmd = &cmds.Command{
158
+var ObjectGetCmd = &cmds.Command{
159
Helptext: cmds.HelpText{
160
Tagline: "Get and serialize the DAG node named by <key>",
161
ShortDescription: `
@@ -229,7 +226,7 @@ This command outputs data in the following encodings:
226
},
227
}
228
232
-var objectStatCmd = &cmds.Command{
229
+var ObjectStatCmd = &cmds.Command{
230
Helptext: cmds.HelpText{
231
Tagline: "Get stats for the DAG node named by <key>",
232
ShortDescription: `
@@ -290,7 +287,7 @@ var objectStatCmd = &cmds.Command{
287
},
288
}
289
293
-var objectPutCmd = &cmds.Command{
290
+var ObjectPutCmd = &cmds.Command{
291
Helptext: cmds.HelpText{
292
Tagline: "Stores input as a DAG object, outputs its key",
293
ShortDescription: `
@@ -377,7 +374,7 @@ and then run
374
Type: Object{},
375
}
376
380
-var objectNewCmd = &cmds.Command{
377
+var ObjectNewCmd = &cmds.Command{
378
Helptext: cmds.HelpText{
379
Tagline: "creates a new object from an ipfs template",
380
ShortDescription: `
@@ -430,235 +427,6 @@ Available templates:
427
Type: Object{},
428
}
429
433
-var objectPatchCmd = &cmds.Command{
434
- Helptext: cmds.HelpText{
435
- Tagline: "Create a new merkledag object based on an existing one",
436
- ShortDescription: `
437
-'ipfs object patch <root> <cmd> <args>' is a plumbing command used to
438
-build custom DAG objects. It adds and removes links from objects, creating a new
439
-object as a result. This is the merkle-dag version of modifying an object. It
440
-can also set the data inside a node with 'set-data' and append to that data as
441
-well with 'append-data'.
442
-
443
-Patch commands:
444
- add-link <name> <ref> - adds a link to a node
445
- rm-link <name> - removes a link from a node
446
- set-data - sets a nodes data from stdin
447
- append-data - appends to a nodes data from stdin
448
-
449
-Examples:
450
-
451
- EMPTY_DIR=$(ipfs object new unixfs-dir)
452
- BAR=$(echo "bar" | ipfs add -q)
453
- ipfs object patch $EMPTY_DIR add-link foo $BAR
454
-
455
-This takes an empty directory, and adds a link named foo under it, pointing to
456
-a file containing 'bar', and returns the hash of the new object.
457
-
458
- ipfs object patch $FOO_BAR rm-link foo
459
-
460
-This removes the link named foo from the hash in $FOO_BAR and returns the
461
-resulting object hash.
462
-
463
-The data inside the node can be modified as well:
464
-
465
- ipfs object patch $FOO_BAR set-data < file.dat
466
- ipfs object patch $FOO_BAR append-data < file.dat
467
-
468
-`,
469
- },
470
- Options: []cmds.Option{
471
- cmds.BoolOption("create", "p", "create intermediate directories on add-link"),
472
- },
473
- Arguments: []cmds.Argument{
474
- cmds.StringArg("root", true, false, "the hash of the node to modify"),
475
- cmds.StringArg("command", true, false, "the operation to perform"),
476
- cmds.StringArg("args", true, true, "extra arguments").EnableStdin(),
477
- },
478
- Type: Object{},
479
- Run: func(req cmds.Request, res cmds.Response) {
480
- nd, err := req.InvocContext().GetNode()
481
- if err != nil {
482
- res.SetError(err, cmds.ErrNormal)
483
- return
484
- }
485
-
486
- rootarg := req.Arguments()[0]
487
- if strings.HasPrefix(rootarg, "/ipfs/") {
488
- rootarg = rootarg[6:]
489
- }
490
- rhash := key.B58KeyDecode(rootarg)
491
- if rhash == "" {
492
- res.SetError(fmt.Errorf("incorrectly formatted root hash: %s", req.Arguments()[0]), cmds.ErrNormal)
493
- return
494
- }
495
-
496
- rnode, err := nd.DAG.Get(req.Context(), rhash)
497
- if err != nil {
498
- res.SetError(err, cmds.ErrNormal)
499
- return
500
- }
501
-
502
- action := req.Arguments()[1]
503
-
504
- switch action {
505
- case "add-link":
506
- k, err := addLinkCaller(req, rnode)
507
- if err != nil {
508
- res.SetError(err, cmds.ErrNormal)
509
- return
510
- }
511
- res.SetOutput(&Object{Hash: k.B58String()})
512
- case "rm-link":
513
- k, err := rmLinkCaller(req, rnode)
514
- if err != nil {
515
- res.SetError(err, cmds.ErrNormal)
516
- return
517
- }
518
- res.SetOutput(&Object{Hash: k.B58String()})
519
- case "set-data":
520
- k, err := setDataCaller(req, rnode)
521
- if err != nil {
522
- res.SetError(err, cmds.ErrNormal)
523
- return
524
- }
525
- res.SetOutput(&Object{Hash: k.B58String()})
526
- case "append-data":
527
- k, err := appendDataCaller(req, rnode)
528
- if err != nil {
529
- res.SetError(err, cmds.ErrNormal)
530
- return
531
- }
532
- res.SetOutput(&Object{Hash: k.B58String()})
533
- default:
534
- res.SetError(fmt.Errorf("unrecognized subcommand"), cmds.ErrNormal)
535
- return
536
- }
537
- },
538
- Marshalers: cmds.MarshalerMap{
539
- cmds.Text: func(res cmds.Response) (io.Reader, error) {
540
- o, ok := res.Output().(*Object)
541
- if !ok {
542
- return nil, u.ErrCast()
543
- }
544
-
545
- return strings.NewReader(o.Hash + "\n"), nil
546
- },
547
- },
548
-}
549
-
550
-func appendDataCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
551
- if len(req.Arguments()) < 3 {
552
- return "", fmt.Errorf("not enough arguments for set-data")
553
- }
554
-
555
- nd, err := req.InvocContext().GetNode()
556
- if err != nil {
557
- return "", err
558
- }
559
-
560
- root.Data = append(root.Data, []byte(req.Arguments()[2])...)
561
-
562
- newkey, err := nd.DAG.Add(root)
563
- if err != nil {
564
- return "", err
565
- }
566
-
567
- return newkey, nil
568
-}
569
-
570
-func setDataCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
571
- if len(req.Arguments()) < 3 {
572
- return "", fmt.Errorf("not enough arguments for set-data")
573
- }
574
-
575
- nd, err := req.InvocContext().GetNode()
576
- if err != nil {
577
- return "", err
578
- }
579
-
580
- root.Data = []byte(req.Arguments()[2])
581
-
582
- newkey, err := nd.DAG.Add(root)
583
- if err != nil {
584
- return "", err
585
- }
586
-
587
- return newkey, nil
588
-}
589
-
590
-func rmLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
591
- if len(req.Arguments()) < 3 {
592
- return "", fmt.Errorf("not enough arguments for rm-link")
593
- }
594
-
595
- nd, err := req.InvocContext().GetNode()
596
- if err != nil {
597
- return "", err
598
- }
599
-
600
- path := req.Arguments()[2]
601
-
602
- e := dagutils.NewDagEditor(root, nd.DAG)
603
-
604
- err = e.RmLink(req.Context(), path)
605
- if err != nil {
606
- return "", err
607
- }
608
-
609
- nnode, err := e.Finalize(nd.DAG)
610
- if err != nil {
611
- return "", err
612
- }
613
-
614
- return nnode.Key()
615
-}
616
-
617
-func addLinkCaller(req cmds.Request, root *dag.Node) (key.Key, error) {
618
- if len(req.Arguments()) < 4 {
619
- return "", fmt.Errorf("not enough arguments for add-link")
620
- }
621
-
622
- nd, err := req.InvocContext().GetNode()
623
- if err != nil {
624
- return "", err
625
- }
626
-
627
- path := req.Arguments()[2]
628
- childk := key.B58KeyDecode(req.Arguments()[3])
629
-
630
- create, _, err := req.Option("create").Bool()
631
- if err != nil {
632
- return "", err
633
- }
634
-
635
- var createfunc func() *dag.Node
636
- if create {
637
- createfunc = func() *dag.Node {
638
- return &dag.Node{Data: ft.FolderPBData()}
639
- }
640
- }
641
-
642
- e := dagutils.NewDagEditor(root, nd.DAG)
643
-
644
- childnd, err := nd.DAG.Get(req.Context(), childk)
645
- if err != nil {
646
- return "", err
647
- }
648
-
649
- err = e.InsertNodeAtPath(req.Context(), path, childnd, createfunc)
650
- if err != nil {
651
- return "", err
652
- }
653
-
654
- nnode, err := e.Finalize(nd.DAG)
655
- if err != nil {
656
- return "", err
657
- }
658
-
659
- return nnode.Key()
660
-}
661
-
430
func nodeFromTemplate(template string) (*dag.Node, error) {
431
switch template {
432
case "unixfs-dir":
@@ -757,7 +525,6 @@ func getObjectEnc(o interface{}) objectEncoding {
525
v, ok := o.(string)
526
if !ok {
527
// chosen as default because it's human readable
760
- log.Warning("option is not a string - falling back to json")
528
return objectEncodingJSON
529
}
530
core/commands/object/patch.go
new
+308
@@ -0,0 +1,308 @@
1
+package objectcmd
2
+
3
+import (
4
+ "io"
5
+ "io/ioutil"
6
+ "strings"
7
+
8
+ key "github.com/ipfs/go-ipfs/blocks/key"
9
+ cmds "github.com/ipfs/go-ipfs/commands"
10
+ core "github.com/ipfs/go-ipfs/core"
11
+ dag "github.com/ipfs/go-ipfs/merkledag"
12
+ dagutils "github.com/ipfs/go-ipfs/merkledag/utils"
13
+ path "github.com/ipfs/go-ipfs/path"
14
+ ft "github.com/ipfs/go-ipfs/unixfs"
15
+ u "github.com/ipfs/go-ipfs/util"
16
+)
17
+
18
+var ObjectPatchCmd = &cmds.Command{
19
+ Helptext: cmds.HelpText{
20
+ Tagline: "Create a new merkledag object based on an existing one",
21
+ ShortDescription: `
22
+'ipfs object patch <root> <cmd> <args>' is a plumbing command used to
23
+build custom DAG objects. It adds and removes links from objects, creating a new
24
+object as a result. This is the merkle-dag version of modifying an object. It
25
+can also set the data inside a node with 'set-data' and append to that data as
26
+well with 'append-data'.
27
+
28
+Patch commands:
29
+ add-link <name> <ref> - adds a link to a node
30
+ rm-link <name> - removes a link from a node
31
+ set-data - sets a nodes data from stdin
32
+ append-data - appends to a nodes data from stdin
33
+
34
+
35
+
36
+ ipfs object patch $FOO_BAR rm-link foo
37
+
38
+This removes the link named foo from the hash in $FOO_BAR and returns the
39
+resulting object hash.
40
+
41
+The data inside the node can be modified as well:
42
+
43
+ ipfs object patch $FOO_BAR set-data < file.dat
44
+ ipfs object patch $FOO_BAR append-data < file.dat
45
+
46
+`,
47
+ },
48
+ Arguments: []cmds.Argument{},
49
+ Subcommands: map[string]*cmds.Command{
50
+ "append-data": patchAppendDataCmd,
51
+ "add-link": patchAddLinkCmd,
52
+ "rm-link": patchRmLinkCmd,
53
+ "set-data": patchSetDataCmd,
54
+ },
55
+}
56
+
57
+func objectMarshaler(res cmds.Response) (io.Reader, error) {
58
+ o, ok := res.Output().(*Object)
59
+ if !ok {
60
+ return nil, u.ErrCast()
61
+ }
62
+
63
+ return strings.NewReader(o.Hash + "\n"), nil
64
+}
65
+
66
+var patchAppendDataCmd = &cmds.Command{
67
+ Helptext: cmds.HelpText{
68
+ Tagline: "Append data to the data segment of a dag node",
69
+ ShortDescription: `
70
+ `,
71
+ },
72
+ Arguments: []cmds.Argument{
73
+ cmds.StringArg("root", true, false, "the hash of the node to modify"),
74
+ cmds.FileArg("data", true, false, "data to append").EnableStdin(),
75
+ },
76
+ Run: func(req cmds.Request, res cmds.Response) {
77
+ nd, err := req.InvocContext().GetNode()
78
+ if err != nil {
79
+ res.SetError(err, cmds.ErrNormal)
80
+ return
81
+ }
82
+
83
+ root, err := path.ParsePath(req.Arguments()[0])
84
+ if err != nil {
85
+ res.SetError(err, cmds.ErrNormal)
86
+ return
87
+ }
88
+
89
+ rootnd, err := core.Resolve(req.Context(), nd, root)
90
+ if err != nil {
91
+ res.SetError(err, cmds.ErrNormal)
92
+ return
93
+ }
94
+
95
+ data, err := ioutil.ReadAll(req.Files())
96
+ if err != nil {
97
+ res.SetError(err, cmds.ErrNormal)
98
+ return
99
+ }
100
+
101
+ rootnd.Data = append(rootnd.Data, data...)
102
+
103
+ newkey, err := nd.DAG.Add(rootnd)
104
+ if err != nil {
105
+ res.SetError(err, cmds.ErrNormal)
106
+ return
107
+ }
108
+
109
+ res.SetOutput(&Object{Hash: newkey.B58String()})
110
+ },
111
+ Type: Object{},
112
+ Marshalers: cmds.MarshalerMap{
113
+ cmds.Text: objectMarshaler,
114
+ },
115
+}
116
+
117
+var patchSetDataCmd = &cmds.Command{
118
+ Helptext: cmds.HelpText{},
119
+ Arguments: []cmds.Argument{
120
+ cmds.StringArg("root", true, false, "the hash of the node to modify"),
121
+ cmds.FileArg("data", true, false, "data fill with").EnableStdin(),
122
+ },
123
+ Run: func(req cmds.Request, res cmds.Response) {
124
+ nd, err := req.InvocContext().GetNode()
125
+ if err != nil {
126
+ res.SetError(err, cmds.ErrNormal)
127
+ return
128
+ }
129
+
130
+ rp, err := path.ParsePath(req.Arguments()[0])
131
+ if err != nil {
132
+ res.SetError(err, cmds.ErrNormal)
133
+ return
134
+ }
135
+
136
+ root, err := core.Resolve(req.Context(), nd, rp)
137
+ if err != nil {
138
+ res.SetError(err, cmds.ErrNormal)
139
+ return
140
+ }
141
+
142
+ data, err := ioutil.ReadAll(req.Files())
143
+ if err != nil {
144
+ res.SetError(err, cmds.ErrNormal)
145
+ return
146
+ }
147
+
148
+ root.Data = data
149
+
150
+ newkey, err := nd.DAG.Add(root)
151
+ if err != nil {
152
+ res.SetError(err, cmds.ErrNormal)
153
+ return
154
+ }
155
+
156
+ res.SetOutput(&Object{Hash: newkey.B58String()})
157
+ },
158
+ Type: Object{},
159
+ Marshalers: cmds.MarshalerMap{
160
+ cmds.Text: objectMarshaler,
161
+ },
162
+}
163
+
164
+var patchRmLinkCmd = &cmds.Command{
165
+ Helptext: cmds.HelpText{},
166
+ Arguments: []cmds.Argument{
167
+ cmds.StringArg("root", true, false, "the hash of the node to modify"),
168
+ cmds.StringArg("link", true, false, "name of the link to remove"),
169
+ },
170
+ Run: func(req cmds.Request, res cmds.Response) {
171
+ nd, err := req.InvocContext().GetNode()
172
+ if err != nil {
173
+ res.SetError(err, cmds.ErrNormal)
174
+ return
175
+ }
176
+
177
+ rootp, err := path.ParsePath(req.Arguments()[0])
178
+ if err != nil {
179
+ res.SetError(err, cmds.ErrNormal)
180
+ return
181
+ }
182
+
183
+ root, err := core.Resolve(req.Context(), nd, rootp)
184
+ if err != nil {
185
+ res.SetError(err, cmds.ErrNormal)
186
+ return
187
+ }
188
+
189
+ path := req.Arguments()[1]
190
+
191
+ e := dagutils.NewDagEditor(root, nd.DAG)
192
+
193
+ err = e.RmLink(req.Context(), path)
194
+ if err != nil {
195
+ res.SetError(err, cmds.ErrNormal)
196
+ return
197
+ }
198
+
199
+ nnode, err := e.Finalize(nd.DAG)
200
+ if err != nil {
201
+ res.SetError(err, cmds.ErrNormal)
202
+ return
203
+ }
204
+
205
+ nk, err := nnode.Key()
206
+ if err != nil {
207
+ res.SetError(err, cmds.ErrNormal)
208
+ return
209
+ }
210
+
211
+ res.SetOutput(&Object{Hash: nk.B58String()})
212
+ },
213
+ Type: Object{},
214
+ Marshalers: cmds.MarshalerMap{
215
+ cmds.Text: objectMarshaler,
216
+ },
217
+}
218
+
219
+var patchAddLinkCmd = &cmds.Command{
220
+ Helptext: cmds.HelpText{
221
+ Tagline: "add a link to a given object",
222
+ ShortDescription: `
223
+Examples:
224
+
225
+ EMPTY_DIR=$(ipfs object new unixfs-dir)
226
+ BAR=$(echo "bar" | ipfs add -q)
227
+ ipfs object patch $EMPTY_DIR add-link foo $BAR
228
+
229
+This takes an empty directory, and adds a link named foo under it, pointing to
230
+a file containing 'bar', and returns the hash of the new object.
231
+`,
232
+ },
233
+ Options: []cmds.Option{
234
+ cmds.BoolOption("p", "create", "create intermediary nodes"),
235
+ },
236
+ Arguments: []cmds.Argument{
237
+ cmds.StringArg("root", true, false, "the hash of the node to modify"),
238
+ cmds.StringArg("name", true, false, "name of link to create"),
239
+ cmds.StringArg("ref", true, false, "ipfs object to add link to"),
240
+ },
241
+ Run: func(req cmds.Request, res cmds.Response) {
242
+ nd, err := req.InvocContext().GetNode()
243
+ if err != nil {
244
+ res.SetError(err, cmds.ErrNormal)
245
+ return
246
+ }
247
+
248
+ rootp, err := path.ParsePath(req.Arguments()[0])
249
+ if err != nil {
250
+ res.SetError(err, cmds.ErrNormal)
251
+ return
252
+ }
253
+
254
+ root, err := core.Resolve(req.Context(), nd, rootp)
255
+ if err != nil {
256
+ res.SetError(err, cmds.ErrNormal)
257
+ return
258
+ }
259
+
260
+ path := req.Arguments()[1]
261
+ childk := key.B58KeyDecode(req.Arguments()[2])
262
+
263
+ create, _, err := req.Option("create").Bool()
264
+ if err != nil {
265
+ res.SetError(err, cmds.ErrNormal)
266
+ return
267
+ }
268
+
269
+ var createfunc func() *dag.Node
270
+ if create {
271
+ createfunc = func() *dag.Node {
272
+ return &dag.Node{Data: ft.FolderPBData()}
273
+ }
274
+ }
275
+
276
+ e := dagutils.NewDagEditor(root, nd.DAG)
277
+
278
+ childnd, err := nd.DAG.Get(req.Context(), childk)
279
+ if err != nil {
280
+ res.SetError(err, cmds.ErrNormal)
281
+ return
282
+ }
283
+
284
+ err = e.InsertNodeAtPath(req.Context(), path, childnd, createfunc)
285
+ if err != nil {
286
+ res.SetError(err, cmds.ErrNormal)
287
+ return
288
+ }
289
+
290
+ nnode, err := e.Finalize(nd.DAG)
291
+ if err != nil {
292
+ res.SetError(err, cmds.ErrNormal)
293
+ return
294
+ }
295
+
296
+ nk, err := nnode.Key()
297
+ if err != nil {
298
+ res.SetError(err, cmds.ErrNormal)
299
+ return
300
+ }
301
+
302
+ res.SetOutput(&Object{Hash: nk.B58String()})
303
+ },
304
+ Type: Object{},
305
+ Marshalers: cmds.MarshalerMap{
306
+ cmds.Text: objectMarshaler,
307
+ },
308
+}
core/commands/root.go
+7
-6
@@ -6,6 +6,7 @@ import (
6
7
cmds "github.com/ipfs/go-ipfs/commands"
8
files "github.com/ipfs/go-ipfs/core/commands/files"
9
+ ocmd "github.com/ipfs/go-ipfs/core/commands/object"
10
unixfs "github.com/ipfs/go-ipfs/core/commands/unixfs"
11
logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log"
12
)
@@ -107,7 +108,7 @@ var rootSubcommands = map[string]*cmds.Command{
108
"ls": LsCmd,
109
"mount": MountCmd,
110
"name": NameCmd,
110
- "object": ObjectCmd,
111
+ "object": ocmd.ObjectCmd,
112
"pin": PinCmd,
113
"ping": PingCmd,
114
"refs": RefsCmd,
@@ -148,11 +149,11 @@ var rootROSubcommands = map[string]*cmds.Command{
149
},
150
"object": &cmds.Command{
151
Subcommands: map[string]*cmds.Command{
151
- "data": objectDataCmd,
152
- "links": objectLinksCmd,
153
- "get": objectGetCmd,
154
- "stat": objectStatCmd,
155
- "patch": objectPatchCmd,
152
+ "data": ocmd.ObjectDataCmd,
153
+ "links": ocmd.ObjectLinksCmd,
154
+ "get": ocmd.ObjectGetCmd,
155
+ "stat": ocmd.ObjectStatCmd,
156
+ "patch": ocmd.ObjectPatchCmd,
157
},
158
},
159
"refs": RefsROCmd,
test/sharness/t0051-object.sh
+1
-1
@@ -16,7 +16,7 @@ test_patch_create_path() {
16
target=$3
17
18
test_expect_success "object patch --create works" '
19
- PCOUT=$(ipfs object patch --create $root add-link $name $target)
19
+ PCOUT=$(ipfs object patch $root add-link --create $name $target)
20
'
21
22
test_expect_success "output looks good" '