package cmd import ( "bytes" "fmt" "io" "os" "path/filepath" "strings" "filippo.io/age" "github.com/urfave/cli/v2" "lukechampine.com/blake3" ) var Clean = &cli.Command{ Name: "clean", Aliases: []string{"c"}, Description: "Git clean filter", Hidden: true, Flags: []cli.Flag{ &cli.StringFlag{ Name: "file", Usage: "File being worked on", Aliases: []string{"f"}, }, }, Action: actionClean, } func actionClean(ctx *cli.Context) error { recipients, err := ageRecipients(ctx.String("file")) if err != nil { return err } dir, err := gitConfigDir(ctx.String("file")) if err != nil { return err } existing, err := os.ReadFile(filepath.Join(dir, "blake3")) if err != nil { // File doesn't exist } var stdin bytes.Buffer hasher := blake3.New(64, nil) hasherTee := io.TeeReader(os.Stdin, hasher) if _, err := io.Copy(&stdin, hasherTee); err != nil { return err } sum := hasher.Sum(nil) if fmt.Sprintf("%x", existing) == fmt.Sprintf("%x", sum) { saved, err := os.ReadFile(filepath.Join(dir, "age")) if err != nil { return err } os.Stdout.Write(saved) return nil } hashFile, err := os.Create(filepath.Join(dir, "blake3")) if err != nil { return err } hashFile.Write(sum) defer hashFile.Close() ageFile, err := os.Create(filepath.Join(dir, "age")) if err != nil { return err } defer ageFile.Close() headDecrypted, headEncrypted, err := headContents(ctx.String("file")) if err != nil { return err } if string(headDecrypted) == stdin.String() { ageFile.Write(headEncrypted) os.Stdout.Write(headEncrypted) return nil } mw := io.MultiWriter(ageFile, os.Stdout) wc, err := age.Encrypt(mw, recipients...) if err != nil { return err } if _, err := io.Copy(wc, &stdin); err != nil { return err } return wc.Close() } func headContents(file string) ([]byte, []byte, error) { out, err := cmd("git", "cat-file", "blob", fmt.Sprintf("HEAD:%s", file)) if err != nil { return nil, nil, nil } identities, err := ageIdentities() if err != nil { return nil, nil, err } r, err := age.Decrypt(strings.NewReader(out), identities...) if err != nil { return nil, nil, err } decrypted, err := io.ReadAll(r) return decrypted, []byte(out), err }