github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/merkledag/utils/diff.go (about) 1 package dagutils 2 3 import ( 4 "bytes" 5 "fmt" 6 "path" 7 8 context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" 9 key "github.com/ipfs/go-ipfs/blocks/key" 10 dag "github.com/ipfs/go-ipfs/merkledag" 11 ) 12 13 const ( 14 Add = iota 15 Remove 16 Mod 17 ) 18 19 type Change struct { 20 Type int 21 Path string 22 Before key.Key 23 After key.Key 24 } 25 26 func (c *Change) String() string { 27 switch c.Type { 28 case Add: 29 return fmt.Sprintf("Added %s at %s", c.After.B58String()[:6], c.Path) 30 case Remove: 31 return fmt.Sprintf("Removed %s from %s", c.Before.B58String()[:6], c.Path) 32 case Mod: 33 return fmt.Sprintf("Changed %s to %s at %s", c.Before.B58String()[:6], c.After.B58String()[:6], c.Path) 34 default: 35 panic("nope") 36 } 37 } 38 39 func ApplyChange(ctx context.Context, ds dag.DAGService, nd *dag.Node, cs []*Change) (*dag.Node, error) { 40 e := NewDagEditor(ds, nd) 41 for _, c := range cs { 42 switch c.Type { 43 case Add: 44 child, err := ds.Get(ctx, c.After) 45 if err != nil { 46 return nil, err 47 } 48 err = e.InsertNodeAtPath(ctx, c.Path, child, nil) 49 if err != nil { 50 return nil, err 51 } 52 53 case Remove: 54 err := e.RmLink(ctx, c.Path) 55 if err != nil { 56 return nil, err 57 } 58 59 case Mod: 60 err := e.RmLink(ctx, c.Path) 61 if err != nil { 62 return nil, err 63 } 64 child, err := ds.Get(ctx, c.After) 65 if err != nil { 66 return nil, err 67 } 68 err = e.InsertNodeAtPath(ctx, c.Path, child, nil) 69 if err != nil { 70 return nil, err 71 } 72 } 73 } 74 return e.GetNode(), nil 75 } 76 77 func Diff(ctx context.Context, ds dag.DAGService, a, b *dag.Node) []*Change { 78 if len(a.Links) == 0 && len(b.Links) == 0 { 79 ak, _ := a.Key() 80 bk, _ := b.Key() 81 return []*Change{ 82 &Change{ 83 Type: Mod, 84 Before: ak, 85 After: bk, 86 }, 87 } 88 } 89 90 var out []*Change 91 clean_a := a.Copy() 92 clean_b := b.Copy() 93 94 // strip out unchanged stuff 95 for _, lnk := range a.Links { 96 l, err := b.GetNodeLink(lnk.Name) 97 if err == nil { 98 if bytes.Equal(l.Hash, lnk.Hash) { 99 // no change... ignore it 100 } else { 101 anode, _ := lnk.GetNode(ctx, ds) 102 bnode, _ := l.GetNode(ctx, ds) 103 sub := Diff(ctx, ds, anode, bnode) 104 105 for _, subc := range sub { 106 subc.Path = path.Join(lnk.Name, subc.Path) 107 out = append(out, subc) 108 } 109 } 110 clean_a.RemoveNodeLink(l.Name) 111 clean_b.RemoveNodeLink(l.Name) 112 } 113 } 114 115 for _, lnk := range clean_a.Links { 116 out = append(out, &Change{ 117 Type: Remove, 118 Path: lnk.Name, 119 Before: key.Key(lnk.Hash), 120 }) 121 } 122 for _, lnk := range clean_b.Links { 123 out = append(out, &Change{ 124 Type: Add, 125 Path: lnk.Name, 126 After: key.Key(lnk.Hash), 127 }) 128 } 129 130 return out 131 } 132 133 type Conflict struct { 134 A *Change 135 B *Change 136 } 137 138 func MergeDiffs(a, b []*Change) ([]*Change, []Conflict) { 139 var out []*Change 140 var conflicts []Conflict 141 paths := make(map[string]*Change) 142 for _, c := range a { 143 paths[c.Path] = c 144 } 145 146 for _, c := range b { 147 if ca, ok := paths[c.Path]; ok { 148 conflicts = append(conflicts, Conflict{ 149 A: ca, 150 B: c, 151 }) 152 } else { 153 out = append(out, c) 154 } 155 } 156 for _, c := range paths { 157 out = append(out, c) 158 } 159 return out, conflicts 160 }