aboutsummaryrefslogtreecommitdiff
path: root/network/receivepack/service/execute.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-26 09:14:59 +0000
committerGravatar Runxi Yu2026-03-26 09:14:59 +0000
commit3d25bda9d5da6814661828adabe8a09f9d01aefb (patch)
treed034e28079333f85e5d7b96d921282eddd4798d6 /network/receivepack/service/execute.go
parentobject/id: Empty tree (diff)
signatureNo signature
network/receivepack: Rename from receivepack
Diffstat (limited to 'network/receivepack/service/execute.go')
-rw-r--r--network/receivepack/service/execute.go123
1 files changed, 123 insertions, 0 deletions
diff --git a/network/receivepack/service/execute.go b/network/receivepack/service/execute.go
new file mode 100644
index 00000000..9f373e0d
--- /dev/null
+++ b/network/receivepack/service/execute.go
@@ -0,0 +1,123 @@
+package service
+
+import (
+ "context"
+ "os"
+
+ "codeberg.org/lindenii/furgit/internal/utils"
+)
+
+// Execute validates one receive-pack request, optionally ingests its pack into
+// quarantine, runs the optional hook, and applies allowed ref updates.
+func (service *Service) Execute(ctx context.Context, req *Request) (*Result, error) {
+ result := &Result{
+ Commands: make([]CommandResult, 0, len(req.Commands)),
+ }
+
+ var (
+ quarantineName string
+ quarantineRoot *os.Root
+ err error
+ )
+
+ quarantineName, quarantineRoot, ok := service.ingestQuarantine(result, req.Commands, req)
+ if !ok {
+ return result, nil
+ }
+
+ if quarantineRoot != nil {
+ defer func() {
+ _ = quarantineRoot.Close()
+ _ = service.opts.ObjectsRoot.RemoveAll(quarantineName)
+ }()
+ }
+
+ for _, command := range req.Commands {
+ result.Planned = append(result.Planned, PlannedUpdate{
+ Name: command.Name,
+ OldID: command.OldID,
+ NewID: command.NewID,
+ Delete: isDelete(command),
+ })
+ }
+
+ if len(req.Commands) == 0 {
+ return result, nil
+ }
+
+ allowedCommands, allowedIndices, rejected, ok, errText := service.runHook(
+ ctx,
+ req,
+ req.Commands,
+ quarantineName,
+ )
+ if !ok {
+ fillCommandErrors(result, req.Commands, errText)
+
+ return result, nil
+ }
+
+ if req.Atomic && len(rejected) != 0 {
+ result.Commands = make([]CommandResult, 0, len(req.Commands))
+ for index, command := range req.Commands {
+ message := rejected[index]
+ if message == "" {
+ message = "atomic push rejected by hook"
+ }
+
+ result.Commands = append(result.Commands, resultForHookRejection(command, message))
+ }
+
+ return result, nil
+ }
+
+ if len(allowedCommands) == 0 {
+ result.Commands = mergeCommandResults(req.Commands, rejected, nil, nil)
+
+ return result, nil
+ }
+
+ if req.PackExpected && quarantineRoot != nil {
+ // Git migrates quarantined objects into permanent storage immediately
+ // before starting ref updates.
+ utils.BestEffortFprintf(service.opts.Progress, "promoting quarantine...\r")
+
+ err = service.promoteQuarantine(quarantineName, quarantineRoot)
+ if err != nil {
+ utils.BestEffortFprintf(service.opts.Progress, "promoting quarantine: failed: %v.\n", err)
+
+ result.UnpackError = err.Error()
+ fillCommandErrors(result, req.Commands, err.Error())
+
+ return result, nil
+ }
+
+ utils.BestEffortFprintf(service.opts.Progress, "promoting quarantine: done.\n")
+ }
+
+ if req.Atomic {
+ subresult := &Result{}
+
+ err := service.applyAtomic(subresult, allowedCommands)
+ if err != nil {
+ return result, err
+ }
+
+ result.Commands = mergeCommandResults(req.Commands, rejected, subresult.Commands, allowedIndices)
+ result.Applied = subresult.Applied
+
+ return result, nil
+ }
+
+ subresult := &Result{}
+
+ err = service.applyBatch(subresult, allowedCommands)
+ if err != nil {
+ return result, err
+ }
+
+ result.Commands = mergeCommandResults(req.Commands, rejected, subresult.Commands, allowedIndices)
+ result.Applied = subresult.Applied
+
+ return result, nil
+}