From 49472ee8f599d290545b97b058d1b5083779c241 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Thu, 4 Sep 2025 03:53:14 +0000 Subject: [PATCH 1/2] feat: improve dotfiles pull file-saving with go-diff - Add github.com/sergi/go-diff dependency for better diff checking - Remove redundant IsEqual check in Save method as it's already done by caller - Use go-diff to check for actual content differences before saving files - Only save files when there are real differences, improving efficiency Co-authored-by: Le He --- go.mod | 1 + go.sum | 9 +++++++++ model/dotfile_apps.go | 36 ++++++++++++++++++++++-------------- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 7630852..492868f 100644 --- a/go.mod +++ b/go.mod @@ -44,6 +44,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sergi/go-diff v1.4.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect diff --git a/go.sum b/go.sum index e88e91a..b21f6b4 100644 --- a/go.sum +++ b/go.sum @@ -33,8 +33,11 @@ github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lithammer/shortuuid/v3 v3.0.7 h1:trX0KTHy4Pbwo/6ia8fscyHoGA+mf1jWbPJVuvyJQQ8= @@ -68,11 +71,14 @@ github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= +github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -150,8 +156,11 @@ google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeB google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/model/dotfile_apps.go b/model/dotfile_apps.go index ac1e8e2..3d6a5e4 100644 --- a/model/dotfile_apps.go +++ b/model/dotfile_apps.go @@ -9,6 +9,7 @@ import ( "strings" "time" + "github.com/sergi/go-diff/diffmatchpatch" "github.com/sirupsen/logrus" ) @@ -207,27 +208,34 @@ func (b *BaseApp) Backup(ctx context.Context, paths []string) error { return nil } -// Save writes new content only for files that don't match +// Save writes new content for files, using diff to check for actual differences func (b *BaseApp) Save(ctx context.Context, files map[string]string) error { - // First check which files need updating - equalityMap, err := b.IsEqual(ctx, files) - if err != nil { - return err - } - - for path, content := range files { - // Skip if file content is equal - if isEqual, exists := equalityMap[path]; exists && isEqual { - logrus.Debugf("Skipping %s - content is identical", path) - continue - } + dmp := diffmatchpatch.New() + for path, newContent := range files { expandedPath, err := b.expandPath(path) if err != nil { logrus.Warnf("Failed to expand path %s: %v", path, err) continue } + // Read existing content if file exists + var existingContent string + if existingBytes, err := os.ReadFile(expandedPath); err == nil { + existingContent = string(existingBytes) + } else if !os.IsNotExist(err) { + logrus.Warnf("Failed to read existing file %s: %v", expandedPath, err) + continue + } + + // Check for differences using go-diff + diffs := dmp.DiffMain(existingContent, newContent, false) + if len(diffs) == 1 && diffs[0].Type == diffmatchpatch.DiffEqual { + // No differences found, skip saving + logrus.Debugf("Skipping %s - content is identical", path) + continue + } + // Ensure directory exists dir := filepath.Dir(expandedPath) if err := os.MkdirAll(dir, 0755); err != nil { @@ -236,7 +244,7 @@ func (b *BaseApp) Save(ctx context.Context, files map[string]string) error { } // Write new content - if err := os.WriteFile(expandedPath, []byte(content), 0644); err != nil { + if err := os.WriteFile(expandedPath, []byte(newContent), 0644); err != nil { logrus.Warnf("Failed to save file %s: %v", expandedPath, err) } else { logrus.Infof("Saved new content to %s", expandedPath) From aab6b9b6a283318f533498bf9e3f989110d2e62e Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Thu, 4 Sep 2025 04:03:48 +0000 Subject: [PATCH 2/2] fix: use merged content from go-diff patches instead of just newContent Apply patches properly using PatchMake and PatchApply to create merged content rather than overwriting with newContent directly. This ensures proper merging behavior when files have conflicts. Co-authored-by: Le He --- model/dotfile_apps.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/model/dotfile_apps.go b/model/dotfile_apps.go index 3d6a5e4..23d9b46 100644 --- a/model/dotfile_apps.go +++ b/model/dotfile_apps.go @@ -236,6 +236,17 @@ func (b *BaseApp) Save(ctx context.Context, files map[string]string) error { continue } + // Create patches from the diffs and apply them to get merged content + patches := dmp.PatchMake(existingContent, diffs) + mergedContent, results := dmp.PatchApply(patches, existingContent) + + // Check if patches were applied successfully + for i, success := range results { + if !success { + logrus.Warnf("Failed to apply patch %d for %s", i, path) + } + } + // Ensure directory exists dir := filepath.Dir(expandedPath) if err := os.MkdirAll(dir, 0755); err != nil { @@ -243,8 +254,8 @@ func (b *BaseApp) Save(ctx context.Context, files map[string]string) error { continue } - // Write new content - if err := os.WriteFile(expandedPath, []byte(newContent), 0644); err != nil { + // Write merged content + if err := os.WriteFile(expandedPath, []byte(mergedContent), 0644); err != nil { logrus.Warnf("Failed to save file %s: %v", expandedPath, err) } else { logrus.Infof("Saved new content to %s", expandedPath)