aboutsummaryrefslogtreecommitdiff
path: root/receivepack/receivepack.go
blob: 9f4a582b31fe009bde626b28ab2b677c4ad2630d (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
package receivepack

import (
	"context"
	"io"

	"codeberg.org/lindenii/furgit/format/pktline"
	common "codeberg.org/lindenii/furgit/protocol/v0v1/server"
	protoreceive "codeberg.org/lindenii/furgit/protocol/v0v1/server/receivepack"
	"codeberg.org/lindenii/furgit/receivepack/internal/service"
)

// ReceivePack serves one receive-pack session over r/w.
func ReceivePack(
	ctx context.Context,
	w pktline.WriteFlusher,
	e io.Writer,
	r io.Reader,
	opts Options,
) error {
	_ = e // TODO: Use stderr/progress sink explicitly as hook/progress behavior expands.

	err := validateOptions(opts)
	if err != nil {
		return err
	}

	version := parseVersion(opts.GitProtocol)

	base := common.NewSession(r, w, common.Options{
		Version:   version,
		Algorithm: opts.Algorithm,
	})

	protoSession := protoreceive.NewSession(base, protoreceive.Capabilities{
		ReportStatus:   true,
		ReportStatusV2: true,
		DeleteRefs:     true,
		SideBand64K:    true,
		Quiet:          true,
		Atomic:         true,
		OfsDelta:       true,
		PushOptions:    true,
		ObjectFormat:   opts.Algorithm,
		// TODO: PushCertNonce, SessionID, Agent, whatever.
	})

	refs, err := advertisedRefs(opts)
	if err != nil {
		return err
	}

	err = protoSession.AdvertiseRefs(common.Advertisement{Refs: refs})
	if err != nil {
		return err
	}

	req, err := protoSession.ReadRequest()
	if err != nil {
		return err
	}

	serviceReq := &service.Request{
		Commands:     translateCommands(req.Commands),
		PushOptions:  append([]string(nil), req.PushOptions...),
		DeleteOnly:   req.DeleteOnly,
		PackExpected: req.PackExpected,
		Pack:         r,
	}

	svc := service.New(service.Options{
		Algorithm:       opts.Algorithm,
		Refs:            opts.Refs,
		ExistingObjects: opts.ExistingObjects,
		ObjectsRoot:     opts.ObjectsRoot,
	})

	result, err := svc.Execute(ctx, serviceReq)
	if err != nil {
		return err
	}

	protoResult := translateResult(result)

	if req.Capabilities.ReportStatusV2 {
		return protoSession.WriteReportStatusV2(protoResult)
	}

	if req.Capabilities.ReportStatus {
		return protoSession.WriteReportStatus(protoResult)
	}

	return nil
}