aboutsummaryrefslogtreecommitdiff
path: root/refstore/files/transaction_prepare.go
blob: 38eea9d82c57df4a411c9cf5ee00fb44e465ed6c (about) (plain) (blame)
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
package files

import (
	"fmt"
	"slices"
)

func (tx *Transaction) prepare() (prepared []preparedTxOp, err error) {
	prepared = make([]preparedTxOp, 0, len(tx.ops))

	defer func() {
		if err != nil {
			_ = tx.cleanup(prepared)
		}
	}()

	targets := make(map[string]struct{}, len(tx.ops))

	for _, op := range tx.ops {
		target, err := tx.resolveTarget(op)
		if err != nil {
			return prepared, err
		}

		targetKey := tx.targetKey(target.loc)
		if _, exists := targets[targetKey]; exists {
			return prepared, fmt.Errorf("refstore/files: duplicate transaction operation for %q", target.name)
		}

		targets[targetKey] = struct{}{}

		prepared = append(prepared, preparedTxOp{
			op:     op,
			target: target,
		})
	}

	deleted := make(map[string]struct{})
	written := make([]string, 0, len(prepared))

	for _, item := range prepared {
		switch item.op.kind {
		case txDelete, txDeleteSymbolic:
			deleted[item.target.name] = struct{}{}
		case txCreate, txUpdate, txCreateSymbolic, txUpdateSymbolic:
			written = append(written, item.target.name)
		case txVerify, txVerifySymbolic:
		}
	}

	existing, err := tx.visibleNames()
	if err != nil {
		return prepared, err
	}

	for _, name := range written {
		err = verifyRefnameAvailable(name, existing, written, deleted)
		if err != nil {
			return prepared, err
		}
	}

	lockNames := make([]string, 0, len(prepared))
	for _, item := range prepared {
		lockNames = append(lockNames, tx.targetKey(item.target.loc))
	}

	slices.Sort(lockNames)

	for _, lockKey := range lockNames {
		err = tx.createLock(refPathFromKey(lockKey))
		if err != nil {
			return prepared, err
		}
	}

	hasDeletes := len(deleted) > 0
	if hasDeletes {
		err = tx.createPackedLock(tx.store.packedRefsTimeout)
		if err != nil {
			return prepared, err
		}
	}

	for i := range prepared {
		item := &prepared[i]

		refState, err := tx.directRead(item.target.name)
		if err != nil {
			return prepared, err
		}

		item.target.ref = refState

		err = tx.verifyCurrent(*item)
		if err != nil {
			return prepared, err
		}
	}

	return prepared, nil
}