package main import ( "io" "log" "os" "path/filepath" "strings" ignore "github.com/sabhiram/go-gitignore" ) type FileMeta struct { Size int64 Mode os.FileMode Symlink string } type Manifest struct { Files map[string]FileMeta } func initialSync(hostDir, dataDir string, rules *ignore.GitIgnore, dryRun bool) (*Manifest, error) { manifest := &Manifest{Files: make(map[string]FileMeta)} err := filepath.Walk(hostDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } relPath, err := filepath.Rel(hostDir, path) if err != nil { return err } if relPath == "." { return nil } if relPath == ".git" || strings.HasPrefix(relPath, ".git"+string(filepath.Separator)) { return filepath.SkipDir } if rules.MatchesPath(relPath) { if info.IsDir() { return filepath.SkipDir } return nil } destPath := filepath.Join(dataDir, relPath) linkTarget := "" if info.Mode()&os.ModeSymlink != 0 { linkTarget, err = os.Readlink(path) if err != nil { return err } } meta := FileMeta{ Size: info.Size(), Mode: info.Mode(), Symlink: linkTarget, } manifest.Files[relPath] = meta if dryRun { log.Printf("[dry-run] would sync: %s", relPath) return nil } if info.IsDir() { return os.MkdirAll(destPath, info.Mode()) } if err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil { return err } if linkTarget != "" { return os.Symlink(linkTarget, destPath) } return copyFile(path, destPath, info.Mode()) }) return manifest, err } func copyFile(src, dst string, mode os.FileMode) error { srcFile, err := os.Open(src) if err != nil { return err } defer srcFile.Close() dstFile, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode) if err != nil { return err } defer dstFile.Close() _, err = io.Copy(dstFile, srcFile) return err }