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  }