aboutsummaryrefslogtreecommitdiff
path: root/receivepack/service/apply.go
blob: 4b61cf3d44ddbda69c5277194142adbcbec7a12a (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
125
126
127
128
129
130
131
132
133
134
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.FprintfBestEffort(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.FprintfBestEffort(service.opts.Progress, "updating refs: failed at %d/%d.\n", i+1, total)

			return nil
		}

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

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

	return nil
}

func (service *Service) applyBatch(result *Result, commands []Command) error {
	total := len(commands)

	utils.FprintfBestEffort(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.FprintfBestEffort(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.FprintfBestEffort(service.opts.Progress, "updating refs: %d/%d\r", i+1, total)
	}

	result.Applied = appliedAny

	if failedCount == 0 {
		utils.FprintfBestEffort(service.opts.Progress, "updating refs: done.\n")
	} else {
		utils.FprintfBestEffort(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 == 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),
	}
}