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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
package service
import (
"codeberg.org/lindenii/furgit/internal/utils"
refstore "codeberg.org/lindenii/furgit/ref/store"
)
func (service *Service) applyAtomic(result *Result, commands []Command) error {
total := len(commands)
utils.BestEffortFprintf(service.opts.Progress, "updating refs: 0/%d\r", total)
tx, err := service.opts.Refs.BeginTransaction()
if err != nil {
return err
}
for i, command := range commands {
err = queueWriteTransaction(tx, command)
if err != nil {
_ = tx.Abort()
fillCommandErrors(result, commands, err.Error())
utils.BestEffortFprintf(service.opts.Progress, "updating refs: failed at %d/%d.\n", i+1, total)
return nil
}
utils.BestEffortFprintf(service.opts.Progress, "updating refs: %d/%d\r", i+1, total)
}
err = tx.Commit()
if err != nil {
fillCommandErrors(result, commands, err.Error())
utils.BestEffortFprintf(service.opts.Progress, "updating refs: failed at commit.\n")
return nil
}
result.Applied = true
for _, command := range commands {
result.Commands = append(result.Commands, successCommandResult(command))
}
utils.BestEffortFprintf(service.opts.Progress, "updating refs: done.\n")
return nil
}
func (service *Service) applyBatch(result *Result, commands []Command) error {
total := len(commands)
utils.BestEffortFprintf(service.opts.Progress, "updating refs...\r")
batch, err := service.opts.Refs.BeginBatch()
if err != nil {
return err
}
for _, command := range commands {
err = queueWriteBatch(batch, command)
if err != nil {
_ = batch.Abort()
fillCommandErrors(result, commands, err.Error())
utils.BestEffortFprintf(service.opts.Progress, "updating refs: failed while queueing batch.\n")
return nil
}
}
batchResults, err := batch.Apply()
if err != nil && len(batchResults) == 0 {
utils.BestEffortFprintf(service.opts.Progress, "updating refs: failed at apply.\n")
return err
}
appliedAny := false
failedCount := 0
for i, command := range commands {
item := successCommandResult(command)
if i < len(batchResults) && batchResults[i].Error != nil {
item.Error = batchResults[i].Error.Error()
failedCount++
} else {
appliedAny = true
}
result.Commands = append(result.Commands, item)
utils.BestEffortFprintf(service.opts.Progress, "updating refs: %d/%d\r", i+1, total)
}
result.Applied = appliedAny
if failedCount == 0 {
utils.BestEffortFprintf(service.opts.Progress, "updating refs: done.\n")
} else {
utils.BestEffortFprintf(service.opts.Progress, "updating refs: failed (%d/%d).\n", failedCount, total)
}
return nil
}
func queueWriteTransaction(tx refstore.Transaction, command Command) error {
if isDelete(command) {
return tx.Delete(command.Name, command.OldID)
}
if command.OldID == command.OldID.Algorithm().Zero() {
return tx.Create(command.Name, command.NewID)
}
return tx.Update(command.Name, command.NewID, command.OldID)
}
func queueWriteBatch(batch refstore.Batch, command Command) error {
if isDelete(command) {
return batch.Delete(command.Name, command.OldID)
}
if command.OldID == command.OldID.Algorithm().Zero() {
return batch.Create(command.Name, command.NewID)
}
return batch.Update(command.Name, command.NewID, command.OldID)
}
func successCommandResult(command Command) CommandResult {
return CommandResult{
Name: command.Name,
RefName: command.Name,
OldID: new(command.OldID),
NewID: new(command.NewID),
}
}
|