aboutsummaryrefslogtreecommitdiff
path: root/receivepack/int_test.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-07 19:40:27 +0800
committerGravatar Runxi Yu2026-03-07 20:53:17 +0800
commit8aa2e9f0903a80c90a9d8308138439d6f8732050 (patch)
treed2fecbf29b0eaa78da87b017005139960783f669 /receivepack/int_test.go
parentrefstore/files: Implement batching (diff)
signatureNo signature
receivepack: Use refs
Diffstat (limited to 'receivepack/int_test.go')
-rw-r--r--receivepack/int_test.go249
1 files changed, 239 insertions, 10 deletions
diff --git a/receivepack/int_test.go b/receivepack/int_test.go
index a790741b..8f0d02e6 100644
--- a/receivepack/int_test.go
+++ b/receivepack/int_test.go
@@ -3,6 +3,7 @@ package receivepack_test
import (
"context"
"fmt"
+ "io"
"strings"
"testing"
@@ -13,7 +14,7 @@ import (
// TODO: actually test with send-pack
-func TestReceivePackDeleteOnlyReportsNotImplemented(t *testing.T) {
+func TestReceivePackDeleteOnlyAtomicDeleteSucceeds(t *testing.T) {
t.Parallel()
//nolint:thelper
@@ -32,11 +33,11 @@ func TestReceivePackDeleteOnlyReportsNotImplemented(t *testing.T) {
)
input.WriteString(pktlineData(
- commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00report-status delete-refs object-format=" + algo.String() + "\n",
+ commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00report-status atomic delete-refs object-format=" + algo.String() + "\n",
))
input.WriteString("0000")
- err := receivepack.ReceivePack(context.Background(), &output, &strings.Builder{}, strings.NewReader(input.String()), receivepack.Options{
+ err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
GitProtocol: "",
Algorithm: algo,
Refs: repo.Refs(),
@@ -47,9 +48,119 @@ func TestReceivePackDeleteOnlyReportsNotImplemented(t *testing.T) {
}
got := output.String()
- if !strings.Contains(got, "ng refs/heads/main ref updates not implemented yet\n") {
+ if !strings.Contains(got, "ok refs/heads/main\n") {
t.Fatalf("unexpected receive-pack output %q", got)
}
+
+ if _, err := repo.Refs().Resolve("refs/heads/main"); err == nil {
+ t.Fatal("refs/heads/main still exists after delete push")
+ }
+ })
+}
+
+func TestReceivePackDeleteOnlyNonAtomicAppliesIndependentDeletes(t *testing.T) {
+ t.Parallel()
+
+ //nolint:thelper
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
+ t.Parallel()
+
+ testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
+ _, _, commitID := testRepo.MakeCommit(t, "base")
+ _, _, staleID := testRepo.MakeCommit(t, "stale")
+ testRepo.UpdateRef(t, "refs/heads/main", commitID)
+ testRepo.UpdateRef(t, "refs/heads/topic", commitID)
+
+ repo := testRepo.OpenRepository(t)
+
+ var (
+ input strings.Builder
+ output bufferWriteFlusher
+ )
+
+ input.WriteString(pktlineData(
+ staleID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00report-status delete-refs object-format=" + algo.String() + "\n",
+ ))
+ input.WriteString(pktlineData(
+ commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/topic\n",
+ ))
+ input.WriteString("0000")
+
+ err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
+ GitProtocol: "",
+ Algorithm: algo,
+ Refs: repo.Refs(),
+ ExistingObjects: repo.Objects(),
+ })
+ if err != nil {
+ t.Fatalf("ReceivePack: %v", err)
+ }
+
+ got := output.String()
+ if !strings.Contains(got, "ng refs/heads/main ") || !strings.Contains(got, "ok refs/heads/topic\n") {
+ t.Fatalf("unexpected receive-pack output %q", got)
+ }
+
+ if _, err := repo.Refs().Resolve("refs/heads/main"); err != nil {
+ t.Fatalf("Resolve(main): %v", err)
+ }
+
+ if _, err := repo.Refs().Resolve("refs/heads/topic"); err == nil {
+ t.Fatal("refs/heads/topic still exists after successful delete")
+ }
+ })
+}
+
+func TestReceivePackDeleteOnlyAtomicFailureLeavesAllRefsUntouched(t *testing.T) {
+ t.Parallel()
+
+ //nolint:thelper
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
+ t.Parallel()
+
+ testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
+ _, _, commitID := testRepo.MakeCommit(t, "base")
+ _, _, staleID := testRepo.MakeCommit(t, "stale")
+ testRepo.UpdateRef(t, "refs/heads/main", commitID)
+ testRepo.UpdateRef(t, "refs/heads/topic", commitID)
+
+ repo := testRepo.OpenRepository(t)
+
+ var (
+ input strings.Builder
+ output bufferWriteFlusher
+ )
+
+ input.WriteString(pktlineData(
+ staleID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00report-status atomic delete-refs object-format=" + algo.String() + "\n",
+ ))
+ input.WriteString(pktlineData(
+ commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/topic\n",
+ ))
+ input.WriteString("0000")
+
+ err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
+ GitProtocol: "",
+ Algorithm: algo,
+ Refs: repo.Refs(),
+ ExistingObjects: repo.Objects(),
+ })
+ if err != nil {
+ t.Fatalf("ReceivePack: %v", err)
+ }
+
+ got := output.String()
+ if !strings.Contains(got, "ng refs/heads/main ") || !strings.Contains(got, "ng refs/heads/topic ") {
+ t.Fatalf("unexpected receive-pack output %q", got)
+ }
+
+ if _, err := repo.Refs().Resolve("refs/heads/main"); err != nil {
+ t.Fatalf("Resolve(main): %v", err)
+ }
+
+ if _, err := repo.Refs().Resolve("refs/heads/topic"); err != nil {
+ t.Fatalf("Resolve(topic): %v", err)
+ }
})
}
@@ -74,7 +185,7 @@ func TestReceivePackAdvertisesResolvedHEAD(t *testing.T) {
input.WriteString("0000")
- err := receivepack.ReceivePack(context.Background(), &output, &strings.Builder{}, strings.NewReader(input.String()), receivepack.Options{
+ err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
Algorithm: algo,
Refs: repo.Refs(),
ExistingObjects: repo.Objects(),
@@ -123,11 +234,11 @@ func TestReceivePackWithoutReportStatusWritesNoStatusPayload(t *testing.T) {
)
input.WriteString(pktlineData(
- commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00delete-refs object-format=" + algo.String() + "\n",
+ commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00delete-refs atomic object-format=" + algo.String() + "\n",
))
input.WriteString("0000")
- err := receivepack.ReceivePack(context.Background(), &output, &strings.Builder{}, strings.NewReader(input.String()), receivepack.Options{
+ err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
Algorithm: algo,
Refs: repo.Refs(),
ExistingObjects: repo.Objects(),
@@ -162,11 +273,11 @@ func testReceivePackProtocolFallback(t *testing.T, gitProtocol string) {
)
input.WriteString(pktlineData(
- commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00report-status delete-refs object-format=" + algo.String() + "\n",
+ commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00report-status atomic delete-refs object-format=" + algo.String() + "\n",
))
input.WriteString("0000")
- err := receivepack.ReceivePack(context.Background(), &output, &strings.Builder{}, strings.NewReader(input.String()), receivepack.Options{
+ err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
GitProtocol: gitProtocol,
Algorithm: algo,
Refs: repo.Refs(),
@@ -205,7 +316,7 @@ func TestReceivePackPackRequestWithoutObjectsRootReportsNotConfigured(t *testing
))
input.WriteString("0000")
- err := receivepack.ReceivePack(context.Background(), &output, &strings.Builder{}, strings.NewReader(input.String()), receivepack.Options{
+ err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
Algorithm: algo,
Refs: repo.Refs(),
ExistingObjects: repo.Objects(),
@@ -221,6 +332,124 @@ func TestReceivePackPackRequestWithoutObjectsRootReportsNotConfigured(t *testing
})
}
+func TestReceivePackPackCreatePromotesObjectsAndUpdatesRef(t *testing.T) {
+ t.Parallel()
+
+ //nolint:thelper
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
+ t.Parallel()
+
+ sender := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
+ _, _, commitID := sender.MakeCommit(t, "pushed commit")
+
+ receiver := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo, Bare: true})
+ repo := receiver.OpenRepository(t)
+ objectsRoot := receiver.OpenObjectsRoot(t)
+
+ packStream := sender.PackObjectsReader(t, []string{commitID.String()}, false)
+ t.Cleanup(func() {
+ _ = packStream.Close()
+ })
+
+ var (
+ input strings.Builder
+ output bufferWriteFlusher
+ )
+
+ input.WriteString(pktlineData(
+ objectid.Zero(algo).String() + " " + commitID.String() + " refs/heads/main\x00report-status-v2 atomic object-format=" + algo.String() + "\n",
+ ))
+ input.WriteString("0000")
+
+ err := receivepack.ReceivePack(
+ context.Background(),
+ &output,
+ io.MultiReader(strings.NewReader(input.String()), packStream),
+ receivepack.Options{
+ Algorithm: algo,
+ Refs: repo.Refs(),
+ ExistingObjects: repo.Objects(),
+ ObjectsRoot: objectsRoot,
+ },
+ )
+ if err != nil {
+ t.Fatalf("ReceivePack: %v", err)
+ }
+
+ got := output.String()
+ if !strings.Contains(got, "unpack ok\n") || !strings.Contains(got, "ok refs/heads/main\n") {
+ t.Fatalf("unexpected receive-pack output %q", got)
+ }
+
+ reopened := receiver.OpenRepository(t)
+
+ resolved, err := reopened.Refs().ResolveFully("refs/heads/main")
+ if err != nil {
+ t.Fatalf("ResolveFully(main): %v", err)
+ }
+
+ if resolved.ID != commitID {
+ t.Fatalf("refs/heads/main = %s, want %s", resolved.ID, commitID)
+ }
+
+ if gotType := receiver.Run(t, "cat-file", "-t", commitID.String()); gotType != "commit" {
+ t.Fatalf("cat-file -t = %q, want commit", gotType)
+ }
+
+ packs := receiver.Run(t, "count-objects", "-v")
+ if !strings.Contains(packs, "packs: 1") {
+ t.Fatalf("count-objects output missing promoted pack: %q", packs)
+ }
+ })
+}
+
+func TestReceivePackReportStatusV2IncludesRefDetails(t *testing.T) {
+ t.Parallel()
+
+ //nolint:thelper
+ testgit.ForEachAlgorithm(t, func(t *testing.T, algo objectid.Algorithm) {
+ t.Parallel()
+
+ testRepo := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: algo})
+ _, _, commitID := testRepo.MakeCommit(t, "base")
+ testRepo.UpdateRef(t, "refs/heads/main", commitID)
+
+ repo := testRepo.OpenRepository(t)
+
+ var (
+ input strings.Builder
+ output bufferWriteFlusher
+ )
+
+ input.WriteString(pktlineData(
+ commitID.String() + " " + objectid.Zero(algo).String() + " refs/heads/main\x00report-status-v2 atomic delete-refs object-format=" + algo.String() + "\n",
+ ))
+ input.WriteString("0000")
+
+ err := receivepack.ReceivePack(context.Background(), &output, strings.NewReader(input.String()), receivepack.Options{
+ Algorithm: algo,
+ Refs: repo.Refs(),
+ ExistingObjects: repo.Objects(),
+ })
+ if err != nil {
+ t.Fatalf("ReceivePack: %v", err)
+ }
+
+ got := output.String()
+ if !strings.Contains(got, "option refname refs/heads/main\n") {
+ t.Fatalf("missing option refname in %q", got)
+ }
+
+ if !strings.Contains(got, "option old-oid "+commitID.String()+"\n") {
+ t.Fatalf("missing option old-oid in %q", got)
+ }
+
+ if !strings.Contains(got, "option new-oid "+objectid.Zero(algo).String()+"\n") {
+ t.Fatalf("missing option new-oid in %q", got)
+ }
+ })
+}
+
type bufferWriteFlusher struct {
strings.Builder
}