Borg/pkg/diff/diff.go
google-labs-jules[bot] 99c635d8df feat: Add diff and sync collection functionality
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>
2026-02-02 00:49:02 +00:00

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
}