mirror of https://github.com/go-gitea/gitea.git
150 lines
3.3 KiB
Go
150 lines
3.3 KiB
Go
|
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
|
||
|
}
|