1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
package merkletrie
import (
"errors"
"fmt"
"io"
"github.com/go-git/go-git/v5/utils/merkletrie/noder"
)
var (
ErrEmptyFileName = errors.New("empty filename in tree entry")
)
// 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.String() == "" {
return ErrEmptyFileName
}
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
}
|