Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge first: Diff trees #2

Merged
merged 20 commits into from
May 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions tar-read.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ func main() {
tarReader := tar.NewReader(f)
targetName := "manifest.json"
var m Manifest
var trees []*FileTree
var layerMap map[string]*FileTree
layerMap = make(map[string]*FileTree)
// var trees []*FileTree
for {
header, err := tarReader.Next()

Expand All @@ -54,12 +56,13 @@ func main() {
if strings.HasSuffix(name, "layer.tar") {
fmt.Println("Containing:")
tree := NewTree()
tree.name = strings.TrimSuffix(name, "layer.tar")
tree.name = name
fmt.Printf("%s\n", tree.name)
fileInfos := getFileList(tarReader, header)
for _, element := range fileInfos {
tree.AddPath(element.path, element)
tree.AddPath(element.path, &element)
}
trees = append(trees, tree)
layerMap[tree.name] = tree
}
default:
fmt.Printf("%s : %c %s %s\n",
Expand All @@ -70,8 +73,26 @@ func main() {
)
}
}
fmt.Printf("%+v\n", m)
var trees []*FileTree
trees = make([]*FileTree, 0)
for _, treeName := range m.Layers {
fmt.Printf("%s\n", treeName)
trees = append(trees, layerMap[treeName])
}

for ix := range trees {
if ix > 0 {
trees[0].Stack(trees[ix])
}
}
// fmt.Printf("%+v\n", m)
// fmt.Printf("%+v\n", layerMap)
fmt.Printf("%+v\n", trees)
// fmt.Printf("Tree 1 is: \n %+v\n", trees[1])
// fmt.Printf("Tree 2 is: \n %+v\n", trees[2])
// trees[1].Stack(trees[2])
// fmt.Printf("Tree 1 stacked with tree 2 is: \n %+v\n", trees[1])
// fmt.Printf("The whle stack is \n %+v \n", trees[0])
}

func getFileList(parentReader *tar.Reader, h *tar.Header) []FileChangeInfo {
Expand Down Expand Up @@ -136,6 +157,7 @@ func makeEntry(r *tar.Reader, h *tar.Header, path string) FileChangeInfo {
path: path,
typeflag: h.Typeflag,
md5sum: hash,
diffType: Unchanged,
}
}

Expand All @@ -145,6 +167,7 @@ type FileChangeInfo struct {
path string
typeflag byte
md5sum [16]byte
diffType DiffType
}

type Manifest struct {
Expand Down
16 changes: 9 additions & 7 deletions tree.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package main

import (
"errors"
"fmt"
"sort"
"strings"
"fmt"
"errors"
)

const (
Expand All @@ -18,7 +18,6 @@ const (
collapsedItem = "⊕ "
)


type FileTree struct {
root *Node
size int
Expand All @@ -43,9 +42,12 @@ func NewTree() (tree *FileTree) {
return tree
}

func NewNode(parent *Node, name string, data interface{}) (node *Node) {
func NewNode(parent *Node, name string, data *FileChangeInfo) (node *Node) {
node = new(Node)
node.name = name
if data == nil {
data = &FileChangeInfo{}
}
node.data = data
node.children = make(map[string]*Node)
node.parent = parent
Expand All @@ -57,7 +59,7 @@ func (tree *FileTree) Root() *Node {
return tree.root
}

func (node *Node) AddChild(name string, data interface{}) (child *Node) {
func (node *Node) AddChild(name string, data *FileChangeInfo) (child *Node) {
child = NewNode(node, name, data)
if node.children[name] != nil {
// tree node already exists, replace the payload, keep the children
Expand Down Expand Up @@ -190,7 +192,7 @@ func (node *Node) Path() string {
return "/" + strings.Join(path, "/")
}

func (tree *FileTree) Stack(upper *FileTree) (error) {
func (tree *FileTree) Stack(upper *FileTree) error {
graft := func(node *Node) error {
if node.IsWhiteout() {
err := tree.RemovePath(node.Path())
Expand Down Expand Up @@ -223,7 +225,7 @@ func (tree *FileTree) GetNode(path string) (*Node, error) {
return node, nil
}

func (tree *FileTree) AddPath(path string, data interface{}) (*Node, error) {
func (tree *FileTree) AddPath(path string, data *FileChangeInfo) (*Node, error) {
nodeNames := strings.Split(path, "/")
node := tree.Root()
for idx, name := range nodeNames {
Expand Down
161 changes: 161 additions & 0 deletions tree_compare.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package main

import (
"bytes"
"fmt"
)

func differ() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}

type DiffType int

// enum to show whether a file has changed
const (
Unchanged DiffType = iota
Changed
Added
Removed
)

func (d DiffType) String() string {
switch d {
case Unchanged:
return "Unchanged"
case Changed:
return "Changed"
case Added:
return "Added"
case Removed:
return "Removed"
default:
return fmt.Sprintf("%d", int(d))
}
}

func compareNodes(a *Node, b *Node) DiffType {
if a == nil && b == nil {
return Unchanged
}
// a is nil but not b
if a == nil && b != nil {
return Added
}

// b is nil but not a
if a != nil && b == nil {
return Removed
}

if b.IsWhiteout() {
return Removed
}
if a.name != b.name {
panic("comparing mismatched nodes")
}
// TODO: fails on nil

return getDiffType(a.data, b.data)
}

func getDiffType(a *FileChangeInfo, b *FileChangeInfo) DiffType {
if a == nil && b == nil {
return Unchanged
}
if a == nil || b == nil {
return Changed
}
if a.typeflag == b.typeflag {
if bytes.Compare(a.md5sum[:], b.md5sum[:]) == 0 {
return Unchanged
}
}
return Changed
}

func mergeDiffTypes(a DiffType, b DiffType) DiffType {
if a == b {
return a
}
return Changed
}

func (tree *FileTree) compareTo(upper *FileTree) error {

// TODO mark all as unchanged
markAllUnchanged := func(node *Node) error {
return node.AssignDiffType(Unchanged)
}
err := tree.Visit(markAllUnchanged)
if err != nil {
return err
}
graft := func(node *Node) error {
if node.IsWhiteout() {
err := tree.MarkRemoved(node.Path())
if err != nil {
return fmt.Errorf("Cannot remove node %s: %v", node.Path(), err.Error())
}
} else {
existingNode, _ := tree.GetNode(node.Path())
if existingNode == nil {
newNode, err := tree.AddPath(node.Path(), node.data)
fmt.Printf("added new node at %s\n", newNode.Path())
if err != nil {
return fmt.Errorf("Cannot add new node %s: %v", node.Path(), err.Error())
}
newNode.AssignDiffType(Added)
} else {
diffType := compareNodes(existingNode, node)
fmt.Printf("found existing node at %s\n", existingNode.Path())
existingNode.DiffTypeFromChildren(diffType)
}
}
return nil
}
return upper.Visit(graft)
}

// THE DIFF_TYPE OF A NODE IS ALWAYS THE DIFF_TYPE OF ITS ATTRIBUTES AND ITS CONTENTS
// THE CONTENTS ARE THE BYTES OF A FILE OR THE CHILDREN OF A DIRECTORY

func (tree *FileTree) MarkRemoved(path string) error {
node, err := tree.GetNode(path)
if err != nil {
return err
}
return node.AssignDiffType(Removed)
}

func (node *Node) IsLeaf() bool {
return len(node.children) == 0
}

func (node *Node) DiffTypeFromChildren(diffType DiffType) error {
if node.IsLeaf() {
node.AssignDiffType(diffType)
return nil
}
myDiffType := diffType

for _, v := range node.children {
vData := v.data
myDiffType = mergeDiffTypes(myDiffType, vData.diffType)

}
node.AssignDiffType(myDiffType)
return nil
}

func (node *Node) AssignDiffType(diffType DiffType) error {
if node.Path() == "/" {
return nil
}
node.data.diffType = diffType
return nil
}
Loading