aboutsummaryrefslogtreecommitdiff
path: root/receivepack/service/apply.go
blob: 0f9e07f9c84c40c98a5df5b01a5ae1bf7d668c5d (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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package service

import (
	"codeberg.org/lindenii/furgit/internal/utils"
	"codeberg.org/lindenii/furgit/objectid"
	"codeberg.org/lindenii/furgit/refstore"
)

func (service *Service) applyAtomic(result *Result, commands []Command) error {
	total := len(commands)
	utils.WriteProgressf(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.WriteProgressf(service.opts.Progress, "updating refs: failed at %d/%d\n", i+1, total)

			return nil
		}

		utils.WriteProgressf(service.opts.Progress, "updating refs: %d/%d\r", i+1, total)
	}

	err = tx.Commit()
	if err != nil {
		fillCommandErrors(result, commands, err.Error())
		utils.WriteProgressf(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.WriteProgressf(service.opts.Progress, "updating refs: done.\n")

	return nil
}

func (service *Service) applyBatch(result *Result, commands []Command) error {
	total := len(commands)
	utils.WriteProgressf(service.opts.Progress, "updating refs...\r")

	batch, err := service.opts.Refs.BeginBatch()
	if err != nil {
		return err
	}

	for _, command := range commands {
		queueWriteBatch(batch, command)
	}

	batchResults, err := batch.Apply()
	if err != nil && len(batchResults) == 0 {
		utils.WriteProgressf(service.opts.Progress, "updating refs: failed at apply\n")

		return err
	}

	appliedAny := false

	for i, command := range commands {
		item := successCommandResult(command)
		if i < len(batchResults) && batchResults[i].Error != nil {
			item.Error = batchResults[i].Error.Error()
		} else {
			appliedAny = true
		}

		result.Commands = append(result.Commands, item)
		utils.WriteProgressf(service.opts.Progress, "updating refs: %d/%d\r", i+1, total)
	}

	result.Applied = appliedAny
	utils.WriteProgressf(service.opts.Progress, "updating refs: done.\n")

	return nil
}

func queueWriteTransaction(tx refstore.Transaction, command Command) error {
	if isDelete(command) {
		return tx.Delete(command.Name, command.OldID)
	}

	if command.OldID == objectid.Zero(command.OldID.Algorithm()) {
		return tx.Create(command.Name, command.NewID)
	}

	return tx.Update(command.Name, command.NewID, command.OldID)
}

func queueWriteBatch(batch refstore.Batch, command Command) {
	if isDelete(command) {
		batch.Delete(command.Name, command.OldID)

		return
	}

	if command.OldID == objectid.Zero(command.OldID.Algorithm()) {
		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:   objectIDPointer(command.OldID),
		NewID:   objectIDPointer(command.NewID),
	}
}