feat: add ignore paths

This commit is contained in:
Samuel N Cui
2026-03-11 01:23:37 +08:00
parent 689959e29f
commit 8a8f54c525
2 changed files with 133 additions and 3 deletions
+85 -2
View File
@@ -3,6 +3,7 @@ package main
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"io/fs"
@@ -12,6 +13,7 @@ import (
"path"
"path/filepath"
"sort"
"strings"
"time"
"github.com/samuelncui/acp"
@@ -32,6 +34,7 @@ type rewriteState struct {
Root string `json:"root"`
Pending []rewriteEntry `json:"pending,omitempty"`
Busy []rewriteEntry `json:"busy,omitempty"`
Missing []rewriteEntry `json:"missing,omitempty"`
TmpFiles []string `json:"tmp_files,omitempty"`
}
@@ -41,6 +44,12 @@ func main() {
statePath := flag.String("state", ".acp-rewrite-state.json", "state storage path")
reportPath := flag.String("report", "", "json report storage path")
reportIndent := flag.Bool("report-indent", false, "json report with indent")
ignorePaths := make([]string, 0, 4)
flag.Func("ignore", "ignore path, file or dir", func(s string) error {
ignorePaths = append(ignorePaths, s)
return nil
})
flag.Parse()
if flag.NArg() == 0 {
@@ -63,6 +72,10 @@ func main() {
logrus.Fatalf("get abs report path fail, %s", err)
}
}
ignoreAbs, err := normalizeIgnorePaths(rootAbs, ignorePaths)
if err != nil {
logrus.Fatalf("normalize ignore path fail, %s", err)
}
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
@@ -87,8 +100,11 @@ func main() {
}
if len(state.Pending) == 0 && len(state.Busy) == 0 {
entries, err := scanEntries(rootAbs, stateAbs, reportAbs)
entries, err := scanEntries(ctx, rootAbs, stateAbs, reportAbs, ignoreAbs)
if err != nil {
if errors.Is(err, context.Canceled) {
return
}
logrus.Fatalf("scan path fail, %s", err)
}
state.Pending = entries
@@ -128,6 +144,22 @@ func main() {
return
}
if _, err := os.Stat(entry.Path); err != nil {
if os.IsNotExist(err) {
state.Missing = append(state.Missing, entry)
if bar != nil {
_ = bar.Add(1)
}
continue
}
logrus.Warnf("stat fail, path= '%s', err= %s", entry.Path, err)
state.Pending = append(state.Pending, entry)
if bar != nil {
_ = bar.Add(1)
}
continue
}
busy, err := isFileBusy(entry.Path)
if err != nil {
logrus.Warnf("check busy fail, path= '%s', err= %s", entry.Path, err)
@@ -170,6 +202,9 @@ func main() {
}
if err != nil {
if ctx.Err() != nil {
_ = os.Remove(tmpPath)
}
logrus.Warnf("rewrite fail, path= '%s', err= %s", entry.Path, err)
state.Pending = append(state.Pending, entry)
if bar != nil {
@@ -204,14 +239,23 @@ func main() {
printDuplicates(reportJobs)
}
func scanEntries(root, statePath, reportPath string) ([]rewriteEntry, error) {
func scanEntries(ctx context.Context, root, statePath, reportPath string, ignorePaths []string) ([]rewriteEntry, error) {
groups := make(map[fileIdentity][]string)
entries := make([]rewriteEntry, 0, 128)
err := filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error {
if ctx.Err() != nil {
return ctx.Err()
}
if err != nil {
return err
}
if shouldIgnorePath(p, ignorePaths) {
if d.IsDir() {
return fs.SkipDir
}
return nil
}
if p == statePath || (reportPath != "" && p == reportPath) {
return nil
}
@@ -256,6 +300,45 @@ func scanEntries(root, statePath, reportPath string) ([]rewriteEntry, error) {
return entries, nil
}
func normalizeIgnorePaths(root string, ignores []string) ([]string, error) {
if len(ignores) == 0 {
return nil, nil
}
normalized := make([]string, 0, len(ignores))
for _, p := range ignores {
if p == "" {
continue
}
if !filepath.IsAbs(p) {
p = filepath.Join(root, p)
}
abs, err := filepath.Abs(p)
if err != nil {
return nil, err
}
normalized = append(normalized, filepath.Clean(abs))
}
return normalized, nil
}
func shouldIgnorePath(p string, ignores []string) bool {
if len(ignores) == 0 {
return false
}
for _, ig := range ignores {
if ig == "" {
continue
}
if p == ig {
return true
}
if strings.HasPrefix(p, ig+string(os.PathSeparator)) {
return true
}
}
return false
}
func newTmpPath(path string, rnd *rand.Rand) (string, error) {
suffix := func() string {
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+48 -1
View File
@@ -1,6 +1,7 @@
package main
import (
"context"
"os"
"path/filepath"
"runtime"
@@ -38,7 +39,7 @@ func TestScanEntriesHardlinks(t *testing.T) {
t.Fatalf("write c: %v", err)
}
entries, err := scanEntries(root, statePath, reportPath)
entries, err := scanEntries(context.Background(), root, statePath, reportPath, nil)
if err != nil {
t.Fatalf("scan entries: %v", err)
}
@@ -69,6 +70,52 @@ func TestScanEntriesHardlinks(t *testing.T) {
}
}
func TestScanEntriesCanceled(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
root := t.TempDir()
entries, err := scanEntries(ctx, root, "", "", nil)
if err == nil || err != context.Canceled {
t.Fatalf("expected canceled, got %v", err)
}
if len(entries) != 0 {
t.Fatalf("entries count = %d", len(entries))
}
}
func TestScanEntriesIgnorePaths(t *testing.T) {
root := t.TempDir()
keep := filepath.Join(root, "keep.txt")
skipDir := filepath.Join(root, "skip")
skipFile := filepath.Join(root, "skip.txt")
if err := os.MkdirAll(skipDir, 0o755); err != nil {
t.Fatalf("mkdir skip: %v", err)
}
if err := os.WriteFile(filepath.Join(skipDir, "a.txt"), []byte("a"), 0o644); err != nil {
t.Fatalf("write skip a: %v", err)
}
if err := os.WriteFile(skipFile, []byte("skip"), 0o644); err != nil {
t.Fatalf("write skip file: %v", err)
}
if err := os.WriteFile(keep, []byte("keep"), 0o644); err != nil {
t.Fatalf("write keep: %v", err)
}
ignorePaths, err := normalizeIgnorePaths(root, []string{"skip", "skip.txt"})
if err != nil {
t.Fatalf("normalize ignore: %v", err)
}
entries, err := scanEntries(context.Background(), root, "", "", ignorePaths)
if err != nil {
t.Fatalf("scan entries: %v", err)
}
if len(entries) != 1 || entries[0].Path != keep {
t.Fatalf("entries = %+v", entries)
}
}
func TestRelinkOnePreserveLinkAttrs(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("hardlink attrs unsupported on windows")