Implement the core logic for comparing two archives (diff) and performing incremental updates (sync). - Introduces a new `borg diff` command to show differences between two collection archives. - Adds new `pkg/diff` and `pkg/sync` packages with corresponding business logic and unit tests. - The `diff` command supports reading compressed archives and prints a formatted summary of added, removed, and modified files. - The `sync` package includes `append`, `mirror`, and `update` strategies. Next steps involve integrating the sync logic into the `collect` commands. Co-authored-by: Snider <631881+Snider@users.noreply.github.com>
93 lines
1.8 KiB
Go
93 lines
1.8 KiB
Go
package diff
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"io/fs"
|
|
|
|
"github.com/Snider/Borg/pkg/datanode"
|
|
)
|
|
|
|
// Diff represents the differences between two DataNodes.
|
|
type Diff struct {
|
|
Added []string
|
|
Removed []string
|
|
Modified []string
|
|
}
|
|
|
|
// fileInfo stores content for comparison.
|
|
type fileInfo struct {
|
|
content []byte
|
|
}
|
|
|
|
// Compare compares two DataNodes and returns a Diff object.
|
|
func Compare(a, b *datanode.DataNode) (*Diff, error) {
|
|
diff := &Diff{}
|
|
filesA := make(map[string]fileInfo)
|
|
filesB := make(map[string]fileInfo)
|
|
|
|
// Walk through the first DataNode and collect file data
|
|
err := a.Walk(".", func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !d.IsDir() {
|
|
file, err := a.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
content, err := io.ReadAll(file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
filesA[path] = fileInfo{content: content}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Walk through the second DataNode and collect file data
|
|
err = b.Walk(".", func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !d.IsDir() {
|
|
file, err := b.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
content, err := io.ReadAll(file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
filesB[path] = fileInfo{content: content}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Find removed and modified files
|
|
for path, infoA := range filesA {
|
|
infoB, ok := filesB[path]
|
|
if !ok {
|
|
diff.Removed = append(diff.Removed, path)
|
|
} else if !bytes.Equal(infoA.content, infoB.content) {
|
|
diff.Modified = append(diff.Modified, path)
|
|
}
|
|
}
|
|
|
|
// Find added files
|
|
for path := range filesB {
|
|
if _, ok := filesA[path]; !ok {
|
|
diff.Added = append(diff.Added, path)
|
|
}
|
|
}
|
|
|
|
return diff, nil
|
|
}
|