blob: 0b50ca71d3facf2702473eeafb7239b10347c292 [file] [log] [blame]
package merkletrie
import (
"fmt"
"io"
"gopkg.in/src-d/go-git.v4/utils/merkletrie/noder"
)
// Action values represent the kind of things a Change can represent:
// insertion, deletions or modifications of files.
type Action int
// The set of possible actions in a change.
const (
_ Action = iota
Insert
Delete
Modify
)
// String returns the action as a human readable text.
func (a Action) String() string {
switch a {
case Insert:
return "Insert"
case Delete:
return "Delete"
case Modify:
return "Modify"
default:
panic(fmt.Sprintf("unsupported action: %d", a))
}
}
// A Change value represent how a noder has change between to merkletries.
type Change struct {
// The noder before the change or nil if it was inserted.
From noder.Path
// The noder after the change or nil if it was deleted.
To noder.Path
}
// Action is convenience method that returns what Action c represents.
func (c *Change) Action() (Action, error) {
if c.From == nil && c.To == nil {
return Action(0), fmt.Errorf("malformed change: nil from and to")
}
if c.From == nil {
return Insert, nil
}
if c.To == nil {
return Delete, nil
}
return Modify, nil
}
// NewInsert returns a new Change representing the insertion of n.
func NewInsert(n noder.Path) Change { return Change{To: n} }
// NewDelete returns a new Change representing the deletion of n.
func NewDelete(n noder.Path) Change { return Change{From: n} }
// NewModify returns a new Change representing that a has been modified and
// it is now b.
func NewModify(a, b noder.Path) Change {
return Change{
From: a,
To: b,
}
}
// String returns a single change in human readable form, using the
// format: '<' + action + space + path + '>'. The contents of the file
// before or after the change are not included in this format.
//
// Example: inserting a file at the path a/b/c.txt will return "<Insert
// a/b/c.txt>".
func (c Change) String() string {
action, err := c.Action()
if err != nil {
panic(err)
}
var path string
if action == Delete {
path = c.From.String()
} else {
path = c.To.String()
}
return fmt.Sprintf("<%s %s>", action, path)
}
// Changes is a list of changes between to merkletries.
type Changes []Change
// NewChanges returns an empty list of changes.
func NewChanges() Changes {
return Changes{}
}
// Add adds the change c to the list of changes.
func (l *Changes) Add(c Change) {
*l = append(*l, c)
}
// AddRecursiveInsert adds the required changes to insert all the
// file-like noders found in root, recursively.
func (l *Changes) AddRecursiveInsert(root noder.Path) error {
return l.addRecursive(root, NewInsert)
}
// AddRecursiveDelete adds the required changes to delete all the
// file-like noders found in root, recursively.
func (l *Changes) AddRecursiveDelete(root noder.Path) error {
return l.addRecursive(root, NewDelete)
}
type noderToChangeFn func(noder.Path) Change // NewInsert or NewDelete
func (l *Changes) addRecursive(root noder.Path, ctor noderToChangeFn) error {
if !root.IsDir() {
l.Add(ctor(root))
return nil
}
i, err := NewIterFromPath(root)
if err != nil {
return err
}
var current noder.Path
for {
if current, err = i.Step(); err != nil {
if err == io.EOF {
break
}
return err
}
if current.IsDir() {
continue
}
l.Add(ctor(current))
}
return nil
}