Home

git-age @1143ef4bfd45527cbf9faa94cc69c557bc00579b - refs - log -
-
https://git.jolheiser.com/git-age.git
git-crypt, but with age
git-age / cmd / clean.go
- raw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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
}