Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWill Chandler <wchandler@gitlab.com>2023-07-05 20:17:48 +0300
committerWill Chandler <wchandler@gitlab.com>2023-07-05 20:17:48 +0300
commit514b8bf2ee9db51fb23f52efbcb93ef6decb3f75 (patch)
tree18d21e2cf92930691fee486186925b98866778ec
parent93b22126b838c5f4eb8a7c978bd6a9a1945d4ef3 (diff)
parent2cd79754f57f8549d568ab01905c5fc8ea515f98 (diff)
Merge branch 'pks-write-ref-sha256' into 'master'
repository: Make WriteRef compatible with SHA256 object format See merge request https://gitlab.com/gitlab-org/gitaly/-/merge_requests/6001 Merged-by: Will Chandler <wchandler@gitlab.com> Approved-by: Justin Tobler <jtobler@gitlab.com> Approved-by: Will Chandler <wchandler@gitlab.com> Co-authored-by: Patrick Steinhardt <psteinhardt@gitlab.com>
-rw-r--r--internal/backup/backup_test.go12
-rw-r--r--internal/backup/repository.go4
-rw-r--r--internal/git/decoder.go2
-rw-r--r--internal/git/localrepo/bundle_test.go8
-rw-r--r--internal/git/localrepo/refs.go8
-rw-r--r--internal/git/localrepo/refs_test.go12
-rw-r--r--internal/git/reference.go8
-rw-r--r--internal/gitaly/service/repository/write_ref.go13
-rw-r--r--internal/gitaly/service/repository/write_ref_test.go516
9 files changed, 334 insertions, 249 deletions
diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go
index 2a27465bc..70cea1550 100644
--- a/internal/backup/backup_test.go
+++ b/internal/backup/backup_test.go
@@ -570,9 +570,9 @@ func TestManager_Restore_latest(t *testing.T) {
)
checksum := new(git.Checksum)
- checksum.Add(git.NewReference("HEAD", master2.String()))
- checksum.Add(git.NewReference("refs/heads/master", master2.String()))
- checksum.Add(git.NewReference("refs/heads/other", other.String()))
+ checksum.Add(git.NewReference("HEAD", master2))
+ checksum.Add(git.NewReference("refs/heads/master", master2))
+ checksum.Add(git.NewReference("refs/heads/other", other))
return repo, checksum
},
@@ -763,9 +763,9 @@ func TestManager_Restore_specific(t *testing.T) {
)
checksum := new(git.Checksum)
- checksum.Add(git.NewReference("HEAD", master2.String()))
- checksum.Add(git.NewReference("refs/heads/master", master2.String()))
- checksum.Add(git.NewReference("refs/heads/other", other.String()))
+ checksum.Add(git.NewReference("HEAD", master2))
+ checksum.Add(git.NewReference("refs/heads/master", master2))
+ checksum.Add(git.NewReference("refs/heads/other", other))
return repo, checksum
},
diff --git a/internal/backup/repository.go b/internal/backup/repository.go
index 220637c0c..6732edb21 100644
--- a/internal/backup/repository.go
+++ b/internal/backup/repository.go
@@ -72,7 +72,7 @@ func (rr *remoteRepository) ListRefs(ctx context.Context) ([]git.Reference, erro
return nil, fmt.Errorf("remote repository: list refs: %w", err)
}
for _, ref := range resp.GetReferences() {
- refs = append(refs, git.NewReference(git.ReferenceName(ref.GetName()), ref.GetTarget()))
+ refs = append(refs, git.NewReference(git.ReferenceName(ref.GetName()), git.ObjectID(ref.GetTarget())))
}
}
@@ -295,7 +295,7 @@ func (r *localRepository) ListRefs(ctx context.Context) ([]git.Reference, error)
case err != nil:
return nil, structerr.NewInternal("local repository: list refs: %w", err)
default:
- head := git.NewReference("HEAD", headOID.String())
+ head := git.NewReference("HEAD", headOID)
refs = append([]git.Reference{head}, refs...)
}
diff --git a/internal/git/decoder.go b/internal/git/decoder.go
index 5d960110f..da5591202 100644
--- a/internal/git/decoder.go
+++ b/internal/git/decoder.go
@@ -42,7 +42,7 @@ func (d *ShowRefDecoder) Decode(ref *Reference) error {
return fmt.Errorf("show-ref decoder: invalid line: %q", line)
}
- *ref = NewReference(ReferenceName(splits[1]), splits[0])
+ *ref = NewReference(ReferenceName(splits[1]), ObjectID(splits[0]))
return nil
}
diff --git a/internal/git/localrepo/bundle_test.go b/internal/git/localrepo/bundle_test.go
index bff66a5de..fa8615323 100644
--- a/internal/git/localrepo/bundle_test.go
+++ b/internal/git/localrepo/bundle_test.go
@@ -161,8 +161,8 @@ func TestRepo_FetchBundle(t *testing.T) {
reader: createBundle(t, cfg, sourceRepoPath),
expectedHeadRef: git.DefaultRef,
expectedRefs: []git.Reference{
- git.NewReference(git.NewReferenceNameFromBranchName("feature"), featureOid.String()),
- git.NewReference(git.DefaultRef, mainOid.String()),
+ git.NewReference(git.NewReferenceNameFromBranchName("feature"), featureOid),
+ git.NewReference(git.DefaultRef, mainOid),
},
}
},
@@ -185,8 +185,8 @@ func TestRepo_FetchBundle(t *testing.T) {
reader: createBundle(t, cfg, sourceRepoPath),
expectedHeadRef: git.NewReferenceNameFromBranchName("feature"),
expectedRefs: []git.Reference{
- git.NewReference(git.NewReferenceNameFromBranchName("feature"), featureOid.String()),
- git.NewReference(git.DefaultRef, mainOid.String()),
+ git.NewReference(git.NewReferenceNameFromBranchName("feature"), featureOid),
+ git.NewReference(git.DefaultRef, mainOid),
},
}
},
diff --git a/internal/git/localrepo/refs.go b/internal/git/localrepo/refs.go
index dd3248f02..8878be895 100644
--- a/internal/git/localrepo/refs.go
+++ b/internal/git/localrepo/refs.go
@@ -126,9 +126,9 @@ func (repo *Repo) getReferences(ctx context.Context, limit uint, patterns ...str
}
if len(line[2]) == 0 {
- refs = append(refs, git.NewReference(git.ReferenceName(line[0]), string(line[1])))
+ refs = append(refs, git.NewReference(git.ReferenceName(line[0]), git.ObjectID(line[1])))
} else {
- refs = append(refs, git.NewSymbolicReference(git.ReferenceName(line[0]), string(line[1])))
+ refs = append(refs, git.NewSymbolicReference(git.ReferenceName(line[0]), git.ReferenceName(line[1])))
}
}
@@ -310,11 +310,11 @@ func (repo *Repo) GetRemoteReferences(ctx context.Context, remote string, opts .
return nil, fmt.Errorf("expected dereferenced symbolic ref %q but got reference %q", symRef, split[1])
}
- refs = append(refs, git.NewSymbolicReference(git.ReferenceName(symRef), split[0]))
+ refs = append(refs, git.NewSymbolicReference(git.ReferenceName(symRef), git.ReferenceName(split[0])))
continue
}
- refs = append(refs, git.NewReference(git.ReferenceName(split[1]), split[0]))
+ refs = append(refs, git.NewReference(git.ReferenceName(split[1]), git.ObjectID(split[0])))
}
if err := scanner.Err(); err != nil {
diff --git a/internal/git/localrepo/refs_test.go b/internal/git/localrepo/refs_test.go
index ff092e640..b4ba959c7 100644
--- a/internal/git/localrepo/refs_test.go
+++ b/internal/git/localrepo/refs_test.go
@@ -82,7 +82,7 @@ func TestRepo_GetReference(t *testing.T) {
{
desc: "fully qualified master branch",
ref: "refs/heads/master",
- expected: git.NewReference("refs/heads/master", commitID.String()),
+ expected: git.NewReference("refs/heads/master", commitID),
},
{
desc: "unqualified master branch fails",
@@ -694,7 +694,7 @@ func TestGuessHead(t *testing.T) {
{"update-ref", "refs/heads/feature", commit1.String()},
{"update-ref", "refs/heads/zucchini", commit1.String()},
},
- head: git.NewReference("HEAD", commit1.String()),
+ head: git.NewReference("HEAD", commit1),
expected: git.DefaultRef,
},
{
@@ -706,7 +706,7 @@ func TestGuessHead(t *testing.T) {
{"update-ref", "refs/heads/feature", commit1.String()},
{"update-ref", "refs/heads/zucchini", commit1.String()},
},
- head: git.NewReference("HEAD", commit1.String()),
+ head: git.NewReference("HEAD", commit1),
expected: git.LegacyDefaultRef,
},
{
@@ -718,7 +718,7 @@ func TestGuessHead(t *testing.T) {
{"update-ref", "refs/heads/feature", commit1.String()},
{"update-ref", "refs/heads/zucchini", commit1.String()},
},
- head: git.NewReference("HEAD", commit1.String()),
+ head: git.NewReference("HEAD", commit1),
expected: "refs/heads/apple",
},
{
@@ -730,7 +730,7 @@ func TestGuessHead(t *testing.T) {
{"update-ref", "refs/heads/feature", commit1.String()},
{"update-ref", "refs/heads/zucchini", commit1.String()},
},
- head: git.NewReference("HEAD", commit1.String()),
+ head: git.NewReference("HEAD", commit1),
expected: "refs/heads/apple",
},
{
@@ -742,7 +742,7 @@ func TestGuessHead(t *testing.T) {
{"update-ref", "refs/heads/feature", commit2.String()},
{"update-ref", "refs/heads/zucchini", commit2.String()},
},
- head: git.NewReference("HEAD", commit1.String()),
+ head: git.NewReference("HEAD", commit1),
expectedErr: fmt.Errorf("guess head: %w", git.ErrReferenceNotFound),
},
} {
diff --git a/internal/git/reference.go b/internal/git/reference.go
index 8ac168dd3..79f998abc 100644
--- a/internal/git/reference.go
+++ b/internal/git/reference.go
@@ -109,19 +109,19 @@ type Reference struct {
}
// NewReference creates a direct reference to an object.
-func NewReference(name ReferenceName, target string) Reference {
+func NewReference(name ReferenceName, target ObjectID) Reference {
return Reference{
Name: name,
- Target: target,
+ Target: string(target),
IsSymbolic: false,
}
}
// NewSymbolicReference creates a symbolic reference to another reference.
-func NewSymbolicReference(name ReferenceName, target string) Reference {
+func NewSymbolicReference(name ReferenceName, target ReferenceName) Reference {
return Reference{
Name: name,
- Target: target,
+ Target: string(target),
IsSymbolic: true,
}
}
diff --git a/internal/gitaly/service/repository/write_ref.go b/internal/gitaly/service/repository/write_ref.go
index 83d4b7e72..76c6d4190 100644
--- a/internal/gitaly/service/repository/write_ref.go
+++ b/internal/gitaly/service/repository/write_ref.go
@@ -39,11 +39,16 @@ func (s *server) writeRef(ctx context.Context, req *gitalypb.WriteRefRequest) er
}
func updateRef(ctx context.Context, repo *localrepo.Repo, req *gitalypb.WriteRefRequest) (returnedErr error) {
+ objectHash, err := repo.ObjectHash(ctx)
+ if err != nil {
+ return fmt.Errorf("detecting object format: %w", err)
+ }
+
var newObjectID git.ObjectID
- if git.ObjectHashSHA1.IsZeroOID(git.ObjectID(req.GetRevision())) {
+ if objectHash.IsZeroOID(git.ObjectID(req.GetRevision())) {
// Passing the all-zeroes object ID as new value means that we should delete the
// reference.
- newObjectID = git.ObjectHashSHA1.ZeroOID
+ newObjectID = objectHash.ZeroOID
} else {
// We need to resolve the new revision in order to make sure that we're actually
// passing an object ID to git-update-ref(1), but more importantly this will also
@@ -58,10 +63,10 @@ func updateRef(ctx context.Context, repo *localrepo.Repo, req *gitalypb.WriteRef
var oldObjectID git.ObjectID
if len(req.GetOldRevision()) > 0 {
- if git.ObjectHashSHA1.IsZeroOID(git.ObjectID(req.GetOldRevision())) {
+ if objectHash.IsZeroOID(git.ObjectID(req.GetOldRevision())) {
// Passing an all-zeroes object ID indicates that we should only update the
// reference if it didn't previously exist.
- oldObjectID = git.ObjectHashSHA1.ZeroOID
+ oldObjectID = objectHash.ZeroOID
} else {
var err error
oldObjectID, err = repo.ResolveRevision(ctx, git.Revision(req.GetOldRevision())+"^{object}")
diff --git a/internal/gitaly/service/repository/write_ref_test.go b/internal/gitaly/service/repository/write_ref_test.go
index 7e525c8a8..b9f029c24 100644
--- a/internal/gitaly/service/repository/write_ref_test.go
+++ b/internal/gitaly/service/repository/write_ref_test.go
@@ -1,293 +1,373 @@
-//go:build !gitaly_test_sha256
-
package repository
import (
"bytes"
- "path/filepath"
+ "fmt"
"testing"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitaly/v16/internal/git"
"gitlab.com/gitlab-org/gitaly/v16/internal/git/gittest"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/git/localrepo"
"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/transaction"
"gitlab.com/gitlab-org/gitaly/v16/internal/grpc/metadata"
- "gitlab.com/gitlab-org/gitaly/v16/internal/helper/text"
"gitlab.com/gitlab-org/gitaly/v16/internal/structerr"
"gitlab.com/gitlab-org/gitaly/v16/internal/testhelper"
"gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testserver"
"gitlab.com/gitlab-org/gitaly/v16/internal/transaction/txinfo"
"gitlab.com/gitlab-org/gitaly/v16/internal/transaction/voting"
"gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
- "google.golang.org/grpc/codes"
)
-func TestWriteRef_successful(t *testing.T) {
+func TestWriteRef(t *testing.T) {
t.Parallel()
+ ctx := testhelper.Context(t)
txManager := transaction.NewTrackingManager()
- cfg, repo, repoPath, client := setupRepositoryService(t, testhelper.Context(t), testserver.WithTransactionManager(txManager))
+ cfg, client := setupRepositoryServiceWithoutRepo(t, testserver.WithTransactionManager(txManager))
- testCases := []struct {
- desc string
- req *gitalypb.WriteRefRequest
+ type setupData struct {
+ request *gitalypb.WriteRefRequest
+ expectedErr error
+ expectedRefs []git.Reference
expectedVotes []transaction.PhasedVote
- }{
- {
- desc: "shell update HEAD to refs/heads/master",
- req: &gitalypb.WriteRefRequest{
- Repository: repo,
- Ref: []byte("HEAD"),
- Revision: []byte("refs/heads/master"),
+ }
+
+ votes := func(ref git.ReferenceName, oldID, newID git.ObjectID) []transaction.PhasedVote {
+ return []transaction.PhasedVote{
+ {
+ Phase: voting.Prepared,
+ Vote: voting.VoteFromData([]byte(fmt.Sprintf("%s %s %s\n", oldID, newID, ref))),
},
- expectedVotes: []transaction.PhasedVote{
- {
- Phase: voting.Prepared,
- Vote: voting.Vote{0xac, 0xba, 0xef, 0x27, 0x5e, 0x46, 0xa7, 0xf1, 0x4c, 0x1e, 0xf4, 0x56, 0xff, 0xf2, 0xc8, 0xbb, 0xe8, 0xc8, 0x47, 0x24},
- },
- {
- Phase: voting.Committed,
- Vote: voting.Vote{0xac, 0xba, 0xef, 0x27, 0x5e, 0x46, 0xa7, 0xf1, 0x4c, 0x1e, 0xf4, 0x56, 0xff, 0xf2, 0xc8, 0xbb, 0xe8, 0xc8, 0x47, 0x24},
- },
+ {
+ Phase: voting.Committed,
+ Vote: voting.VoteFromData([]byte(fmt.Sprintf("%s %s %s\n", oldID, newID, ref))),
},
- },
+ }
+ }
+
+ for _, tc := range []struct {
+ desc string
+ setup func(t *testing.T) setupData
+ }{
{
- desc: "shell update refs/heads/master",
- req: &gitalypb.WriteRefRequest{
- Repository: repo,
- Ref: []byte("refs/heads/master"),
- Revision: []byte("b83d6e391c22777fca1ed3012fce84f633d7fed0"),
- },
- expectedVotes: []transaction.PhasedVote{
- {
- Phase: voting.Prepared,
- Vote: voting.Vote{0xd4, 0x6c, 0x98, 0x49, 0xde, 0x17, 0x55, 0xae, 0xb, 0x48, 0x7f, 0xac, 0x57, 0x77, 0x7d, 0xae, 0xb0, 0xf9, 0x64, 0x72},
- },
- {
- Phase: voting.Committed,
- Vote: voting.Vote{0xd4, 0x6c, 0x98, 0x49, 0xde, 0x17, 0x55, 0xae, 0xb, 0x48, 0x7f, 0xac, 0x57, 0x77, 0x7d, 0xae, 0xb0, 0xf9, 0x64, 0x72},
- },
+ desc: "empty revision",
+ setup: func(t *testing.T) setupData {
+ repo, _ := gittest.CreateRepository(t, ctx, cfg)
+
+ return setupData{
+ request: &gitalypb.WriteRefRequest{
+ Repository: repo,
+ Ref: []byte("refs/heads/master"),
+ },
+ expectedErr: structerr.NewInvalidArgument("invalid revision: empty revision"),
+ expectedRefs: []git.Reference{
+ git.NewSymbolicReference("HEAD", git.DefaultRef),
+ },
+ expectedVotes: []transaction.PhasedVote{},
+ }
},
},
{
- desc: "shell update refs/heads/master w/ validation",
- req: &gitalypb.WriteRefRequest{
- Repository: repo,
- Ref: []byte("refs/heads/master"),
- Revision: []byte("498214de67004b1da3d820901307bed2a68a8ef6"),
- OldRevision: []byte("b83d6e391c22777fca1ed3012fce84f633d7fed0"),
- },
- expectedVotes: []transaction.PhasedVote{
- {
- Phase: voting.Prepared,
- Vote: voting.Vote{0xcb, 0xfc, 0x7f, 0x79, 0x80, 0x7a, 0x33, 0x8e, 0xd5, 0x6a, 0xb7, 0x8e, 0x7f, 0xf8, 0xe, 0xf8, 0x8, 0xb5, 0x52, 0x1a},
- },
- {
- Phase: voting.Committed,
- Vote: voting.Vote{0xcb, 0xfc, 0x7f, 0x79, 0x80, 0x7a, 0x33, 0x8e, 0xd5, 0x6a, 0xb7, 0x8e, 0x7f, 0xf8, 0xe, 0xf8, 0x8, 0xb5, 0x52, 0x1a},
- },
+ desc: "empty ref name",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
+
+ commitID := gittest.WriteCommit(t, cfg, repoPath)
+
+ return setupData{
+ request: &gitalypb.WriteRefRequest{
+ Repository: repo,
+ Revision: []byte(commitID),
+ },
+ expectedErr: structerr.NewInvalidArgument("invalid ref: empty revision"),
+ expectedRefs: []git.Reference{
+ git.NewSymbolicReference("HEAD", git.DefaultRef),
+ },
+ expectedVotes: []transaction.PhasedVote{},
+ }
},
},
{
- desc: "race-free creation of reference",
- req: &gitalypb.WriteRefRequest{
- Repository: repo,
- Ref: []byte("refs/heads/branch"),
- Revision: []byte("498214de67004b1da3d820901307bed2a68a8ef6"),
- OldRevision: []byte("0000000000000000000000000000000000000000"),
- },
- expectedVotes: []transaction.PhasedVote{
- {
- Phase: voting.Prepared,
- Vote: voting.Vote{0xb0, 0xcc, 0xb4, 0x9a, 0x88, 0xc3, 0x67, 0x63, 0xd0, 0xfb, 0x94, 0xd5, 0x84, 0x43, 0x67, 0x18, 0x1e, 0xdb, 0xa0, 0x1e},
- },
- {
- Phase: voting.Committed,
- Vote: voting.Vote{0xb0, 0xcc, 0xb4, 0x9a, 0x88, 0xc3, 0x67, 0x63, 0xd0, 0xfb, 0x94, 0xd5, 0x84, 0x43, 0x67, 0x18, 0x1e, 0xdb, 0xa0, 0x1e},
- },
+ desc: "non-prefixed ref name",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
+
+ commitID := gittest.WriteCommit(t, cfg, repoPath)
+
+ return setupData{
+ request: &gitalypb.WriteRefRequest{
+ Repository: repo,
+ Ref: []byte("master"),
+ Revision: []byte(commitID),
+ },
+ expectedErr: structerr.NewInvalidArgument("ref has to be a full reference"),
+ expectedRefs: []git.Reference{
+ git.NewSymbolicReference("HEAD", git.DefaultRef),
+ },
+ expectedVotes: []transaction.PhasedVote{},
+ }
},
},
{
- desc: "race-free delete of reference",
- req: &gitalypb.WriteRefRequest{
- Repository: repo,
- Ref: []byte("refs/heads/branch"),
- Revision: []byte("0000000000000000000000000000000000000000"),
- OldRevision: []byte("498214de67004b1da3d820901307bed2a68a8ef6"),
- },
- expectedVotes: []transaction.PhasedVote{
- {
- Phase: voting.Prepared,
- Vote: voting.Vote{0xa8, 0x2f, 0xc7, 0x22, 0xac, 0x82, 0xe1, 0x12, 0xb7, 0x78, 0xa6, 0x3f, 0xd6, 0x4b, 0x68, 0xc0, 0x12, 0xb6, 0x17, 0x8c},
- },
- {
- Phase: voting.Committed,
- Vote: voting.Vote{0xa8, 0x2f, 0xc7, 0x22, 0xac, 0x82, 0xe1, 0x12, 0xb7, 0x78, 0xa6, 0x3f, 0xd6, 0x4b, 0x68, 0xc0, 0x12, 0xb6, 0x17, 0x8c},
- },
- },
- },
- }
-
- ctx, err := txinfo.InjectTransaction(testhelper.Context(t), 1, "node", true)
- require.NoError(t, err)
- ctx = metadata.IncomingToOutgoing(ctx)
-
- for _, tc := range testCases {
- t.Run(tc.desc, func(t *testing.T) {
- txManager.Reset()
- _, err = client.WriteRef(ctx, tc.req)
- require.NoError(t, err)
-
- require.Equal(t, tc.expectedVotes, txManager.Votes())
-
- if bytes.Equal(tc.req.Ref, []byte("HEAD")) {
- content := testhelper.MustReadFile(t, filepath.Join(repoPath, "HEAD"))
-
- refRevision := bytes.Join([][]byte{[]byte("ref: "), tc.req.Revision, []byte("\n")}, nil)
-
- require.EqualValues(t, refRevision, content)
- return
- }
-
- revParseCmd := gittest.NewCommand(t, cfg, "-C", repoPath, "rev-parse", "--verify", string(tc.req.Ref))
- output, err := revParseCmd.CombinedOutput()
-
- if git.ObjectHashSHA1.IsZeroOID(git.ObjectID(tc.req.GetRevision())) {
- // If the new OID is the all-zeroes object ID then it indicates we
- // should delete the branch. It's thus expected to get an error when
- // we try to resolve the reference here given that it should not
- // exist anymore.
- require.Error(t, err)
- require.Equal(t, "fatal: Needed a single revision\n", string(output))
- } else {
- require.NoError(t, err)
- require.Equal(t, string(tc.req.Revision), text.ChompBytes(output))
- }
- })
- }
-}
-
-func TestWriteRef_validation(t *testing.T) {
- t.Parallel()
+ desc: "revision contains \\x00",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
- ctx := testhelper.Context(t)
- _, repo, _, client := setupRepositoryService(t, ctx)
+ commitID := []byte(gittest.WriteCommit(t, cfg, repoPath).String())
+ commitID[10] = '\x00'
- testCases := []struct {
- desc string
- req *gitalypb.WriteRefRequest
- }{
- {
- desc: "empty revision",
- req: &gitalypb.WriteRefRequest{
- Repository: repo,
- Ref: []byte("refs/heads/master"),
+ return setupData{
+ request: &gitalypb.WriteRefRequest{
+ Repository: repo,
+ Ref: []byte("refs/heads/master"),
+ Revision: commitID,
+ },
+ expectedErr: structerr.NewInvalidArgument("invalid revision: revision can't contain NUL"),
+ expectedRefs: []git.Reference{
+ git.NewSymbolicReference("HEAD", git.DefaultRef),
+ },
+ expectedVotes: []transaction.PhasedVote{},
+ }
},
},
{
- desc: "empty ref name",
- req: &gitalypb.WriteRefRequest{
- Repository: repo,
- Revision: []byte("498214de67004b1da3d820901307bed2a68a8ef6"),
+ desc: "ref contains whitespace",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
+
+ commitID := gittest.WriteCommit(t, cfg, repoPath).String()
+
+ return setupData{
+ request: &gitalypb.WriteRefRequest{
+ Repository: repo,
+ Ref: []byte("refs/heads /master"),
+ Revision: []byte(commitID),
+ },
+ expectedErr: structerr.NewInvalidArgument("invalid ref: revision can't contain whitespace"),
+ expectedRefs: []git.Reference{
+ git.NewSymbolicReference("HEAD", git.DefaultRef),
+ },
+ expectedVotes: []transaction.PhasedVote{},
+ }
},
},
{
- desc: "non-prefixed ref name for shell",
- req: &gitalypb.WriteRefRequest{
- Repository: repo,
- Ref: []byte("master"),
- Revision: []byte("498214de67004b1da3d820901307bed2a68a8ef6"),
+ desc: "invalid revision",
+ setup: func(t *testing.T) setupData {
+ repo, _ := gittest.CreateRepository(t, ctx, cfg)
+
+ return setupData{
+ request: &gitalypb.WriteRefRequest{
+ Repository: repo,
+ Ref: []byte("refs/heads/master"),
+ Revision: []byte("--output=/meow"),
+ },
+ expectedErr: structerr.NewInvalidArgument("invalid revision: revision can't start with '-'"),
+ expectedRefs: []git.Reference{
+ git.NewSymbolicReference("HEAD", git.DefaultRef),
+ },
+ expectedVotes: []transaction.PhasedVote{},
+ }
},
},
{
- desc: "revision contains \\x00",
- req: &gitalypb.WriteRefRequest{
- Repository: repo,
- Ref: []byte("refs/heads/master"),
- Revision: []byte("012301230123\x001243"),
+ desc: "revision refers to missing reference",
+ setup: func(t *testing.T) setupData {
+ repo, _ := gittest.CreateRepository(t, ctx, cfg)
+
+ return setupData{
+ request: &gitalypb.WriteRefRequest{
+ Repository: repo,
+ Ref: []byte("refs/heads/main"),
+ Revision: []byte("refs/heads/missing"),
+ },
+ expectedErr: structerr.NewInternal("resolving new revision: reference not found"),
+ expectedRefs: []git.Reference{
+ git.NewSymbolicReference("HEAD", git.DefaultRef),
+ },
+ expectedVotes: []transaction.PhasedVote{},
+ }
},
},
{
- desc: "ref contains \\x00",
- req: &gitalypb.WriteRefRequest{
- Repository: repo,
- Ref: []byte("refs/head\x00s/master\x00"),
- Revision: []byte("0123012301231243"),
+ desc: "revision refers to missing object",
+ setup: func(t *testing.T) setupData {
+ repo, _ := gittest.CreateRepository(t, ctx, cfg)
+
+ return setupData{
+ request: &gitalypb.WriteRefRequest{
+ Repository: repo,
+ Ref: []byte("refs/heads/main"),
+ Revision: bytes.Repeat([]byte("1"), gittest.DefaultObjectHash.EncodedLen()),
+ },
+ expectedErr: structerr.NewInternal("resolving new revision: reference not found"),
+ expectedRefs: []git.Reference{
+ git.NewSymbolicReference("HEAD", git.DefaultRef),
+ },
+ expectedVotes: []transaction.PhasedVote{},
+ }
},
},
{
- desc: "ref contains whitespace",
- req: &gitalypb.WriteRefRequest{
- Repository: repo,
- Ref: []byte("refs/heads /master"),
- Revision: []byte("0123012301231243"),
+ desc: "old revision refers to missing reference",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
+
+ commitID := gittest.WriteCommit(t, cfg, repoPath)
+
+ return setupData{
+ request: &gitalypb.WriteRefRequest{
+ Repository: repo,
+ Ref: []byte("refs/heads/main"),
+ Revision: []byte(commitID),
+ OldRevision: bytes.Repeat([]byte("1"), gittest.DefaultObjectHash.EncodedLen()),
+ },
+ expectedErr: structerr.NewInternal("resolving old revision: reference not found"),
+ expectedRefs: []git.Reference{
+ git.NewSymbolicReference("HEAD", git.DefaultRef),
+ },
+ expectedVotes: []transaction.PhasedVote{},
+ }
},
},
{
- desc: "invalid revision",
- req: &gitalypb.WriteRefRequest{
- Repository: repo,
- Ref: []byte("refs/heads/master"),
- Revision: []byte("--output=/meow"),
- },
- },
- }
+ desc: "update default branch",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
- for _, tc := range testCases {
- t.Run(tc.desc, func(t *testing.T) {
- _, err := client.WriteRef(ctx, tc.req)
+ defaultCommit := gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch(git.DefaultBranch))
+ newCommit := gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("new-default"))
- testhelper.RequireGrpcCode(t, err, codes.InvalidArgument)
- })
- }
-}
+ return setupData{
+ request: &gitalypb.WriteRefRequest{
+ Repository: repo,
+ Ref: []byte("HEAD"),
+ Revision: []byte("refs/heads/new-default"),
+ },
+ expectedRefs: []git.Reference{
+ git.NewSymbolicReference("HEAD", "refs/heads/new-default"),
+ git.NewReference(git.DefaultRef, defaultCommit),
+ git.NewReference("refs/heads/new-default", newCommit),
+ },
+ expectedVotes: []transaction.PhasedVote{
+ {Phase: voting.Prepared, Vote: voting.VoteFromData([]byte("ref: refs/heads/new-default\n"))},
+ {Phase: voting.Committed, Vote: voting.VoteFromData([]byte("ref: refs/heads/new-default\n"))},
+ },
+ }
+ },
+ },
+ {
+ desc: "reference update without expected commit ID",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
-func TestWriteRef_missingRevisions(t *testing.T) {
- t.Parallel()
+ oldCommit := gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("branch"))
+ newCommit := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(oldCommit))
- ctx := testhelper.Context(t)
- cfg, client := setupRepositoryServiceWithoutRepo(t)
+ return setupData{
+ request: &gitalypb.WriteRefRequest{
+ Repository: repo,
+ Ref: []byte("refs/heads/branch"),
+ Revision: []byte(newCommit),
+ },
+ expectedRefs: []git.Reference{
+ git.NewSymbolicReference("HEAD", git.DefaultRef),
+ git.NewReference("refs/heads/branch", newCommit),
+ },
+ expectedVotes: votes("refs/heads/branch", gittest.DefaultObjectHash.ZeroOID, newCommit),
+ }
+ },
+ },
+ {
+ desc: "reference update with expected commit ID",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
- repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
- commitID := gittest.WriteCommit(t, cfg, repoPath)
+ oldCommit := gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("branch"))
+ newCommit := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(oldCommit))
- for _, tc := range []struct {
- desc string
- request *gitalypb.WriteRefRequest
- expectedErr error
- }{
- {
- desc: "revision refers to missing reference",
- request: &gitalypb.WriteRefRequest{
- Repository: repo,
- Ref: []byte("refs/heads/main"),
- Revision: []byte("refs/heads/missing"),
+ return setupData{
+ request: &gitalypb.WriteRefRequest{
+ Repository: repo,
+ Ref: []byte("refs/heads/branch"),
+ Revision: []byte(newCommit),
+ OldRevision: []byte(oldCommit),
+ },
+ expectedRefs: []git.Reference{
+ git.NewSymbolicReference("HEAD", git.DefaultRef),
+ git.NewReference("refs/heads/branch", newCommit),
+ },
+ expectedVotes: votes("refs/heads/branch", oldCommit, newCommit),
+ }
},
- expectedErr: structerr.NewInternal("resolving new revision: reference not found"),
},
{
- desc: "revision refers to missing object",
- request: &gitalypb.WriteRefRequest{
- Repository: repo,
- Ref: []byte("refs/heads/main"),
- Revision: bytes.Repeat([]byte("1"), gittest.DefaultObjectHash.EncodedLen()),
+ desc: "reference creation with expected commit ID",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
+
+ commitID := gittest.WriteCommit(t, cfg, repoPath)
+
+ return setupData{
+ request: &gitalypb.WriteRefRequest{
+ Repository: repo,
+ Ref: []byte("refs/heads/branch"),
+ Revision: []byte(commitID),
+ OldRevision: []byte(gittest.DefaultObjectHash.ZeroOID),
+ },
+ expectedRefs: []git.Reference{
+ git.NewSymbolicReference("HEAD", git.DefaultRef),
+ git.NewReference("refs/heads/branch", commitID),
+ },
+ expectedVotes: votes("refs/heads/branch", gittest.DefaultObjectHash.ZeroOID, commitID),
+ }
},
- expectedErr: structerr.NewInternal("resolving new revision: reference not found"),
},
{
- desc: "old revision refers to missing reference",
- request: &gitalypb.WriteRefRequest{
- Repository: repo,
- Ref: []byte("refs/heads/main"),
- Revision: []byte(commitID),
- OldRevision: bytes.Repeat([]byte("1"), gittest.DefaultObjectHash.EncodedLen()),
+ desc: "reference deletion with expected commit ID",
+ setup: func(t *testing.T) setupData {
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg)
+
+ commitID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithBranch("branch"))
+
+ return setupData{
+ request: &gitalypb.WriteRefRequest{
+ Repository: repo,
+ Ref: []byte("refs/heads/branch"),
+ Revision: []byte(gittest.DefaultObjectHash.ZeroOID),
+ OldRevision: []byte(commitID),
+ },
+ expectedRefs: []git.Reference{
+ git.NewSymbolicReference("HEAD", git.DefaultRef),
+ },
+ expectedVotes: votes("refs/heads/branch", commitID, gittest.DefaultObjectHash.ZeroOID),
+ }
},
- expectedErr: structerr.NewInternal("resolving old revision: reference not found"),
},
} {
t.Run(tc.desc, func(t *testing.T) {
- _, err := client.WriteRef(ctx, tc.request)
- testhelper.RequireGrpcError(t, tc.expectedErr, err)
+ setup := tc.setup(t)
+
+ txManager.Reset()
+
+ ctx, err := txinfo.InjectTransaction(ctx, 1, "node", true)
+ require.NoError(t, err)
+ ctx = metadata.IncomingToOutgoing(ctx)
+
+ _, err = client.WriteRef(ctx, setup.request)
+ testhelper.RequireGrpcError(t, setup.expectedErr, err)
+
+ repo := localrepo.NewTestRepo(t, cfg, setup.request.GetRepository())
+ refs, err := repo.GetReferences(ctx)
+ require.NoError(t, err)
+ defaultBranch, err := repo.HeadReference(ctx)
+ require.NoError(t, err)
+ require.Equal(t, setup.expectedRefs, append([]git.Reference{
+ git.NewSymbolicReference("HEAD", defaultBranch),
+ }, refs...))
+
+ require.Equal(t, setup.expectedVotes, txManager.Votes())
})
}
}