aboutsummaryrefslogtreecommitdiff
path: root/ref/store/files/batch_apply.go
diff options
context:
space:
mode:
Diffstat (limited to 'ref/store/files/batch_apply.go')
-rw-r--r--ref/store/files/batch_apply.go136
1 files changed, 136 insertions, 0 deletions
diff --git a/ref/store/files/batch_apply.go b/ref/store/files/batch_apply.go
new file mode 100644
index 00000000..d6fb1a4d
--- /dev/null
+++ b/ref/store/files/batch_apply.go
@@ -0,0 +1,136 @@
+package files
+
+import "codeberg.org/lindenii/furgit/ref/store"
+
+func (batch *Batch) Apply() ([]refstore.BatchResult, error) {
+ results := make([]refstore.BatchResult, len(batch.ops))
+ remainingIdx := make([]int, 0, len(batch.ops))
+ remainingOps := make([]queuedUpdate, 0, len(batch.ops))
+ seenTargets := make(map[string]struct{}, len(batch.ops))
+ executor := &refUpdateExecutor{store: batch.store}
+
+ for i, op := range batch.ops {
+ results[i].Name = op.name
+
+ err := executor.validateQueuedUpdate(op)
+ if err != nil {
+ results[i].Status = refstore.BatchStatusRejected
+ results[i].Error = batchResultError(err)
+
+ continue
+ }
+
+ target, err := executor.resolveQueuedUpdateTarget(op)
+ if err != nil {
+ if isBatchRejected(err) {
+ results[i].Status = refstore.BatchStatusRejected
+ results[i].Error = batchResultError(err)
+
+ continue
+ }
+
+ results[i].Status = refstore.BatchStatusFatal
+ results[i].Error = batchResultError(err)
+
+ for j := i + 1; j < len(results); j++ {
+ results[j].Name = batch.ops[j].name
+ results[j].Status = refstore.BatchStatusNotAttempted
+ results[j].Error = batchResultError(err)
+ }
+
+ return results, err
+ }
+
+ targetKey := updateTargetKey(target.loc)
+ if _, exists := seenTargets[targetKey]; exists {
+ results[i].Status = refstore.BatchStatusRejected
+ results[i].Error = &refstore.DuplicateUpdateError{}
+
+ continue
+ }
+
+ seenTargets[targetKey] = struct{}{}
+
+ remainingIdx = append(remainingIdx, i)
+ remainingOps = append(remainingOps, op)
+ }
+
+ for len(remainingOps) > 0 {
+ prepared, err := executor.prepareUpdates(remainingOps)
+ if err == nil {
+ err = executor.commitPreparedUpdates(prepared)
+ if err == nil {
+ for _, idx := range remainingIdx {
+ results[idx].Status = refstore.BatchStatusApplied
+ }
+
+ return results, nil
+ }
+
+ fatalName := batchResultName(err)
+
+ fatalMarked := false
+ for i, idx := range remainingIdx {
+ if !fatalMarked && remainingOps[i].name == fatalName && fatalName != "" {
+ results[idx].Status = refstore.BatchStatusFatal
+ results[idx].Error = batchResultError(err)
+ fatalMarked = true
+
+ continue
+ }
+
+ results[idx].Status = refstore.BatchStatusNotAttempted
+ results[idx].Error = batchResultError(err)
+ }
+
+ return results, err
+ }
+
+ if !isBatchRejected(err) {
+ fatalName := batchResultName(err)
+
+ fatalMarked := false
+ for i, idx := range remainingIdx {
+ if !fatalMarked && remainingOps[i].name == fatalName && fatalName != "" {
+ results[idx].Status = refstore.BatchStatusFatal
+ results[idx].Error = batchResultError(err)
+ fatalMarked = true
+
+ continue
+ }
+
+ results[idx].Status = refstore.BatchStatusNotAttempted
+ results[idx].Error = batchResultError(err)
+ }
+
+ return results, err
+ }
+
+ name := batchResultName(err)
+ rejectedAt := -1
+
+ for i, op := range remainingOps {
+ if op.name == name {
+ rejectedAt = i
+
+ break
+ }
+ }
+
+ if rejectedAt < 0 {
+ for _, idx := range remainingIdx {
+ results[idx].Status = refstore.BatchStatusNotAttempted
+ results[idx].Error = batchResultError(err)
+ }
+
+ return results, err
+ }
+
+ results[remainingIdx[rejectedAt]].Status = refstore.BatchStatusRejected
+ results[remainingIdx[rejectedAt]].Error = batchResultError(err)
+ remainingIdx = append(remainingIdx[:rejectedAt], remainingIdx[rejectedAt+1:]...)
+ remainingOps = append(remainingOps[:rejectedAt], remainingOps[rejectedAt+1:]...)
+ }
+
+ return results, nil
+}