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:
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--.ruby-version2
-rw-r--r--.tool-versions2
-rw-r--r--Makefile7
-rw-r--r--doc/PROCESS.md45
-rw-r--r--doc/help_text_style_guide.md2
-rw-r--r--internal/backup/backup.go54
-rw-r--r--internal/backup/backup_test.go115
-rw-r--r--internal/backup/pipeline.go34
-rw-r--r--internal/backup/pipeline_test.go61
-rw-r--r--internal/backup/server_side.go56
-rw-r--r--internal/backup/server_side_test.go125
-rw-r--r--internal/cli/gitalybackup/create.go2
-rw-r--r--internal/cli/gitalybackup/restore.go34
-rw-r--r--internal/cli/gitalybackup/restore_test.go20
-rw-r--r--internal/featureflag/ff_exec_command_directly_in_cgroup.go2
-rw-r--r--internal/featureflag/ff_return_structed_errors_in_revert.go13
-rw-r--r--internal/gitaly/repoutil/create.go17
-rw-r--r--internal/gitaly/repoutil/create_test.go66
-rw-r--r--internal/gitaly/service/operations/revert.go78
-rw-r--r--internal/gitaly/service/operations/revert_test.go118
-rw-r--r--internal/gitaly/service/repository/remove_all_test.go1
-rw-r--r--internal/gitaly/storage/storagemgr/testhelper_test.go100
-rw-r--r--internal/gitaly/storage/storagemgr/transaction_manager.go464
-rw-r--r--internal/gitaly/storage/storagemgr/transaction_manager_housekeeping_test.go1214
-rw-r--r--internal/gitaly/storage/storagemgr/transaction_manager_test.go426
-rw-r--r--internal/praefect/datastore/repository_store.go27
-rw-r--r--internal/praefect/remove_all.go1
-rw-r--r--internal/praefect/remove_all_test.go1
-rw-r--r--internal/praefect/server.go3
-rw-r--r--internal/praefect/walkrepos.go47
-rw-r--r--internal/praefect/walkrepos_test.go88
-rw-r--r--proto/go/gitalypb/log.pb.go259
-rw-r--r--proto/go/gitalypb/operations.pb.go1328
-rw-r--r--proto/go/gitalypb/repository.pb.go52
-rw-r--r--proto/go/gitalypb/repository_grpc.pb.go5
-rw-r--r--proto/log.proto18
-rw-r--r--proto/operations.proto14
-rw-r--r--proto/repository.proto2
-rw-r--r--tools/protoc-gen-doc/go.mod4
-rw-r--r--tools/protoc-gen-doc/go.sum17
-rwxr-xr-xtools/protogem/build-proto-gem34
-rw-r--r--tools/protolint/go.mod12
-rw-r--r--tools/protolint/go.sum12
44 files changed, 4055 insertions, 929 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 178c7a3b4..e321f29ca 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -22,7 +22,7 @@ default:
pgbouncer: "1.17.0"
postgres_default: "12-alpine"
postgres_minimum: "11-alpine"
- ruby: "3.1"
+ ruby: "3.2"
rust: "1.65"
ubi: "8.6"
diff --git a/.ruby-version b/.ruby-version
index 0aec50e6e..be94e6f53 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-3.1.4
+3.2.2
diff --git a/.tool-versions b/.tool-versions
index 9b11817d4..4578c3c20 100644
--- a/.tool-versions
+++ b/.tool-versions
@@ -1,3 +1,3 @@
# Versions of Gitaly dependencies managed by asdf.
golang 1.21.4 1.20.10
-ruby 3.1.4
+ruby 3.2.2
diff --git a/Makefile b/Makefile
index fb5a9904a..b5a757c30 100644
--- a/Makefile
+++ b/Makefile
@@ -195,6 +195,8 @@ TEST_REPO_DIR := ${BUILD_DIR}/testrepos
BENCHMARK_REPO := ${TEST_REPO_DIR}/benchmark.git
## Options to pass to the script which builds the Gitaly gem
BUILD_GEM_OPTIONS ?=
+## Options to override the name of Gitaly gem
+BUILD_GEM_NAME ?= gitaly
# All executables provided by Gitaly.
GITALY_EXECUTABLES = $(addprefix ${BUILD_DIR}/bin/,$(notdir $(shell find ${SOURCE_DIR}/cmd -mindepth 1 -maxdepth 1 -type d -print)))
@@ -465,9 +467,10 @@ lint-proto: ${PROTOC} ${PROTOLINT} ${PROTOC_GEN_GITALY_LINT}
.PHONY: build-proto-gem
## Build the Ruby Gem that contains Gitaly's Protobuf definitons.
build-proto-gem:
- ${Q}rm -rf "${BUILD_DIR}/gitaly.gem" && mkdir -p ${BUILD_DIR}
+ ${Q}rm -rf "${BUILD_DIR}/${BUILD_GEM_NAME}.gem" && mkdir -p ${BUILD_DIR}
+ ${Q}rm -rf "${BUILD_DIR}/${BUILD_GEM_NAME}-gem" && mkdir -p ${BUILD_DIR}/${BUILD_GEM_NAME}-gem
${Q}cd "${SOURCE_DIR}"/tools/protogem && bundle install
- ${Q}"${SOURCE_DIR}"/tools/protogem/build-proto-gem -o "${BUILD_DIR}/gitaly.gem" ${BUILD_GEM_OPTIONS}
+ ${Q}"${SOURCE_DIR}"/tools/protogem/build-proto-gem -o "${BUILD_DIR}/${BUILD_GEM_NAME}.gem" --name ${BUILD_GEM_NAME} --working-dir ${BUILD_DIR}/${BUILD_GEM_NAME}-gem ${BUILD_GEM_OPTIONS}
.PHONY: publish-proto-gem
## Build and publish the Ruby Gem that contains Gitaly's Protobuf definitons.
diff --git a/doc/PROCESS.md b/doc/PROCESS.md
index ba695920a..c266071b5 100644
--- a/doc/PROCESS.md
+++ b/doc/PROCESS.md
@@ -667,6 +667,51 @@ If the changes needed are not yet released, [create a release candidate](#creati
- Run `make publish-proto-gem`, which automatically builds any publishes the
Protobuf Gem.
+## Using an unpublished Gem in GitLab Rails
+
+Sometimes, we need to work on both Gitaly and GitLab Rails implementations in parallel. Ideally, we can define and
+publish all Protobuf changes beforehand. In practice, a complicated change might require modifying the Protobuf multiple
+times until reaching a stable state. It's more convenient to let GitLab Rails point the gem to the the developing branch
+in Gitaly.
+
+In the local environment, we can point it to the developing gem using following steps:
+
+- In Gitaly directory, run `BUILD_GEM_OPTIONS="--skip-verify-tag" make build-proto-gem`. This command
+ builds gem at `_built/gitaly.gem` and the gem contents to the `_build/gitaly-gem` directory.
+- Modify GitLab Rails' Gemfile:
+
+ ```ruby
+ gem 'gitaly', path: '/path/to/gitaly/_build/gitaly-gem'
+ ```
+
+- Re-run bundler to update the gem.
+
+Unfortunately, the Gitaly repository does not include the gemspec or any auto-generated Ruby code. All of it is
+generated on the fly while running `make build-proto-gem`. Thus, we cannot point the `Gemfile` to the developing branch
+directly on CI environment.
+
+As a workaround, we can build and publish the gem under a different name for development purpose only. Here's [an
+example](https://rubygems.org/gems/gitaly-qmnguyen).
+
+- Run `BUILD_GEM_OPTIONS="--skip-verify-tag" BUILD_GEM_NAME="gitaly-yourname" make build-proto-gem`. This command
+ builds the gem under a different name. The resulting gem is located at `_build/gitaly-yourname.gem`.
+- Run `gem push _build/gitaly-yourname.gem` to publish that custom gem to rubygems.org or similar package registry.
+- Modify `gitaly` gem in GitLab Rails' `Gemfile`:
+
+ ```ruby
+ gem 'gitaly-yourname'
+ ```
+
+ Or, we can specify a particular version.
+
+ ```ruby
+ gem 'gitaly-yourname', '~> 16.7.0.pre.0f0db5710'
+ ```
+
+After the development phase, please remember to remove this workaround and restore the `Gemfile` to the official Gitaly gem release.
+
+Note: Instead of setting `BUILD_GEM_OPTIONS` and `BUILD_GEM_NAME` in every command, we can also set them in `config.mak`.
+
## Publishing the go module
If an [updated version](https://golang.org/doc/modules/release-workflow) of the go module is needed, it can be [published](https://golang.org/doc/modules/publishing)
diff --git a/doc/help_text_style_guide.md b/doc/help_text_style_guide.md
index 529df6985..22d170b58 100644
--- a/doc/help_text_style_guide.md
+++ b/doc/help_text_style_guide.md
@@ -112,7 +112,7 @@ When providing values for the `Description` field key:
## Related topics
-- [Voice and tone](https://design.gitlab.com/content/voice-and-tone) from GitLab Design System.
+- [UI text](https://design.gitlab.com/content/ui-text) from GitLab Design System.
- [Language](https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#language) from GitLab
Documentation Style Guide.
- [Command Line Interface Guidelines](https://clig.dev).
diff --git a/internal/backup/backup.go b/internal/backup/backup.go
index 5eed0724a..a15f47abe 100644
--- a/internal/backup/backup.go
+++ b/internal/backup/backup.go
@@ -211,25 +211,56 @@ func NewManagerLocal(
}
}
-// RemoveAllRepositories removes all repositories in the specified storage name.
-func (mgr *Manager) RemoveAllRepositories(ctx context.Context, req *RemoveAllRepositoriesRequest) error {
- if err := setContextServerInfo(ctx, &req.Server, req.StorageName); err != nil {
- return fmt.Errorf("manager: %w", err)
+// RemoveRepository removes the specified repository from its storage.
+func (mgr *Manager) RemoveRepository(ctx context.Context, req *RemoveRepositoryRequest) error {
+ if err := setContextServerInfo(ctx, &req.Server, req.Repo.StorageName); err != nil {
+ return fmt.Errorf("remove repo: set context: %w", err)
}
repoClient, err := mgr.newRepoClient(ctx, req.Server)
if err != nil {
- return fmt.Errorf("manager: %w", err)
+ return fmt.Errorf("remove repo: create client: %w", err)
}
- _, err = repoClient.RemoveAll(ctx, &gitalypb.RemoveAllRequest{StorageName: req.StorageName})
+ _, err = repoClient.RemoveRepository(ctx, &gitalypb.RemoveRepositoryRequest{Repository: req.Repo})
if err != nil {
- return fmt.Errorf("manager: %w", err)
+ return fmt.Errorf("remove repo: remove: %w", err)
}
return nil
}
+// ListRepositories returns a list of repositories found in the given storage.
+func (mgr *Manager) ListRepositories(ctx context.Context, req *ListRepositoriesRequest) (repos []*gitalypb.Repository, err error) {
+ if err := setContextServerInfo(ctx, &req.Server, req.StorageName); err != nil {
+ return nil, fmt.Errorf("list repos: set context: %w", err)
+ }
+
+ internalClient, err := mgr.newInternalClient(ctx, req.Server)
+ if err != nil {
+ return nil, fmt.Errorf("list repos: create client: %w", err)
+ }
+
+ stream, err := internalClient.WalkRepos(ctx, &gitalypb.WalkReposRequest{StorageName: req.StorageName})
+ if err != nil {
+ return nil, fmt.Errorf("list repos: walk: %w", err)
+ }
+
+ for {
+ resp, err := stream.Recv()
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ return nil, fmt.Errorf("list repos: receiving messages: %w", err)
+ }
+
+ repos = append(repos, &gitalypb.Repository{RelativePath: resp.RelativePath, StorageName: req.StorageName})
+ }
+
+ return repos, nil
+}
+
// Create creates a repository backup.
func (mgr *Manager) Create(ctx context.Context, req *CreateRequest) error {
if req.VanityRepository == nil {
@@ -573,3 +604,12 @@ func (mgr *Manager) newRepoClient(ctx context.Context, server storage.ServerInfo
return gitalypb.NewRepositoryServiceClient(conn), nil
}
+
+func (mgr *Manager) newInternalClient(ctx context.Context, server storage.ServerInfo) (gitalypb.InternalGitalyClient, error) {
+ conn, err := mgr.conns.Dial(ctx, server.Address, server.Token)
+ if err != nil {
+ return nil, err
+ }
+
+ return gitalypb.NewInternalGitalyClient(conn), nil
+}
diff --git a/internal/backup/backup_test.go b/internal/backup/backup_test.go
index 14341ec6a..81e87a33a 100644
--- a/internal/backup/backup_test.go
+++ b/internal/backup/backup_test.go
@@ -28,13 +28,10 @@ import (
"google.golang.org/protobuf/proto"
)
-func TestManager_RemoveAllRepositories(t *testing.T) {
- testhelper.SkipWithWAL(t, `
-RemoveAll is removing the entire content of the storage. This would also remove the database's and
-the transaction manager's disk state. The RPC needs to be updated to shut down all partitions and
-the database and only then perform the removal.
-
-Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`)
+func TestManager_RemoveRepository(t *testing.T) {
+ if testhelper.IsPraefectEnabled() {
+ t.Skip("local backup manager expects to operate on the local filesystem so cannot operate through praefect")
+ }
t.Parallel()
@@ -58,11 +55,109 @@ Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`)
require.NoError(t, err)
fsBackup := backup.NewManager(sink, locator, pool)
- err = fsBackup.RemoveAllRepositories(ctx, &backup.RemoveAllRepositoriesRequest{
- Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token},
- StorageName: repo.StorageName,
+ err = fsBackup.RemoveRepository(ctx, &backup.RemoveRepositoryRequest{
+ Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token},
+ Repo: repo,
})
require.NoError(t, err)
+ require.NoDirExists(t, repoPath)
+
+ // With an invalid repository
+ err = fsBackup.RemoveRepository(ctx, &backup.RemoveRepositoryRequest{
+ Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token},
+ Repo: &gitalypb.Repository{StorageName: "nonexistent", RelativePath: "nonexistent"},
+ })
+
+ require.EqualError(t, err, "remove repo: remove: rpc error: code = InvalidArgument desc = storage name not found")
+}
+
+func TestManager_ListRepositories(t *testing.T) {
+ t.Parallel()
+
+ for _, tc := range []struct {
+ desc string
+ repos map[string][]*gitalypb.Repository
+ }{
+ {
+ desc: "no repos",
+ repos: make(map[string][]*gitalypb.Repository),
+ },
+ {
+ desc: "repos in a single storage",
+ repos: map[string][]*gitalypb.Repository{
+ "storage-1": {
+ {RelativePath: "a", StorageName: "storage-1"},
+ {RelativePath: "b", StorageName: "storage-1"},
+ },
+ },
+ },
+ {
+ desc: "repos in multiple storages",
+ repos: map[string][]*gitalypb.Repository{
+ "storage-1": {
+ {RelativePath: "a", StorageName: "storage-1"},
+ {RelativePath: "b", StorageName: "storage-1"},
+ },
+ "storage-2": {
+ {RelativePath: "c", StorageName: "storage-2"},
+ {RelativePath: "d", StorageName: "storage-2"},
+ },
+ },
+ },
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ if testhelper.IsPraefectEnabled() {
+ t.Skip("local backup manager expects to operate on the local filesystem so cannot operate through praefect")
+ }
+
+ var storages []string
+ for storageName := range tc.repos {
+ storages = append(storages, storageName)
+ }
+
+ // We don't really need a "default" storage, but this makes initialisation cleaner since
+ // WithStorages() takes at least one argument.
+ cfg := testcfg.Build(t, testcfg.WithStorages("default", storages...))
+ cfg.SocketPath = testserver.RunGitalyServer(t, cfg, setup.RegisterAll)
+
+ ctx := testhelper.Context(t)
+
+ for storageName, repos := range tc.repos {
+ for _, repo := range repos {
+ storagePath, ok := cfg.StoragePath(storageName)
+ require.True(t, ok)
+
+ _, _ = gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{
+ SkipCreationViaService: true,
+ RelativePath: repo.RelativePath,
+ Storage: config.Storage{Name: storageName, Path: storagePath},
+ })
+ }
+ }
+
+ pool := client.NewPool()
+ defer testhelper.MustClose(t, pool)
+
+ backupRoot := testhelper.TempDir(t)
+ sink := backup.NewFilesystemSink(backupRoot)
+ defer testhelper.MustClose(t, sink)
+
+ locator, err := backup.ResolveLocator("pointer", sink)
+ require.NoError(t, err)
+
+ fsBackup := backup.NewManager(sink, locator, pool)
+
+ for storageName, repos := range tc.repos {
+ actualRepos, err := fsBackup.ListRepositories(ctx, &backup.ListRepositoriesRequest{
+ Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token},
+ StorageName: storageName,
+ })
+
+ require.NoError(t, err)
+ require.EqualValues(t, repos, actualRepos)
+ }
+ })
+ }
}
func TestManager_Create(t *testing.T) {
diff --git a/internal/backup/pipeline.go b/internal/backup/pipeline.go
index 5672493ac..3eb91de91 100644
--- a/internal/backup/pipeline.go
+++ b/internal/backup/pipeline.go
@@ -16,7 +16,8 @@ import (
type Strategy interface {
Create(context.Context, *CreateRequest) error
Restore(context.Context, *RestoreRequest) error
- RemoveAllRepositories(context.Context, *RemoveAllRepositoriesRequest) error
+ ListRepositories(context.Context, *ListRepositoriesRequest) ([]*gitalypb.Repository, error)
+ RemoveRepository(context.Context, *RemoveRepositoryRequest) error
}
// CreateRequest is the request to create a backup
@@ -52,9 +53,14 @@ type RestoreRequest struct {
BackupID string
}
-// RemoveAllRepositoriesRequest is the request to remove all repositories in the specified
-// storage name.
-type RemoveAllRepositoriesRequest struct {
+// RemoveRepositoryRequest is a request to remove an individual repository from its storage.
+type RemoveRepositoryRequest struct {
+ Server storage.ServerInfo
+ Repo *gitalypb.Repository
+}
+
+// ListRepositoriesRequest is the request to list repositories in a given storage.
+type ListRepositoriesRequest struct {
Server storage.ServerInfo
StorageName string
}
@@ -181,6 +187,9 @@ type Pipeline struct {
pipelineError error
cmdErrors *commandErrors
+
+ processedRepos map[string]map[*gitalypb.Repository]struct{}
+ processedReposMu sync.Mutex
}
// NewPipeline creates a pipeline that executes backup and restore jobs.
@@ -195,6 +204,7 @@ func NewPipeline(log log.Logger, opts ...PipelineOption) (*Pipeline, error) {
done: make(chan struct{}),
workersByStorage: make(map[string]chan *contextCommand),
cmdErrors: &commandErrors{},
+ processedRepos: make(map[string]map[*gitalypb.Repository]struct{}),
}
for _, opt := range opts {
@@ -252,19 +262,19 @@ func (p *Pipeline) Handle(ctx context.Context, cmd Command) {
}
// Done waits for any in progress jobs to complete then reports any accumulated errors
-func (p *Pipeline) Done() error {
+func (p *Pipeline) Done() (processedRepos map[string]map[*gitalypb.Repository]struct{}, err error) {
close(p.done)
p.workerWg.Wait()
if p.pipelineError != nil {
- return fmt.Errorf("pipeline: %w", p.pipelineError)
+ return nil, fmt.Errorf("pipeline: %w", p.pipelineError)
}
if len(p.cmdErrors.errs) > 0 {
- return fmt.Errorf("pipeline: %w", p.cmdErrors)
+ return nil, fmt.Errorf("pipeline: %w", p.cmdErrors)
}
- return nil
+ return p.processedRepos, nil
}
// getWorker finds the channel associated with a storage. When no channel is
@@ -325,6 +335,14 @@ func (p *Pipeline) processCommand(ctx context.Context, cmd Command) {
return
}
+ storageName := cmd.Repository().StorageName
+ p.processedReposMu.Lock()
+ if _, ok := p.processedRepos[storageName]; !ok {
+ p.processedRepos[storageName] = make(map[*gitalypb.Repository]struct{})
+ }
+ p.processedRepos[storageName][cmd.Repository()] = struct{}{}
+ p.processedReposMu.Unlock()
+
log.Info(fmt.Sprintf("completed %s", cmd.Name()))
}
diff --git a/internal/backup/pipeline_test.go b/internal/backup/pipeline_test.go
index 04c539a5f..37f7c2e5b 100644
--- a/internal/backup/pipeline_test.go
+++ b/internal/backup/pipeline_test.go
@@ -98,7 +98,8 @@ func TestPipeline(t *testing.T) {
p.Handle(ctx, NewCreateCommand(strategy, CreateRequest{Repository: &gitalypb.Repository{StorageName: "storage1"}}))
p.Handle(ctx, NewCreateCommand(strategy, CreateRequest{Repository: &gitalypb.Repository{StorageName: "storage2"}}))
}
- require.NoError(t, p.Done())
+ _, err = p.Done()
+ require.NoError(t, err)
})
}
})
@@ -115,14 +116,16 @@ func TestPipeline(t *testing.T) {
p.Handle(ctx, NewCreateCommand(strategy, CreateRequest{Repository: &gitalypb.Repository{StorageName: "default"}}))
- require.EqualError(t, p.Done(), "pipeline: context canceled")
+ _, err = p.Done()
+ require.EqualError(t, err, "pipeline: context canceled")
})
}
type MockStrategy struct {
- CreateFunc func(context.Context, *CreateRequest) error
- RestoreFunc func(context.Context, *RestoreRequest) error
- RemoveAllRepositoriesFunc func(context.Context, *RemoveAllRepositoriesRequest) error
+ CreateFunc func(context.Context, *CreateRequest) error
+ RestoreFunc func(context.Context, *RestoreRequest) error
+ RemoveRepositoryFunc func(context.Context, *RemoveRepositoryRequest) error
+ ListRepositoriesFunc func(context.Context, *ListRepositoriesRequest) ([]*gitalypb.Repository, error)
}
func (s MockStrategy) Create(ctx context.Context, req *CreateRequest) error {
@@ -139,13 +142,20 @@ func (s MockStrategy) Restore(ctx context.Context, req *RestoreRequest) error {
return nil
}
-func (s MockStrategy) RemoveAllRepositories(ctx context.Context, req *RemoveAllRepositoriesRequest) error {
- if s.RemoveAllRepositoriesFunc != nil {
- return s.RemoveAllRepositoriesFunc(ctx, req)
+func (s MockStrategy) RemoveRepository(ctx context.Context, req *RemoveRepositoryRequest) error {
+ if s.RemoveRepositoryFunc != nil {
+ return s.RemoveRepositoryFunc(ctx, req)
}
return nil
}
+func (s MockStrategy) ListRepositories(ctx context.Context, req *ListRepositoriesRequest) ([]*gitalypb.Repository, error) {
+ if s.ListRepositoriesFunc != nil {
+ return s.ListRepositoriesFunc(ctx, req)
+ }
+ return nil, nil
+}
+
func testPipeline(t *testing.T, init func() *Pipeline) {
strategy := MockStrategy{
CreateFunc: func(_ context.Context, req *CreateRequest) error {
@@ -222,7 +232,7 @@ func testPipeline(t *testing.T, init func() *Pipeline) {
require.Equal(t, tc.level, logEntry.Level)
}
- err := p.Done()
+ _, err := p.Done()
if tc.level == logrus.ErrorLevel {
require.EqualError(t, err, "pipeline: 1 failures encountered:\n - c.git: assert.AnError general error for testing\n")
@@ -258,7 +268,7 @@ func testPipeline(t *testing.T, init func() *Pipeline) {
for _, cmd := range commands {
p.Handle(ctx, cmd)
}
- err := p.Done()
+ _, err := p.Done()
require.EqualError(t, err, "pipeline: 1 failures encountered:\n - c.git: assert.AnError general error for testing\n")
})
}
@@ -309,3 +319,34 @@ func TestPipelineError(t *testing.T) {
})
}
}
+
+func TestPipelineProcessedRepos(t *testing.T) {
+ strategy := MockStrategy{}
+
+ repos := map[string]map[*gitalypb.Repository]struct{}{
+ "storage1": {
+ &gitalypb.Repository{RelativePath: "a.git", StorageName: "storage1"}: struct{}{},
+ &gitalypb.Repository{RelativePath: "b.git", StorageName: "storage1"}: struct{}{},
+ },
+ "storage2": {
+ &gitalypb.Repository{RelativePath: "c.git", StorageName: "storage2"}: struct{}{},
+ },
+ "storage3": {
+ &gitalypb.Repository{RelativePath: "d.git", StorageName: "storage3"}: struct{}{},
+ },
+ }
+
+ p, err := NewPipeline(testhelper.SharedLogger(t))
+ require.NoError(t, err)
+
+ ctx := testhelper.Context(t)
+ for _, v := range repos {
+ for repo := range v {
+ p.Handle(ctx, NewRestoreCommand(strategy, RestoreRequest{Repository: repo}))
+ }
+ }
+
+ processedRepos, err := p.Done()
+ require.NoError(t, err)
+ require.EqualValues(t, repos, processedRepos)
+}
diff --git a/internal/backup/server_side.go b/internal/backup/server_side.go
index 35654f215..a1a9a37eb 100644
--- a/internal/backup/server_side.go
+++ b/internal/backup/server_side.go
@@ -2,7 +2,9 @@ package backup
import (
"context"
+ "errors"
"fmt"
+ "io"
"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage"
"gitlab.com/gitlab-org/gitaly/v16/internal/grpc/client"
@@ -93,25 +95,56 @@ func (ss ServerSideAdapter) Restore(ctx context.Context, req *RestoreRequest) er
return nil
}
-// RemoveAllRepositories removes all repositories in the specified storage name.
-func (ss ServerSideAdapter) RemoveAllRepositories(ctx context.Context, req *RemoveAllRepositoriesRequest) error {
- if err := setContextServerInfo(ctx, &req.Server, req.StorageName); err != nil {
- return fmt.Errorf("server-side remove all: %w", err)
+// RemoveRepository removes the specified repository from its storage.
+func (ss ServerSideAdapter) RemoveRepository(ctx context.Context, req *RemoveRepositoryRequest) error {
+ if err := setContextServerInfo(ctx, &req.Server, req.Repo.StorageName); err != nil {
+ return fmt.Errorf("server-side remove repo: set context: %w", err)
}
repoClient, err := ss.newRepoClient(ctx, req.Server)
if err != nil {
- return fmt.Errorf("server-side remove all: %w", err)
+ return fmt.Errorf("server-side remove repo: create client: %w", err)
}
- _, err = repoClient.RemoveAll(ctx, &gitalypb.RemoveAllRequest{StorageName: req.StorageName})
+ _, err = repoClient.RemoveRepository(ctx, &gitalypb.RemoveRepositoryRequest{Repository: req.Repo})
if err != nil {
- return fmt.Errorf("server-side remove all: %w", err)
+ return fmt.Errorf("server-side remove repo: remove: %w", err)
}
return nil
}
+// ListRepositories returns a list of repositories found in the given storage.
+func (ss ServerSideAdapter) ListRepositories(ctx context.Context, req *ListRepositoriesRequest) (repos []*gitalypb.Repository, err error) {
+ if err := setContextServerInfo(ctx, &req.Server, req.StorageName); err != nil {
+ return nil, fmt.Errorf("server-side list repos: set context: %w", err)
+ }
+
+ internalClient, err := ss.newInternalClient(ctx, req.Server)
+ if err != nil {
+ return nil, fmt.Errorf("server-side list repos: create client: %w", err)
+ }
+
+ stream, err := internalClient.WalkRepos(ctx, &gitalypb.WalkReposRequest{StorageName: req.StorageName})
+ if err != nil {
+ return nil, fmt.Errorf("server-side list repos: walk: %w", err)
+ }
+
+ for {
+ resp, err := stream.Recv()
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ repos = append(repos, &gitalypb.Repository{RelativePath: resp.RelativePath, StorageName: req.StorageName})
+ }
+
+ return repos, nil
+}
+
func (ss ServerSideAdapter) newRepoClient(ctx context.Context, server storage.ServerInfo) (gitalypb.RepositoryServiceClient, error) {
conn, err := ss.pool.Dial(ctx, server.Address, server.Token)
if err != nil {
@@ -120,3 +153,12 @@ func (ss ServerSideAdapter) newRepoClient(ctx context.Context, server storage.Se
return gitalypb.NewRepositoryServiceClient(conn), nil
}
+
+func (ss ServerSideAdapter) newInternalClient(ctx context.Context, server storage.ServerInfo) (gitalypb.InternalGitalyClient, error) {
+ conn, err := ss.pool.Dial(ctx, server.Address, server.Token)
+ if err != nil {
+ return nil, err
+ }
+
+ return gitalypb.NewInternalGitalyClient(conn), nil
+}
diff --git a/internal/backup/server_side_test.go b/internal/backup/server_side_test.go
index 2acc547c5..69669df60 100644
--- a/internal/backup/server_side_test.go
+++ b/internal/backup/server_side_test.go
@@ -13,9 +13,11 @@ import (
"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/service/setup"
"gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage"
"gitlab.com/gitlab-org/gitaly/v16/internal/grpc/client"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/praefect/datastore"
"gitlab.com/gitlab-org/gitaly/v16/internal/structerr"
"gitlab.com/gitlab-org/gitaly/v16/internal/testhelper"
"gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testcfg"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testdb"
"gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testserver"
"gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
)
@@ -257,28 +259,15 @@ func TestServerSideAdapter_Restore(t *testing.T) {
}
}
-func TestServerSideAdapter_RemoveAllRepositories(t *testing.T) {
- testhelper.SkipWithWAL(t, `
-RemoveAll is removing the entire content of the storage. This would also remove the database's and
-the transaction manager's disk state. The RPC needs to be updated to shut down all partitions and
-the database and only then perform the removal.
-
-Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`)
-
+func TestServerSideAdapter_RemoveRepository(t *testing.T) {
t.Parallel()
- backupRoot := testhelper.TempDir(t)
- sink := backup.NewFilesystemSink(backupRoot)
- defer testhelper.MustClose(t, sink)
-
- locator, err := backup.ResolveLocator("pointer", sink)
- require.NoError(t, err)
+ db := testdb.New(t)
+ db.TruncateAll(t)
+ datastore.NewPostgresRepositoryStore(db, map[string][]string{"virtual-storage": {"default"}})
cfg := testcfg.Build(t)
- cfg.SocketPath = testserver.RunGitalyServer(t, cfg, setup.RegisterAll,
- testserver.WithBackupSink(sink),
- testserver.WithBackupLocator(locator),
- )
+ cfg.SocketPath = testserver.RunGitalyServer(t, cfg, setup.RegisterAll)
ctx := testhelper.Context(t)
@@ -289,9 +278,103 @@ Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`)
defer testhelper.MustClose(t, pool)
adapter := backup.NewServerSideAdapter(pool)
- err = adapter.RemoveAllRepositories(ctx, &backup.RemoveAllRepositoriesRequest{
- Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token},
- StorageName: repo.StorageName,
+ err := adapter.RemoveRepository(ctx, &backup.RemoveRepositoryRequest{
+ Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token},
+ Repo: repo,
})
require.NoError(t, err)
+ require.NoDirExists(t, repoPath)
+
+ // With an invalid repository
+ err = adapter.RemoveRepository(ctx, &backup.RemoveRepositoryRequest{
+ Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token},
+ Repo: &gitalypb.Repository{StorageName: "default", RelativePath: "nonexistent"},
+ })
+
+ require.EqualError(t, err, "server-side remove repo: remove: rpc error: code = NotFound desc = repository does not exist")
+}
+
+func TestServerSideAdapter_ListRepositories(t *testing.T) {
+ t.Parallel()
+
+ for _, tc := range []struct {
+ desc string
+ repos map[string][]*gitalypb.Repository
+ }{
+ {
+ desc: "no repos",
+ repos: make(map[string][]*gitalypb.Repository),
+ },
+ {
+ desc: "repos in a single storage",
+ repos: map[string][]*gitalypb.Repository{
+ "storage-1": {
+ {RelativePath: "a", StorageName: "storage-1"},
+ {RelativePath: "b", StorageName: "storage-1"},
+ },
+ },
+ },
+ {
+ desc: "repos in multiple storages",
+ repos: map[string][]*gitalypb.Repository{
+ "storage-1": {
+ {RelativePath: "a", StorageName: "storage-1"},
+ {RelativePath: "b", StorageName: "storage-1"},
+ },
+ "storage-2": {
+ {RelativePath: "c", StorageName: "storage-2"},
+ {RelativePath: "d", StorageName: "storage-2"},
+ },
+ },
+ },
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ var storages []string
+ for storageName := range tc.repos {
+ storages = append(storages, storageName)
+ }
+
+ db := testdb.New(t)
+ db.TruncateAll(t)
+ rs := datastore.NewPostgresRepositoryStore(db, map[string][]string{"virtual-storage": storages})
+
+ // We don't really need a "default" storage, but this makes initialisation cleaner since
+ // WithStorages() takes at least one argument.
+ cfg := testcfg.Build(t, testcfg.WithStorages("default", storages...))
+ cfg.SocketPath = testserver.RunGitalyServer(t, cfg, setup.RegisterAll)
+
+ ctx := testhelper.Context(t)
+
+ repoID := 1
+ for storageName, repos := range tc.repos {
+ for _, repo := range repos {
+ storagePath, ok := cfg.StoragePath(storageName)
+ require.True(t, ok)
+
+ _, _ = gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{
+ RelativePath: repo.RelativePath,
+ Storage: config.Storage{Name: storageName, Path: storagePath},
+ })
+
+ require.NoError(t, rs.CreateRepository(ctx, int64(repoID), "virtual-storage", repo.RelativePath, repo.RelativePath, storageName, nil, nil, false, false))
+
+ repoID++
+ }
+ }
+
+ pool := client.NewPool()
+ defer testhelper.MustClose(t, pool)
+
+ adapter := backup.NewServerSideAdapter(pool)
+
+ for storageName, repos := range tc.repos {
+ actualRepos, err := adapter.ListRepositories(ctx, &backup.ListRepositoriesRequest{
+ Server: storage.ServerInfo{Address: cfg.SocketPath, Token: cfg.Auth.Token},
+ StorageName: storageName,
+ })
+ require.NoError(t, err)
+ require.EqualValues(t, repos, actualRepos)
+ }
+ })
+ }
}
diff --git a/internal/cli/gitalybackup/create.go b/internal/cli/gitalybackup/create.go
index 4ad653490..be7e75cdb 100644
--- a/internal/cli/gitalybackup/create.go
+++ b/internal/cli/gitalybackup/create.go
@@ -174,7 +174,7 @@ func (cmd *createSubcommand) run(ctx context.Context, logger log.Logger, stdin i
}))
}
- if err := pipeline.Done(); err != nil {
+ if _, err := pipeline.Done(); err != nil {
return fmt.Errorf("create: %w", err)
}
return nil
diff --git a/internal/cli/gitalybackup/restore.go b/internal/cli/gitalybackup/restore.go
index de9e2cd3d..defffa223 100644
--- a/internal/cli/gitalybackup/restore.go
+++ b/internal/cli/gitalybackup/restore.go
@@ -135,15 +135,18 @@ func (cmd *restoreSubcommand) run(ctx context.Context, logger log.Logger, stdin
manager = backup.NewManager(sink, locator, pool)
}
+ // Get the set of existing repositories keyed by storage. We'll later use this to determine any
+ // dangling repos that should be removed.
+ existingRepos := make(map[string][]*gitalypb.Repository)
for _, storageName := range cmd.removeAllRepositories {
- err := manager.RemoveAllRepositories(ctx, &backup.RemoveAllRepositoriesRequest{
+ repos, err := manager.ListRepositories(ctx, &backup.ListRepositoriesRequest{
StorageName: storageName,
})
if err != nil {
- // Treat RemoveAll failures as soft failures until we can determine
- // how often it fails.
- logger.WithError(err).WithField("storage_name", storageName).Warn("failed to remove all repositories")
+ logger.WithError(err).WithField("storage_name", storageName).Warn("failed to list repositories")
}
+
+ existingRepos[storageName] = repos
}
var opts []backup.PipelineOption
@@ -178,8 +181,29 @@ func (cmd *restoreSubcommand) run(ctx context.Context, logger log.Logger, stdin
}))
}
- if err := pipeline.Done(); err != nil {
+ restoredRepos, err := pipeline.Done()
+ if err != nil {
return fmt.Errorf("restore: %w", err)
}
+
+ var removalErrors []error
+ for storageName, repos := range existingRepos {
+ for _, repo := range repos {
+ if dangling := restoredRepos[storageName][repo]; dangling == struct{}{} {
+ // If we have dangling repos (those which exist in the storage but
+ // weren't part of the restore), they need to be deleted so the
+ // state of repos in Gitaly matches that in the Rails DB.
+ if err := manager.RemoveRepository(ctx, &backup.RemoveRepositoryRequest{Repo: repo}); err != nil {
+ removalErrors = append(removalErrors, fmt.Errorf("storage_name %q relative_path %q: %w", storageName, repo.RelativePath, err))
+ }
+ }
+ }
+ }
+
+ if len(removalErrors) > 0 {
+ return fmt.Errorf("remove dangling repositories: %d failures encountered: %w",
+ len(removalErrors), errors.Join(removalErrors...))
+ }
+
return nil
}
diff --git a/internal/cli/gitalybackup/restore_test.go b/internal/cli/gitalybackup/restore_test.go
index 4eae52e4c..6d235525c 100644
--- a/internal/cli/gitalybackup/restore_test.go
+++ b/internal/cli/gitalybackup/restore_test.go
@@ -75,12 +75,6 @@ Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`)
}))
}
- require.NoError(t, encoder.Encode(map[string]string{
- "address": "invalid",
- "token": "invalid",
- "relative_path": "invalid",
- }))
-
ctx = testhelper.MergeIncomingMetadata(ctx, testcfg.GitalyServersMetadataFromCfg(t, cfg))
args := []string{
@@ -103,9 +97,7 @@ Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`)
require.DirExists(t, existRepoPath)
- require.EqualError(t,
- cmd.RunContext(ctx, args),
- "restore: pipeline: 1 failures encountered:\n - invalid: manager: could not dial source: invalid connection string: \"invalid\"\n")
+ require.NoError(t, cmd.RunContext(ctx, args))
require.NoDirExists(t, existRepoPath)
@@ -179,12 +171,6 @@ Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`)
}))
}
- require.NoError(t, encoder.Encode(map[string]string{
- "address": "invalid",
- "token": "invalid",
- "relative_path": "invalid",
- }))
-
ctx = testhelper.MergeIncomingMetadata(ctx, testcfg.GitalyServersMetadataFromCfg(t, cfg))
args := []string{
@@ -207,9 +193,7 @@ Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`)
require.DirExists(t, existRepoPath)
- require.EqualError(t,
- cmd.RunContext(ctx, args),
- "restore: pipeline: 1 failures encountered:\n - invalid: server-side restore: could not dial source: invalid connection string: \"invalid\"\n")
+ require.NoError(t, cmd.RunContext(ctx, args))
require.NoDirExists(t, existRepoPath)
diff --git a/internal/featureflag/ff_exec_command_directly_in_cgroup.go b/internal/featureflag/ff_exec_command_directly_in_cgroup.go
index a47b0339f..247837116 100644
--- a/internal/featureflag/ff_exec_command_directly_in_cgroup.go
+++ b/internal/featureflag/ff_exec_command_directly_in_cgroup.go
@@ -5,5 +5,5 @@ var ExecCommandDirectlyInCgroup = NewFeatureFlag(
"exec_command_directly_in_cgroup",
"v16.5.0",
"https://gitlab.com/gitlab-org/gitaly/-/issues/5639",
- false,
+ true,
)
diff --git a/internal/featureflag/ff_return_structed_errors_in_revert.go b/internal/featureflag/ff_return_structed_errors_in_revert.go
new file mode 100644
index 000000000..52efdae02
--- /dev/null
+++ b/internal/featureflag/ff_return_structed_errors_in_revert.go
@@ -0,0 +1,13 @@
+package featureflag
+
+// ReturnStructuredErrorsInUserRevert enables return structured errors in UserRevert.
+// Modify the RPC UserRevert to return structured errors instead of
+// inline errors. Modify the handling of the following four
+// errors: 'Conflict', 'Changes Already Applied', 'Branch diverged',
+// and 'CustomHookError'. Returns the corresponding structured error.
+var ReturnStructuredErrorsInUserRevert = NewFeatureFlag(
+ "return_structured_errors_in_revert",
+ "v16.8.0",
+ "https://gitlab.com/gitlab-org/gitaly/-/issues/5752",
+ false,
+)
diff --git a/internal/gitaly/repoutil/create.go b/internal/gitaly/repoutil/create.go
index 83eb2d847..1bc49ab7c 100644
--- a/internal/gitaly/repoutil/create.go
+++ b/internal/gitaly/repoutil/create.go
@@ -3,6 +3,7 @@ package repoutil
import (
"bytes"
"context"
+ "errors"
"fmt"
"io"
"io/fs"
@@ -81,8 +82,12 @@ func Create(
// The repository must not exist on disk already, or otherwise we won't be able to
// create it with atomic semantics.
- if _, err := os.Stat(targetPath); !os.IsNotExist(err) {
- return structerr.NewAlreadyExists("repository exists already")
+ if _, err := os.Stat(targetPath); !errors.Is(err, fs.ErrNotExist) {
+ if err == nil {
+ return structerr.NewAlreadyExists("repository exists already")
+ }
+
+ return fmt.Errorf("pre-lock stat: %w", err)
}
newRepo, newRepoDir, err := tempdir.NewRepository(ctx, repository.GetStorageName(), logger, locator)
@@ -216,8 +221,12 @@ func Create(
// and seeded our temporary repository. While we would notice this at the point of moving
// the repository into place, we want to be as sure as possible that the action will succeed
// previous to the first transactional vote.
- if _, err := os.Stat(targetPath); !os.IsNotExist(err) {
- return structerr.NewAlreadyExists("repository exists already")
+ if _, err := os.Stat(targetPath); !errors.Is(err, fs.ErrNotExist) {
+ if err == nil {
+ return structerr.NewAlreadyExists("repository exists already")
+ }
+
+ return fmt.Errorf("post-lock stat: %w", err)
}
if err := transaction.VoteOnContext(ctx, txManager, vote, voting.Prepared); err != nil {
diff --git a/internal/gitaly/repoutil/create_test.go b/internal/gitaly/repoutil/create_test.go
index e449ff448..bed75c705 100644
--- a/internal/gitaly/repoutil/create_test.go
+++ b/internal/gitaly/repoutil/create_test.go
@@ -4,9 +4,11 @@ import (
"context"
"errors"
"fmt"
+ "io/fs"
"os"
"path/filepath"
"strings"
+ "syscall"
"testing"
"github.com/stretchr/testify/require"
@@ -54,6 +56,14 @@ func TestCreate(t *testing.T) {
}
}
+ type requireErrorFunc func(*testing.T, config.Cfg, string, error)
+
+ equalError := func(expected error) requireErrorFunc {
+ return func(t *testing.T, _ config.Cfg, _ string, actual error) {
+ require.Equal(t, actual, expected)
+ }
+ }
+
for _, tc := range []struct {
desc string
opts []CreateOption
@@ -67,7 +77,7 @@ func TestCreate(t *testing.T) {
realRepoPath string,
)
transactional bool
- expectedErr error
+ requireError requireErrorFunc
}{
{
desc: "no seeding",
@@ -111,7 +121,7 @@ func TestCreate(t *testing.T) {
require.NoDirExists(t, realRepoPath)
require.NoDirExists(t, tempRepoPath)
},
- expectedErr: errors.New("some error"),
+ requireError: equalError(errors.New("some error")),
},
{
desc: "preexisting directory",
@@ -128,7 +138,38 @@ func TestCreate(t *testing.T) {
requireFullRepackTimestampExists(t, realRepoPath, false)
},
- expectedErr: structerr.NewAlreadyExists("repository exists already"),
+ requireError: equalError(structerr.NewAlreadyExists("repository exists already")),
+ },
+ {
+ desc: "pre-lock stat fails",
+ setup: func(t *testing.T, repo *gitalypb.Repository, repoPath string) {
+ require.NoError(t, os.MkdirAll(repoPath, perm.PublicDir))
+ parentDir := filepath.Dir(repoPath)
+ // Drop permissions to trigger a stat failure.
+ require.NoError(t, os.Chmod(parentDir, 0))
+ // Restore the permissions so the directory can be cleaned up.
+ t.Cleanup(func() { require.NoError(t, os.Chmod(parentDir, perm.PublicDir)) })
+ },
+ verify: func(t *testing.T, tempRepo *gitalypb.Repository, tempRepoPath string, realRepo *gitalypb.Repository, realRepoPath string) {
+ // Restore the permissions so the below checks work.
+ require.NoError(t, os.Chmod(filepath.Dir(realRepoPath), perm.PublicDir))
+
+ require.NoDirExists(t, tempRepoPath)
+
+ require.DirExists(t, realRepoPath)
+ dirEntries, err := os.ReadDir(realRepoPath)
+ require.NoError(t, err)
+ require.Empty(t, dirEntries, "directory should not have been modified")
+
+ requireFullRepackTimestampExists(t, realRepoPath, false)
+ },
+ requireError: func(t *testing.T, cfg config.Cfg, relativePath string, actual error) {
+ require.Equal(t, fmt.Errorf("pre-lock stat: %w", &fs.PathError{
+ Op: "stat",
+ Path: filepath.Join(cfg.Storages[0].Path, relativePath),
+ Err: syscall.EACCES,
+ }), actual)
+ },
},
{
desc: "locked",
@@ -147,7 +188,7 @@ func TestCreate(t *testing.T) {
requireFullRepackTimestampExists(t, realRepoPath, false)
},
- expectedErr: fmt.Errorf("locking repository: %w", safe.ErrFileAlreadyLocked),
+ requireError: equalError(fmt.Errorf("locking repository: %w", safe.ErrFileAlreadyLocked)),
},
{
desc: "successful transaction",
@@ -180,7 +221,7 @@ func TestCreate(t *testing.T) {
require.NoDirExists(t, tempRepoPath)
require.NoDirExists(t, realRepoPath)
},
- expectedErr: structerr.NewFailedPrecondition("preparatory vote: %w", errors.New("vote failed")),
+ requireError: equalError(structerr.NewFailedPrecondition("preparatory vote: %w", errors.New("vote failed"))),
},
{
desc: "failing post-commit vote",
@@ -203,7 +244,7 @@ func TestCreate(t *testing.T) {
requireFullRepackTimestampExists(t, realRepoPath, true)
},
- expectedErr: structerr.NewFailedPrecondition("committing vote: %w", errors.New("vote failed")),
+ requireError: equalError(structerr.NewFailedPrecondition("committing vote: %w", errors.New("vote failed"))),
},
{
desc: "voting happens after lock",
@@ -227,7 +268,7 @@ func TestCreate(t *testing.T) {
require.NoDirExists(t, tempRepoPath)
require.NoDirExists(t, realRepoPath)
},
- expectedErr: fmt.Errorf("locking repository: %w", errors.New("file already locked")),
+ requireError: equalError(fmt.Errorf("locking repository: %w", errors.New("file already locked"))),
},
{
desc: "vote is deterministic",
@@ -345,7 +386,8 @@ func TestCreate(t *testing.T) {
}
var tempRepo *gitalypb.Repository
- require.Equal(t, tc.expectedErr, Create(ctx, logger, locator, gitCmdFactory, txManager, repoCounter, repo, func(tr *gitalypb.Repository) error {
+
+ err = Create(ctx, logger, locator, gitCmdFactory, txManager, repoCounter, repo, func(tr *gitalypb.Repository) error {
tempRepo = tr
// The temporary repository must have been created in Gitaly's
@@ -365,7 +407,13 @@ func TestCreate(t *testing.T) {
require.Equal(t, "true", text.ChompBytes(isBareRepo))
return nil
- }, tc.opts...))
+ }, tc.opts...)
+
+ if tc.requireError != nil {
+ tc.requireError(t, cfg, repo.RelativePath, err)
+ } else {
+ require.NoError(t, err)
+ }
var tempRepoPath string
if tempRepo != nil {
diff --git a/internal/gitaly/service/operations/revert.go b/internal/gitaly/service/operations/revert.go
index 90d03a7dd..72e45dc8f 100644
--- a/internal/gitaly/service/operations/revert.go
+++ b/internal/gitaly/service/operations/revert.go
@@ -6,6 +6,7 @@ import (
"fmt"
"time"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/featureflag"
"gitlab.com/gitlab-org/gitaly/v16/internal/git"
"gitlab.com/gitlab-org/gitaly/v16/internal/git/localrepo"
"gitlab.com/gitlab-org/gitaly/v16/internal/git/remoterepo"
@@ -87,22 +88,46 @@ func (s *Server) UserRevert(ctx context.Context, req *gitalypb.UserRevertRequest
if err != nil {
var conflictErr *localrepo.MergeTreeConflictError
if errors.As(err, &conflictErr) {
- return &gitalypb.UserRevertResponse{
- // it's better that this error matches the git2go for now
- CreateTreeError: "revert: could not apply due to conflicts",
- CreateTreeErrorCode: gitalypb.UserRevertResponse_CONFLICT,
- }, nil
+ if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) {
+ conflictingFiles := make([][]byte, 0, len(conflictErr.ConflictingFileInfo))
+ for _, conflictingFileInfo := range conflictErr.ConflictingFileInfo {
+ conflictingFiles = append(conflictingFiles, []byte(conflictingFileInfo.FileName))
+ }
+ return nil, structerr.NewFailedPrecondition("revert: there are conflicting files").WithDetail(
+ &gitalypb.UserRevertError{
+ Error: &gitalypb.UserRevertError_MergeConflict{
+ MergeConflict: &gitalypb.MergeConflictError{
+ ConflictingFiles: conflictingFiles,
+ },
+ },
+ })
+ } else {
+ return &gitalypb.UserRevertResponse{
+ // it's better that this error matches the git2go for now
+ CreateTreeError: "revert: could not apply due to conflicts",
+ CreateTreeErrorCode: gitalypb.UserRevertResponse_CONFLICT,
+ }, nil
+ }
}
return nil, structerr.NewInternal("merge-tree: %w", err)
}
if oursCommit.TreeId == treeOID.String() {
- return &gitalypb.UserRevertResponse{
- // it's better that this error matches the git2go for now
- CreateTreeError: "revert: could not apply because the result was empty",
- CreateTreeErrorCode: gitalypb.UserRevertResponse_EMPTY,
- }, nil
+ if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) {
+ return nil, structerr.NewFailedPrecondition("revert: could not apply because the result was empty").WithDetail(
+ &gitalypb.UserRevertError{
+ Error: &gitalypb.UserRevertError_ChangesAlreadyApplied{
+ ChangesAlreadyApplied: &gitalypb.ChangesAlreadyAppliedError{},
+ },
+ })
+ } else {
+ return &gitalypb.UserRevertResponse{
+ // it's better that this error matches the git2go for now
+ CreateTreeError: "revert: could not apply because the result was empty",
+ CreateTreeErrorCode: gitalypb.UserRevertResponse_EMPTY,
+ }, nil
+ }
}
newrev, err = quarantineRepo.WriteCommit(
@@ -166,18 +191,39 @@ func (s *Server) UserRevert(ctx context.Context, req *gitalypb.UserRevertRequest
return nil, structerr.NewInternal("checking for ancestry: %w", err)
}
if !ancestor {
- return &gitalypb.UserRevertResponse{
- CommitError: "Branch diverged",
- }, nil
+ if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) {
+ return nil, structerr.NewFailedPrecondition("revert: branch diverged").WithDetail(
+ &gitalypb.UserRevertError{
+ Error: &gitalypb.UserRevertError_NotAncestor{
+ NotAncestor: &gitalypb.NotAncestorError{
+ ParentRevision: []byte(oldrev.Revision()),
+ ChildRevision: []byte(newrev.Revision()),
+ },
+ },
+ })
+ } else {
+ return &gitalypb.UserRevertResponse{
+ CommitError: "Branch diverged",
+ }, nil
+ }
}
}
if err := s.updateReferenceWithHooks(ctx, req.GetRepository(), req.User, quarantineDir, referenceName, newrev, oldrev); err != nil {
var customHookErr updateref.CustomHookError
if errors.As(err, &customHookErr) {
- return &gitalypb.UserRevertResponse{
- PreReceiveError: customHookErr.Error(),
- }, nil
+ if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) {
+ return nil, structerr.NewPermissionDenied("revert: custom hook error").WithDetail(
+ &gitalypb.UserRevertError{
+ Error: &gitalypb.UserRevertError_CustomHook{
+ CustomHook: customHookErr.Proto(),
+ },
+ })
+ } else {
+ return &gitalypb.UserRevertResponse{
+ PreReceiveError: customHookErr.Error(),
+ }, nil
+ }
}
return nil, fmt.Errorf("update reference with hooks: %w", err)
diff --git a/internal/gitaly/service/operations/revert_test.go b/internal/gitaly/service/operations/revert_test.go
index 225669662..754bf0748 100644
--- a/internal/gitaly/service/operations/revert_test.go
+++ b/internal/gitaly/service/operations/revert_test.go
@@ -19,6 +19,8 @@ import (
"gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testcfg"
"gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testserver"
"gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
)
@@ -427,6 +429,7 @@ func TestServer_UserRevert_quarantine(t *testing.T) {
testhelper.NewFeatureSets(
featureflag.GPGSigning,
+ featureflag.ReturnStructuredErrorsInUserRevert,
).Run(t, testServerUserRevertQuarantine)
}
@@ -468,9 +471,22 @@ func testServerUserRevertQuarantine(t *testing.T, ctx context.Context) {
Message: []byte("Reverting " + revertedCommit.Id),
Timestamp: &timestamppb.Timestamp{Seconds: 12345},
})
- require.NoError(t, err)
- require.NotNil(t, response)
- require.NotEmpty(t, response.PreReceiveError)
+ if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) {
+ expectedError := structerr.NewPermissionDenied("revert: custom hook error").WithDetail(
+ &gitalypb.UserRevertError{
+ Error: &gitalypb.UserRevertError_CustomHook{
+ CustomHook: &gitalypb.CustomHookError{
+ HookType: gitalypb.CustomHookError_HOOK_TYPE_PRERECEIVE,
+ },
+ },
+ })
+ testhelper.RequireGrpcError(t, expectedError, err)
+ } else {
+ require.NoError(t, err)
+ require.NotNil(t, response)
+ require.NotEmpty(t, response.PreReceiveError)
+
+ }
objectHash, err := repo.ObjectHash(ctx)
require.NoError(t, err)
@@ -599,6 +615,7 @@ func TestServer_UserRevert_stableID(t *testing.T) {
testhelper.NewFeatureSets(
featureflag.GPGSigning,
+ featureflag.ReturnStructuredErrorsInUserRevert,
).Run(t, testServerUserRevertStableID)
}
@@ -652,8 +669,10 @@ func testServerUserRevertStableID(t *testing.T, ctx context.Context) {
"sha256": "28b57208e72bc2317143571997b9cfc444a51b52a43dde1c0282633a2b60de71",
}),
}, response.BranchUpdate)
- require.Empty(t, response.CreateTreeError)
- require.Empty(t, response.CreateTreeErrorCode)
+ if !featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) {
+ require.Empty(t, response.CreateTreeError)
+ require.Empty(t, response.CreateTreeErrorCode)
+ }
// headCommit is pointed commit after revert
headCommit, err := repo.ReadCommit(ctx, git.Revision(git.DefaultBranch))
@@ -695,6 +714,7 @@ func TestServer_UserRevert_successfulIntoEmptyRepo(t *testing.T) {
testhelper.NewFeatureSets(
featureflag.GPGSigning,
+ featureflag.ReturnStructuredErrorsInUserRevert,
).Run(t, testServerUserRevertSuccessfulIntoEmptyRepo)
}
@@ -752,8 +772,10 @@ func testServerUserRevertSuccessfulIntoEmptyRepo(t *testing.T, ctx context.Conte
}
require.Equal(t, expectedBranchUpdate, response.BranchUpdate)
- require.Empty(t, response.CreateTreeError)
- require.Empty(t, response.CreateTreeErrorCode)
+ if !featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) {
+ require.Empty(t, response.CreateTreeError)
+ require.Empty(t, response.CreateTreeErrorCode)
+ }
require.Equal(t, request.Message, headCommit.Subject)
require.Equal(t, revertedCommit.Id, headCommit.ParentIds[0])
gittest.RequireTree(t, cfg, repoPath, response.BranchUpdate.CommitId,
@@ -767,6 +789,7 @@ func TestServer_UserRevert_successfulGitHooks(t *testing.T) {
testhelper.NewFeatureSets(
featureflag.GPGSigning,
+ featureflag.ReturnStructuredErrorsInUserRevert,
).Run(t, testServerUserRevertSuccessfulGitHooks)
}
@@ -806,7 +829,9 @@ func testServerUserRevertSuccessfulGitHooks(t *testing.T, ctx context.Context) {
response, err := client.UserRevert(ctx, request)
require.NoError(t, err)
- require.Empty(t, response.PreReceiveError)
+ if !featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) {
+ require.Empty(t, response.PreReceiveError)
+ }
headCommit, err := repo.ReadCommit(ctx, git.Revision(destinationBranch))
require.NoError(t, err)
gittest.RequireTree(t, cfg, repoPath, headCommit.Id, nil)
@@ -822,6 +847,7 @@ func TestServer_UserRevert_failedDueToPreReceiveError(t *testing.T) {
testhelper.NewFeatureSets(
featureflag.GPGSigning,
+ featureflag.ReturnStructuredErrorsInUserRevert,
).Run(t, testServerUserRevertFailedDueToPreReceiveError)
}
@@ -859,9 +885,19 @@ func testServerUserRevertFailedDueToPreReceiveError(t *testing.T, ctx context.Co
t.Run(hookName, func(t *testing.T) {
gittest.WriteCustomHook(t, repoPath, hookName, hookContent)
- response, err := client.UserRevert(ctx, request)
- require.NoError(t, err)
- require.Contains(t, response.PreReceiveError, "GL_ID="+gittest.TestUser.GlId)
+ if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) {
+ _, err := client.UserRevert(ctx, request)
+ actualStatus, _ := status.FromError(err)
+ require.Equal(t, actualStatus.Code(), codes.PermissionDenied)
+ require.Equal(t, actualStatus.Message(), "revert: custom hook error")
+ revertError, ok := actualStatus.Details()[0].(*gitalypb.UserRevertError)
+ require.True(t, ok)
+ require.Contains(t, revertError.GetCustomHook().String(), "GL_ID="+gittest.TestUser.GlId)
+ } else {
+ response, err := client.UserRevert(ctx, request)
+ require.NoError(t, err)
+ require.Contains(t, response.PreReceiveError, "GL_ID="+gittest.TestUser.GlId)
+ }
})
}
}
@@ -871,6 +907,7 @@ func TestServer_UserRevert_failedDueToCreateTreeErrorConflict(t *testing.T) {
testhelper.NewFeatureSets(
featureflag.GPGSigning,
+ featureflag.ReturnStructuredErrorsInUserRevert,
).Run(t, testServerUserRevertFailedDueToCreateTreeErrorConflict)
}
@@ -917,10 +954,20 @@ func testServerUserRevertFailedDueToCreateTreeErrorConflict(t *testing.T, ctx co
Message: []byte("Reverting " + revertedCommit.Id),
}
- response, err := client.UserRevert(ctx, request)
- require.NoError(t, err)
- require.NotEmpty(t, response.CreateTreeError)
- require.Equal(t, gitalypb.UserRevertResponse_CONFLICT, response.CreateTreeErrorCode)
+ if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) {
+ _, err = client.UserRevert(ctx, request)
+ actualStatus, _ := status.FromError(err)
+ require.Equal(t, actualStatus.Code(), codes.FailedPrecondition)
+ require.Equal(t, actualStatus.Message(), "revert: there are conflicting files")
+ revertError, ok := actualStatus.Details()[0].(*gitalypb.UserRevertError)
+ require.True(t, ok)
+ require.NotNil(t, revertError.GetMergeConflict())
+ } else {
+ response, err := client.UserRevert(ctx, request)
+ require.NoError(t, err)
+ require.NotEmpty(t, response.CreateTreeError)
+ require.Equal(t, gitalypb.UserRevertResponse_CONFLICT, response.CreateTreeErrorCode)
+ }
}
func TestServer_UserRevert_failedDueToCreateTreeErrorEmpty(t *testing.T) {
@@ -928,6 +975,7 @@ func TestServer_UserRevert_failedDueToCreateTreeErrorEmpty(t *testing.T) {
testhelper.NewFeatureSets(
featureflag.GPGSigning,
+ featureflag.ReturnStructuredErrorsInUserRevert,
).Run(t, testServerUserRevertFailedDueToCreateTreeErrorEmpty)
}
@@ -989,13 +1037,25 @@ func testServerUserRevertFailedDueToCreateTreeErrorEmpty(t *testing.T, ctx conte
response, err := client.UserRevert(ctx, request)
require.NoError(t, err)
- require.Empty(t, response.CreateTreeError)
- require.Equal(t, gitalypb.UserRevertResponse_NONE, response.CreateTreeErrorCode)
- response, err = client.UserRevert(ctx, request)
- require.NoError(t, err)
- require.NotEmpty(t, response.CreateTreeError)
- require.Equal(t, gitalypb.UserRevertResponse_EMPTY, response.CreateTreeErrorCode)
+ if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) {
+ _, err = client.UserRevert(ctx, request)
+
+ expectedError := structerr.NewFailedPrecondition("revert: could not apply because the result was empty").WithDetail(
+ &gitalypb.UserRevertError{
+ Error: &gitalypb.UserRevertError_ChangesAlreadyApplied{},
+ })
+ testhelper.RequireGrpcError(t, expectedError, err)
+ } else {
+ require.NoError(t, err)
+ require.Empty(t, response.CreateTreeError)
+ require.Equal(t, gitalypb.UserRevertResponse_NONE, response.CreateTreeErrorCode)
+
+ response, err = client.UserRevert(ctx, request)
+ require.NoError(t, err)
+ require.NotEmpty(t, response.CreateTreeError)
+ require.Equal(t, gitalypb.UserRevertResponse_EMPTY, response.CreateTreeErrorCode)
+ }
}
func TestServer_UserRevert_failedDueToCommitError(t *testing.T) {
@@ -1003,6 +1063,7 @@ func TestServer_UserRevert_failedDueToCommitError(t *testing.T) {
testhelper.NewFeatureSets(
featureflag.GPGSigning,
+ featureflag.ReturnStructuredErrorsInUserRevert,
).Run(t, testServerUserRevertFailedDueToCommitError)
}
@@ -1045,8 +1106,17 @@ func testServerUserRevertFailedDueToCommitError(t *testing.T, ctx context.Contex
Message: []byte("Reverting " + revertedCommit.Id),
StartBranchName: []byte(sourceBranch),
}
-
response, err := client.UserRevert(ctx, request)
- require.NoError(t, err)
- require.Equal(t, "Branch diverged", response.CommitError)
+
+ if featureflag.ReturnStructuredErrorsInUserRevert.IsEnabled(ctx) {
+ actualStatus, _ := status.FromError(err)
+ require.Equal(t, actualStatus.Code(), codes.FailedPrecondition)
+ require.Equal(t, actualStatus.Message(), "revert: branch diverged")
+ revertError, ok := actualStatus.Details()[0].(*gitalypb.UserRevertError)
+ require.True(t, ok)
+ require.NotNil(t, revertError.GetNotAncestor())
+ } else {
+ require.NoError(t, err)
+ require.Equal(t, "Branch diverged", response.CommitError)
+ }
}
diff --git a/internal/gitaly/service/repository/remove_all_test.go b/internal/gitaly/service/repository/remove_all_test.go
index 785f54b01..fd913523a 100644
--- a/internal/gitaly/service/repository/remove_all_test.go
+++ b/internal/gitaly/service/repository/remove_all_test.go
@@ -28,6 +28,7 @@ Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`)
require.DirExists(t, repo1Path)
require.DirExists(t, repo1Path)
+ //nolint:staticcheck
_, err := client.RemoveAll(ctx, &gitalypb.RemoveAllRequest{
StorageName: cfg.Storages[0].Name,
})
diff --git a/internal/gitaly/storage/storagemgr/testhelper_test.go b/internal/gitaly/storage/storagemgr/testhelper_test.go
index 28e8aee9d..73b2cdd71 100644
--- a/internal/gitaly/storage/storagemgr/testhelper_test.go
+++ b/internal/gitaly/storage/storagemgr/testhelper_test.go
@@ -3,12 +3,14 @@ package storagemgr
import (
"bytes"
"context"
+ "errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"reflect"
"sort"
+ "strings"
"sync"
"testing"
@@ -48,6 +50,17 @@ type RepositoryState struct {
Objects []git.ObjectID
// Alternate is the content of 'objects/info/alternates'.
Alternate string
+ // PackedRefs is the expected state of the packed-refs and loose references.
+ PackedRefs *PackedRefsState
+}
+
+// PackedRefsState describes the asserted state of packed-refs and loose references. It's mostly used for verifying
+// pack-refs housekeeping task.
+type PackedRefsState struct {
+ // PackedRefsContent is the content of pack-refs file, line by line
+ PackedRefsContent []string
+ // LooseReferences is the exact list of loose references outside packed-refs.
+ LooseReferences map[git.ReferenceName]git.ObjectID
}
// RequireRepositoryState asserts the given repository matches the expected state.
@@ -63,6 +76,27 @@ func RequireRepositoryState(tb testing.TB, ctx context.Context, cfg config.Cfg,
actualReferences, err := repo.GetReferences(ctx)
require.NoError(tb, err)
+ actualPackedRefsState, err := collectPackedRefsState(tb, expected, repoPath)
+ require.NoError(tb, err)
+
+ // Assert if there is any empty directory in the refs hierarchy excepts for heads and tags
+ rootRefsDir := filepath.Join(repoPath, "refs")
+ ignoredDirs := map[string]struct{}{
+ rootRefsDir: {},
+ filepath.Join(rootRefsDir, "heads"): {},
+ filepath.Join(rootRefsDir, "tags"): {},
+ }
+ require.NoError(tb, filepath.WalkDir(rootRefsDir, func(path string, entry fs.DirEntry, err error) error {
+ if entry.IsDir() {
+ if _, exist := ignoredDirs[path]; !exist {
+ isEmpty, err := isDirEmpty(path)
+ require.NoError(tb, err)
+ require.Falsef(tb, isEmpty, "there shouldn't be any empty directory in the refs hierarchy %s", path)
+ }
+ }
+ return nil
+ }))
+
expectedObjects := []git.ObjectID{}
if expected.Objects != nil {
expectedObjects = expected.Objects
@@ -90,17 +124,57 @@ func RequireRepositoryState(tb testing.TB, ctx context.Context, cfg config.Cfg,
References: expected.References,
Objects: expectedObjects,
Alternate: expected.Alternate,
+ PackedRefs: expected.PackedRefs,
},
RepositoryState{
DefaultBranch: headReference,
References: actualReferences,
Objects: actualObjects,
Alternate: string(alternate),
+ PackedRefs: actualPackedRefsState,
},
)
testhelper.RequireDirectoryState(tb, filepath.Join(repoPath, repoutil.CustomHooksDir), "", expected.CustomHooks)
}
+func collectPackedRefsState(tb testing.TB, expected RepositoryState, repoPath string) (*PackedRefsState, error) {
+ if expected.PackedRefs == nil {
+ return nil, nil
+ }
+
+ packRefsFile, err := os.ReadFile(filepath.Join(repoPath, "packed-refs"))
+ if errors.Is(err, os.ErrNotExist) {
+ // Treat missing packed-refs file as empty.
+ packRefsFile = nil
+ } else {
+ require.NoError(tb, err)
+ }
+ // Walk and collect loose refs.
+ looseReferences := map[git.ReferenceName]git.ObjectID{}
+ refsPath := filepath.Join(repoPath, "refs")
+ require.NoError(tb, filepath.WalkDir(refsPath, func(path string, entry fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if !entry.IsDir() {
+ ref, err := filepath.Rel(repoPath, path)
+ if err != nil {
+ return fmt.Errorf("extracting ref name: %w", err)
+ }
+ oid, err := os.ReadFile(path)
+ require.NoError(tb, err)
+
+ looseReferences[git.ReferenceName(ref)] = git.ObjectID(strings.TrimSpace(string(oid)))
+ }
+ return nil
+ }))
+
+ return &PackedRefsState{
+ PackedRefsContent: strings.Split(strings.TrimSpace(string(packRefsFile)), "\n"),
+ LooseReferences: looseReferences,
+ }, nil
+}
+
type repositoryBuilder func(relativePath string) *localrepo.Repo
// RepositoryStates describes the state of repositories in a storage. The key is the relative path of a repository that
@@ -210,6 +284,11 @@ type testTransactionCommit struct {
Pack []byte
}
+type testTransactionTag struct {
+ Name string
+ OID git.ObjectID
+}
+
type testTransactionCommits struct {
First testTransactionCommit
Second testTransactionCommit
@@ -228,6 +307,7 @@ type testTransactionSetup struct {
ObjectHash git.ObjectHash
NonExistentOID git.ObjectID
Commits testTransactionCommits
+ AnnotatedTags []testTransactionTag
}
type testTransactionHooks struct {
@@ -307,6 +387,12 @@ type CreateRepository struct {
Alternate string
}
+// RunPackRefs calls pack-refs housekeeping task on a transaction.
+type RunPackRefs struct {
+ // TransactionID is the transaction for which the pack-refs task runs.
+ TransactionID int
+}
+
// Commit calls Commit on a transaction.
type Commit struct {
// TransactionID identifies the transaction to commit.
@@ -392,6 +478,9 @@ type StateAssertion struct {
Repositories RepositoryStates
}
+// AdhocAssertion allows a test to add some custom assertions apart from the built-in assertions above.
+type AdhocAssertion func(*testing.T, context.Context, *TransactionManager)
+
// steps defines execution steps in a test. Each test case can define multiple steps to exercise
// more complex behavior.
type steps []any
@@ -399,6 +488,7 @@ type steps []any
type transactionTestCase struct {
desc string
steps steps
+ customSetup func(*testing.T, context.Context, partitionID, string) testTransactionSetup
expectedState StateAssertion
}
@@ -677,6 +767,11 @@ func runTransactionTest(t *testing.T, ctx context.Context, tc transactionTestCas
},
repoutil.WithObjectHash(setup.ObjectHash),
))
+ case RunPackRefs:
+ require.Contains(t, openTransactions, step.TransactionID, "test error: pack-refs housekeeping task aborted on committed before beginning it")
+
+ transaction := openTransactions[step.TransactionID]
+ transaction.PackRefs()
case RepositoryAssertion:
require.Contains(t, openTransactions, step.TransactionID, "test error: transaction's snapshot asserted before beginning it")
transaction := openTransactions[step.TransactionID]
@@ -693,6 +788,8 @@ func runTransactionTest(t *testing.T, ctx context.Context, tc transactionTestCas
}),
)
}, step.Repositories)
+ case AdhocAssertion:
+ step(t, ctx, transactionManager)
default:
t.Fatalf("unhandled step type: %T", step)
}
@@ -721,6 +818,9 @@ func runTransactionTest(t *testing.T, ctx context.Context, tc transactionTestCas
setup.Commits.Third.OID,
setup.Commits.Diverging.OID,
}
+ for _, tag := range setup.AnnotatedTags {
+ state.Objects = append(state.Objects, tag.OID)
+ }
}
if state.DefaultBranch == "" {
diff --git a/internal/gitaly/storage/storagemgr/transaction_manager.go b/internal/gitaly/storage/storagemgr/transaction_manager.go
index 584cd56fa..59dce61cd 100644
--- a/internal/gitaly/storage/storagemgr/transaction_manager.go
+++ b/internal/gitaly/storage/storagemgr/transaction_manager.go
@@ -2,6 +2,7 @@ package storagemgr
import (
"bytes"
+ "container/list"
"context"
"encoding/binary"
"errors"
@@ -56,6 +57,25 @@ var (
// errAlternateAlreadyLinked is returned when attempting to set an alternate on a repository that
// already has one.
errAlternateAlreadyLinked = errors.New("repository already has an alternate")
+ // errConflictRepositoryDeletion is returned when an operation conflicts with repository deletion in another
+ // transaction.
+ errConflictRepositoryDeletion = errors.New("detected an update conflicting with repository deletion")
+ // errPackRefsConflictRefDeletion is returned when there is a committed ref deletion before pack-refs
+ // task is committed. The transaction should be aborted.
+ errPackRefsConflictRefDeletion = errors.New("detected a conflict with reference deletion when committing packed-refs")
+ // errHousekeepingConflictOtherUpdates is returned when the transaction includes housekeeping alongside
+ // with other updates.
+ errHousekeepingConflictOtherUpdates = errors.New("housekeeping in the same transaction with other updates")
+ // errHousekeepingConflictConcurrent is returned when there are another concurrent housekeeping task.
+ errHousekeepingConflictConcurrent = errors.New("conflict with another concurrent housekeeping task")
+
+ // Below errors are used to error out in cases when updates have been staged in a read-only transaction.
+ errReadOnlyReferenceUpdates = errors.New("reference updates staged in a read-only transaction")
+ errReadOnlyDefaultBranchUpdate = errors.New("default branch update staged in a read-only transaction")
+ errReadOnlyCustomHooksUpdate = errors.New("custom hooks update staged in a read-only transaction")
+ errReadOnlyRepositoryDeletion = errors.New("repository deletion staged in a read-only transaction")
+ errReadOnlyObjectsIncluded = errors.New("objects staged in a read-only transaction")
+ errReadOnlyHousekeeping = errors.New("housekeeping in a read-only transaction")
)
// InvalidReferenceFormatError is returned when a reference name was invalid.
@@ -126,6 +146,19 @@ type repositoryCreation struct {
objectHash git.ObjectHash
}
+// runHousekeeping models housekeeping tasks. It is supposed to handle housekeeping tasks for repositories
+// such as the cleanup of unneeded files and optimizations for the repository's data structures.
+type runHousekeeping struct {
+ packRefs *runPackRefs
+}
+
+// runPackRefs models refs packing housekeeping task. It packs heads and tags for efficient repository access.
+type runPackRefs struct {
+ // PrunedRefs contain a list of references pruned by the `git-pack-refs` command. They are used
+ // for comparing to the ref list of the destination repository
+ PrunedRefs map[git.ReferenceName]struct{}
+}
+
// ReferenceUpdates contains references to update. Reference name is used as the key and the value
// is the expected old tip and the desired new tip.
type ReferenceUpdates map[git.ReferenceName]ReferenceUpdate
@@ -213,6 +246,7 @@ type Transaction struct {
deleteRepository bool
includedObjects map[git.ObjectID]struct{}
alternateUpdate *alternateUpdate
+ runHousekeeping *runHousekeeping
}
// Begin opens a new transaction. The caller must call either Commit or Rollback to release
@@ -259,6 +293,16 @@ func (mgr *TransactionManager) Begin(ctx context.Context, relativePath string, s
mgr.snapshotLocks[txn.snapshotLSN].activeSnapshotters.Add(1)
defer mgr.snapshotLocks[txn.snapshotLSN].activeSnapshotters.Done()
readReady := mgr.snapshotLocks[txn.snapshotLSN].applied
+
+ var entry *committedEntry
+ if !txn.readOnly {
+ var err error
+ entry, err = mgr.updateCommittedEntry(txn.snapshotLSN)
+ if err != nil {
+ return nil, err
+ }
+ }
+
mgr.mutex.Unlock()
txn.finish = func() error {
@@ -270,6 +314,12 @@ func (mgr *TransactionManager) Begin(ctx context.Context, relativePath string, s
}
}
+ if !txn.readOnly {
+ mgr.mutex.Lock()
+ defer mgr.mutex.Unlock()
+ mgr.cleanCommittedEntry(entry)
+ }
+
return nil
}
@@ -364,15 +414,6 @@ func (txn *Transaction) updateState(newState transactionState) error {
}
}
-// Below errors are used to error out in cases when updates have been staged in a read-only transaction.
-var (
- errReadOnlyReferenceUpdates = errors.New("reference updates staged in a read-only transaction")
- errReadOnlyDefaultBranchUpdate = errors.New("default branch update staged in a read-only transaction")
- errReadOnlyCustomHooksUpdate = errors.New("custom hooks update staged in a read-only transaction")
- errReadOnlyRepositoryDeletion = errors.New("repository deletion staged in a read-only transaction")
- errReadOnlyObjectsIncluded = errors.New("objects staged in a read-only transaction")
-)
-
// Commit performs the changes. If no error is returned, the transaction was successful and the changes
// have been performed. If an error was returned, the transaction may or may not be persisted.
func (txn *Transaction) Commit(ctx context.Context) (returnedErr error) {
@@ -401,11 +442,21 @@ func (txn *Transaction) Commit(ctx context.Context) (returnedErr error) {
return errReadOnlyRepositoryDeletion
case txn.includedObjects != nil:
return errReadOnlyObjectsIncluded
+ case txn.runHousekeeping != nil:
+ return errReadOnlyHousekeeping
default:
return nil
}
}
+ if txn.runHousekeeping != nil && (txn.referenceUpdates != nil ||
+ txn.defaultBranchUpdate != nil ||
+ txn.customHooksUpdate != nil ||
+ txn.deleteRepository ||
+ txn.includedObjects != nil) {
+ return errHousekeepingConflictOtherUpdates
+ }
+
return txn.commit(ctx, txn)
}
@@ -562,6 +613,17 @@ func (txn *Transaction) SetCustomHooks(customHooksTAR []byte) {
txn.customHooksUpdate = &CustomHooksUpdate{CustomHooksTAR: customHooksTAR}
}
+// PackRefs sets pack-refs housekeeping task as a part of the transaction. The transaction can only runs other
+// housekeeping tasks in the same transaction. No other updates are allowed.
+func (txn *Transaction) PackRefs() {
+ if txn.runHousekeeping == nil {
+ txn.runHousekeeping = &runHousekeeping{}
+ }
+ txn.runHousekeeping.packRefs = &runPackRefs{
+ PrunedRefs: map[git.ReferenceName]struct{}{},
+ }
+}
+
// IncludeObject includes the given object and its dependencies in the transaction's logged pack file even
// if the object is unreachable from the references.
func (txn *Transaction) IncludeObject(oid git.ObjectID) {
@@ -599,6 +661,17 @@ type snapshotLock struct {
activeSnapshotters sync.WaitGroup
}
+// committedEntry is a wrapper for a log entry. It is used to keep track of entries in which their snapshots are still
+// accessed by other transactions.
+type committedEntry struct {
+ // lsn is the associated LSN of the entry
+ lsn LSN
+ // entry is the pointer to the corresponding log entry.
+ entry *gitalypb.LogEntry
+ // snapshotReaders accounts for the number of transaction readers of the snapshot.
+ snapshotReaders int
+}
+
// TransactionManager is responsible for transaction management of a single repository. Each repository has
// a single TransactionManager; it is the repository's single-writer. It accepts writes one at a time from
// the admissionQueue. Each admitted write is processed in three steps:
@@ -683,7 +756,8 @@ type TransactionManager struct {
// Run and Begin which are ran in different goroutines.
mutex sync.Mutex
- // snapshotLocks contains state used for synchronizing snapshotters with the log application.
+ // snapshotLocks contains state used for synchronizing snapshotters with the log application. The
+ // lock is released after the corresponding log entry is applied.
snapshotLocks map[LSN]*snapshotLock
// appendedLSN holds the LSN of the last log entry appended to the partition's write-ahead log.
@@ -697,6 +771,12 @@ type TransactionManager struct {
// the partition. It's keyed by the LSN the transaction is waiting to be applied and the
// value is the resultChannel that is waiting the result.
awaitingTransactions map[LSN]resultChannel
+ // committedEntries keeps some latest appended log entries around. Some types of transactions, such as
+ // housekeeping, operate on snapshot repository. There is a gap between transaction doing its work and the time
+ // when it is committed. They need to verify if concurrent operations can cause conflict. These log entries are
+ // still kept around even after they are applied. They are removed when there are no active readers accessing
+ // the corresponding snapshots.
+ committedEntries *list.List
}
// NewTransactionManager returns a new TransactionManager for the given repository.
@@ -730,6 +810,7 @@ func NewTransactionManager(
stagingDirectory: stagingDir,
housekeepingManager: housekeepingManager,
awaitingTransactions: make(map[LSN]resultChannel),
+ committedEntries: list.New(),
}
}
@@ -754,6 +835,11 @@ func (mgr *TransactionManager) commit(ctx context.Context, transaction *Transact
}
}
+ // Create a directory to store all staging files.
+ if err := os.Mkdir(transaction.walFilesPath(), perm.PrivateDir); err != nil {
+ return fmt.Errorf("create wal files directory: %w", err)
+ }
+
if err := mgr.setupStagingRepository(ctx, transaction); err != nil {
return fmt.Errorf("setup staging repository: %w", err)
}
@@ -766,6 +852,10 @@ func (mgr *TransactionManager) commit(ctx context.Context, transaction *Transact
return fmt.Errorf("pack objects: %w", err)
}
+ if err := mgr.prepareHousekeeping(ctx, transaction); err != nil {
+ return fmt.Errorf("preparing housekeeping: %w", err)
+ }
+
select {
case mgr.admissionQueue <- transaction:
transaction.admitted = true
@@ -1017,10 +1107,6 @@ func (mgr *TransactionManager) packObjects(ctx context.Context, transaction *Tra
group.Go(func() (returnedErr error) {
defer packReader.CloseWithError(returnedErr)
- if err := os.Mkdir(transaction.walFilesPath(), perm.PrivateDir); err != nil {
- return fmt.Errorf("create wal files directory: %w", err)
- }
-
// index-pack places the pack, index, and reverse index into the repository's object directory.
// The staging repository is configured with a quarantine so we execute it there.
var stdout, stderr bytes.Buffer
@@ -1052,6 +1138,92 @@ func (mgr *TransactionManager) packObjects(ctx context.Context, transaction *Tra
return group.Wait()
}
+// prepareHousekeeping composes and prepares necessary steps on the staging repository before the changes are staged and
+// applied. All commands run in the scope of the staging repository. Thus, we can avoid any impact on other concurrent
+// transactions.
+func (mgr *TransactionManager) prepareHousekeeping(ctx context.Context, transaction *Transaction) error {
+ if transaction.runHousekeeping == nil {
+ return nil
+ }
+ if err := mgr.preparePackRefs(ctx, transaction); err != nil {
+ return err
+ }
+ return nil
+}
+
+// preparePackRefs runs git-pack-refs command against the snapshot repository. It collects the resulting packed-refs
+// file and the list of pruned references. Unfortunately, git-pack-refs doesn't output which refs are pruned. So, we
+// performed two ref walkings before and after running the command. The difference between the two walks is the list of
+// pruned refs. This workaround works but is not performant on large repositories with huge amount of loose references.
+// Smaller repositories or ones that run housekeeping frequent won't have this issue.
+// The work of adding pruned refs dump to `git-pack-refs` is tracked here:
+// https://gitlab.com/gitlab-org/git/-/issues/222
+func (mgr *TransactionManager) preparePackRefs(ctx context.Context, transaction *Transaction) error {
+ if transaction.runHousekeeping.packRefs == nil {
+ return nil
+ }
+
+ runPackRefs := transaction.runHousekeeping.packRefs
+ repoPath := mgr.getAbsolutePath(transaction.snapshotRepository.GetRelativePath())
+
+ if err := mgr.removePackedRefsLocks(mgr.ctx, repoPath); err != nil {
+ return fmt.Errorf("remove stale packed-refs locks: %w", err)
+ }
+ // First walk to collect the list of loose refs.
+ looseReferences := make(map[git.ReferenceName]struct{})
+ if err := filepath.WalkDir(filepath.Join(repoPath, "refs"), func(path string, entry fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if !entry.IsDir() {
+ // Get fully qualified refs.
+ ref, err := filepath.Rel(repoPath, path)
+ if err != nil {
+ return fmt.Errorf("extracting ref name: %w", err)
+ }
+ looseReferences[git.ReferenceName(ref)] = struct{}{}
+ }
+ return nil
+ }); err != nil {
+ return fmt.Errorf("initial walking refs directory: %w", err)
+ }
+
+ // Execute git-pack-refs command. The command runs in the scope of the snapshot repository. Thus, we can
+ // let it prune the ref references without causing any impact to other concurrent transactions.
+ var stderr bytes.Buffer
+ if err := transaction.snapshotRepository.ExecAndWait(ctx, git.Command{
+ Name: "pack-refs",
+ Flags: []git.Option{git.Flag{Name: "--all"}},
+ }, git.WithStderr(&stderr)); err != nil {
+ return structerr.New("exec pack-refs: %w", err).WithMetadata("stderr", stderr.String())
+ }
+
+ // Copy the resulting packed-refs file to the WAL directory.
+ if err := os.Link(
+ filepath.Join(filepath.Join(repoPath, "packed-refs")),
+ filepath.Join(transaction.walFilesPath(), "packed-refs"),
+ ); err != nil {
+ return fmt.Errorf("copying packed-refs file to WAL directory: %w", err)
+ }
+ if err := safe.NewSyncer().Sync(transaction.walFilesPath()); err != nil {
+ return fmt.Errorf("sync: %w", err)
+ }
+
+ // Second walk and compare with the initial list of loose references. Any disappeared refs are pruned.
+ for ref := range looseReferences {
+ _, err := os.Stat(filepath.Join(repoPath, ref.String()))
+ if err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ runPackRefs.PrunedRefs[ref] = struct{}{}
+ } else {
+ return fmt.Errorf("second walk refs directory: %w", err)
+ }
+ }
+ }
+
+ return nil
+}
+
// unwrapExpectedError unwraps expected errors that may occur and returns them directly to the caller.
func unwrapExpectedError(err error) error {
// The manager controls its own execution context and it is canceled only when Stop is called.
@@ -1181,9 +1353,27 @@ func (mgr *TransactionManager) processTransaction() (returnedErr error) {
}
nextLSN := mgr.appendedLSN + 1
+ var shouldStoreWALFiles bool
+
if transaction.packPrefix != "" {
+ shouldStoreWALFiles = true
logEntry.PackPrefix = transaction.packPrefix
+ }
+
+ if transaction.deleteRepository {
+ logEntry.RepositoryDeletion = &gitalypb.LogEntry_RepositoryDeletion{}
+ }
+
+ if transaction.runHousekeeping != nil {
+ shouldStoreWALFiles = true
+ housekeepingEntry, err := mgr.verifyHousekeeping(mgr.ctx, transaction)
+ if err != nil {
+ return fmt.Errorf("verifying pack refs: %w", err)
+ }
+ logEntry.Housekeeping = housekeepingEntry
+ }
+ if shouldStoreWALFiles {
removeFiles, err := mgr.storeWALFiles(mgr.ctx, nextLSN, transaction)
cleanUps = append(cleanUps, func() error {
// The transaction's files might have been moved successfully in to the log.
@@ -1203,10 +1393,6 @@ func (mgr *TransactionManager) processTransaction() (returnedErr error) {
}
}
- if transaction.deleteRepository {
- logEntry.RepositoryDeletion = &gitalypb.LogEntry_RepositoryDeletion{}
- }
-
return mgr.appendLogEntry(nextLSN, logEntry)
}(); err != nil {
transaction.result <- err
@@ -1403,7 +1589,7 @@ func (mgr *TransactionManager) storeWALFiles(ctx context.Context, lsn LSN, trans
}
removeFiles = func() error {
- if err := os.Remove(destinationPath); err != nil {
+ if err := os.RemoveAll(destinationPath); err != nil {
return fmt.Errorf("remove wal files: %w", err)
}
@@ -1613,6 +1799,95 @@ func (mgr *TransactionManager) verifyDefaultBranchUpdate(ctx context.Context, tr
return nil
}
+// verifyHousekeeping verifies if all included housekeeping tasks can be performed. Although it's feasible for multiple
+// housekeeping tasks running at the same time, it's not guaranteed they are conflict-free. So, we need to ensure there
+// is no other concurrent housekeeping task. Each sub-task also needs specific verification.
+func (mgr *TransactionManager) verifyHousekeeping(ctx context.Context, transaction *Transaction) (*gitalypb.LogEntry_Housekeeping, error) {
+ mgr.mutex.Lock()
+ defer mgr.mutex.Unlock()
+
+ // Check for any concurrent housekeeping between this transaction's snapshot LSN and the latest appended LSN.
+ elm := mgr.committedEntries.Front()
+ for elm != nil {
+ entry := elm.Value.(*committedEntry)
+ if entry.lsn > transaction.snapshotLSN && entry.entry.RelativePath == transaction.relativePath {
+ if entry.entry.GetHousekeeping() != nil {
+ return nil, errHousekeepingConflictConcurrent
+ }
+ if entry.entry.GetRepositoryDeletion() != nil {
+ return nil, errConflictRepositoryDeletion
+ }
+ }
+ elm = elm.Next()
+ }
+
+ packRefsEntry, err := mgr.verifyPackRefs(mgr.ctx, transaction)
+ if err != nil {
+ return nil, fmt.Errorf("verifying pack refs: %w", err)
+ }
+
+ return &gitalypb.LogEntry_Housekeeping{
+ PackRefs: packRefsEntry,
+ }, nil
+}
+
+// verifyPackRefs verifies if the pack-refs housekeeping task can be logged. Ideally, we can just apply the packed-refs
+// file and prune the loose references. Unfortunately, there could be a ref modification between the time the pack-refs
+// command runs and the time this transaction is logged. Thus, we need to verify if the transaction conflicts with the
+// current state of the repository.
+//
+// There are three cases when a reference is modified:
+// - Reference creation: this is the easiest case. The new reference exists as a loose reference on disk and shadows the
+// one in the packed-ref.
+// - Reference update: similarly, the loose reference shadows the one in packed-refs with the new OID. However, we need
+// to remove it from the list of pruned references. Otherwise, the repository continues to use the old OID.
+// - Reference deletion. When a reference is deleted, both loose reference and the entry in the packed-refs file are
+// removed. The reflogs are also removed. In addition, we don't use reflogs in Gitaly as core.logAllRefUpdates defaults
+// to false in bare repositories. It could of course be that an admin manually enabled it by modifying the config
+// on-disk directly. There is no way to extract reference deletion between two states.
+//
+// In theory, if there is any reference deletion, it can be removed from the packed-refs file. However, it requires
+// parsing and regenerating the packed-refs file. So, let's settle down with a conflict error at this point.
+func (mgr *TransactionManager) verifyPackRefs(ctx context.Context, transaction *Transaction) (*gitalypb.LogEntry_Housekeeping_PackRefs, error) {
+ if transaction.runHousekeeping.packRefs == nil {
+ return nil, nil
+ }
+
+ objectHash, err := transaction.stagingRepository.ObjectHash(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("object hash: %w", err)
+ }
+ packRefs := transaction.runHousekeeping.packRefs
+
+ // Check for any concurrent ref deletion between this transaction's snapshot LSN to the end.
+ elm := mgr.committedEntries.Front()
+ for elm != nil {
+ entry := elm.Value.(*committedEntry)
+ if entry.lsn > transaction.snapshotLSN && entry.entry.RelativePath == transaction.relativePath {
+ for _, refTransaction := range entry.entry.ReferenceTransactions {
+ for _, change := range refTransaction.Changes {
+ if objectHash.IsZeroOID(git.ObjectID(change.GetNewOid())) {
+ // Oops, there is a reference deletion. Bail out.
+ return nil, errPackRefsConflictRefDeletion
+ }
+ // Ref update. Remove the updated ref from the list of pruned refs so that the
+ // new OID in loose reference shadows the outdated OID in packed-refs.
+ delete(packRefs.PrunedRefs, git.ReferenceName(change.GetReferenceName()))
+ }
+ }
+ }
+ elm = elm.Next()
+ }
+
+ var prunedRefs [][]byte
+ for ref := range packRefs.PrunedRefs {
+ prunedRefs = append(prunedRefs, []byte(ref))
+ }
+ return &gitalypb.LogEntry_Housekeeping_PackRefs{
+ PrunedRefs: prunedRefs,
+ }, nil
+}
+
// applyDefaultBranchUpdate applies the default branch update to the repository from the log entry.
func (mgr *TransactionManager) applyDefaultBranchUpdate(ctx context.Context, logEntry *gitalypb.LogEntry) error {
if logEntry.DefaultBranchUpdate == nil {
@@ -1713,6 +1988,10 @@ func (mgr *TransactionManager) appendLogEntry(nextLSN LSN, logEntry *gitalypb.Lo
mgr.mutex.Lock()
mgr.appendedLSN = nextLSN
mgr.snapshotLocks[nextLSN] = &snapshotLock{applied: make(chan struct{})}
+ mgr.committedEntries.PushBack(&committedEntry{
+ lsn: nextLSN,
+ entry: logEntry,
+ })
mgr.mutex.Unlock()
return nil
@@ -1767,6 +2046,10 @@ func (mgr *TransactionManager) applyLogEntry(ctx context.Context, lsn LSN) error
if err := mgr.applyCustomHooks(ctx, logEntry); err != nil {
return fmt.Errorf("apply custom hooks: %w", err)
}
+
+ if err := mgr.applyHousekeeping(ctx, lsn, logEntry); err != nil {
+ return fmt.Errorf("apply housekeeping: %w", err)
+ }
}
if err := mgr.storeAppliedLSN(lsn); err != nil {
@@ -2086,6 +2369,104 @@ func (mgr *TransactionManager) applyCustomHooks(ctx context.Context, logEntry *g
return nil
}
+// applyHousekeeping applies housekeeping results to the target repository.
+func (mgr *TransactionManager) applyHousekeeping(ctx context.Context, lsn LSN, logEntry *gitalypb.LogEntry) error {
+ if logEntry.Housekeeping == nil {
+ return nil
+ }
+ repositoryPath := mgr.getAbsolutePath(logEntry.RelativePath)
+ if logEntry.Housekeeping.PackRefs != nil {
+ // Remove packed-refs lock. While we shouldn't be producing any new stale locks, it makes sense to have
+ // this for historic state until we're certain none of the repositories contain stale locks anymore.
+ // This clean up is not needed afterward.
+ if err := mgr.removePackedRefsLocks(ctx, repositoryPath); err != nil {
+ return fmt.Errorf("applying pack-refs: %w", err)
+ }
+
+ packedRefsPath := filepath.Join(repositoryPath, "packed-refs")
+ // Replace the packed-refs file.
+ if err := os.Remove(packedRefsPath); err != nil {
+ if !errors.Is(err, os.ErrNotExist) {
+ return fmt.Errorf("removing existing pack-refs: %w", err)
+ }
+ }
+ if err := os.Link(
+ filepath.Join(walFilesPathForLSN(mgr.stateDirectory, lsn), "packed-refs"),
+ packedRefsPath,
+ ); err != nil {
+ return fmt.Errorf("linking new packed-refs: %w", err)
+ }
+
+ modifiedDirs := map[string]struct{}{}
+ // Prune loose references. The log entry carries the list of fully qualified references to prune.
+ for _, ref := range logEntry.Housekeeping.PackRefs.PrunedRefs {
+ path := filepath.Join(repositoryPath, string(ref))
+ if err := os.Remove(path); err != nil {
+ if !errors.Is(err, os.ErrNotExist) {
+ return structerr.New("pruning loose reference: %w", err).WithMetadata("ref", path)
+ }
+ }
+ modifiedDirs[filepath.Dir(path)] = struct{}{}
+ }
+
+ syncer := safe.NewSyncer()
+ // Traverse all modified dirs back to the root "refs" dir of the repository. Remove any empty directory
+ // along the way. It prevents leaving empty dirs around after a loose ref is pruned. `git-pack-refs`
+ // command does dir removal for us, but in staginge repository during preparation stage. In the actual
+ // repository, we need to do it ourselves.
+ rootRefDir := filepath.Join(repositoryPath, "refs")
+ for dir := range modifiedDirs {
+ for dir != rootRefDir {
+ if isEmpty, err := isDirEmpty(dir); err != nil {
+ // If a dir does not exist, it properly means a directory may already be deleted by a
+ // previous interrupted attempt on applying the log entry. We simply ignore the error
+ // and move up the directory hierarchy.
+ if errors.Is(err, fs.ErrNotExist) {
+ dir = filepath.Dir(dir)
+ continue
+ } else {
+ return fmt.Errorf("checking empty ref dir: %w", err)
+ }
+ } else if !isEmpty {
+ break
+ }
+
+ if err := os.Remove(dir); err != nil {
+ return fmt.Errorf("removing empty ref dir: %w", err)
+ }
+ dir = filepath.Dir(dir)
+ }
+ // If there is any empty dir along the way, it's removed and dir pointer moves up until the dir
+ // is not empty or reaching the root dir. That one should be fsynced to flush the dir removal.
+ // If there is no empty dir, it stays at the dir of pruned refs, which also needs a flush.
+ if err := syncer.Sync(dir); err != nil {
+ return fmt.Errorf("sync dir: %w", err)
+ }
+ }
+
+ // Sync the root of the repository to flush packed-refs replacement.
+ if err := syncer.SyncParent(packedRefsPath); err != nil {
+ return fmt.Errorf("sync parent: %w", err)
+ }
+ }
+ return nil
+}
+
+// isDirEmpty checks if a directory is empty.
+func isDirEmpty(dir string) (bool, error) {
+ f, err := os.Open(dir)
+ if err != nil {
+ return false, err
+ }
+ defer f.Close()
+
+ // Read at most one entry from the directory. If we get EOF, the directory is empty
+ if _, err = f.Readdirnames(1); errors.Is(err, io.EOF) {
+ return true, nil
+ }
+ return false, err
+}
+
// deleteLogEntry deletes the log entry at the given LSN from the log.
func (mgr *TransactionManager) deleteLogEntry(lsn LSN) error {
return mgr.deleteKey(keyLogEntry(mgr.partitionID, lsn))
@@ -2154,6 +2535,51 @@ func (mgr *TransactionManager) deleteKey(key []byte) error {
})
}
+// updateCommittedEntry updates the reader counter of the committed entry of the snapshot that this transaction depends on.
+func (mgr *TransactionManager) updateCommittedEntry(snapshotLSN LSN) (*committedEntry, error) {
+ // Since the goroutine doing this is holding the lock, the snapshotLSN shouldn't change and no new transactions
+ // can be committed or added. That should guarantee .Back() is always the latest transaction and the one we're
+ // using to base our snapshot on.
+ if elm := mgr.committedEntries.Back(); elm != nil {
+ entry := elm.Value.(*committedEntry)
+ entry.snapshotReaders++
+ return entry, nil
+ }
+
+ entry := &committedEntry{
+ lsn: snapshotLSN,
+ snapshotReaders: 1,
+ // The log entry is left nil. This doesn't matter as the conflict checking only
+ // needs it when checking for conflicts with transactions committed after we took
+ // our snapshot.
+ //
+ // This `committedEntry` only exists to record the `snapshotReaders` at this LSN.
+ }
+
+ mgr.committedEntries.PushBack(entry)
+
+ return entry, nil
+}
+
+// cleanCommittedEntry reduces the snapshot readers counter of the committed entry. It also removes entries with no more
+// readers at the head of the list.
+func (mgr *TransactionManager) cleanCommittedEntry(entry *committedEntry) {
+ entry.snapshotReaders--
+
+ elm := mgr.committedEntries.Front()
+ for elm != nil {
+ front := elm.Value.(*committedEntry)
+ if front.snapshotReaders > 0 {
+ // If the first entry had still some snapshot readers, that means
+ // our transaction was not the oldest reader. We can't remove any entries
+ // as they'll still be needed for conlict checking the older transactions.
+ return
+ }
+ mgr.committedEntries.Remove(elm)
+ elm = mgr.committedEntries.Front()
+ }
+}
+
// keyAppliedLSN returns the database key storing a partition's last applied log entry's LSN.
func keyAppliedLSN(ptnID partitionID) []byte {
return []byte(fmt.Sprintf("partition/%s/applied_lsn", ptnID.MarshalBinary()))
diff --git a/internal/gitaly/storage/storagemgr/transaction_manager_housekeeping_test.go b/internal/gitaly/storage/storagemgr/transaction_manager_housekeeping_test.go
new file mode 100644
index 000000000..a2b5ad5ea
--- /dev/null
+++ b/internal/gitaly/storage/storagemgr/transaction_manager_housekeeping_test.go
@@ -0,0 +1,1214 @@
+package storagemgr
+
+import (
+ "context"
+ "fmt"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "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/gitaly/config"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/helper/perm"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper"
+)
+
+func generateHousekeepingTests(t *testing.T, ctx context.Context, testPartitionID partitionID, relativePath string) []transactionTestCase {
+ customSetup := func(t *testing.T, ctx context.Context, testPartitionID partitionID, relativePath string) testTransactionSetup {
+ setup := setupTest(t, ctx, testPartitionID, relativePath)
+ gittest.WriteRef(t, setup.Config, setup.RepositoryPath, "refs/heads/main", setup.Commits.First.OID)
+ gittest.WriteRef(t, setup.Config, setup.RepositoryPath, "refs/heads/branch-1", setup.Commits.Second.OID)
+ gittest.WriteRef(t, setup.Config, setup.RepositoryPath, "refs/heads/branch-2", setup.Commits.Third.OID)
+
+ gittest.WriteTag(t, setup.Config, setup.RepositoryPath, "v1.0.0", setup.Commits.Diverging.OID.Revision())
+ annotatedTag := gittest.WriteTag(t, setup.Config, setup.RepositoryPath, "v2.0.0", setup.Commits.Diverging.OID.Revision(), gittest.WriteTagConfig{
+ Message: "annotated tag",
+ })
+ setup.AnnotatedTags = append(setup.AnnotatedTags, testTransactionTag{
+ Name: "v2.0.0",
+ OID: annotatedTag,
+ })
+
+ return setup
+ }
+ setup := customSetup(t, ctx, testPartitionID, relativePath)
+ lightweightTag := setup.Commits.Diverging.OID
+ annotatedTag := setup.AnnotatedTags[0]
+
+ directoryStateWithPackedRefs := func(lsn LSN) testhelper.DirectoryState {
+ return testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ // LSN is when a log entry is appended, it's different from transaction ID.
+ fmt.Sprintf("/wal/%d", lsn): {Mode: fs.ModeDir | perm.PrivateDir},
+ fmt.Sprintf("/wal/%s/packed-refs", lsn): packRefsDirectoryEntry(setup.Config),
+ }
+ }
+
+ defaultRefs := []git.Reference{
+ {Name: "refs/heads/branch-1", Target: setup.Commits.Second.OID.String()},
+ {Name: "refs/heads/branch-2", Target: setup.Commits.Third.OID.String()},
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ {Name: "refs/tags/v1.0.0", Target: lightweightTag.String()},
+ {Name: "refs/tags/v2.0.0", Target: annotatedTag.OID.String()},
+ }
+
+ return []transactionTestCase{
+ {
+ desc: "run pack-refs on a repository without packed-refs",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunPackRefs{
+ TransactionID: 1,
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ Commit{
+ TransactionID: 2,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID},
+ },
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Directory: directoryStateWithPackedRefs(1),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/branch-1", Target: setup.Commits.Second.OID.String()},
+ {Name: "refs/heads/branch-2", Target: setup.Commits.Third.OID.String()},
+ // `main` points to the second commit now
+ {Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()},
+ {Name: "refs/tags/v1.0.0", Target: lightweightTag.String()},
+ {Name: "refs/tags/v2.0.0", Target: annotatedTag.OID.String()},
+ },
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{
+ "# pack-refs with: peeled fully-peeled sorted ",
+ fmt.Sprintf("%s refs/heads/branch-1", setup.Commits.Second.OID.String()),
+ fmt.Sprintf("%s refs/heads/branch-2", setup.Commits.Third.OID.String()),
+ // But `main` in packed-refs file points to the first
+ // commit.
+ fmt.Sprintf("%s refs/heads/main", setup.Commits.First.OID.String()),
+ fmt.Sprintf("%s refs/tags/v1.0.0", lightweightTag.String()),
+ fmt.Sprintf("%s refs/tags/v2.0.0", annotatedTag.OID.String()),
+ fmt.Sprintf("^%s", setup.Commits.Diverging.OID.String()),
+ },
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ // It's shadowed by the loose reference.
+ "refs/heads/main": setup.Commits.Second.OID,
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "run pack-refs on a repository with an existing packed-refs",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ repoPath := filepath.Join(storagePath, setup.RelativePath)
+ // Execute pack-refs command without going through transaction manager
+ gittest.Exec(tb, cfg, "-C", repoPath, "pack-refs", "--all")
+
+ // Add artifactual packed-refs.lock. The pack-refs task should ignore
+ // the lock and move on.
+ require.NoError(t, os.WriteFile(
+ filepath.Join(repoPath, "packed-refs.lock"),
+ []byte{},
+ perm.PrivateFile,
+ ))
+ require.NoError(t, os.WriteFile(
+ filepath.Join(repoPath, "packed-refs.new"),
+ []byte{},
+ perm.PrivateFile,
+ ))
+ },
+ },
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID},
+ "refs/heads/branch-3": {OldOID: gittest.DefaultObjectHash.ZeroOID, NewOID: setup.Commits.Diverging.OID},
+ },
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ RunPackRefs{
+ TransactionID: 2,
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Directory: directoryStateWithPackedRefs(2),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/branch-1", Target: setup.Commits.Second.OID.String()},
+ {Name: "refs/heads/branch-2", Target: setup.Commits.Third.OID.String()},
+ {Name: "refs/heads/branch-3", Target: setup.Commits.Diverging.OID.String()},
+ {Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()},
+ {Name: "refs/tags/v1.0.0", Target: lightweightTag.String()},
+ {Name: "refs/tags/v2.0.0", Target: annotatedTag.OID.String()},
+ },
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{
+ // All refs are packed to the packed-refs file.
+ "# pack-refs with: peeled fully-peeled sorted ",
+ fmt.Sprintf("%s refs/heads/branch-1", setup.Commits.Second.OID.String()),
+ fmt.Sprintf("%s refs/heads/branch-2", setup.Commits.Third.OID.String()),
+ fmt.Sprintf("%s refs/heads/branch-3", setup.Commits.Diverging.OID.String()),
+ fmt.Sprintf("%s refs/heads/main", setup.Commits.Second.OID.String()),
+ fmt.Sprintf("%s refs/tags/v1.0.0", lightweightTag.String()),
+ fmt.Sprintf("%s refs/tags/v2.0.0", annotatedTag.OID.String()),
+ fmt.Sprintf("^%s", setup.Commits.Diverging.OID.String()),
+ },
+ LooseReferences: map[git.ReferenceName]git.ObjectID{},
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "run pack-refs, all refs outside refs/heads and refs/tags are packed",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/keep-around/1": {OldOID: gittest.DefaultObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ "refs/merge-requests/1": {OldOID: gittest.DefaultObjectHash.ZeroOID, NewOID: setup.Commits.Second.OID},
+ "refs/very/deep/nested/ref": {OldOID: gittest.DefaultObjectHash.ZeroOID, NewOID: setup.Commits.Third.OID},
+ },
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ RunPackRefs{
+ TransactionID: 2,
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Directory: directoryStateWithPackedRefs(2),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/branch-1", Target: setup.Commits.Second.OID.String()},
+ {Name: "refs/heads/branch-2", Target: setup.Commits.Third.OID.String()},
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ {Name: "refs/keep-around/1", Target: setup.Commits.First.OID.String()},
+ {Name: "refs/merge-requests/1", Target: setup.Commits.Second.OID.String()},
+ {Name: "refs/tags/v1.0.0", Target: lightweightTag.String()},
+ {Name: "refs/tags/v2.0.0", Target: annotatedTag.OID.String()},
+ {Name: "refs/very/deep/nested/ref", Target: setup.Commits.Third.OID.String()},
+ },
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{
+ "# pack-refs with: peeled fully-peeled sorted ",
+ fmt.Sprintf("%s refs/heads/branch-1", setup.Commits.Second.OID.String()),
+ fmt.Sprintf("%s refs/heads/branch-2", setup.Commits.Third.OID.String()),
+ fmt.Sprintf("%s refs/heads/main", setup.Commits.First.OID.String()),
+ fmt.Sprintf("%s refs/keep-around/1", setup.Commits.First.OID.String()),
+ fmt.Sprintf("%s refs/merge-requests/1", setup.Commits.Second.OID.String()),
+ fmt.Sprintf("%s refs/tags/v1.0.0", lightweightTag.String()),
+ fmt.Sprintf("%s refs/tags/v2.0.0", annotatedTag.OID.String()),
+ fmt.Sprintf("^%s", setup.Commits.Diverging.OID.String()),
+ fmt.Sprintf("%s refs/very/deep/nested/ref", setup.Commits.Third.OID.String()),
+ },
+ LooseReferences: map[git.ReferenceName]git.ObjectID{},
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "concurrent ref creation before pack-refs task is committed",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunPackRefs{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 2,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch-3": {OldOID: gittest.DefaultObjectHash.ZeroOID, NewOID: setup.Commits.Diverging.OID},
+ "refs/keep-around/1": {OldOID: gittest.DefaultObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Directory: directoryStateWithPackedRefs(2),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/branch-1", Target: setup.Commits.Second.OID.String()},
+ {Name: "refs/heads/branch-2", Target: setup.Commits.Third.OID.String()},
+ {Name: "refs/heads/branch-3", Target: setup.Commits.Diverging.OID.String()},
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ {Name: "refs/keep-around/1", Target: setup.Commits.First.OID.String()},
+ {Name: "refs/tags/v1.0.0", Target: lightweightTag.String()},
+ {Name: "refs/tags/v2.0.0", Target: annotatedTag.OID.String()},
+ },
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{
+ "# pack-refs with: peeled fully-peeled sorted ",
+ fmt.Sprintf("%s refs/heads/branch-1", setup.Commits.Second.OID.String()),
+ fmt.Sprintf("%s refs/heads/branch-2", setup.Commits.Third.OID.String()),
+ fmt.Sprintf("%s refs/heads/main", setup.Commits.First.OID.String()),
+ fmt.Sprintf("%s refs/tags/v1.0.0", lightweightTag.String()),
+ fmt.Sprintf("%s refs/tags/v2.0.0", annotatedTag.OID.String()),
+ fmt.Sprintf("^%s", setup.Commits.Diverging.OID.String()),
+ },
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ // Although ref creation commits beforehand, pack-refs
+ // task is unaware of these new refs. It keeps them as
+ // loose refs.
+ "refs/heads/branch-3": setup.Commits.Diverging.OID,
+ "refs/keep-around/1": setup.Commits.First.OID,
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "concurrent ref creation after pack-refs task is committed",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunPackRefs{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Commit{
+ TransactionID: 2,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch-3": {OldOID: gittest.DefaultObjectHash.ZeroOID, NewOID: setup.Commits.Diverging.OID},
+ "refs/keep-around/1": {OldOID: gittest.DefaultObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ },
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Directory: directoryStateWithPackedRefs(1),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/branch-1", Target: setup.Commits.Second.OID.String()},
+ {Name: "refs/heads/branch-2", Target: setup.Commits.Third.OID.String()},
+ {Name: "refs/heads/branch-3", Target: setup.Commits.Diverging.OID.String()},
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ {Name: "refs/keep-around/1", Target: setup.Commits.First.OID.String()},
+ {Name: "refs/tags/v1.0.0", Target: lightweightTag.String()},
+ {Name: "refs/tags/v2.0.0", Target: annotatedTag.OID.String()},
+ },
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{
+ "# pack-refs with: peeled fully-peeled sorted ",
+ fmt.Sprintf("%s refs/heads/branch-1", setup.Commits.Second.OID.String()),
+ fmt.Sprintf("%s refs/heads/branch-2", setup.Commits.Third.OID.String()),
+ fmt.Sprintf("%s refs/heads/main", setup.Commits.First.OID.String()),
+ fmt.Sprintf("%s refs/tags/v1.0.0", lightweightTag.String()),
+ fmt.Sprintf("%s refs/tags/v2.0.0", annotatedTag.OID.String()),
+ fmt.Sprintf("^%s", setup.Commits.Diverging.OID.String()),
+ },
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ // pack-refs task is unaware of these new refs. It keeps
+ // them as loose refs.
+ "refs/heads/branch-3": setup.Commits.Diverging.OID,
+ "refs/keep-around/1": setup.Commits.First.OID,
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "concurrent ref updates before pack-refs task is committed",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunPackRefs{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 2,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID},
+ "refs/heads/branch-1": {OldOID: setup.Commits.Second.OID, NewOID: setup.Commits.Third.OID},
+ "refs/heads/branch-2": {OldOID: setup.Commits.Third.OID, NewOID: setup.Commits.Diverging.OID},
+ "refs/tags/v1.0.0": {OldOID: setup.Commits.Diverging.OID, NewOID: setup.Commits.First.OID},
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Directory: directoryStateWithPackedRefs(2),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/branch-1", Target: setup.Commits.Third.OID.String()},
+ {Name: "refs/heads/branch-2", Target: setup.Commits.Diverging.OID.String()},
+ {Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()},
+ {Name: "refs/tags/v1.0.0", Target: setup.Commits.First.OID.String()},
+ {Name: "refs/tags/v2.0.0", Target: annotatedTag.OID.String()},
+ },
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{
+ "# pack-refs with: peeled fully-peeled sorted ",
+ fmt.Sprintf("%s refs/heads/branch-1", setup.Commits.Second.OID.String()), // Outdated
+ fmt.Sprintf("%s refs/heads/branch-2", setup.Commits.Third.OID.String()), // Outdated
+ fmt.Sprintf("%s refs/heads/main", setup.Commits.First.OID.String()), // Outdated
+ fmt.Sprintf("%s refs/tags/v1.0.0", lightweightTag.String()), // Outdated
+ fmt.Sprintf("%s refs/tags/v2.0.0", annotatedTag.OID.String()), // Still up-to-date
+ fmt.Sprintf("^%s", setup.Commits.Diverging.OID.String()),
+ },
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ // Updated refs shadow the ones in the packed-refs file.
+ "refs/heads/main": setup.Commits.Second.OID,
+ "refs/heads/branch-1": setup.Commits.Third.OID,
+ "refs/heads/branch-2": setup.Commits.Diverging.OID,
+ "refs/tags/v1.0.0": setup.Commits.First.OID,
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "concurrent ref updates after pack-refs task is committed",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunPackRefs{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Commit{
+ TransactionID: 2,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID},
+ "refs/heads/branch-1": {OldOID: setup.Commits.Second.OID, NewOID: setup.Commits.Third.OID},
+ "refs/heads/branch-2": {OldOID: setup.Commits.Third.OID, NewOID: setup.Commits.Diverging.OID},
+ "refs/tags/v1.0.0": {OldOID: setup.Commits.Diverging.OID, NewOID: setup.Commits.First.OID},
+ },
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Directory: directoryStateWithPackedRefs(1),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/branch-1", Target: setup.Commits.Third.OID.String()},
+ {Name: "refs/heads/branch-2", Target: setup.Commits.Diverging.OID.String()},
+ {Name: "refs/heads/main", Target: setup.Commits.Second.OID.String()},
+ {Name: "refs/tags/v1.0.0", Target: setup.Commits.First.OID.String()},
+ {Name: "refs/tags/v2.0.0", Target: annotatedTag.OID.String()},
+ },
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{
+ "# pack-refs with: peeled fully-peeled sorted ",
+ fmt.Sprintf("%s refs/heads/branch-1", setup.Commits.Second.OID.String()), // Outdated
+ fmt.Sprintf("%s refs/heads/branch-2", setup.Commits.Third.OID.String()), // Outdated
+ fmt.Sprintf("%s refs/heads/main", setup.Commits.First.OID.String()), // Outdated
+ fmt.Sprintf("%s refs/tags/v1.0.0", lightweightTag.String()), // Outdated
+ fmt.Sprintf("%s refs/tags/v2.0.0", annotatedTag.OID.String()),
+ fmt.Sprintf("^%s", setup.Commits.Diverging.OID.String()),
+ },
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.Second.OID,
+ "refs/heads/branch-1": setup.Commits.Third.OID,
+ "refs/heads/branch-2": setup.Commits.Diverging.OID,
+ "refs/tags/v1.0.0": setup.Commits.First.OID,
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "concurrent ref deletion before pack-refs is committed",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunPackRefs{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 2,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch-1": {OldOID: setup.Commits.Second.OID, NewOID: gittest.DefaultObjectHash.ZeroOID},
+ "refs/tags/v1.0.0": {OldOID: lightweightTag, NewOID: gittest.DefaultObjectHash.ZeroOID},
+ },
+ },
+ Commit{
+ TransactionID: 1,
+ ExpectedError: errPackRefsConflictRefDeletion,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/branch-2", Target: setup.Commits.Third.OID.String()},
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ {Name: "refs/tags/v2.0.0", Target: annotatedTag.OID.String()},
+ },
+ PackedRefs: &PackedRefsState{
+ // Empty packed-refs. It means the pack-refs task is not
+ // executed.
+ PackedRefsContent: []string{""},
+ // Deleted refs went away.
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/branch-2": setup.Commits.Third.OID,
+ "refs/heads/main": setup.Commits.First.OID,
+ "refs/tags/v2.0.0": annotatedTag.OID,
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "concurrent ref deletion before pack-refs is committed",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ },
+ RunPackRefs{
+ TransactionID: 1,
+ },
+ Commit{
+ TransactionID: 2,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.ObjectHash.ZeroOID},
+ },
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ Commit{
+ TransactionID: 3,
+ },
+ Commit{
+ TransactionID: 1,
+ ExpectedError: errPackRefsConflictRefDeletion,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Repositories: RepositoryStates{
+ relativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/branch-1", Target: setup.Commits.Second.OID.String()},
+ {Name: "refs/heads/branch-2", Target: setup.Commits.Third.OID.String()},
+ {Name: "refs/tags/v1.0.0", Target: lightweightTag.String()},
+ {Name: "refs/tags/v2.0.0", Target: annotatedTag.OID.String()},
+ },
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{""},
+ LooseReferences: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/branch-1": setup.Commits.Second.OID,
+ "refs/heads/branch-2": setup.Commits.Third.OID,
+ "refs/tags/v1.0.0": lightweightTag,
+ "refs/tags/v2.0.0": annotatedTag.OID,
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "concurrent ref deletion in other repository of a pool",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ Commit{
+ TransactionID: 3,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch-1": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ },
+ },
+ Begin{
+ TransactionID: 4,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 3,
+ },
+ Begin{
+ TransactionID: 5,
+ RelativePath: "pool",
+ ExpectedSnapshotLSN: 3,
+ },
+ RunPackRefs{
+ TransactionID: 5,
+ },
+ Commit{
+ TransactionID: 4,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch-1": {OldOID: setup.Commits.First.OID, NewOID: gittest.DefaultObjectHash.ZeroOID},
+ },
+ },
+ Commit{
+ TransactionID: 5,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(5).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{
+ "# pack-refs with: peeled fully-peeled sorted ",
+ fmt.Sprintf("%s refs/heads/main", setup.Commits.First.OID.String()),
+ },
+ LooseReferences: map[git.ReferenceName]git.ObjectID{},
+ },
+ },
+ "member": {
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/1/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ ),
+ "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ "/wal/5": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/5/packed-refs": packRefsDirectoryEntry(setup.Config),
+ },
+ },
+ },
+ {
+ desc: "concurrent ref deletion after pack-refs is committed",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunPackRefs{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Commit{
+ TransactionID: 2,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch-1": {OldOID: setup.Commits.Second.OID, NewOID: gittest.DefaultObjectHash.ZeroOID},
+ "refs/tags/v1.0.0": {OldOID: lightweightTag, NewOID: gittest.DefaultObjectHash.ZeroOID},
+ },
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Directory: directoryStateWithPackedRefs(1),
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/branch-2", Target: setup.Commits.Third.OID.String()},
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ {Name: "refs/tags/v2.0.0", Target: annotatedTag.OID.String()},
+ },
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{
+ "# pack-refs with: peeled fully-peeled sorted ",
+ fmt.Sprintf("%s refs/heads/branch-2", setup.Commits.Third.OID.String()),
+ fmt.Sprintf("%s refs/heads/main", setup.Commits.First.OID.String()),
+ fmt.Sprintf("%s refs/tags/v2.0.0", annotatedTag.OID.String()),
+ fmt.Sprintf("^%s", setup.Commits.Diverging.OID.String()),
+ },
+ LooseReferences: map[git.ReferenceName]git.ObjectID{},
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "empty directories are pruned after interrupted log application",
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/empty-dir/parent/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ },
+ },
+ CloseManager{},
+ StartManager{
+ Hooks: testTransactionHooks{
+ BeforeStoreAppliedLSN: func(hookContext) {
+ panic(errSimulatedCrash)
+ },
+ },
+ ExpectedError: errSimulatedCrash,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ RunPackRefs{
+ TransactionID: 2,
+ },
+ Commit{
+ TransactionID: 2,
+ ExpectedError: ErrTransactionProcessingStopped,
+ },
+ AssertManager{
+ ExpectedError: errSimulatedCrash,
+ },
+ StartManager{
+ ModifyStorage: func(tb testing.TB, cfg config.Cfg, storagePath string) {
+ // Create the directory that was removed already by the pack-refs task.
+ // This way we can assert reapplying the log entry will successfully remove
+ // the all directories even if the reference deletion was already applied.
+ require.NoError(tb, os.MkdirAll(
+ filepath.Join(storagePath, setup.RelativePath, "refs", "heads", "empty-dir"),
+ perm.PrivateDir,
+ ))
+ },
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/2": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/2/packed-refs": packRefsDirectoryEntry(setup.Config),
+ },
+ Repositories: RepositoryStates{
+ relativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/empty-dir/parent/main", Target: setup.Commits.First.OID.String()},
+ },
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{
+ "# pack-refs with: peeled fully-peeled sorted ",
+ fmt.Sprintf("%s refs/heads/empty-dir/parent/main", setup.Commits.First.OID.String()),
+ },
+ LooseReferences: map[git.ReferenceName]git.ObjectID{},
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "housekeeping fails in read-only transaction",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ RelativePath: setup.RelativePath,
+ ReadOnly: true,
+ },
+ RunPackRefs{},
+ Commit{
+ ExpectedError: errReadOnlyHousekeeping,
+ },
+ },
+ expectedState: StateAssertion{
+ Repositories: RepositoryStates{
+ relativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: defaultRefs,
+ },
+ },
+ },
+ },
+ {
+ desc: "housekeeping fails when there are other updates in transaction",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ RelativePath: setup.RelativePath,
+ },
+ RunPackRefs{},
+ Commit{
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.Commits.First.OID, NewOID: setup.Commits.Second.OID},
+ },
+ ExpectedError: errHousekeepingConflictOtherUpdates,
+ },
+ },
+ expectedState: StateAssertion{
+ Repositories: RepositoryStates{
+ relativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: defaultRefs,
+ },
+ },
+ },
+ },
+ {
+ desc: "housekeeping transaction runs concurrently with another housekeeping transaction",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunPackRefs{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ },
+ RunPackRefs{
+ TransactionID: 2,
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Commit{
+ TransactionID: 2,
+ ExpectedError: errHousekeepingConflictConcurrent,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(1).toProto(),
+ },
+ Directory: directoryStateWithPackedRefs(1),
+ Repositories: RepositoryStates{
+ relativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: defaultRefs,
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{
+ "# pack-refs with: peeled fully-peeled sorted ",
+ fmt.Sprintf("%s refs/heads/branch-1", setup.Commits.Second.OID.String()),
+ fmt.Sprintf("%s refs/heads/branch-2", setup.Commits.Third.OID.String()),
+ fmt.Sprintf("%s refs/heads/main", setup.Commits.First.OID.String()),
+ fmt.Sprintf("%s refs/tags/v1.0.0", lightweightTag.String()),
+ fmt.Sprintf("%s refs/tags/v2.0.0", annotatedTag.OID.String()),
+ fmt.Sprintf("^%s", setup.Commits.Diverging.OID.String()),
+ },
+ LooseReferences: map[git.ReferenceName]git.ObjectID{},
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "housekeeping transaction runs after another housekeeping transaction in other repository of a pool",
+ steps: steps{
+ RemoveRepository{},
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: "pool",
+ },
+ CreateRepository{
+ TransactionID: 1,
+ References: map[git.ReferenceName]git.ObjectID{
+ "refs/heads/main": setup.Commits.First.OID,
+ },
+ Packs: [][]byte{setup.Commits.First.Pack},
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 2,
+ Alternate: "../../pool/objects",
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: "member",
+ ExpectedSnapshotLSN: 2,
+ },
+ Begin{
+ TransactionID: 4,
+ RelativePath: "pool",
+ ExpectedSnapshotLSN: 2,
+ },
+ RunPackRefs{
+ TransactionID: 3,
+ },
+ RunPackRefs{
+ TransactionID: 4,
+ },
+ Commit{
+ TransactionID: 3,
+ },
+ Commit{
+ TransactionID: 4,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(4).toProto(),
+ },
+ Repositories: RepositoryStates{
+ "pool": {
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/main", Target: setup.Commits.First.OID.String()},
+ },
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{
+ "# pack-refs with: peeled fully-peeled sorted ",
+ fmt.Sprintf("%s refs/heads/main", setup.Commits.First.OID.String()),
+ },
+ LooseReferences: map[git.ReferenceName]git.ObjectID{},
+ },
+ },
+ "member": {
+ Objects: []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ Alternate: "../../pool/objects",
+ },
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1/objects.idx": indexFileDirectoryEntry(setup.Config),
+ "/wal/1/objects.pack": packFileDirectoryEntry(
+ setup.Config,
+ []git.ObjectID{
+ setup.ObjectHash.EmptyTreeOID,
+ setup.Commits.First.OID,
+ },
+ ),
+ "/wal/1/objects.rev": reverseIndexFileDirectoryEntry(setup.Config),
+ "/wal/3": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/3/packed-refs": packRefsDirectoryEntry(setup.Config),
+ "/wal/4": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/4/packed-refs": packRefsDirectoryEntry(setup.Config),
+ },
+ },
+ },
+ {
+ desc: "housekeeping transaction runs after another housekeeping transaction",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunPackRefs{
+ TransactionID: 1,
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ RunPackRefs{
+ TransactionID: 2,
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/1/packed-refs": packRefsDirectoryEntry(setup.Config),
+ "/wal/2": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal/2/packed-refs": packRefsDirectoryEntry(setup.Config),
+ },
+ Repositories: RepositoryStates{
+ relativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: defaultRefs,
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{
+ "# pack-refs with: peeled fully-peeled sorted ",
+ fmt.Sprintf("%s refs/heads/branch-1", setup.Commits.Second.OID.String()),
+ fmt.Sprintf("%s refs/heads/branch-2", setup.Commits.Third.OID.String()),
+ fmt.Sprintf("%s refs/heads/main", setup.Commits.First.OID.String()),
+ fmt.Sprintf("%s refs/tags/v1.0.0", lightweightTag.String()),
+ fmt.Sprintf("%s refs/tags/v2.0.0", annotatedTag.OID.String()),
+ fmt.Sprintf("^%s", setup.Commits.Diverging.OID.String()),
+ },
+ LooseReferences: map[git.ReferenceName]git.ObjectID{},
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "housekeeping transaction runs concurrently with a repository deletion",
+ customSetup: customSetup,
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ RunPackRefs{
+ TransactionID: 1,
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 2,
+ DeleteRepository: true,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ CreateRepository{
+ TransactionID: 3,
+ },
+ Commit{
+ TransactionID: 3,
+ },
+ Commit{
+ TransactionID: 1,
+ ExpectedError: errConflictRepositoryDeletion,
+ },
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Directory: testhelper.DirectoryState{
+ "/": {Mode: fs.ModeDir | perm.PrivateDir},
+ "/wal": {Mode: fs.ModeDir | perm.PrivateDir},
+ },
+ Repositories: RepositoryStates{
+ relativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: nil,
+ PackedRefs: &PackedRefsState{
+ PackedRefsContent: []string{""},
+ LooseReferences: map[git.ReferenceName]git.ObjectID{},
+ },
+ Objects: []git.ObjectID{},
+ },
+ },
+ },
+ },
+ }
+}
diff --git a/internal/gitaly/storage/storagemgr/transaction_manager_test.go b/internal/gitaly/storage/storagemgr/transaction_manager_test.go
index a4b613497..3383e289c 100644
--- a/internal/gitaly/storage/storagemgr/transaction_manager_test.go
+++ b/internal/gitaly/storage/storagemgr/transaction_manager_test.go
@@ -3,6 +3,7 @@ package storagemgr
import (
"archive/tar"
"bytes"
+ "container/list"
"context"
"encoding/hex"
"errors"
@@ -114,6 +115,16 @@ func packFileDirectoryEntry(cfg config.Cfg, expectedObjects []git.ObjectID) test
}
}
+// packRefsDirectoryEntry returns a DirectoryEntry that checks for the existence of packed-refs file. The content does
+// not matter because it will be asserted in the repository state insteaad.
+func packRefsDirectoryEntry(cfg config.Cfg) testhelper.DirectoryEntry {
+ return testhelper.DirectoryEntry{
+ Mode: perm.SharedFile,
+ Content: "",
+ ParseContent: func(testing.TB, string, []byte) any { return "" },
+ }
+}
+
// indexFileDirectoryEntry returns a DirectoryEntry that asserts the given pack file index is valid.
func indexFileDirectoryEntry(cfg config.Cfg) testhelper.DirectoryEntry {
return testhelper.DirectoryEntry{
@@ -153,103 +164,104 @@ func reverseIndexFileDirectoryEntry(cfg config.Cfg) testhelper.DirectoryEntry {
}
}
-func TestTransactionManager(t *testing.T) {
- t.Parallel()
+func setupTest(t *testing.T, ctx context.Context, testPartitionID partitionID, relativePath string) testTransactionSetup {
+ t.Helper()
+
+ cfg := testcfg.Build(t)
+
+ repo, repoPath := gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{
+ SkipCreationViaService: true,
+ RelativePath: relativePath,
+ })
+
+ firstCommitOID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents())
+ secondCommitOID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(firstCommitOID))
+ thirdCommitOID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(secondCommitOID))
+ divergingCommitOID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(firstCommitOID), gittest.WithMessage("diverging commit"))
+
+ cmdFactory := gittest.NewCommandFactory(t, cfg)
+ catfileCache := catfile.NewCache(cfg)
+ t.Cleanup(catfileCache.Stop)
+
+ logger := testhelper.NewLogger(t)
+ locator := config.NewLocator(cfg)
+ localRepo := localrepo.New(
+ logger,
+ locator,
+ cmdFactory,
+ catfileCache,
+ repo,
+ )
- ctx := testhelper.Context(t)
+ objectHash, err := localRepo.ObjectHash(ctx)
+ require.NoError(t, err)
- // testPartitionID is the partition ID used in the tests for the TransactionManager.
- const testPartitionID partitionID = 1
+ hasher := objectHash.Hash()
+ _, err = hasher.Write([]byte("content does not matter"))
+ require.NoError(t, err)
+ nonExistentOID, err := objectHash.FromHex(hex.EncodeToString(hasher.Sum(nil)))
+ require.NoError(t, err)
- setupTest := func(t *testing.T, relativePath string) testTransactionSetup {
+ packCommit := func(oid git.ObjectID) []byte {
t.Helper()
- cfg := testcfg.Build(t)
-
- repo, repoPath := gittest.CreateRepository(t, ctx, cfg, gittest.CreateRepositoryConfig{
- SkipCreationViaService: true,
- RelativePath: relativePath,
- })
-
- firstCommitOID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents())
- secondCommitOID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(firstCommitOID))
- thirdCommitOID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(secondCommitOID))
- divergingCommitOID := gittest.WriteCommit(t, cfg, repoPath, gittest.WithParents(firstCommitOID), gittest.WithMessage("diverging commit"))
-
- cmdFactory := gittest.NewCommandFactory(t, cfg)
- catfileCache := catfile.NewCache(cfg)
- t.Cleanup(catfileCache.Stop)
-
- logger := testhelper.NewLogger(t)
- locator := config.NewLocator(cfg)
- localRepo := localrepo.New(
- logger,
- locator,
- cmdFactory,
- catfileCache,
- repo,
+ var pack bytes.Buffer
+ require.NoError(t,
+ localRepo.PackObjects(ctx, strings.NewReader(oid.String()), &pack),
)
- objectHash, err := localRepo.ObjectHash(ctx)
- require.NoError(t, err)
-
- hasher := objectHash.Hash()
- _, err = hasher.Write([]byte("content does not matter"))
- require.NoError(t, err)
- nonExistentOID, err := objectHash.FromHex(hex.EncodeToString(hasher.Sum(nil)))
- require.NoError(t, err)
+ return pack.Bytes()
+ }
- packCommit := func(oid git.ObjectID) []byte {
- t.Helper()
+ return testTransactionSetup{
+ PartitionID: testPartitionID,
+ RelativePath: relativePath,
+ RepositoryPath: repoPath,
+ Repo: localRepo,
+ Config: cfg,
+ ObjectHash: objectHash,
+ CommandFactory: cmdFactory,
+ RepositoryFactory: localrepo.NewFactory(logger, locator, cmdFactory, catfileCache),
+ NonExistentOID: nonExistentOID,
+ Commits: testTransactionCommits{
+ First: testTransactionCommit{
+ OID: firstCommitOID,
+ Pack: packCommit(firstCommitOID),
+ },
+ Second: testTransactionCommit{
+ OID: secondCommitOID,
+ Pack: packCommit(secondCommitOID),
+ },
+ Third: testTransactionCommit{
+ OID: thirdCommitOID,
+ Pack: packCommit(thirdCommitOID),
+ },
+ Diverging: testTransactionCommit{
+ OID: divergingCommitOID,
+ Pack: packCommit(divergingCommitOID),
+ },
+ },
+ }
+}
- var pack bytes.Buffer
- require.NoError(t,
- localRepo.PackObjects(ctx, strings.NewReader(oid.String()), &pack),
- )
+func TestTransactionManager(t *testing.T) {
+ t.Parallel()
- return pack.Bytes()
- }
+ ctx := testhelper.Context(t)
- return testTransactionSetup{
- PartitionID: testPartitionID,
- RelativePath: relativePath,
- RepositoryPath: repoPath,
- Repo: localRepo,
- Config: cfg,
- ObjectHash: objectHash,
- CommandFactory: cmdFactory,
- RepositoryFactory: localrepo.NewFactory(logger, locator, cmdFactory, catfileCache),
- NonExistentOID: nonExistentOID,
- Commits: testTransactionCommits{
- First: testTransactionCommit{
- OID: firstCommitOID,
- Pack: packCommit(firstCommitOID),
- },
- Second: testTransactionCommit{
- OID: secondCommitOID,
- Pack: packCommit(secondCommitOID),
- },
- Third: testTransactionCommit{
- OID: thirdCommitOID,
- Pack: packCommit(thirdCommitOID),
- },
- Diverging: testTransactionCommit{
- OID: divergingCommitOID,
- Pack: packCommit(divergingCommitOID),
- },
- },
- }
- }
+ // testPartitionID is the partition ID used in the tests for the TransactionManager.
+ const testPartitionID partitionID = 1
// A clean repository is setup for each test. We build a setup ahead of the tests here once to
// get deterministic commit IDs, relative path and object hash we can use to build the declarative
// test cases.
relativePath := gittest.NewRepositoryName(t)
- setup := setupTest(t, relativePath)
+ setup := setupTest(t, ctx, testPartitionID, relativePath)
var testCases []transactionTestCase
subTests := [][]transactionTestCase{
generateCommonTests(t, ctx, setup),
+ generateCommittedEntriesTests(t, setup),
generateInvalidReferencesTests(t, setup),
generateModifyReferencesTests(t, setup),
generateCreateRepositoryTests(t, setup),
@@ -257,6 +269,7 @@ func TestTransactionManager(t *testing.T) {
generateDefaultBranchTests(t, setup),
generateAlternateTests(t, setup),
generateCustomHooksTests(t, setup),
+ generateHousekeepingTests(t, ctx, testPartitionID, relativePath),
}
for _, subCases := range subTests {
testCases = append(testCases, subCases...)
@@ -268,7 +281,12 @@ func TestTransactionManager(t *testing.T) {
t.Parallel()
// Setup the repository with the exact same state as what was used to build the test cases.
- setup := setupTest(t, relativePath)
+ var setup testTransactionSetup
+ if tc.customSetup != nil {
+ setup = tc.customSetup(t, ctx, testPartitionID, relativePath)
+ } else {
+ setup = setupTest(t, ctx, testPartitionID, relativePath)
+ }
runTransactionTest(t, ctx, tc, setup)
})
}
@@ -1503,6 +1521,258 @@ func generateCommonTests(t *testing.T, ctx context.Context, setup testTransactio
}
}
+func generateCommittedEntriesTests(t *testing.T, setup testTransactionSetup) []transactionTestCase {
+ assertCommittedEntries := func(t *testing.T, expected []*committedEntry, actualList *list.List) {
+ require.Equal(t, len(expected), actualList.Len())
+
+ i := 0
+ for elm := actualList.Front(); elm != nil; elm = elm.Next() {
+ actual := elm.Value.(*committedEntry)
+ require.Equal(t, expected[i].lsn, actual.lsn)
+ require.Equal(t, expected[i].snapshotReaders, actual.snapshotReaders)
+ testhelper.ProtoEqual(t, expected[i].entry, actual.entry)
+ i++
+ }
+ }
+
+ refChangeLogEntry := func(ref string, oid git.ObjectID) *gitalypb.LogEntry {
+ return &gitalypb.LogEntry{
+ RelativePath: setup.RelativePath,
+ ReferenceTransactions: []*gitalypb.LogEntry_ReferenceTransaction{
+ {
+ Changes: []*gitalypb.LogEntry_ReferenceTransaction_Change{
+ {
+ ReferenceName: []byte(ref),
+ NewOid: []byte(oid),
+ },
+ },
+ },
+ },
+ }
+ }
+
+ return []transactionTestCase{
+ {
+ desc: "manager has just initialized",
+ steps: steps{
+ StartManager{},
+ AdhocAssertion(func(t *testing.T, ctx context.Context, tm *TransactionManager) {
+ assertCommittedEntries(t, []*committedEntry{}, tm.committedEntries)
+ }),
+ },
+ },
+ {
+ desc: "a transaction has one reader",
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ AdhocAssertion(func(t *testing.T, ctx context.Context, tm *TransactionManager) {
+ assertCommittedEntries(t, []*committedEntry{
+ {
+ lsn: 0,
+ snapshotReaders: 1,
+ },
+ }, tm.committedEntries)
+ }),
+ Commit{
+ TransactionID: 1,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch-1": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ },
+ },
+ AdhocAssertion(func(t *testing.T, ctx context.Context, tm *TransactionManager) {
+ assertCommittedEntries(t, []*committedEntry{}, tm.committedEntries)
+ }),
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ AdhocAssertion(func(t *testing.T, ctx context.Context, tm *TransactionManager) {
+ assertCommittedEntries(t, []*committedEntry{
+ {
+ lsn: 1,
+ snapshotReaders: 1,
+ },
+ }, tm.committedEntries)
+ }),
+ Commit{
+ TransactionID: 2,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ },
+ },
+ AdhocAssertion(func(t *testing.T, ctx context.Context, tm *TransactionManager) {
+ assertCommittedEntries(t, []*committedEntry{}, tm.committedEntries)
+ }),
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(2).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/branch-1", Target: string(setup.Commits.First.OID)},
+ {Name: "refs/heads/main", Target: string(setup.Commits.First.OID)},
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "a transaction has multiple readers",
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ },
+ Commit{
+ TransactionID: 1,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/main": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ },
+ },
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ Begin{
+ TransactionID: 3,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 1,
+ },
+ AdhocAssertion(func(t *testing.T, ctx context.Context, tm *TransactionManager) {
+ assertCommittedEntries(t, []*committedEntry{
+ {
+ lsn: 1,
+ snapshotReaders: 2,
+ },
+ }, tm.committedEntries)
+ }),
+ Commit{
+ TransactionID: 2,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch-1": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ },
+ },
+ AdhocAssertion(func(t *testing.T, ctx context.Context, tm *TransactionManager) {
+ assertCommittedEntries(t, []*committedEntry{
+ {
+ lsn: 1,
+ snapshotReaders: 1,
+ },
+ {
+ lsn: 2,
+ entry: refChangeLogEntry("refs/heads/branch-1", setup.Commits.First.OID),
+ },
+ }, tm.committedEntries)
+ }),
+ Begin{
+ TransactionID: 4,
+ RelativePath: setup.RelativePath,
+ ExpectedSnapshotLSN: 2,
+ },
+ AdhocAssertion(func(t *testing.T, ctx context.Context, tm *TransactionManager) {
+ assertCommittedEntries(t, []*committedEntry{
+ {
+ lsn: 1,
+ snapshotReaders: 1,
+ },
+ {
+ lsn: 2,
+ snapshotReaders: 1,
+ entry: refChangeLogEntry("refs/heads/branch-1", setup.Commits.First.OID),
+ },
+ }, tm.committedEntries)
+ }),
+ Commit{
+ TransactionID: 3,
+ ReferenceUpdates: ReferenceUpdates{
+ "refs/heads/branch-2": {OldOID: setup.ObjectHash.ZeroOID, NewOID: setup.Commits.First.OID},
+ },
+ },
+ AdhocAssertion(func(t *testing.T, ctx context.Context, tm *TransactionManager) {
+ assertCommittedEntries(t, []*committedEntry{
+ {
+ lsn: 2,
+ entry: refChangeLogEntry("refs/heads/branch-1", setup.Commits.First.OID),
+ snapshotReaders: 1,
+ },
+ {
+ lsn: 3,
+ entry: refChangeLogEntry("refs/heads/branch-2", setup.Commits.First.OID),
+ },
+ }, tm.committedEntries)
+ }),
+ Rollback{
+ TransactionID: 4,
+ },
+ AdhocAssertion(func(t *testing.T, ctx context.Context, tm *TransactionManager) {
+ assertCommittedEntries(t, []*committedEntry{}, tm.committedEntries)
+ }),
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{
+ string(keyAppliedLSN(setup.PartitionID)): LSN(3).toProto(),
+ },
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ References: []git.Reference{
+ {Name: "refs/heads/branch-1", Target: string(setup.Commits.First.OID)},
+ {Name: "refs/heads/branch-2", Target: string(setup.Commits.First.OID)},
+ {Name: "refs/heads/main", Target: string(setup.Commits.First.OID)},
+ },
+ },
+ },
+ },
+ },
+ {
+ desc: "committed read-only transaction are not kept",
+ steps: steps{
+ StartManager{},
+ Begin{
+ TransactionID: 1,
+ RelativePath: setup.RelativePath,
+ ReadOnly: true,
+ },
+ Commit{
+ TransactionID: 1,
+ },
+ AdhocAssertion(func(t *testing.T, ctx context.Context, tm *TransactionManager) {
+ assertCommittedEntries(t, []*committedEntry{}, tm.committedEntries)
+ }),
+ Begin{
+ TransactionID: 2,
+ RelativePath: setup.RelativePath,
+ ReadOnly: true,
+ },
+ Commit{
+ TransactionID: 2,
+ },
+ AdhocAssertion(func(t *testing.T, ctx context.Context, tm *TransactionManager) {
+ assertCommittedEntries(t, []*committedEntry{}, tm.committedEntries)
+ }),
+ },
+ expectedState: StateAssertion{
+ Database: DatabaseState{},
+ Repositories: RepositoryStates{
+ setup.RelativePath: {
+ DefaultBranch: "refs/heads/main",
+ },
+ },
+ },
+ },
+ }
+}
+
// BenchmarkTransactionManager benchmarks the transaction throughput of the TransactionManager at various levels
// of concurrency and transaction sizes.
func BenchmarkTransactionManager(b *testing.B) {
diff --git a/internal/praefect/datastore/repository_store.go b/internal/praefect/datastore/repository_store.go
index 77dce6ba0..4925fcb50 100644
--- a/internal/praefect/datastore/repository_store.go
+++ b/internal/praefect/datastore/repository_store.go
@@ -111,6 +111,8 @@ type RepositoryStore interface {
MarkVirtualStorageUnverified(ctx context.Context, virtualStorage string) (int64, error)
// MarkStorageUnverified marsk all replicas on the storage as unverified.
MarkStorageUnverified(ctx context.Context, virtualStorage, storage string) (int64, error)
+ // ListRepositoryPaths retrieves the relative path for all repositories present on the given virtual storage.
+ ListRepositoryPaths(ctx context.Context, virtualStorage string) ([]string, error)
}
// PostgresRepositoryStore is a Postgres implementation of RepositoryStore.
@@ -916,3 +918,28 @@ func (rs *PostgresRepositoryStore) GetReplicaPath(ctx context.Context, repositor
return replicaPath, nil
}
+
+// ListRepositoryPaths retrieves the relative path for all repositories present on the given virtual storage.
+func (rs *PostgresRepositoryStore) ListRepositoryPaths(ctx context.Context, virtualStorage string) ([]string, error) {
+ rows, err := rs.db.QueryContext(ctx, `
+SELECT relative_path
+FROM repositories
+WHERE virtual_storage = $1
+`, virtualStorage)
+ if err != nil {
+ return nil, fmt.Errorf("query: %w", err)
+ }
+ defer rows.Close()
+
+ var relativePaths []string
+ for rows.Next() {
+ var relativePath string
+ if err := rows.Scan(&relativePath); err != nil {
+ return nil, fmt.Errorf("scan: %w", err)
+ }
+
+ relativePaths = append(relativePaths, relativePath)
+ }
+
+ return relativePaths, rows.Err()
+}
diff --git a/internal/praefect/remove_all.go b/internal/praefect/remove_all.go
index 9fa08206f..9c383cd0e 100644
--- a/internal/praefect/remove_all.go
+++ b/internal/praefect/remove_all.go
@@ -32,6 +32,7 @@ func RemoveAllHandler(rs datastore.RepositoryStore, conns Connections) grpc.Stre
conn := conn
group.Go(func() error {
+ //nolint:staticcheck
_, err := gitalypb.NewRepositoryServiceClient(conn).RemoveAll(ctx, &gitalypb.RemoveAllRequest{
StorageName: rewrittenStorage,
})
diff --git a/internal/praefect/remove_all_test.go b/internal/praefect/remove_all_test.go
index 965ffb95b..6b5e6f848 100644
--- a/internal/praefect/remove_all_test.go
+++ b/internal/praefect/remove_all_test.go
@@ -96,6 +96,7 @@ Issue: https://gitlab.com/gitlab-org/gitaly/-/issues/5269`)
_, err = client.RepositorySize(ctx, &gitalypb.RepositorySizeRequest{Repository: &gitalypb.Repository{}})
testhelper.RequireGrpcError(t, errServedByGitaly, err)
+ //nolint:staticcheck
resp, err := client.RemoveAll(ctx, &gitalypb.RemoveAllRequest{StorageName: virtualStorage})
require.NoError(t, err)
diff --git a/internal/praefect/server.go b/internal/praefect/server.go
index 54cbee079..c52b160f5 100644
--- a/internal/praefect/server.go
+++ b/internal/praefect/server.go
@@ -195,6 +195,9 @@ func NewGRPCServer(
"DeleteObjectPool": DeleteObjectPoolHandler(deps.RepositoryStore, deps.Logger, deps.Conns),
"GetObjectPool": GetObjectPoolHandler(deps.RepositoryStore, deps.Router),
})
+ proxy.RegisterStreamHandlers(srv, "gitaly.InternalGitaly", map[string]grpc.StreamHandler{
+ "WalkRepos": WalkReposHandler(deps.RepositoryStore),
+ })
}
return srv
diff --git a/internal/praefect/walkrepos.go b/internal/praefect/walkrepos.go
new file mode 100644
index 000000000..1f321a4c7
--- /dev/null
+++ b/internal/praefect/walkrepos.go
@@ -0,0 +1,47 @@
+package praefect
+
+import (
+ "fmt"
+
+ "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/praefect/datastore"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/structerr"
+ "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
+ "google.golang.org/grpc"
+)
+
+// WalkReposHandler implements an interceptor for the WalkRepos RPC, invoked when calling
+// through Praefect. Instead of walking the storage directory in the filesystem, this Praefect
+// implementation queries the database for all known repositories in the given virtual storage.
+// As a consequence, the modification_time parameter can't be populated in the response.
+func WalkReposHandler(rs datastore.RepositoryStore) grpc.StreamHandler {
+ return func(srv interface{}, stream grpc.ServerStream) error {
+ sendRepo := func(relPath string) error {
+ return stream.SendMsg(&gitalypb.WalkReposResponse{
+ RelativePath: relPath,
+ })
+ }
+
+ var req gitalypb.WalkReposRequest
+ if err := stream.RecvMsg(&req); err != nil {
+ return fmt.Errorf("receive request: %w", err)
+ }
+
+ if req.StorageName == "" {
+ return structerr.NewInvalidArgument("%w", storage.ErrStorageNotSet)
+ }
+
+ repos, err := rs.ListRepositoryPaths(stream.Context(), req.StorageName)
+ if err != nil {
+ return structerr.NewInternal("list repository paths: %w", err)
+ }
+
+ for _, repo := range repos {
+ if err := sendRepo(repo); err != nil {
+ return structerr.NewInternal("send repository path: %w", err)
+ }
+ }
+
+ return nil
+ }
+}
diff --git a/internal/praefect/walkrepos_test.go b/internal/praefect/walkrepos_test.go
new file mode 100644
index 000000000..63301caf5
--- /dev/null
+++ b/internal/praefect/walkrepos_test.go
@@ -0,0 +1,88 @@
+package praefect
+
+import (
+ "net"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/grpc/protoregistry"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/praefect/config"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/praefect/datastore"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/structerr"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/testhelper/testdb"
+ "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials/insecure"
+)
+
+func TestWalkReposHandler(t *testing.T) {
+ t.Parallel()
+
+ db := testdb.New(t)
+ for _, tc := range []struct {
+ desc string
+ request *gitalypb.WalkReposRequest
+ responses []*gitalypb.WalkReposResponse
+ expectedErr error
+ }{
+ {
+ desc: "missing storage name",
+ request: &gitalypb.WalkReposRequest{},
+ expectedErr: structerr.NewInvalidArgument("%w", storage.ErrStorageNotSet),
+ },
+ {
+ desc: "repositories found",
+ request: &gitalypb.WalkReposRequest{StorageName: "virtual-storage"},
+ responses: []*gitalypb.WalkReposResponse{
+ {RelativePath: "relative-path"},
+ {RelativePath: "relative-path-2"},
+ },
+ },
+ } {
+ t.Run(tc.desc, func(t *testing.T) {
+ db.TruncateAll(t)
+ rs := datastore.NewPostgresRepositoryStore(db, map[string][]string{"virtual-storage": {"storage"}})
+ ctx := testhelper.Context(t)
+
+ require.NoError(t, rs.CreateRepository(ctx, 0, "virtual-storage", "relative-path", "relative-path", "storage", nil, nil, false, false))
+ require.NoError(t, rs.CreateRepository(ctx, 1, "virtual-storage", "relative-path-2", "relative-path-2", "storage", nil, nil, false, false))
+
+ tmp := testhelper.TempDir(t)
+
+ ln, err := net.Listen("unix", filepath.Join(tmp, "praefect"))
+ require.NoError(t, err)
+
+ srv := NewGRPCServer(&Dependencies{
+ Config: config.Config{Failover: config.Failover{ElectionStrategy: config.ElectionStrategyPerRepository}},
+ Logger: testhelper.SharedLogger(t),
+ RepositoryStore: rs,
+ Registry: protoregistry.GitalyProtoPreregistered,
+ }, nil)
+ defer srv.Stop()
+
+ go testhelper.MustServe(t, srv, ln)
+
+ clientConn, err := grpc.DialContext(ctx, "unix://"+ln.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
+ require.NoError(t, err)
+ defer testhelper.MustClose(t, clientConn)
+
+ client := gitalypb.NewInternalGitalyClient(clientConn)
+
+ stream, err := client.WalkRepos(ctx, tc.request)
+ if tc.expectedErr != nil {
+ // Consume the first message and test for errors only if we're expecting an error.
+ _, err = stream.Recv()
+ testhelper.RequireGrpcError(t, tc.expectedErr, err)
+ return
+ }
+ require.NoError(t, err)
+
+ actualRepos, err := testhelper.Receive(stream.Recv)
+ require.NoError(t, err)
+ testhelper.ProtoEqual(t, tc.responses, actualRepos)
+ })
+ }
+}
diff --git a/proto/go/gitalypb/log.pb.go b/proto/go/gitalypb/log.pb.go
index ccc1f4f1b..10f881f87 100644
--- a/proto/go/gitalypb/log.pb.go
+++ b/proto/go/gitalypb/log.pb.go
@@ -51,6 +51,8 @@ type LogEntry struct {
RepositoryCreation *LogEntry_RepositoryCreation `protobuf:"bytes,7,opt,name=repository_creation,json=repositoryCreation,proto3" json:"repository_creation,omitempty"`
// alternate_update records a change to the repository's 'objects/info/alternates' file.
AlternateUpdate *LogEntry_AlternateUpdate `protobuf:"bytes,8,opt,name=alternate_update,json=alternateUpdate,proto3" json:"alternate_update,omitempty"`
+ // housekeeping, when set, indicates this log entry contains a housekeeping task.
+ Housekeeping *LogEntry_Housekeeping `protobuf:"bytes,9,opt,name=housekeeping,proto3" json:"housekeeping,omitempty"`
}
func (x *LogEntry) Reset() {
@@ -141,6 +143,13 @@ func (x *LogEntry) GetAlternateUpdate() *LogEntry_AlternateUpdate {
return nil
}
+func (x *LogEntry) GetHousekeeping() *LogEntry_Housekeeping {
+ if x != nil {
+ return x.Housekeeping
+ }
+ return nil
+}
+
// LSN serializes a log sequence number. It's used for storing a partition's
// applied LSN in the database.
//
@@ -482,6 +491,57 @@ func (x *LogEntry_AlternateUpdate) GetPath() string {
return ""
}
+// Housekeeping models a housekeeping run. It is supposed to handle housekeeping tasks for repositories such as the
+// cleanup of unneeded files and optimizations for the repository's data structures. It is a collection of smaller
+// tasks.
+type LogEntry_Housekeeping struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // pack_refs signifies if the housekeeping run includes a pack-refs task.
+ PackRefs *LogEntry_Housekeeping_PackRefs `protobuf:"bytes,1,opt,name=pack_refs,json=packRefs,proto3" json:"pack_refs,omitempty"`
+}
+
+func (x *LogEntry_Housekeeping) Reset() {
+ *x = LogEntry_Housekeeping{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_log_proto_msgTypes[8]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *LogEntry_Housekeeping) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LogEntry_Housekeeping) ProtoMessage() {}
+
+func (x *LogEntry_Housekeeping) ProtoReflect() protoreflect.Message {
+ mi := &file_log_proto_msgTypes[8]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use LogEntry_Housekeeping.ProtoReflect.Descriptor instead.
+func (*LogEntry_Housekeeping) Descriptor() ([]byte, []int) {
+ return file_log_proto_rawDescGZIP(), []int{0, 6}
+}
+
+func (x *LogEntry_Housekeeping) GetPackRefs() *LogEntry_Housekeeping_PackRefs {
+ if x != nil {
+ return x.PackRefs
+ }
+ return nil
+}
+
// Change models a single reference change.
type LogEntry_ReferenceTransaction_Change struct {
state protoimpl.MessageState
@@ -500,7 +560,7 @@ type LogEntry_ReferenceTransaction_Change struct {
func (x *LogEntry_ReferenceTransaction_Change) Reset() {
*x = LogEntry_ReferenceTransaction_Change{}
if protoimpl.UnsafeEnabled {
- mi := &file_log_proto_msgTypes[8]
+ mi := &file_log_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -513,7 +573,7 @@ func (x *LogEntry_ReferenceTransaction_Change) String() string {
func (*LogEntry_ReferenceTransaction_Change) ProtoMessage() {}
func (x *LogEntry_ReferenceTransaction_Change) ProtoReflect() protoreflect.Message {
- mi := &file_log_proto_msgTypes[8]
+ mi := &file_log_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -543,12 +603,63 @@ func (x *LogEntry_ReferenceTransaction_Change) GetNewOid() []byte {
return nil
}
+// PackRefs models a pack-refs housekeeping task. This task is to pack loose references into a singular packed-refs
+// file to optimize ref accessing time. In other words, it's a wrapper for git-pack-refs command.
+type LogEntry_Housekeeping_PackRefs struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // pruned_refs is the list of fully qualified references to be pruned. Gitaly removes the loose reference files on
+ // the disk. They still stay intact in the packed-refs.
+ PrunedRefs [][]byte `protobuf:"bytes,1,rep,name=pruned_refs,json=prunedRefs,proto3" json:"pruned_refs,omitempty"`
+}
+
+func (x *LogEntry_Housekeeping_PackRefs) Reset() {
+ *x = LogEntry_Housekeeping_PackRefs{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_log_proto_msgTypes[10]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *LogEntry_Housekeeping_PackRefs) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LogEntry_Housekeeping_PackRefs) ProtoMessage() {}
+
+func (x *LogEntry_Housekeeping_PackRefs) ProtoReflect() protoreflect.Message {
+ mi := &file_log_proto_msgTypes[10]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use LogEntry_Housekeeping_PackRefs.ProtoReflect.Descriptor instead.
+func (*LogEntry_Housekeeping_PackRefs) Descriptor() ([]byte, []int) {
+ return file_log_proto_rawDescGZIP(), []int{0, 6, 0}
+}
+
+func (x *LogEntry_Housekeeping_PackRefs) GetPrunedRefs() [][]byte {
+ if x != nil {
+ return x.PrunedRefs
+ }
+ return nil
+}
+
var File_log_proto protoreflect.FileDescriptor
var file_log_proto_rawDesc = []byte{
0x0a, 0x09, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x67, 0x69, 0x74,
0x61, 0x6c, 0x79, 0x1a, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74,
- 0x6f, 0x22, 0x8b, 0x08, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x23,
+ 0x6f, 0x22, 0xd1, 0x09, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x23,
0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50,
0x61, 0x74, 0x68, 0x12, 0x5c, 0x0a, 0x16, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65,
@@ -585,40 +696,52 @@ var file_log_proto_rawDesc = []byte{
0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e,
0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61,
0x74, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e,
- 0x61, 0x74, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x1a, 0xa8, 0x01, 0x0a, 0x14, 0x52, 0x65,
- 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69,
- 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20,
- 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4c, 0x6f, 0x67,
- 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x54,
- 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67,
- 0x65, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x1a, 0x48, 0x0a, 0x06, 0x43, 0x68,
- 0x61, 0x6e, 0x67, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
- 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x72, 0x65,
- 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x6e,
- 0x65, 0x77, 0x5f, 0x6f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x6e, 0x65,
- 0x77, 0x4f, 0x69, 0x64, 0x1a, 0x3c, 0x0a, 0x13, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42,
- 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x72,
- 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x4e, 0x61,
- 0x6d, 0x65, 0x1a, 0x3d, 0x0a, 0x11, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x6f, 0x6f, 0x6b,
- 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x75, 0x73, 0x74, 0x6f,
- 0x6d, 0x5f, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x5f, 0x74, 0x61, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x0c, 0x52, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x6f, 0x6f, 0x6b, 0x73, 0x54, 0x61,
- 0x72, 0x1a, 0x4f, 0x0a, 0x12, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x43,
- 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x0d, 0x6f, 0x62, 0x6a, 0x65, 0x63,
- 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14,
- 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x6f,
- 0x72, 0x6d, 0x61, 0x74, 0x52, 0x0c, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46, 0x6f, 0x72, 0x6d,
- 0x61, 0x74, 0x1a, 0x14, 0x0a, 0x12, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79,
- 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x25, 0x0a, 0x0f, 0x41, 0x6c, 0x74, 0x65,
- 0x72, 0x6e, 0x61, 0x74, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70,
- 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22,
- 0x1b, 0x0a, 0x03, 0x4c, 0x53, 0x4e, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
- 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x34, 0x5a, 0x32,
- 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61,
- 0x62, 0x2d, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2f, 0x76, 0x31, 0x36,
- 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79,
- 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x61, 0x74, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x68, 0x6f, 0x75,
+ 0x73, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72,
+ 0x79, 0x2e, 0x48, 0x6f, 0x75, 0x73, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0c,
+ 0x68, 0x6f, 0x75, 0x73, 0x65, 0x6b, 0x65, 0x65, 0x70, 0x69, 0x6e, 0x67, 0x1a, 0xa8, 0x01, 0x0a,
+ 0x14, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73,
+ 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e,
+ 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
+ 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x68,
+ 0x61, 0x6e, 0x67, 0x65, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x1a, 0x48, 0x0a,
+ 0x06, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x66, 0x65, 0x72,
+ 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
+ 0x0d, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x17,
+ 0x0a, 0x07, 0x6e, 0x65, 0x77, 0x5f, 0x6f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
+ 0x06, 0x6e, 0x65, 0x77, 0x4f, 0x69, 0x64, 0x1a, 0x3c, 0x0a, 0x13, 0x44, 0x65, 0x66, 0x61, 0x75,
+ 0x6c, 0x74, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x25,
+ 0x0a, 0x0e, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
+ 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x3d, 0x0a, 0x11, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48,
+ 0x6f, 0x6f, 0x6b, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x63, 0x75,
+ 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x68, 0x6f, 0x6f, 0x6b, 0x73, 0x5f, 0x74, 0x61, 0x72, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x6f, 0x6f, 0x6b,
+ 0x73, 0x54, 0x61, 0x72, 0x1a, 0x4f, 0x0a, 0x12, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
+ 0x72, 0x79, 0x43, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x0d, 0x6f, 0x62,
+ 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0e, 0x32, 0x14, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63,
+ 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x0c, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x46,
+ 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x1a, 0x14, 0x0a, 0x12, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x25, 0x0a, 0x0f, 0x41,
+ 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x12,
+ 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61,
+ 0x74, 0x68, 0x1a, 0x80, 0x01, 0x0a, 0x0c, 0x48, 0x6f, 0x75, 0x73, 0x65, 0x6b, 0x65, 0x65, 0x70,
+ 0x69, 0x6e, 0x67, 0x12, 0x43, 0x0a, 0x09, 0x70, 0x61, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x66, 0x73,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e,
+ 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x48, 0x6f, 0x75, 0x73, 0x65, 0x6b, 0x65,
+ 0x65, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x66, 0x73, 0x52, 0x08,
+ 0x70, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x66, 0x73, 0x1a, 0x2b, 0x0a, 0x08, 0x50, 0x61, 0x63, 0x6b,
+ 0x52, 0x65, 0x66, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x75, 0x6e, 0x65, 0x64, 0x5f, 0x72,
+ 0x65, 0x66, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x75, 0x6e, 0x65,
+ 0x64, 0x52, 0x65, 0x66, 0x73, 0x22, 0x1b, 0x0a, 0x03, 0x4c, 0x53, 0x4e, 0x12, 0x14, 0x0a, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x69, 0x74, 0x61,
+ 0x6c, 0x79, 0x2f, 0x76, 0x31, 0x36, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f,
+ 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -633,7 +756,7 @@ func file_log_proto_rawDescGZIP() []byte {
return file_log_proto_rawDescData
}
-var file_log_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
+var file_log_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
var file_log_proto_goTypes = []interface{}{
(*LogEntry)(nil), // 0: gitaly.LogEntry
(*LSN)(nil), // 1: gitaly.LSN
@@ -643,23 +766,27 @@ var file_log_proto_goTypes = []interface{}{
(*LogEntry_RepositoryCreation)(nil), // 5: gitaly.LogEntry.RepositoryCreation
(*LogEntry_RepositoryDeletion)(nil), // 6: gitaly.LogEntry.RepositoryDeletion
(*LogEntry_AlternateUpdate)(nil), // 7: gitaly.LogEntry.AlternateUpdate
- (*LogEntry_ReferenceTransaction_Change)(nil), // 8: gitaly.LogEntry.ReferenceTransaction.Change
- (ObjectFormat)(0), // 9: gitaly.ObjectFormat
+ (*LogEntry_Housekeeping)(nil), // 8: gitaly.LogEntry.Housekeeping
+ (*LogEntry_ReferenceTransaction_Change)(nil), // 9: gitaly.LogEntry.ReferenceTransaction.Change
+ (*LogEntry_Housekeeping_PackRefs)(nil), // 10: gitaly.LogEntry.Housekeeping.PackRefs
+ (ObjectFormat)(0), // 11: gitaly.ObjectFormat
}
var file_log_proto_depIdxs = []int32{
- 2, // 0: gitaly.LogEntry.reference_transactions:type_name -> gitaly.LogEntry.ReferenceTransaction
- 3, // 1: gitaly.LogEntry.default_branch_update:type_name -> gitaly.LogEntry.DefaultBranchUpdate
- 4, // 2: gitaly.LogEntry.custom_hooks_update:type_name -> gitaly.LogEntry.CustomHooksUpdate
- 6, // 3: gitaly.LogEntry.repository_deletion:type_name -> gitaly.LogEntry.RepositoryDeletion
- 5, // 4: gitaly.LogEntry.repository_creation:type_name -> gitaly.LogEntry.RepositoryCreation
- 7, // 5: gitaly.LogEntry.alternate_update:type_name -> gitaly.LogEntry.AlternateUpdate
- 8, // 6: gitaly.LogEntry.ReferenceTransaction.changes:type_name -> gitaly.LogEntry.ReferenceTransaction.Change
- 9, // 7: gitaly.LogEntry.RepositoryCreation.object_format:type_name -> gitaly.ObjectFormat
- 8, // [8:8] is the sub-list for method output_type
- 8, // [8:8] is the sub-list for method input_type
- 8, // [8:8] is the sub-list for extension type_name
- 8, // [8:8] is the sub-list for extension extendee
- 0, // [0:8] is the sub-list for field type_name
+ 2, // 0: gitaly.LogEntry.reference_transactions:type_name -> gitaly.LogEntry.ReferenceTransaction
+ 3, // 1: gitaly.LogEntry.default_branch_update:type_name -> gitaly.LogEntry.DefaultBranchUpdate
+ 4, // 2: gitaly.LogEntry.custom_hooks_update:type_name -> gitaly.LogEntry.CustomHooksUpdate
+ 6, // 3: gitaly.LogEntry.repository_deletion:type_name -> gitaly.LogEntry.RepositoryDeletion
+ 5, // 4: gitaly.LogEntry.repository_creation:type_name -> gitaly.LogEntry.RepositoryCreation
+ 7, // 5: gitaly.LogEntry.alternate_update:type_name -> gitaly.LogEntry.AlternateUpdate
+ 8, // 6: gitaly.LogEntry.housekeeping:type_name -> gitaly.LogEntry.Housekeeping
+ 9, // 7: gitaly.LogEntry.ReferenceTransaction.changes:type_name -> gitaly.LogEntry.ReferenceTransaction.Change
+ 11, // 8: gitaly.LogEntry.RepositoryCreation.object_format:type_name -> gitaly.ObjectFormat
+ 10, // 9: gitaly.LogEntry.Housekeeping.pack_refs:type_name -> gitaly.LogEntry.Housekeeping.PackRefs
+ 10, // [10:10] is the sub-list for method output_type
+ 10, // [10:10] is the sub-list for method input_type
+ 10, // [10:10] is the sub-list for extension type_name
+ 10, // [10:10] is the sub-list for extension extendee
+ 0, // [0:10] is the sub-list for field type_name
}
func init() { file_log_proto_init() }
@@ -766,6 +893,18 @@ func file_log_proto_init() {
}
}
file_log_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*LogEntry_Housekeeping); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_log_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*LogEntry_ReferenceTransaction_Change); i {
case 0:
return &v.state
@@ -777,6 +916,18 @@ func file_log_proto_init() {
return nil
}
}
+ file_log_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*LogEntry_Housekeeping_PackRefs); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
}
type x struct{}
out := protoimpl.TypeBuilder{
@@ -784,7 +935,7 @@ func file_log_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_log_proto_rawDesc,
NumEnums: 0,
- NumMessages: 9,
+ NumMessages: 11,
NumExtensions: 0,
NumServices: 0,
},
diff --git a/proto/go/gitalypb/operations.pb.go b/proto/go/gitalypb/operations.pb.go
index ce8526742..f0a62bf94 100644
--- a/proto/go/gitalypb/operations.pb.go
+++ b/proto/go/gitalypb/operations.pb.go
@@ -138,7 +138,7 @@ func (x UserCommitFilesActionHeader_ActionType) Number() protoreflect.EnumNumber
// Deprecated: Use UserCommitFilesActionHeader_ActionType.Descriptor instead.
func (UserCommitFilesActionHeader_ActionType) EnumDescriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{28, 0}
+ return file_operations_proto_rawDescGZIP(), []int{29, 0}
}
// UserCreateBranchRequest is a request for the UserCreateBranch RPC.
@@ -2538,6 +2538,120 @@ func (x *UserRevertResponse) GetCreateTreeErrorCode() UserRevertResponse_CreateT
return UserRevertResponse_NONE
}
+// UserRevertError is an error returned by the UserRevert RPC.
+type UserRevertError struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Types that are assignable to Error:
+ //
+ // *UserRevertError_MergeConflict
+ // *UserRevertError_ChangesAlreadyApplied
+ // *UserRevertError_CustomHook
+ // *UserRevertError_NotAncestor
+ Error isUserRevertError_Error `protobuf_oneof:"error"`
+}
+
+func (x *UserRevertError) Reset() {
+ *x = UserRevertError{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_operations_proto_msgTypes[28]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *UserRevertError) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UserRevertError) ProtoMessage() {}
+
+func (x *UserRevertError) ProtoReflect() protoreflect.Message {
+ mi := &file_operations_proto_msgTypes[28]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use UserRevertError.ProtoReflect.Descriptor instead.
+func (*UserRevertError) Descriptor() ([]byte, []int) {
+ return file_operations_proto_rawDescGZIP(), []int{28}
+}
+
+func (m *UserRevertError) GetError() isUserRevertError_Error {
+ if m != nil {
+ return m.Error
+ }
+ return nil
+}
+
+func (x *UserRevertError) GetMergeConflict() *MergeConflictError {
+ if x, ok := x.GetError().(*UserRevertError_MergeConflict); ok {
+ return x.MergeConflict
+ }
+ return nil
+}
+
+func (x *UserRevertError) GetChangesAlreadyApplied() *ChangesAlreadyAppliedError {
+ if x, ok := x.GetError().(*UserRevertError_ChangesAlreadyApplied); ok {
+ return x.ChangesAlreadyApplied
+ }
+ return nil
+}
+
+func (x *UserRevertError) GetCustomHook() *CustomHookError {
+ if x, ok := x.GetError().(*UserRevertError_CustomHook); ok {
+ return x.CustomHook
+ }
+ return nil
+}
+
+func (x *UserRevertError) GetNotAncestor() *NotAncestorError {
+ if x, ok := x.GetError().(*UserRevertError_NotAncestor); ok {
+ return x.NotAncestor
+ }
+ return nil
+}
+
+type isUserRevertError_Error interface {
+ isUserRevertError_Error()
+}
+
+type UserRevertError_MergeConflict struct {
+ // merge_conflict is returned if there is a conflict when applying the revert.
+ MergeConflict *MergeConflictError `protobuf:"bytes,1,opt,name=merge_conflict,json=mergeConflict,proto3,oneof"`
+}
+
+type UserRevertError_ChangesAlreadyApplied struct {
+ // changes_already_applied is returned if the result after applying the revert is empty.
+ ChangesAlreadyApplied *ChangesAlreadyAppliedError `protobuf:"bytes,2,opt,name=changes_already_applied,json=changesAlreadyApplied,proto3,oneof"`
+}
+
+type UserRevertError_CustomHook struct {
+ // custom_hook contains the error message if the pre-receive hook failed.
+ CustomHook *CustomHookError `protobuf:"bytes,3,opt,name=custom_hook,json=customHook,proto3,oneof"`
+}
+
+type UserRevertError_NotAncestor struct {
+ // not_ancestor is returned if the old tip of the target branch is not an ancestor of the new commit.
+ NotAncestor *NotAncestorError `protobuf:"bytes,4,opt,name=not_ancestor,json=notAncestor,proto3,oneof"`
+}
+
+func (*UserRevertError_MergeConflict) isUserRevertError_Error() {}
+
+func (*UserRevertError_ChangesAlreadyApplied) isUserRevertError_Error() {}
+
+func (*UserRevertError_CustomHook) isUserRevertError_Error() {}
+
+func (*UserRevertError_NotAncestor) isUserRevertError_Error() {}
+
// UserCommitFilesActionHeader contains the details of the action to be performed.
type UserCommitFilesActionHeader struct {
state protoimpl.MessageState
@@ -2574,7 +2688,7 @@ type UserCommitFilesActionHeader struct {
func (x *UserCommitFilesActionHeader) Reset() {
*x = UserCommitFilesActionHeader{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[28]
+ mi := &file_operations_proto_msgTypes[29]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2587,7 +2701,7 @@ func (x *UserCommitFilesActionHeader) String() string {
func (*UserCommitFilesActionHeader) ProtoMessage() {}
func (x *UserCommitFilesActionHeader) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[28]
+ mi := &file_operations_proto_msgTypes[29]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2600,7 +2714,7 @@ func (x *UserCommitFilesActionHeader) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserCommitFilesActionHeader.ProtoReflect.Descriptor instead.
func (*UserCommitFilesActionHeader) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{28}
+ return file_operations_proto_rawDescGZIP(), []int{29}
}
func (x *UserCommitFilesActionHeader) GetAction() UserCommitFilesActionHeader_ActionType {
@@ -2661,7 +2775,7 @@ type UserCommitFilesAction struct {
func (x *UserCommitFilesAction) Reset() {
*x = UserCommitFilesAction{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[29]
+ mi := &file_operations_proto_msgTypes[30]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2674,7 +2788,7 @@ func (x *UserCommitFilesAction) String() string {
func (*UserCommitFilesAction) ProtoMessage() {}
func (x *UserCommitFilesAction) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[29]
+ mi := &file_operations_proto_msgTypes[30]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2687,7 +2801,7 @@ func (x *UserCommitFilesAction) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserCommitFilesAction.ProtoReflect.Descriptor instead.
func (*UserCommitFilesAction) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{29}
+ return file_operations_proto_rawDescGZIP(), []int{30}
}
func (m *UserCommitFilesAction) GetUserCommitFilesActionPayload() isUserCommitFilesAction_UserCommitFilesActionPayload {
@@ -2784,7 +2898,7 @@ type UserCommitFilesRequestHeader struct {
func (x *UserCommitFilesRequestHeader) Reset() {
*x = UserCommitFilesRequestHeader{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[30]
+ mi := &file_operations_proto_msgTypes[31]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2797,7 +2911,7 @@ func (x *UserCommitFilesRequestHeader) String() string {
func (*UserCommitFilesRequestHeader) ProtoMessage() {}
func (x *UserCommitFilesRequestHeader) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[30]
+ mi := &file_operations_proto_msgTypes[31]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2810,7 +2924,7 @@ func (x *UserCommitFilesRequestHeader) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserCommitFilesRequestHeader.ProtoReflect.Descriptor instead.
func (*UserCommitFilesRequestHeader) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{30}
+ return file_operations_proto_rawDescGZIP(), []int{31}
}
func (x *UserCommitFilesRequestHeader) GetRepository() *Repository {
@@ -2913,7 +3027,7 @@ type UserCommitFilesRequest struct {
func (x *UserCommitFilesRequest) Reset() {
*x = UserCommitFilesRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[31]
+ mi := &file_operations_proto_msgTypes[32]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -2926,7 +3040,7 @@ func (x *UserCommitFilesRequest) String() string {
func (*UserCommitFilesRequest) ProtoMessage() {}
func (x *UserCommitFilesRequest) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[31]
+ mi := &file_operations_proto_msgTypes[32]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -2939,7 +3053,7 @@ func (x *UserCommitFilesRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserCommitFilesRequest.ProtoReflect.Descriptor instead.
func (*UserCommitFilesRequest) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{31}
+ return file_operations_proto_rawDescGZIP(), []int{32}
}
func (m *UserCommitFilesRequest) GetUserCommitFilesRequestPayload() isUserCommitFilesRequest_UserCommitFilesRequestPayload {
@@ -3000,7 +3114,7 @@ type UserCommitFilesResponse struct {
func (x *UserCommitFilesResponse) Reset() {
*x = UserCommitFilesResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[32]
+ mi := &file_operations_proto_msgTypes[33]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3013,7 +3127,7 @@ func (x *UserCommitFilesResponse) String() string {
func (*UserCommitFilesResponse) ProtoMessage() {}
func (x *UserCommitFilesResponse) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[32]
+ mi := &file_operations_proto_msgTypes[33]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3026,7 +3140,7 @@ func (x *UserCommitFilesResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserCommitFilesResponse.ProtoReflect.Descriptor instead.
func (*UserCommitFilesResponse) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{32}
+ return file_operations_proto_rawDescGZIP(), []int{33}
}
func (x *UserCommitFilesResponse) GetBranchUpdate() *OperationBranchUpdate {
@@ -3068,7 +3182,7 @@ type UserCommitFilesError struct {
func (x *UserCommitFilesError) Reset() {
*x = UserCommitFilesError{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[33]
+ mi := &file_operations_proto_msgTypes[34]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3081,7 +3195,7 @@ func (x *UserCommitFilesError) String() string {
func (*UserCommitFilesError) ProtoMessage() {}
func (x *UserCommitFilesError) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[33]
+ mi := &file_operations_proto_msgTypes[34]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3094,7 +3208,7 @@ func (x *UserCommitFilesError) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserCommitFilesError.ProtoReflect.Descriptor instead.
func (*UserCommitFilesError) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{33}
+ return file_operations_proto_rawDescGZIP(), []int{34}
}
func (m *UserCommitFilesError) GetError() isUserCommitFilesError_Error {
@@ -3168,7 +3282,7 @@ type UserRebaseConfirmableRequest struct {
func (x *UserRebaseConfirmableRequest) Reset() {
*x = UserRebaseConfirmableRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[34]
+ mi := &file_operations_proto_msgTypes[35]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3181,7 +3295,7 @@ func (x *UserRebaseConfirmableRequest) String() string {
func (*UserRebaseConfirmableRequest) ProtoMessage() {}
func (x *UserRebaseConfirmableRequest) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[34]
+ mi := &file_operations_proto_msgTypes[35]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3194,7 +3308,7 @@ func (x *UserRebaseConfirmableRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserRebaseConfirmableRequest.ProtoReflect.Descriptor instead.
func (*UserRebaseConfirmableRequest) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{34}
+ return file_operations_proto_rawDescGZIP(), []int{35}
}
func (m *UserRebaseConfirmableRequest) GetUserRebaseConfirmableRequestPayload() isUserRebaseConfirmableRequest_UserRebaseConfirmableRequestPayload {
@@ -3256,7 +3370,7 @@ type UserRebaseConfirmableResponse struct {
func (x *UserRebaseConfirmableResponse) Reset() {
*x = UserRebaseConfirmableResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[35]
+ mi := &file_operations_proto_msgTypes[36]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3269,7 +3383,7 @@ func (x *UserRebaseConfirmableResponse) String() string {
func (*UserRebaseConfirmableResponse) ProtoMessage() {}
func (x *UserRebaseConfirmableResponse) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[35]
+ mi := &file_operations_proto_msgTypes[36]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3282,7 +3396,7 @@ func (x *UserRebaseConfirmableResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserRebaseConfirmableResponse.ProtoReflect.Descriptor instead.
func (*UserRebaseConfirmableResponse) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{35}
+ return file_operations_proto_rawDescGZIP(), []int{36}
}
func (m *UserRebaseConfirmableResponse) GetUserRebaseConfirmableResponsePayload() isUserRebaseConfirmableResponse_UserRebaseConfirmableResponsePayload {
@@ -3358,7 +3472,7 @@ type UserSquashRequest struct {
func (x *UserSquashRequest) Reset() {
*x = UserSquashRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[36]
+ mi := &file_operations_proto_msgTypes[37]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3371,7 +3485,7 @@ func (x *UserSquashRequest) String() string {
func (*UserSquashRequest) ProtoMessage() {}
func (x *UserSquashRequest) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[36]
+ mi := &file_operations_proto_msgTypes[37]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3384,7 +3498,7 @@ func (x *UserSquashRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserSquashRequest.ProtoReflect.Descriptor instead.
func (*UserSquashRequest) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{36}
+ return file_operations_proto_rawDescGZIP(), []int{37}
}
func (x *UserSquashRequest) GetRepository() *Repository {
@@ -3449,7 +3563,7 @@ type UserSquashResponse struct {
func (x *UserSquashResponse) Reset() {
*x = UserSquashResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[37]
+ mi := &file_operations_proto_msgTypes[38]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3462,7 +3576,7 @@ func (x *UserSquashResponse) String() string {
func (*UserSquashResponse) ProtoMessage() {}
func (x *UserSquashResponse) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[37]
+ mi := &file_operations_proto_msgTypes[38]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3475,7 +3589,7 @@ func (x *UserSquashResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserSquashResponse.ProtoReflect.Descriptor instead.
func (*UserSquashResponse) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{37}
+ return file_operations_proto_rawDescGZIP(), []int{38}
}
func (x *UserSquashResponse) GetSquashSha() string {
@@ -3501,7 +3615,7 @@ type UserRebaseConfirmableError struct {
func (x *UserRebaseConfirmableError) Reset() {
*x = UserRebaseConfirmableError{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[38]
+ mi := &file_operations_proto_msgTypes[39]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3514,7 +3628,7 @@ func (x *UserRebaseConfirmableError) String() string {
func (*UserRebaseConfirmableError) ProtoMessage() {}
func (x *UserRebaseConfirmableError) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[38]
+ mi := &file_operations_proto_msgTypes[39]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3527,7 +3641,7 @@ func (x *UserRebaseConfirmableError) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserRebaseConfirmableError.ProtoReflect.Descriptor instead.
func (*UserRebaseConfirmableError) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{38}
+ return file_operations_proto_rawDescGZIP(), []int{39}
}
func (m *UserRebaseConfirmableError) GetError() isUserRebaseConfirmableError_Error {
@@ -3588,7 +3702,7 @@ type UserSquashError struct {
func (x *UserSquashError) Reset() {
*x = UserSquashError{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[39]
+ mi := &file_operations_proto_msgTypes[40]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3601,7 +3715,7 @@ func (x *UserSquashError) String() string {
func (*UserSquashError) ProtoMessage() {}
func (x *UserSquashError) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[39]
+ mi := &file_operations_proto_msgTypes[40]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3614,7 +3728,7 @@ func (x *UserSquashError) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserSquashError.ProtoReflect.Descriptor instead.
func (*UserSquashError) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{39}
+ return file_operations_proto_rawDescGZIP(), []int{40}
}
func (m *UserSquashError) GetError() isUserSquashError_Error {
@@ -3674,7 +3788,7 @@ type UserApplyPatchRequest struct {
func (x *UserApplyPatchRequest) Reset() {
*x = UserApplyPatchRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[40]
+ mi := &file_operations_proto_msgTypes[41]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3687,7 +3801,7 @@ func (x *UserApplyPatchRequest) String() string {
func (*UserApplyPatchRequest) ProtoMessage() {}
func (x *UserApplyPatchRequest) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[40]
+ mi := &file_operations_proto_msgTypes[41]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3700,7 +3814,7 @@ func (x *UserApplyPatchRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserApplyPatchRequest.ProtoReflect.Descriptor instead.
func (*UserApplyPatchRequest) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{40}
+ return file_operations_proto_rawDescGZIP(), []int{41}
}
func (m *UserApplyPatchRequest) GetUserApplyPatchRequestPayload() isUserApplyPatchRequest_UserApplyPatchRequestPayload {
@@ -3756,7 +3870,7 @@ type UserApplyPatchResponse struct {
func (x *UserApplyPatchResponse) Reset() {
*x = UserApplyPatchResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[41]
+ mi := &file_operations_proto_msgTypes[42]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3769,7 +3883,7 @@ func (x *UserApplyPatchResponse) String() string {
func (*UserApplyPatchResponse) ProtoMessage() {}
func (x *UserApplyPatchResponse) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[41]
+ mi := &file_operations_proto_msgTypes[42]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3782,7 +3896,7 @@ func (x *UserApplyPatchResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserApplyPatchResponse.ProtoReflect.Descriptor instead.
func (*UserApplyPatchResponse) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{41}
+ return file_operations_proto_rawDescGZIP(), []int{42}
}
func (x *UserApplyPatchResponse) GetBranchUpdate() *OperationBranchUpdate {
@@ -3830,7 +3944,7 @@ type UserUpdateSubmoduleRequest struct {
func (x *UserUpdateSubmoduleRequest) Reset() {
*x = UserUpdateSubmoduleRequest{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[42]
+ mi := &file_operations_proto_msgTypes[43]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3843,7 +3957,7 @@ func (x *UserUpdateSubmoduleRequest) String() string {
func (*UserUpdateSubmoduleRequest) ProtoMessage() {}
func (x *UserUpdateSubmoduleRequest) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[42]
+ mi := &file_operations_proto_msgTypes[43]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3856,7 +3970,7 @@ func (x *UserUpdateSubmoduleRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserUpdateSubmoduleRequest.ProtoReflect.Descriptor instead.
func (*UserUpdateSubmoduleRequest) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{42}
+ return file_operations_proto_rawDescGZIP(), []int{43}
}
func (x *UserUpdateSubmoduleRequest) GetRepository() *Repository {
@@ -3933,7 +4047,7 @@ type UserUpdateSubmoduleResponse struct {
func (x *UserUpdateSubmoduleResponse) Reset() {
*x = UserUpdateSubmoduleResponse{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[43]
+ mi := &file_operations_proto_msgTypes[44]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -3946,7 +4060,7 @@ func (x *UserUpdateSubmoduleResponse) String() string {
func (*UserUpdateSubmoduleResponse) ProtoMessage() {}
func (x *UserUpdateSubmoduleResponse) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[43]
+ mi := &file_operations_proto_msgTypes[44]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -3959,7 +4073,7 @@ func (x *UserUpdateSubmoduleResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserUpdateSubmoduleResponse.ProtoReflect.Descriptor instead.
func (*UserUpdateSubmoduleResponse) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{43}
+ return file_operations_proto_rawDescGZIP(), []int{44}
}
func (x *UserUpdateSubmoduleResponse) GetBranchUpdate() *OperationBranchUpdate {
@@ -4023,7 +4137,7 @@ type UserRebaseConfirmableRequest_Header struct {
func (x *UserRebaseConfirmableRequest_Header) Reset() {
*x = UserRebaseConfirmableRequest_Header{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[44]
+ mi := &file_operations_proto_msgTypes[45]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -4036,7 +4150,7 @@ func (x *UserRebaseConfirmableRequest_Header) String() string {
func (*UserRebaseConfirmableRequest_Header) ProtoMessage() {}
func (x *UserRebaseConfirmableRequest_Header) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[44]
+ mi := &file_operations_proto_msgTypes[45]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -4049,7 +4163,7 @@ func (x *UserRebaseConfirmableRequest_Header) ProtoReflect() protoreflect.Messag
// Deprecated: Use UserRebaseConfirmableRequest_Header.ProtoReflect.Descriptor instead.
func (*UserRebaseConfirmableRequest_Header) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{34, 0}
+ return file_operations_proto_rawDescGZIP(), []int{35, 0}
}
func (x *UserRebaseConfirmableRequest_Header) GetRepository() *Repository {
@@ -4146,7 +4260,7 @@ type UserApplyPatchRequest_Header struct {
func (x *UserApplyPatchRequest_Header) Reset() {
*x = UserApplyPatchRequest_Header{}
if protoimpl.UnsafeEnabled {
- mi := &file_operations_proto_msgTypes[45]
+ mi := &file_operations_proto_msgTypes[46]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -4159,7 +4273,7 @@ func (x *UserApplyPatchRequest_Header) String() string {
func (*UserApplyPatchRequest_Header) ProtoMessage() {}
func (x *UserApplyPatchRequest_Header) ProtoReflect() protoreflect.Message {
- mi := &file_operations_proto_msgTypes[45]
+ mi := &file_operations_proto_msgTypes[46]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -4172,7 +4286,7 @@ func (x *UserApplyPatchRequest_Header) ProtoReflect() protoreflect.Message {
// Deprecated: Use UserApplyPatchRequest_Header.ProtoReflect.Descriptor instead.
func (*UserApplyPatchRequest_Header) Descriptor() ([]byte, []int) {
- return file_operations_proto_rawDescGZIP(), []int{40, 0}
+ return file_operations_proto_rawDescGZIP(), []int{41, 0}
}
func (x *UserApplyPatchRequest_Header) GetRepository() *Repository {
@@ -4594,367 +4708,386 @@ var file_operations_proto_rawDesc = []byte{
0x61, 0x74, 0x65, 0x54, 0x72, 0x65, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x08, 0x0a, 0x04,
0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x4d, 0x50, 0x54, 0x59, 0x10,
0x01, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x4f, 0x4e, 0x46, 0x4c, 0x49, 0x43, 0x54, 0x10, 0x02, 0x22,
- 0xf5, 0x02, 0x0a, 0x1b, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69,
- 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12,
- 0x46, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
- 0x2e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d,
- 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65,
- 0x61, 0x64, 0x65, 0x72, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52,
- 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f,
- 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65,
- 0x50, 0x61, 0x74, 0x68, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73,
- 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x72, 0x65,
- 0x76, 0x69, 0x6f, 0x75, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x61, 0x73,
- 0x65, 0x36, 0x34, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28,
- 0x08, 0x52, 0x0d, 0x62, 0x61, 0x73, 0x65, 0x36, 0x34, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
- 0x12, 0x29, 0x0a, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65,
- 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x65, 0x78, 0x65, 0x63,
- 0x75, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69,
- 0x6e, 0x66, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01,
- 0x28, 0x08, 0x52, 0x0c, 0x69, 0x6e, 0x66, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
- 0x22, 0x55, 0x0a, 0x0a, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a,
- 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x52,
- 0x45, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x49, 0x52, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50,
- 0x44, 0x41, 0x54, 0x45, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x4f, 0x56, 0x45, 0x10, 0x03,
- 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05,
- 0x43, 0x48, 0x4d, 0x4f, 0x44, 0x10, 0x05, 0x22, 0x96, 0x01, 0x0a, 0x15, 0x55, 0x73, 0x65, 0x72,
- 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f,
- 0x6e, 0x12, 0x3d, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x0b, 0x32, 0x23, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43,
- 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e,
- 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
- 0x12, 0x1a, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x0c, 0x48, 0x00, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x22, 0x0a, 0x20,
- 0x75, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65,
- 0x73, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
- 0x22, 0xa2, 0x04, 0x0a, 0x1c, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46,
- 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65,
- 0x72, 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18,
- 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52,
- 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52,
- 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75,
- 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61,
- 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x0a,
- 0x0b, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01,
- 0x28, 0x0c, 0x52, 0x0a, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25,
- 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
- 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x4d, 0x65,
- 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f,
- 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
- 0x0c, 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x4e,
- 0x61, 0x6d, 0x65, 0x12, 0x2e, 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x61, 0x75,
- 0x74, 0x68, 0x6f, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c,
- 0x52, 0x11, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x45, 0x6d,
- 0x61, 0x69, 0x6c, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x62, 0x72, 0x61,
- 0x6e, 0x63, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f,
- 0x73, 0x74, 0x61, 0x72, 0x74, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x12,
- 0x3d, 0x0a, 0x10, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
- 0x6f, 0x72, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61,
- 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x0f, 0x73,
- 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x14,
- 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66,
- 0x6f, 0x72, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x68,
- 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x53, 0x68,
- 0x61, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0b,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
- 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x28, 0x0a, 0x10, 0x65,
- 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x6c, 0x64, 0x5f, 0x6f, 0x69, 0x64, 0x18,
- 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4f,
- 0x6c, 0x64, 0x4f, 0x69, 0x64, 0x22, 0xb6, 0x01, 0x0a, 0x16, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f,
- 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x12, 0x3e, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
- 0x32, 0x24, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f,
- 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
- 0x12, 0x37, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
- 0x32, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f,
- 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48,
- 0x00, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x23, 0x0a, 0x21, 0x75, 0x73, 0x65,
- 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x72,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xaa,
- 0x01, 0x0a, 0x17, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c,
- 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x62, 0x72,
- 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
- 0x52, 0x0c, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1f,
- 0x0a, 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12,
- 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x65,
- 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x52,
- 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xd3, 0x01, 0x0a, 0x14,
- 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x45,
- 0x72, 0x72, 0x6f, 0x72, 0x12, 0x3d, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63,
- 0x68, 0x65, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74,
- 0x61, 0x6c, 0x79, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x45,
- 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x68,
- 0x65, 0x63, 0x6b, 0x12, 0x37, 0x0a, 0x0c, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x70, 0x64,
- 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61,
- 0x6c, 0x79, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52,
- 0x0b, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0b,
- 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x68, 0x6f, 0x6f, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28,
- 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f,
- 0x6d, 0x48, 0x6f, 0x6f, 0x6b, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x75,
- 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x6f, 0x6f, 0x6b, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f,
- 0x72, 0x22, 0xb1, 0x04, 0x0a, 0x1c, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65,
- 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x12, 0x45, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72,
- 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c,
- 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48,
- 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x05, 0x61, 0x70, 0x70,
- 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c,
- 0x79, 0x1a, 0x86, 0x03, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0a,
+ 0xb8, 0x02, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x45, 0x72,
+ 0x72, 0x6f, 0x72, 0x12, 0x43, 0x0a, 0x0e, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
+ 0x66, 0x6c, 0x69, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x69,
+ 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69,
+ 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0d, 0x6d, 0x65, 0x72, 0x67, 0x65,
+ 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x12, 0x5c, 0x0a, 0x17, 0x63, 0x68, 0x61, 0x6e,
+ 0x67, 0x65, 0x73, 0x5f, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x61, 0x70, 0x70, 0x6c,
+ 0x69, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x69, 0x74, 0x61,
+ 0x6c, 0x79, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64,
+ 0x79, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52,
+ 0x15, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x41, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x41,
+ 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x12, 0x3a, 0x0a, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d,
+ 0x5f, 0x68, 0x6f, 0x6f, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x69,
+ 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x6f, 0x6f, 0x6b, 0x45,
+ 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x6f,
+ 0x6f, 0x6b, 0x12, 0x3d, 0x0a, 0x0c, 0x6e, 0x6f, 0x74, 0x5f, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x74,
+ 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c,
+ 0x79, 0x2e, 0x4e, 0x6f, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x45, 0x72, 0x72,
+ 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0b, 0x6e, 0x6f, 0x74, 0x41, 0x6e, 0x63, 0x65, 0x73, 0x74, 0x6f,
+ 0x72, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xf5, 0x02, 0x0a, 0x1b, 0x55,
+ 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x06, 0x61, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2e, 0x2e, 0x67, 0x69, 0x74,
+ 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69,
+ 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2e,
+ 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69,
+ 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12,
+ 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x70, 0x61, 0x74, 0x68,
+ 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73,
+ 0x50, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x62, 0x61, 0x73, 0x65, 0x36, 0x34, 0x5f, 0x63,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x62, 0x61,
+ 0x73, 0x65, 0x36, 0x34, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x65,
+ 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x18,
+ 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x46, 0x69,
+ 0x6c, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x66, 0x65, 0x72, 0x5f,
+ 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x69,
+ 0x6e, 0x66, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x55, 0x0a, 0x0a, 0x41,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45,
+ 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x5f,
+ 0x44, 0x49, 0x52, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10,
+ 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x4f, 0x56, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44,
+ 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x48, 0x4d, 0x4f, 0x44,
+ 0x10, 0x05, 0x22, 0x96, 0x01, 0x0a, 0x15, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69,
+ 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x06,
+ 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x67,
+ 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x46, 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x64, 0x65,
+ 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x07, 0x63,
+ 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07,
+ 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x42, 0x22, 0x0a, 0x20, 0x75, 0x73, 0x65, 0x72, 0x5f,
+ 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x61, 0x63, 0x74,
+ 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xa2, 0x04, 0x0a, 0x1c,
+ 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0a,
0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69,
0x74, 0x6f, 0x72, 0x79, 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f,
0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73,
- 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x09, 0x72, 0x65, 0x62, 0x61,
- 0x73, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52,
- 0x08, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61,
- 0x6e, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63,
- 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x73, 0x68, 0x61, 0x18,
- 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x53, 0x68, 0x61,
- 0x12, 0x3f, 0x0a, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73,
- 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69,
- 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52,
- 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
- 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x62, 0x72, 0x61, 0x6e,
- 0x63, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65,
- 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x69, 0x74, 0x5f, 0x70, 0x75,
- 0x73, 0x68, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09,
- 0x52, 0x0e, 0x67, 0x69, 0x74, 0x50, 0x75, 0x73, 0x68, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
- 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
- 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
- 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x29, 0x0a, 0x27, 0x75, 0x73,
- 0x65, 0x72, 0x5f, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72,
- 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61,
- 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xbf, 0x01, 0x0a, 0x1d, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65,
- 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0a, 0x72, 0x65, 0x62, 0x61, 0x73,
- 0x65, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x72,
- 0x65, 0x62, 0x61, 0x73, 0x65, 0x53, 0x68, 0x61, 0x12, 0x27, 0x0a, 0x0e, 0x72, 0x65, 0x62, 0x61,
- 0x73, 0x65, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
- 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65,
- 0x64, 0x42, 0x2a, 0x0a, 0x28, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65,
- 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x73,
- 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x4a, 0x04, 0x08,
- 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x52, 0x11, 0x70, 0x72, 0x65, 0x5f, 0x72,
- 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x09, 0x67, 0x69,
- 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xc3, 0x02, 0x0a, 0x11, 0x55, 0x73, 0x65, 0x72,
- 0x53, 0x71, 0x75, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a,
- 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73,
- 0x69, 0x74, 0x6f, 0x72, 0x79, 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70,
- 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18,
- 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55,
- 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x61,
- 0x72, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74,
- 0x61, 0x72, 0x74, 0x53, 0x68, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x5f, 0x73, 0x68,
- 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x64, 0x53, 0x68, 0x61, 0x12,
- 0x24, 0x0a, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32,
- 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x06, 0x61,
- 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f,
- 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x63,
- 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x09,
- 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x72, 0x61, 0x6e,
+ 0x63, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x62,
+ 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
+ 0x0c, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+ 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f,
+ 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2e,
+ 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x5f,
+ 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x2a,
+ 0x0a, 0x11, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6e,
+ 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x73, 0x74, 0x61, 0x72, 0x74,
+ 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x10, 0x73, 0x74,
+ 0x61, 0x72, 0x74, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x08,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65,
+ 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x0f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x52,
+ 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72,
+ 0x63, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12,
+ 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x0a, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x53, 0x68, 0x61, 0x12, 0x38, 0x0a, 0x09,
+ 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d,
- 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04,
- 0x10, 0x05, 0x52, 0x09, 0x73, 0x71, 0x75, 0x61, 0x73, 0x68, 0x5f, 0x69, 0x64, 0x22, 0x5d, 0x0a,
- 0x12, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f,
- 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x71, 0x75, 0x61, 0x73, 0x68, 0x5f, 0x73, 0x68,
- 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x71, 0x75, 0x61, 0x73, 0x68, 0x53,
- 0x68, 0x61, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x52, 0x11,
- 0x70, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f,
- 0x72, 0x52, 0x09, 0x67, 0x69, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xab, 0x01, 0x0a,
- 0x1a, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
- 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x45, 0x0a, 0x0f, 0x72,
- 0x65, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4d, 0x65,
- 0x72, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72,
- 0x48, 0x00, 0x52, 0x0e, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69,
- 0x63, 0x74, 0x12, 0x3d, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x68, 0x65,
- 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c,
- 0x79, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x45, 0x72, 0x72,
- 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63,
- 0x6b, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xac, 0x01, 0x0a, 0x0f, 0x55,
- 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61, 0x73, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x49,
- 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69,
- 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c,
- 0x79, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f,
- 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76,
- 0x65, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x62,
- 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4d, 0x65, 0x72, 0x67,
- 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00,
- 0x52, 0x0e, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74,
- 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x87, 0x03, 0x0a, 0x15, 0x55, 0x73,
- 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x12, 0x3e, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65,
- 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61,
- 0x64, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x02,
- 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x1a,
- 0xed, 0x01, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65,
- 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12,
- 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
- 0x72, 0x79, 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69,
- 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72,
- 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
- 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x74,
- 0x61, 0x72, 0x67, 0x65, 0x74, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x38, 0x0a, 0x09, 0x74,
- 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
+ 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x28, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74,
+ 0x65, 0x64, 0x5f, 0x6f, 0x6c, 0x64, 0x5f, 0x6f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4f, 0x6c, 0x64, 0x4f, 0x69, 0x64,
+ 0x22, 0xb6, 0x01, 0x0a, 0x16, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46,
+ 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3e, 0x0a, 0x06, 0x68,
+ 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x69,
+ 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46,
+ 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65,
+ 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x37, 0x0a, 0x06, 0x61,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x69,
+ 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46,
+ 0x69, 0x6c, 0x65, 0x73, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x06, 0x61, 0x63,
+ 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x23, 0x0a, 0x21, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xaa, 0x01, 0x0a, 0x17, 0x55, 0x73,
+ 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f,
+ 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67,
+ 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42,
+ 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x62, 0x72, 0x61,
+ 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x64,
+ 0x65, 0x78, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
+ 0x69, 0x6e, 0x64, 0x65, 0x78, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72,
+ 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18,
+ 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76,
+ 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xd3, 0x01, 0x0a, 0x14, 0x55, 0x73, 0x65, 0x72, 0x43,
+ 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12,
+ 0x3d, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x41,
+ 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48,
+ 0x00, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x37,
+ 0x0a, 0x0c, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x49, 0x6e,
+ 0x64, 0x65, 0x78, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0b, 0x69, 0x6e, 0x64, 0x65,
+ 0x78, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x3a, 0x0a, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f,
+ 0x6d, 0x5f, 0x68, 0x6f, 0x6f, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67,
+ 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48, 0x6f, 0x6f, 0x6b,
+ 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x48,
+ 0x6f, 0x6f, 0x6b, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xb1, 0x04, 0x0a,
+ 0x1c, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+ 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x45, 0x0a,
+ 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e,
+ 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73,
+ 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65,
+ 0x61, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x1a, 0x86, 0x03, 0x0a,
+ 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69,
+ 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x42,
+ 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
+ 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75,
+ 0x73, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x09, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x69, 0x64,
+ 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x08, 0x72, 0x65, 0x62, 0x61,
+ 0x73, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x04,
+ 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1d, 0x0a, 0x0a,
+ 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x09, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x53, 0x68, 0x61, 0x12, 0x3f, 0x0a, 0x11, 0x72,
+ 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79,
+ 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e,
+ 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x10, 0x72, 0x65, 0x6d, 0x6f,
+ 0x74, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x23, 0x0a, 0x0d,
+ 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x07, 0x20,
+ 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63,
+ 0x68, 0x12, 0x28, 0x0a, 0x10, 0x67, 0x69, 0x74, 0x5f, 0x70, 0x75, 0x73, 0x68, 0x5f, 0x6f, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x67, 0x69, 0x74,
+ 0x50, 0x75, 0x73, 0x68, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x38, 0x0a, 0x09, 0x74,
+ 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65,
- 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x28, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65,
- 0x64, 0x5f, 0x6f, 0x6c, 0x64, 0x5f, 0x6f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4f, 0x6c, 0x64, 0x4f, 0x69, 0x64, 0x42,
- 0x22, 0x0a, 0x20, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x5f, 0x70, 0x61,
- 0x74, 0x63, 0x68, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c,
- 0x6f, 0x61, 0x64, 0x22, 0x5c, 0x0a, 0x16, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79,
- 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a,
- 0x0d, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70,
- 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64,
- 0x61, 0x74, 0x65, 0x52, 0x0c, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74,
- 0x65, 0x22, 0xd8, 0x02, 0x0a, 0x1a, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
- 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65,
- 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, 0x0a,
- 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, 0x73,
- 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c,
- 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a,
- 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x09, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x53, 0x68, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x62,
- 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x72, 0x61,
- 0x6e, 0x63, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
- 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
- 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73,
- 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x69,
- 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65,
- 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
- 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
- 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
- 0x6d, 0x70, 0x12, 0x28, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6f,
- 0x6c, 0x64, 0x5f, 0x6f, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x78,
- 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4f, 0x6c, 0x64, 0x4f, 0x69, 0x64, 0x22, 0xc9, 0x01, 0x0a,
- 0x1b, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f,
- 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d,
- 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70, 0x65,
- 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61,
- 0x74, 0x65, 0x52, 0x0c, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
- 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f,
- 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65,
- 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x21, 0x0a, 0x0c,
- 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4a,
- 0x04, 0x08, 0x03, 0x10, 0x04, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x72,
- 0x65, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, 0xc9, 0x0b, 0x0a, 0x10, 0x4f, 0x70, 0x65,
- 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5d, 0x0a,
- 0x10, 0x55, 0x73, 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63,
- 0x68, 0x12, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43,
- 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72,
- 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70,
- 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5d, 0x0a, 0x10,
- 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68,
- 0x12, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70,
- 0x64, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x55,
- 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f,
- 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5d, 0x0a, 0x10, 0x55,
- 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12,
- 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c,
- 0x65, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x1a, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65,
- 0x6c, 0x65, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
- 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x54, 0x0a, 0x0d, 0x55, 0x73,
- 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x12, 0x1c, 0x2e, 0x67, 0x69,
- 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54,
- 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61,
- 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67,
- 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01,
- 0x12, 0x54, 0x0a, 0x0d, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61,
- 0x67, 0x12, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44,
- 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
- 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c,
- 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06,
- 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x57, 0x0a, 0x0e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65,
- 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x12, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c,
- 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79,
- 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12,
- 0x5a, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x54, 0x6f, 0x52,
- 0x65, 0x66, 0x12, 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72,
- 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72,
- 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f,
- 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5e, 0x0a, 0x0f, 0x55,
- 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1e,
- 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67,
- 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f,
- 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67,
+ 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x29, 0x0a, 0x27, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x72, 0x65,
+ 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65,
+ 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
+ 0x22, 0xbf, 0x01, 0x0a, 0x1d, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43,
+ 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+ 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0a, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x68, 0x61,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65,
+ 0x53, 0x68, 0x61, 0x12, 0x27, 0x0a, 0x0e, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x61, 0x70,
+ 0x70, 0x6c, 0x69, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0d, 0x72,
+ 0x65, 0x62, 0x61, 0x73, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x42, 0x2a, 0x0a, 0x28,
+ 0x75, 0x73, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
+ 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04,
+ 0x08, 0x04, 0x10, 0x05, 0x52, 0x11, 0x70, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76,
+ 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x09, 0x67, 0x69, 0x74, 0x5f, 0x65, 0x72, 0x72,
+ 0x6f, 0x72, 0x22, 0xc3, 0x02, 0x0a, 0x11, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61, 0x73,
+ 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f,
+ 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67,
+ 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79,
+ 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
+ 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04,
+ 0x75, 0x73, 0x65, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x73, 0x68,
+ 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x61, 0x72, 0x74, 0x53, 0x68,
+ 0x61, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x06, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x06, 0x65, 0x6e, 0x64, 0x53, 0x68, 0x61, 0x12, 0x24, 0x0a, 0x06, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74,
+ 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61,
+ 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73,
+ 0x74, 0x61, 0x6d, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
+ 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
+ 0x70, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x52, 0x09, 0x73,
+ 0x71, 0x75, 0x61, 0x73, 0x68, 0x5f, 0x69, 0x64, 0x22, 0x5d, 0x0a, 0x12, 0x55, 0x73, 0x65, 0x72,
+ 0x53, 0x71, 0x75, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d,
+ 0x0a, 0x0a, 0x73, 0x71, 0x75, 0x61, 0x73, 0x68, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x09, 0x73, 0x71, 0x75, 0x61, 0x73, 0x68, 0x53, 0x68, 0x61, 0x4a, 0x04, 0x08,
+ 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x52, 0x11, 0x70, 0x72, 0x65, 0x5f, 0x72,
+ 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x09, 0x67, 0x69,
+ 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xab, 0x01, 0x0a, 0x1a, 0x55, 0x73, 0x65, 0x72,
+ 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c,
+ 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65,
+ 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x43, 0x6f,
+ 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0e, 0x72,
+ 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x12, 0x3d, 0x0a,
+ 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x41, 0x63, 0x63,
+ 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52,
+ 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x42, 0x07, 0x0a, 0x05,
+ 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xac, 0x01, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71,
+ 0x75, 0x61, 0x73, 0x68, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x49, 0x0a, 0x10, 0x72, 0x65, 0x73,
+ 0x6f, 0x6c, 0x76, 0x65, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x73,
+ 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f,
+ 0x72, 0x48, 0x00, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x52, 0x65, 0x76, 0x69,
+ 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x0f, 0x72, 0x65, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x63,
+ 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
+ 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x43, 0x6f, 0x6e, 0x66,
+ 0x6c, 0x69, 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x0e, 0x72, 0x65, 0x62,
+ 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x42, 0x07, 0x0a, 0x05, 0x65,
+ 0x72, 0x72, 0x6f, 0x72, 0x22, 0x87, 0x03, 0x0a, 0x15, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70,
+ 0x6c, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3e,
+ 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24,
+ 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c,
+ 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x48, 0x65,
+ 0x61, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1a,
+ 0x0a, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48,
+ 0x00, 0x52, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x1a, 0xed, 0x01, 0x0a, 0x06, 0x48,
+ 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61,
+ 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x42, 0x04, 0x98,
+ 0xc6, 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12,
+ 0x20, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e,
+ 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65,
+ 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x72, 0x61, 0x6e,
+ 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
+ 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
+ 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+ 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65,
+ 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
+ 0x12, 0x28, 0x0a, 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x6c, 0x64,
+ 0x5f, 0x6f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x65,
+ 0x63, 0x74, 0x65, 0x64, 0x4f, 0x6c, 0x64, 0x4f, 0x69, 0x64, 0x42, 0x22, 0x0a, 0x20, 0x75, 0x73,
+ 0x65, 0x72, 0x5f, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x72,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x5c,
+ 0x0a, 0x16, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x62, 0x72, 0x61, 0x6e,
+ 0x63, 0x68, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0c,
+ 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x22, 0xd8, 0x02, 0x0a,
+ 0x1a, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f,
+ 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x0a, 0x72,
+ 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x12, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x42, 0x04, 0x98, 0xc6, 0x2c, 0x01, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73,
+ 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65,
+ 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x69,
+ 0x74, 0x5f, 0x73, 0x68, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x53, 0x68, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68,
+ 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1c,
+ 0x0a, 0x09, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
+ 0x0c, 0x52, 0x09, 0x73, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x0e,
+ 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x06,
+ 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x4d, 0x65, 0x73, 0x73,
+ 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
+ 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+ 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x28, 0x0a,
+ 0x10, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x6c, 0x64, 0x5f, 0x6f, 0x69,
+ 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65,
+ 0x64, 0x4f, 0x6c, 0x64, 0x4f, 0x69, 0x64, 0x22, 0xc9, 0x01, 0x0a, 0x1b, 0x55, 0x73, 0x65, 0x72,
+ 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52,
+ 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x62, 0x72, 0x61, 0x6e, 0x63,
+ 0x68, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d,
+ 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x62,
+ 0x72, 0x61, 0x6e, 0x63, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x70,
+ 0x72, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69,
+ 0x76, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x69,
+ 0x74, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63,
+ 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04,
+ 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x65, 0x72,
+ 0x72, 0x6f, 0x72, 0x32, 0xc9, 0x0b, 0x0a, 0x10, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5d, 0x0a, 0x10, 0x55, 0x73, 0x65, 0x72,
+ 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x67,
+ 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
+ 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e,
+ 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
- 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x51, 0x0a, 0x0c, 0x55,
- 0x73, 0x65, 0x72, 0x46, 0x46, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1b, 0x2e, 0x67, 0x69,
- 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, 0x46, 0x42, 0x72, 0x61, 0x6e, 0x63,
- 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c,
- 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x46, 0x46, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65,
- 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x57,
- 0x0a, 0x0e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x68, 0x65, 0x72, 0x72, 0x79, 0x50, 0x69, 0x63, 0x6b,
- 0x12, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x68,
- 0x65, 0x72, 0x72, 0x79, 0x50, 0x69, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
- 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x68, 0x65,
- 0x72, 0x72, 0x79, 0x50, 0x69, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
- 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5c, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x43,
- 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x69, 0x74,
- 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69,
- 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x69, 0x74,
- 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69,
- 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28,
- 0x02, 0x08, 0x01, 0x28, 0x01, 0x12, 0x70, 0x0a, 0x15, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62,
- 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x24,
- 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61,
- 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73,
- 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61,
- 0x62, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28,
- 0x02, 0x08, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x4b, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x52,
- 0x65, 0x76, 0x65, 0x72, 0x74, 0x12, 0x19, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55,
- 0x73, 0x65, 0x72, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x1a, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65,
- 0x76, 0x65, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97,
- 0x28, 0x02, 0x08, 0x01, 0x12, 0x4b, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61,
- 0x73, 0x68, 0x12, 0x19, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72,
- 0x53, 0x71, 0x75, 0x61, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e,
- 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61, 0x73,
- 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08,
- 0x01, 0x12, 0x59, 0x0a, 0x0e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x61,
- 0x74, 0x63, 0x68, 0x12, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65,
- 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5d, 0x0a, 0x10, 0x55, 0x73, 0x65, 0x72, 0x55,
+ 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x67, 0x69,
+ 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42,
+ 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67,
+ 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
+ 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06,
+ 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5d, 0x0a, 0x10, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65,
+ 0x6c, 0x65, 0x74, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x67, 0x69, 0x74,
+ 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x72,
+ 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x69,
+ 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42,
+ 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa,
+ 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x54, 0x0a, 0x0d, 0x55, 0x73, 0x65, 0x72, 0x43, 0x72, 0x65,
+ 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x12, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e,
+ 0x55, 0x73, 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73,
+ 0x65, 0x72, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x54, 0x0a, 0x0d, 0x55,
+ 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x12, 0x1c, 0x2e, 0x67,
+ 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
+ 0x54, 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x69, 0x74,
+ 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61,
+ 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08,
+ 0x01, 0x12, 0x57, 0x0a, 0x0e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x54, 0x6f,
+ 0x52, 0x65, 0x66, 0x12, 0x1d, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65,
+ 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72,
- 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
- 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x28, 0x01, 0x12, 0x66, 0x0a, 0x13,
- 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64,
- 0x75, 0x6c, 0x65, 0x12, 0x22, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65,
- 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79,
- 0x2e, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f,
- 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97,
- 0x28, 0x02, 0x08, 0x01, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x63,
- 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x69,
- 0x74, 0x61, 0x6c, 0x79, 0x2f, 0x76, 0x31, 0x36, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67,
- 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
- 0x6f, 0x33,
+ 0x4d, 0x65, 0x72, 0x67, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+ 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5a, 0x0a, 0x0f, 0x55, 0x73,
+ 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x12, 0x1e, 0x2e,
+ 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73,
+ 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e,
+ 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73,
+ 0x65, 0x54, 0x6f, 0x52, 0x65, 0x66, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06,
+ 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x5e, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65,
+ 0x72, 0x67, 0x65, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61,
+ 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x42, 0x72, 0x61, 0x6e,
+ 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61,
+ 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65, 0x72, 0x67, 0x65, 0x42, 0x72, 0x61, 0x6e,
+ 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02,
+ 0x08, 0x01, 0x28, 0x01, 0x30, 0x01, 0x12, 0x51, 0x0a, 0x0c, 0x55, 0x73, 0x65, 0x72, 0x46, 0x46,
+ 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1b, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e,
+ 0x55, 0x73, 0x65, 0x72, 0x46, 0x46, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65,
+ 0x72, 0x46, 0x46, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x57, 0x0a, 0x0e, 0x55, 0x73, 0x65,
+ 0x72, 0x43, 0x68, 0x65, 0x72, 0x72, 0x79, 0x50, 0x69, 0x63, 0x6b, 0x12, 0x1d, 0x2e, 0x67, 0x69,
+ 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x68, 0x65, 0x72, 0x72, 0x79, 0x50,
+ 0x69, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x67, 0x69, 0x74,
+ 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x68, 0x65, 0x72, 0x72, 0x79, 0x50, 0x69,
+ 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02,
+ 0x08, 0x01, 0x12, 0x5c, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55,
+ 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55,
+ 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x28, 0x01,
+ 0x12, 0x70, 0x0a, 0x15, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f,
+ 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x24, 0x2e, 0x67, 0x69, 0x74, 0x61,
+ 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e,
+ 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+ 0x25, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x62,
+ 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x28, 0x01,
+ 0x30, 0x01, 0x12, 0x4b, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74,
+ 0x12, 0x19, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65,
+ 0x76, 0x65, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x69,
+ 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x76, 0x65, 0x72, 0x74, 0x52,
+ 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12,
+ 0x4b, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61, 0x73, 0x68, 0x12, 0x19, 0x2e,
+ 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61, 0x73,
+ 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c,
+ 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x71, 0x75, 0x61, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70,
+ 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x59, 0x0a, 0x0e,
+ 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x12, 0x1d,
+ 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c,
+ 0x79, 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e,
+ 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x41, 0x70, 0x70, 0x6c, 0x79,
+ 0x50, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa,
+ 0x97, 0x28, 0x02, 0x08, 0x01, 0x28, 0x01, 0x12, 0x66, 0x0a, 0x13, 0x55, 0x73, 0x65, 0x72, 0x55,
+ 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x22,
+ 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x55, 0x70, 0x64, 0x61,
+ 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72,
+ 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52,
+ 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x42,
+ 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69,
+ 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2f,
+ 0x76, 0x31, 0x36, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x67, 0x69, 0x74,
+ 0x61, 0x6c, 0x79, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -4970,7 +5103,7 @@ func file_operations_proto_rawDescGZIP() []byte {
}
var file_operations_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
-var file_operations_proto_msgTypes = make([]protoimpl.MessageInfo, 46)
+var file_operations_proto_msgTypes = make([]protoimpl.MessageInfo, 47)
var file_operations_proto_goTypes = []interface{}{
(UserRevertResponse_CreateTreeError)(0), // 0: gitaly.UserRevertResponse.CreateTreeError
(UserCommitFilesActionHeader_ActionType)(0), // 1: gitaly.UserCommitFilesActionHeader.ActionType
@@ -5002,167 +5135,172 @@ var file_operations_proto_goTypes = []interface{}{
(*UserCherryPickError)(nil), // 27: gitaly.UserCherryPickError
(*UserRevertRequest)(nil), // 28: gitaly.UserRevertRequest
(*UserRevertResponse)(nil), // 29: gitaly.UserRevertResponse
- (*UserCommitFilesActionHeader)(nil), // 30: gitaly.UserCommitFilesActionHeader
- (*UserCommitFilesAction)(nil), // 31: gitaly.UserCommitFilesAction
- (*UserCommitFilesRequestHeader)(nil), // 32: gitaly.UserCommitFilesRequestHeader
- (*UserCommitFilesRequest)(nil), // 33: gitaly.UserCommitFilesRequest
- (*UserCommitFilesResponse)(nil), // 34: gitaly.UserCommitFilesResponse
- (*UserCommitFilesError)(nil), // 35: gitaly.UserCommitFilesError
- (*UserRebaseConfirmableRequest)(nil), // 36: gitaly.UserRebaseConfirmableRequest
- (*UserRebaseConfirmableResponse)(nil), // 37: gitaly.UserRebaseConfirmableResponse
- (*UserSquashRequest)(nil), // 38: gitaly.UserSquashRequest
- (*UserSquashResponse)(nil), // 39: gitaly.UserSquashResponse
- (*UserRebaseConfirmableError)(nil), // 40: gitaly.UserRebaseConfirmableError
- (*UserSquashError)(nil), // 41: gitaly.UserSquashError
- (*UserApplyPatchRequest)(nil), // 42: gitaly.UserApplyPatchRequest
- (*UserApplyPatchResponse)(nil), // 43: gitaly.UserApplyPatchResponse
- (*UserUpdateSubmoduleRequest)(nil), // 44: gitaly.UserUpdateSubmoduleRequest
- (*UserUpdateSubmoduleResponse)(nil), // 45: gitaly.UserUpdateSubmoduleResponse
- (*UserRebaseConfirmableRequest_Header)(nil), // 46: gitaly.UserRebaseConfirmableRequest.Header
- (*UserApplyPatchRequest_Header)(nil), // 47: gitaly.UserApplyPatchRequest.Header
- (*Repository)(nil), // 48: gitaly.Repository
- (*User)(nil), // 49: gitaly.User
- (*Branch)(nil), // 50: gitaly.Branch
- (*CustomHookError)(nil), // 51: gitaly.CustomHookError
- (*AccessCheckError)(nil), // 52: gitaly.AccessCheckError
- (*ReferenceUpdateError)(nil), // 53: gitaly.ReferenceUpdateError
- (*timestamppb.Timestamp)(nil), // 54: google.protobuf.Timestamp
- (*Tag)(nil), // 55: gitaly.Tag
- (*ReferenceExistsError)(nil), // 56: gitaly.ReferenceExistsError
- (*MergeConflictError)(nil), // 57: gitaly.MergeConflictError
- (*GitCommit)(nil), // 58: gitaly.GitCommit
- (*NotAncestorError)(nil), // 59: gitaly.NotAncestorError
- (*ChangesAlreadyAppliedError)(nil), // 60: gitaly.ChangesAlreadyAppliedError
- (*IndexError)(nil), // 61: gitaly.IndexError
- (*ResolveRevisionError)(nil), // 62: gitaly.ResolveRevisionError
+ (*UserRevertError)(nil), // 30: gitaly.UserRevertError
+ (*UserCommitFilesActionHeader)(nil), // 31: gitaly.UserCommitFilesActionHeader
+ (*UserCommitFilesAction)(nil), // 32: gitaly.UserCommitFilesAction
+ (*UserCommitFilesRequestHeader)(nil), // 33: gitaly.UserCommitFilesRequestHeader
+ (*UserCommitFilesRequest)(nil), // 34: gitaly.UserCommitFilesRequest
+ (*UserCommitFilesResponse)(nil), // 35: gitaly.UserCommitFilesResponse
+ (*UserCommitFilesError)(nil), // 36: gitaly.UserCommitFilesError
+ (*UserRebaseConfirmableRequest)(nil), // 37: gitaly.UserRebaseConfirmableRequest
+ (*UserRebaseConfirmableResponse)(nil), // 38: gitaly.UserRebaseConfirmableResponse
+ (*UserSquashRequest)(nil), // 39: gitaly.UserSquashRequest
+ (*UserSquashResponse)(nil), // 40: gitaly.UserSquashResponse
+ (*UserRebaseConfirmableError)(nil), // 41: gitaly.UserRebaseConfirmableError
+ (*UserSquashError)(nil), // 42: gitaly.UserSquashError
+ (*UserApplyPatchRequest)(nil), // 43: gitaly.UserApplyPatchRequest
+ (*UserApplyPatchResponse)(nil), // 44: gitaly.UserApplyPatchResponse
+ (*UserUpdateSubmoduleRequest)(nil), // 45: gitaly.UserUpdateSubmoduleRequest
+ (*UserUpdateSubmoduleResponse)(nil), // 46: gitaly.UserUpdateSubmoduleResponse
+ (*UserRebaseConfirmableRequest_Header)(nil), // 47: gitaly.UserRebaseConfirmableRequest.Header
+ (*UserApplyPatchRequest_Header)(nil), // 48: gitaly.UserApplyPatchRequest.Header
+ (*Repository)(nil), // 49: gitaly.Repository
+ (*User)(nil), // 50: gitaly.User
+ (*Branch)(nil), // 51: gitaly.Branch
+ (*CustomHookError)(nil), // 52: gitaly.CustomHookError
+ (*AccessCheckError)(nil), // 53: gitaly.AccessCheckError
+ (*ReferenceUpdateError)(nil), // 54: gitaly.ReferenceUpdateError
+ (*timestamppb.Timestamp)(nil), // 55: google.protobuf.Timestamp
+ (*Tag)(nil), // 56: gitaly.Tag
+ (*ReferenceExistsError)(nil), // 57: gitaly.ReferenceExistsError
+ (*MergeConflictError)(nil), // 58: gitaly.MergeConflictError
+ (*GitCommit)(nil), // 59: gitaly.GitCommit
+ (*NotAncestorError)(nil), // 60: gitaly.NotAncestorError
+ (*ChangesAlreadyAppliedError)(nil), // 61: gitaly.ChangesAlreadyAppliedError
+ (*IndexError)(nil), // 62: gitaly.IndexError
+ (*ResolveRevisionError)(nil), // 63: gitaly.ResolveRevisionError
}
var file_operations_proto_depIdxs = []int32{
- 48, // 0: gitaly.UserCreateBranchRequest.repository:type_name -> gitaly.Repository
- 49, // 1: gitaly.UserCreateBranchRequest.user:type_name -> gitaly.User
- 50, // 2: gitaly.UserCreateBranchResponse.branch:type_name -> gitaly.Branch
- 51, // 3: gitaly.UserCreateBranchError.custom_hook:type_name -> gitaly.CustomHookError
- 48, // 4: gitaly.UserUpdateBranchRequest.repository:type_name -> gitaly.Repository
- 49, // 5: gitaly.UserUpdateBranchRequest.user:type_name -> gitaly.User
- 48, // 6: gitaly.UserDeleteBranchRequest.repository:type_name -> gitaly.Repository
- 49, // 7: gitaly.UserDeleteBranchRequest.user:type_name -> gitaly.User
- 52, // 8: gitaly.UserDeleteBranchError.access_check:type_name -> gitaly.AccessCheckError
- 53, // 9: gitaly.UserDeleteBranchError.reference_update:type_name -> gitaly.ReferenceUpdateError
- 51, // 10: gitaly.UserDeleteBranchError.custom_hook:type_name -> gitaly.CustomHookError
- 48, // 11: gitaly.UserDeleteTagRequest.repository:type_name -> gitaly.Repository
- 49, // 12: gitaly.UserDeleteTagRequest.user:type_name -> gitaly.User
- 48, // 13: gitaly.UserCreateTagRequest.repository:type_name -> gitaly.Repository
- 49, // 14: gitaly.UserCreateTagRequest.user:type_name -> gitaly.User
- 54, // 15: gitaly.UserCreateTagRequest.timestamp:type_name -> google.protobuf.Timestamp
- 55, // 16: gitaly.UserCreateTagResponse.tag:type_name -> gitaly.Tag
- 52, // 17: gitaly.UserCreateTagError.access_check:type_name -> gitaly.AccessCheckError
- 53, // 18: gitaly.UserCreateTagError.reference_update:type_name -> gitaly.ReferenceUpdateError
- 51, // 19: gitaly.UserCreateTagError.custom_hook:type_name -> gitaly.CustomHookError
- 56, // 20: gitaly.UserCreateTagError.reference_exists:type_name -> gitaly.ReferenceExistsError
- 48, // 21: gitaly.UserMergeBranchRequest.repository:type_name -> gitaly.Repository
- 49, // 22: gitaly.UserMergeBranchRequest.user:type_name -> gitaly.User
- 54, // 23: gitaly.UserMergeBranchRequest.timestamp:type_name -> google.protobuf.Timestamp
+ 49, // 0: gitaly.UserCreateBranchRequest.repository:type_name -> gitaly.Repository
+ 50, // 1: gitaly.UserCreateBranchRequest.user:type_name -> gitaly.User
+ 51, // 2: gitaly.UserCreateBranchResponse.branch:type_name -> gitaly.Branch
+ 52, // 3: gitaly.UserCreateBranchError.custom_hook:type_name -> gitaly.CustomHookError
+ 49, // 4: gitaly.UserUpdateBranchRequest.repository:type_name -> gitaly.Repository
+ 50, // 5: gitaly.UserUpdateBranchRequest.user:type_name -> gitaly.User
+ 49, // 6: gitaly.UserDeleteBranchRequest.repository:type_name -> gitaly.Repository
+ 50, // 7: gitaly.UserDeleteBranchRequest.user:type_name -> gitaly.User
+ 53, // 8: gitaly.UserDeleteBranchError.access_check:type_name -> gitaly.AccessCheckError
+ 54, // 9: gitaly.UserDeleteBranchError.reference_update:type_name -> gitaly.ReferenceUpdateError
+ 52, // 10: gitaly.UserDeleteBranchError.custom_hook:type_name -> gitaly.CustomHookError
+ 49, // 11: gitaly.UserDeleteTagRequest.repository:type_name -> gitaly.Repository
+ 50, // 12: gitaly.UserDeleteTagRequest.user:type_name -> gitaly.User
+ 49, // 13: gitaly.UserCreateTagRequest.repository:type_name -> gitaly.Repository
+ 50, // 14: gitaly.UserCreateTagRequest.user:type_name -> gitaly.User
+ 55, // 15: gitaly.UserCreateTagRequest.timestamp:type_name -> google.protobuf.Timestamp
+ 56, // 16: gitaly.UserCreateTagResponse.tag:type_name -> gitaly.Tag
+ 53, // 17: gitaly.UserCreateTagError.access_check:type_name -> gitaly.AccessCheckError
+ 54, // 18: gitaly.UserCreateTagError.reference_update:type_name -> gitaly.ReferenceUpdateError
+ 52, // 19: gitaly.UserCreateTagError.custom_hook:type_name -> gitaly.CustomHookError
+ 57, // 20: gitaly.UserCreateTagError.reference_exists:type_name -> gitaly.ReferenceExistsError
+ 49, // 21: gitaly.UserMergeBranchRequest.repository:type_name -> gitaly.Repository
+ 50, // 22: gitaly.UserMergeBranchRequest.user:type_name -> gitaly.User
+ 55, // 23: gitaly.UserMergeBranchRequest.timestamp:type_name -> google.protobuf.Timestamp
22, // 24: gitaly.UserMergeBranchResponse.branch_update:type_name -> gitaly.OperationBranchUpdate
- 52, // 25: gitaly.UserMergeBranchError.access_check:type_name -> gitaly.AccessCheckError
- 53, // 26: gitaly.UserMergeBranchError.reference_update:type_name -> gitaly.ReferenceUpdateError
- 51, // 27: gitaly.UserMergeBranchError.custom_hook:type_name -> gitaly.CustomHookError
- 57, // 28: gitaly.UserMergeBranchError.merge_conflict:type_name -> gitaly.MergeConflictError
- 48, // 29: gitaly.UserMergeToRefRequest.repository:type_name -> gitaly.Repository
- 49, // 30: gitaly.UserMergeToRefRequest.user:type_name -> gitaly.User
- 54, // 31: gitaly.UserMergeToRefRequest.timestamp:type_name -> google.protobuf.Timestamp
- 48, // 32: gitaly.UserRebaseToRefRequest.repository:type_name -> gitaly.Repository
- 49, // 33: gitaly.UserRebaseToRefRequest.user:type_name -> gitaly.User
- 54, // 34: gitaly.UserRebaseToRefRequest.timestamp:type_name -> google.protobuf.Timestamp
- 48, // 35: gitaly.UserFFBranchRequest.repository:type_name -> gitaly.Repository
- 49, // 36: gitaly.UserFFBranchRequest.user:type_name -> gitaly.User
+ 53, // 25: gitaly.UserMergeBranchError.access_check:type_name -> gitaly.AccessCheckError
+ 54, // 26: gitaly.UserMergeBranchError.reference_update:type_name -> gitaly.ReferenceUpdateError
+ 52, // 27: gitaly.UserMergeBranchError.custom_hook:type_name -> gitaly.CustomHookError
+ 58, // 28: gitaly.UserMergeBranchError.merge_conflict:type_name -> gitaly.MergeConflictError
+ 49, // 29: gitaly.UserMergeToRefRequest.repository:type_name -> gitaly.Repository
+ 50, // 30: gitaly.UserMergeToRefRequest.user:type_name -> gitaly.User
+ 55, // 31: gitaly.UserMergeToRefRequest.timestamp:type_name -> google.protobuf.Timestamp
+ 49, // 32: gitaly.UserRebaseToRefRequest.repository:type_name -> gitaly.Repository
+ 50, // 33: gitaly.UserRebaseToRefRequest.user:type_name -> gitaly.User
+ 55, // 34: gitaly.UserRebaseToRefRequest.timestamp:type_name -> google.protobuf.Timestamp
+ 49, // 35: gitaly.UserFFBranchRequest.repository:type_name -> gitaly.Repository
+ 50, // 36: gitaly.UserFFBranchRequest.user:type_name -> gitaly.User
22, // 37: gitaly.UserFFBranchResponse.branch_update:type_name -> gitaly.OperationBranchUpdate
- 48, // 38: gitaly.UserCherryPickRequest.repository:type_name -> gitaly.Repository
- 49, // 39: gitaly.UserCherryPickRequest.user:type_name -> gitaly.User
- 58, // 40: gitaly.UserCherryPickRequest.commit:type_name -> gitaly.GitCommit
- 48, // 41: gitaly.UserCherryPickRequest.start_repository:type_name -> gitaly.Repository
- 54, // 42: gitaly.UserCherryPickRequest.timestamp:type_name -> google.protobuf.Timestamp
+ 49, // 38: gitaly.UserCherryPickRequest.repository:type_name -> gitaly.Repository
+ 50, // 39: gitaly.UserCherryPickRequest.user:type_name -> gitaly.User
+ 59, // 40: gitaly.UserCherryPickRequest.commit:type_name -> gitaly.GitCommit
+ 49, // 41: gitaly.UserCherryPickRequest.start_repository:type_name -> gitaly.Repository
+ 55, // 42: gitaly.UserCherryPickRequest.timestamp:type_name -> google.protobuf.Timestamp
22, // 43: gitaly.UserCherryPickResponse.branch_update:type_name -> gitaly.OperationBranchUpdate
- 57, // 44: gitaly.UserCherryPickError.cherry_pick_conflict:type_name -> gitaly.MergeConflictError
- 59, // 45: gitaly.UserCherryPickError.target_branch_diverged:type_name -> gitaly.NotAncestorError
- 60, // 46: gitaly.UserCherryPickError.changes_already_applied:type_name -> gitaly.ChangesAlreadyAppliedError
- 52, // 47: gitaly.UserCherryPickError.access_check:type_name -> gitaly.AccessCheckError
- 48, // 48: gitaly.UserRevertRequest.repository:type_name -> gitaly.Repository
- 49, // 49: gitaly.UserRevertRequest.user:type_name -> gitaly.User
- 58, // 50: gitaly.UserRevertRequest.commit:type_name -> gitaly.GitCommit
- 48, // 51: gitaly.UserRevertRequest.start_repository:type_name -> gitaly.Repository
- 54, // 52: gitaly.UserRevertRequest.timestamp:type_name -> google.protobuf.Timestamp
+ 58, // 44: gitaly.UserCherryPickError.cherry_pick_conflict:type_name -> gitaly.MergeConflictError
+ 60, // 45: gitaly.UserCherryPickError.target_branch_diverged:type_name -> gitaly.NotAncestorError
+ 61, // 46: gitaly.UserCherryPickError.changes_already_applied:type_name -> gitaly.ChangesAlreadyAppliedError
+ 53, // 47: gitaly.UserCherryPickError.access_check:type_name -> gitaly.AccessCheckError
+ 49, // 48: gitaly.UserRevertRequest.repository:type_name -> gitaly.Repository
+ 50, // 49: gitaly.UserRevertRequest.user:type_name -> gitaly.User
+ 59, // 50: gitaly.UserRevertRequest.commit:type_name -> gitaly.GitCommit
+ 49, // 51: gitaly.UserRevertRequest.start_repository:type_name -> gitaly.Repository
+ 55, // 52: gitaly.UserRevertRequest.timestamp:type_name -> google.protobuf.Timestamp
22, // 53: gitaly.UserRevertResponse.branch_update:type_name -> gitaly.OperationBranchUpdate
0, // 54: gitaly.UserRevertResponse.create_tree_error_code:type_name -> gitaly.UserRevertResponse.CreateTreeError
- 1, // 55: gitaly.UserCommitFilesActionHeader.action:type_name -> gitaly.UserCommitFilesActionHeader.ActionType
- 30, // 56: gitaly.UserCommitFilesAction.header:type_name -> gitaly.UserCommitFilesActionHeader
- 48, // 57: gitaly.UserCommitFilesRequestHeader.repository:type_name -> gitaly.Repository
- 49, // 58: gitaly.UserCommitFilesRequestHeader.user:type_name -> gitaly.User
- 48, // 59: gitaly.UserCommitFilesRequestHeader.start_repository:type_name -> gitaly.Repository
- 54, // 60: gitaly.UserCommitFilesRequestHeader.timestamp:type_name -> google.protobuf.Timestamp
- 32, // 61: gitaly.UserCommitFilesRequest.header:type_name -> gitaly.UserCommitFilesRequestHeader
- 31, // 62: gitaly.UserCommitFilesRequest.action:type_name -> gitaly.UserCommitFilesAction
- 22, // 63: gitaly.UserCommitFilesResponse.branch_update:type_name -> gitaly.OperationBranchUpdate
- 52, // 64: gitaly.UserCommitFilesError.access_check:type_name -> gitaly.AccessCheckError
- 61, // 65: gitaly.UserCommitFilesError.index_update:type_name -> gitaly.IndexError
- 51, // 66: gitaly.UserCommitFilesError.custom_hook:type_name -> gitaly.CustomHookError
- 46, // 67: gitaly.UserRebaseConfirmableRequest.header:type_name -> gitaly.UserRebaseConfirmableRequest.Header
- 48, // 68: gitaly.UserSquashRequest.repository:type_name -> gitaly.Repository
- 49, // 69: gitaly.UserSquashRequest.user:type_name -> gitaly.User
- 49, // 70: gitaly.UserSquashRequest.author:type_name -> gitaly.User
- 54, // 71: gitaly.UserSquashRequest.timestamp:type_name -> google.protobuf.Timestamp
- 57, // 72: gitaly.UserRebaseConfirmableError.rebase_conflict:type_name -> gitaly.MergeConflictError
- 52, // 73: gitaly.UserRebaseConfirmableError.access_check:type_name -> gitaly.AccessCheckError
- 62, // 74: gitaly.UserSquashError.resolve_revision:type_name -> gitaly.ResolveRevisionError
- 57, // 75: gitaly.UserSquashError.rebase_conflict:type_name -> gitaly.MergeConflictError
- 47, // 76: gitaly.UserApplyPatchRequest.header:type_name -> gitaly.UserApplyPatchRequest.Header
- 22, // 77: gitaly.UserApplyPatchResponse.branch_update:type_name -> gitaly.OperationBranchUpdate
- 48, // 78: gitaly.UserUpdateSubmoduleRequest.repository:type_name -> gitaly.Repository
- 49, // 79: gitaly.UserUpdateSubmoduleRequest.user:type_name -> gitaly.User
- 54, // 80: gitaly.UserUpdateSubmoduleRequest.timestamp:type_name -> google.protobuf.Timestamp
- 22, // 81: gitaly.UserUpdateSubmoduleResponse.branch_update:type_name -> gitaly.OperationBranchUpdate
- 48, // 82: gitaly.UserRebaseConfirmableRequest.Header.repository:type_name -> gitaly.Repository
- 49, // 83: gitaly.UserRebaseConfirmableRequest.Header.user:type_name -> gitaly.User
- 48, // 84: gitaly.UserRebaseConfirmableRequest.Header.remote_repository:type_name -> gitaly.Repository
- 54, // 85: gitaly.UserRebaseConfirmableRequest.Header.timestamp:type_name -> google.protobuf.Timestamp
- 48, // 86: gitaly.UserApplyPatchRequest.Header.repository:type_name -> gitaly.Repository
- 49, // 87: gitaly.UserApplyPatchRequest.Header.user:type_name -> gitaly.User
- 54, // 88: gitaly.UserApplyPatchRequest.Header.timestamp:type_name -> google.protobuf.Timestamp
- 2, // 89: gitaly.OperationService.UserCreateBranch:input_type -> gitaly.UserCreateBranchRequest
- 5, // 90: gitaly.OperationService.UserUpdateBranch:input_type -> gitaly.UserUpdateBranchRequest
- 7, // 91: gitaly.OperationService.UserDeleteBranch:input_type -> gitaly.UserDeleteBranchRequest
- 12, // 92: gitaly.OperationService.UserCreateTag:input_type -> gitaly.UserCreateTagRequest
- 10, // 93: gitaly.OperationService.UserDeleteTag:input_type -> gitaly.UserDeleteTagRequest
- 18, // 94: gitaly.OperationService.UserMergeToRef:input_type -> gitaly.UserMergeToRefRequest
- 20, // 95: gitaly.OperationService.UserRebaseToRef:input_type -> gitaly.UserRebaseToRefRequest
- 15, // 96: gitaly.OperationService.UserMergeBranch:input_type -> gitaly.UserMergeBranchRequest
- 23, // 97: gitaly.OperationService.UserFFBranch:input_type -> gitaly.UserFFBranchRequest
- 25, // 98: gitaly.OperationService.UserCherryPick:input_type -> gitaly.UserCherryPickRequest
- 33, // 99: gitaly.OperationService.UserCommitFiles:input_type -> gitaly.UserCommitFilesRequest
- 36, // 100: gitaly.OperationService.UserRebaseConfirmable:input_type -> gitaly.UserRebaseConfirmableRequest
- 28, // 101: gitaly.OperationService.UserRevert:input_type -> gitaly.UserRevertRequest
- 38, // 102: gitaly.OperationService.UserSquash:input_type -> gitaly.UserSquashRequest
- 42, // 103: gitaly.OperationService.UserApplyPatch:input_type -> gitaly.UserApplyPatchRequest
- 44, // 104: gitaly.OperationService.UserUpdateSubmodule:input_type -> gitaly.UserUpdateSubmoduleRequest
- 3, // 105: gitaly.OperationService.UserCreateBranch:output_type -> gitaly.UserCreateBranchResponse
- 6, // 106: gitaly.OperationService.UserUpdateBranch:output_type -> gitaly.UserUpdateBranchResponse
- 8, // 107: gitaly.OperationService.UserDeleteBranch:output_type -> gitaly.UserDeleteBranchResponse
- 13, // 108: gitaly.OperationService.UserCreateTag:output_type -> gitaly.UserCreateTagResponse
- 11, // 109: gitaly.OperationService.UserDeleteTag:output_type -> gitaly.UserDeleteTagResponse
- 19, // 110: gitaly.OperationService.UserMergeToRef:output_type -> gitaly.UserMergeToRefResponse
- 21, // 111: gitaly.OperationService.UserRebaseToRef:output_type -> gitaly.UserRebaseToRefResponse
- 16, // 112: gitaly.OperationService.UserMergeBranch:output_type -> gitaly.UserMergeBranchResponse
- 24, // 113: gitaly.OperationService.UserFFBranch:output_type -> gitaly.UserFFBranchResponse
- 26, // 114: gitaly.OperationService.UserCherryPick:output_type -> gitaly.UserCherryPickResponse
- 34, // 115: gitaly.OperationService.UserCommitFiles:output_type -> gitaly.UserCommitFilesResponse
- 37, // 116: gitaly.OperationService.UserRebaseConfirmable:output_type -> gitaly.UserRebaseConfirmableResponse
- 29, // 117: gitaly.OperationService.UserRevert:output_type -> gitaly.UserRevertResponse
- 39, // 118: gitaly.OperationService.UserSquash:output_type -> gitaly.UserSquashResponse
- 43, // 119: gitaly.OperationService.UserApplyPatch:output_type -> gitaly.UserApplyPatchResponse
- 45, // 120: gitaly.OperationService.UserUpdateSubmodule:output_type -> gitaly.UserUpdateSubmoduleResponse
- 105, // [105:121] is the sub-list for method output_type
- 89, // [89:105] is the sub-list for method input_type
- 89, // [89:89] is the sub-list for extension type_name
- 89, // [89:89] is the sub-list for extension extendee
- 0, // [0:89] is the sub-list for field type_name
+ 58, // 55: gitaly.UserRevertError.merge_conflict:type_name -> gitaly.MergeConflictError
+ 61, // 56: gitaly.UserRevertError.changes_already_applied:type_name -> gitaly.ChangesAlreadyAppliedError
+ 52, // 57: gitaly.UserRevertError.custom_hook:type_name -> gitaly.CustomHookError
+ 60, // 58: gitaly.UserRevertError.not_ancestor:type_name -> gitaly.NotAncestorError
+ 1, // 59: gitaly.UserCommitFilesActionHeader.action:type_name -> gitaly.UserCommitFilesActionHeader.ActionType
+ 31, // 60: gitaly.UserCommitFilesAction.header:type_name -> gitaly.UserCommitFilesActionHeader
+ 49, // 61: gitaly.UserCommitFilesRequestHeader.repository:type_name -> gitaly.Repository
+ 50, // 62: gitaly.UserCommitFilesRequestHeader.user:type_name -> gitaly.User
+ 49, // 63: gitaly.UserCommitFilesRequestHeader.start_repository:type_name -> gitaly.Repository
+ 55, // 64: gitaly.UserCommitFilesRequestHeader.timestamp:type_name -> google.protobuf.Timestamp
+ 33, // 65: gitaly.UserCommitFilesRequest.header:type_name -> gitaly.UserCommitFilesRequestHeader
+ 32, // 66: gitaly.UserCommitFilesRequest.action:type_name -> gitaly.UserCommitFilesAction
+ 22, // 67: gitaly.UserCommitFilesResponse.branch_update:type_name -> gitaly.OperationBranchUpdate
+ 53, // 68: gitaly.UserCommitFilesError.access_check:type_name -> gitaly.AccessCheckError
+ 62, // 69: gitaly.UserCommitFilesError.index_update:type_name -> gitaly.IndexError
+ 52, // 70: gitaly.UserCommitFilesError.custom_hook:type_name -> gitaly.CustomHookError
+ 47, // 71: gitaly.UserRebaseConfirmableRequest.header:type_name -> gitaly.UserRebaseConfirmableRequest.Header
+ 49, // 72: gitaly.UserSquashRequest.repository:type_name -> gitaly.Repository
+ 50, // 73: gitaly.UserSquashRequest.user:type_name -> gitaly.User
+ 50, // 74: gitaly.UserSquashRequest.author:type_name -> gitaly.User
+ 55, // 75: gitaly.UserSquashRequest.timestamp:type_name -> google.protobuf.Timestamp
+ 58, // 76: gitaly.UserRebaseConfirmableError.rebase_conflict:type_name -> gitaly.MergeConflictError
+ 53, // 77: gitaly.UserRebaseConfirmableError.access_check:type_name -> gitaly.AccessCheckError
+ 63, // 78: gitaly.UserSquashError.resolve_revision:type_name -> gitaly.ResolveRevisionError
+ 58, // 79: gitaly.UserSquashError.rebase_conflict:type_name -> gitaly.MergeConflictError
+ 48, // 80: gitaly.UserApplyPatchRequest.header:type_name -> gitaly.UserApplyPatchRequest.Header
+ 22, // 81: gitaly.UserApplyPatchResponse.branch_update:type_name -> gitaly.OperationBranchUpdate
+ 49, // 82: gitaly.UserUpdateSubmoduleRequest.repository:type_name -> gitaly.Repository
+ 50, // 83: gitaly.UserUpdateSubmoduleRequest.user:type_name -> gitaly.User
+ 55, // 84: gitaly.UserUpdateSubmoduleRequest.timestamp:type_name -> google.protobuf.Timestamp
+ 22, // 85: gitaly.UserUpdateSubmoduleResponse.branch_update:type_name -> gitaly.OperationBranchUpdate
+ 49, // 86: gitaly.UserRebaseConfirmableRequest.Header.repository:type_name -> gitaly.Repository
+ 50, // 87: gitaly.UserRebaseConfirmableRequest.Header.user:type_name -> gitaly.User
+ 49, // 88: gitaly.UserRebaseConfirmableRequest.Header.remote_repository:type_name -> gitaly.Repository
+ 55, // 89: gitaly.UserRebaseConfirmableRequest.Header.timestamp:type_name -> google.protobuf.Timestamp
+ 49, // 90: gitaly.UserApplyPatchRequest.Header.repository:type_name -> gitaly.Repository
+ 50, // 91: gitaly.UserApplyPatchRequest.Header.user:type_name -> gitaly.User
+ 55, // 92: gitaly.UserApplyPatchRequest.Header.timestamp:type_name -> google.protobuf.Timestamp
+ 2, // 93: gitaly.OperationService.UserCreateBranch:input_type -> gitaly.UserCreateBranchRequest
+ 5, // 94: gitaly.OperationService.UserUpdateBranch:input_type -> gitaly.UserUpdateBranchRequest
+ 7, // 95: gitaly.OperationService.UserDeleteBranch:input_type -> gitaly.UserDeleteBranchRequest
+ 12, // 96: gitaly.OperationService.UserCreateTag:input_type -> gitaly.UserCreateTagRequest
+ 10, // 97: gitaly.OperationService.UserDeleteTag:input_type -> gitaly.UserDeleteTagRequest
+ 18, // 98: gitaly.OperationService.UserMergeToRef:input_type -> gitaly.UserMergeToRefRequest
+ 20, // 99: gitaly.OperationService.UserRebaseToRef:input_type -> gitaly.UserRebaseToRefRequest
+ 15, // 100: gitaly.OperationService.UserMergeBranch:input_type -> gitaly.UserMergeBranchRequest
+ 23, // 101: gitaly.OperationService.UserFFBranch:input_type -> gitaly.UserFFBranchRequest
+ 25, // 102: gitaly.OperationService.UserCherryPick:input_type -> gitaly.UserCherryPickRequest
+ 34, // 103: gitaly.OperationService.UserCommitFiles:input_type -> gitaly.UserCommitFilesRequest
+ 37, // 104: gitaly.OperationService.UserRebaseConfirmable:input_type -> gitaly.UserRebaseConfirmableRequest
+ 28, // 105: gitaly.OperationService.UserRevert:input_type -> gitaly.UserRevertRequest
+ 39, // 106: gitaly.OperationService.UserSquash:input_type -> gitaly.UserSquashRequest
+ 43, // 107: gitaly.OperationService.UserApplyPatch:input_type -> gitaly.UserApplyPatchRequest
+ 45, // 108: gitaly.OperationService.UserUpdateSubmodule:input_type -> gitaly.UserUpdateSubmoduleRequest
+ 3, // 109: gitaly.OperationService.UserCreateBranch:output_type -> gitaly.UserCreateBranchResponse
+ 6, // 110: gitaly.OperationService.UserUpdateBranch:output_type -> gitaly.UserUpdateBranchResponse
+ 8, // 111: gitaly.OperationService.UserDeleteBranch:output_type -> gitaly.UserDeleteBranchResponse
+ 13, // 112: gitaly.OperationService.UserCreateTag:output_type -> gitaly.UserCreateTagResponse
+ 11, // 113: gitaly.OperationService.UserDeleteTag:output_type -> gitaly.UserDeleteTagResponse
+ 19, // 114: gitaly.OperationService.UserMergeToRef:output_type -> gitaly.UserMergeToRefResponse
+ 21, // 115: gitaly.OperationService.UserRebaseToRef:output_type -> gitaly.UserRebaseToRefResponse
+ 16, // 116: gitaly.OperationService.UserMergeBranch:output_type -> gitaly.UserMergeBranchResponse
+ 24, // 117: gitaly.OperationService.UserFFBranch:output_type -> gitaly.UserFFBranchResponse
+ 26, // 118: gitaly.OperationService.UserCherryPick:output_type -> gitaly.UserCherryPickResponse
+ 35, // 119: gitaly.OperationService.UserCommitFiles:output_type -> gitaly.UserCommitFilesResponse
+ 38, // 120: gitaly.OperationService.UserRebaseConfirmable:output_type -> gitaly.UserRebaseConfirmableResponse
+ 29, // 121: gitaly.OperationService.UserRevert:output_type -> gitaly.UserRevertResponse
+ 40, // 122: gitaly.OperationService.UserSquash:output_type -> gitaly.UserSquashResponse
+ 44, // 123: gitaly.OperationService.UserApplyPatch:output_type -> gitaly.UserApplyPatchResponse
+ 46, // 124: gitaly.OperationService.UserUpdateSubmodule:output_type -> gitaly.UserUpdateSubmoduleResponse
+ 109, // [109:125] is the sub-list for method output_type
+ 93, // [93:109] is the sub-list for method input_type
+ 93, // [93:93] is the sub-list for extension type_name
+ 93, // [93:93] is the sub-list for extension extendee
+ 0, // [0:93] is the sub-list for field type_name
}
func init() { file_operations_proto_init() }
@@ -5511,7 +5649,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserCommitFilesActionHeader); i {
+ switch v := v.(*UserRevertError); i {
case 0:
return &v.state
case 1:
@@ -5523,7 +5661,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserCommitFilesAction); i {
+ switch v := v.(*UserCommitFilesActionHeader); i {
case 0:
return &v.state
case 1:
@@ -5535,7 +5673,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserCommitFilesRequestHeader); i {
+ switch v := v.(*UserCommitFilesAction); i {
case 0:
return &v.state
case 1:
@@ -5547,7 +5685,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserCommitFilesRequest); i {
+ switch v := v.(*UserCommitFilesRequestHeader); i {
case 0:
return &v.state
case 1:
@@ -5559,7 +5697,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserCommitFilesResponse); i {
+ switch v := v.(*UserCommitFilesRequest); i {
case 0:
return &v.state
case 1:
@@ -5571,7 +5709,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserCommitFilesError); i {
+ switch v := v.(*UserCommitFilesResponse); i {
case 0:
return &v.state
case 1:
@@ -5583,7 +5721,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserRebaseConfirmableRequest); i {
+ switch v := v.(*UserCommitFilesError); i {
case 0:
return &v.state
case 1:
@@ -5595,7 +5733,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserRebaseConfirmableResponse); i {
+ switch v := v.(*UserRebaseConfirmableRequest); i {
case 0:
return &v.state
case 1:
@@ -5607,7 +5745,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserSquashRequest); i {
+ switch v := v.(*UserRebaseConfirmableResponse); i {
case 0:
return &v.state
case 1:
@@ -5619,7 +5757,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserSquashResponse); i {
+ switch v := v.(*UserSquashRequest); i {
case 0:
return &v.state
case 1:
@@ -5631,7 +5769,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserRebaseConfirmableError); i {
+ switch v := v.(*UserSquashResponse); i {
case 0:
return &v.state
case 1:
@@ -5643,7 +5781,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserSquashError); i {
+ switch v := v.(*UserRebaseConfirmableError); i {
case 0:
return &v.state
case 1:
@@ -5655,7 +5793,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserApplyPatchRequest); i {
+ switch v := v.(*UserSquashError); i {
case 0:
return &v.state
case 1:
@@ -5667,7 +5805,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserApplyPatchResponse); i {
+ switch v := v.(*UserApplyPatchRequest); i {
case 0:
return &v.state
case 1:
@@ -5679,7 +5817,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserUpdateSubmoduleRequest); i {
+ switch v := v.(*UserApplyPatchResponse); i {
case 0:
return &v.state
case 1:
@@ -5691,7 +5829,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserUpdateSubmoduleResponse); i {
+ switch v := v.(*UserUpdateSubmoduleRequest); i {
case 0:
return &v.state
case 1:
@@ -5703,7 +5841,7 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UserRebaseConfirmableRequest_Header); i {
+ switch v := v.(*UserUpdateSubmoduleResponse); i {
case 0:
return &v.state
case 1:
@@ -5715,6 +5853,18 @@ func file_operations_proto_init() {
}
}
file_operations_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*UserRebaseConfirmableRequest_Header); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_operations_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*UserApplyPatchRequest_Header); i {
case 0:
return &v.state
@@ -5753,36 +5903,42 @@ func file_operations_proto_init() {
(*UserCherryPickError_ChangesAlreadyApplied)(nil),
(*UserCherryPickError_AccessCheck)(nil),
}
- file_operations_proto_msgTypes[29].OneofWrappers = []interface{}{
+ file_operations_proto_msgTypes[28].OneofWrappers = []interface{}{
+ (*UserRevertError_MergeConflict)(nil),
+ (*UserRevertError_ChangesAlreadyApplied)(nil),
+ (*UserRevertError_CustomHook)(nil),
+ (*UserRevertError_NotAncestor)(nil),
+ }
+ file_operations_proto_msgTypes[30].OneofWrappers = []interface{}{
(*UserCommitFilesAction_Header)(nil),
(*UserCommitFilesAction_Content)(nil),
}
- file_operations_proto_msgTypes[31].OneofWrappers = []interface{}{
+ file_operations_proto_msgTypes[32].OneofWrappers = []interface{}{
(*UserCommitFilesRequest_Header)(nil),
(*UserCommitFilesRequest_Action)(nil),
}
- file_operations_proto_msgTypes[33].OneofWrappers = []interface{}{
+ file_operations_proto_msgTypes[34].OneofWrappers = []interface{}{
(*UserCommitFilesError_AccessCheck)(nil),
(*UserCommitFilesError_IndexUpdate)(nil),
(*UserCommitFilesError_CustomHook)(nil),
}
- file_operations_proto_msgTypes[34].OneofWrappers = []interface{}{
+ file_operations_proto_msgTypes[35].OneofWrappers = []interface{}{
(*UserRebaseConfirmableRequest_Header_)(nil),
(*UserRebaseConfirmableRequest_Apply)(nil),
}
- file_operations_proto_msgTypes[35].OneofWrappers = []interface{}{
+ file_operations_proto_msgTypes[36].OneofWrappers = []interface{}{
(*UserRebaseConfirmableResponse_RebaseSha)(nil),
(*UserRebaseConfirmableResponse_RebaseApplied)(nil),
}
- file_operations_proto_msgTypes[38].OneofWrappers = []interface{}{
+ file_operations_proto_msgTypes[39].OneofWrappers = []interface{}{
(*UserRebaseConfirmableError_RebaseConflict)(nil),
(*UserRebaseConfirmableError_AccessCheck)(nil),
}
- file_operations_proto_msgTypes[39].OneofWrappers = []interface{}{
+ file_operations_proto_msgTypes[40].OneofWrappers = []interface{}{
(*UserSquashError_ResolveRevision)(nil),
(*UserSquashError_RebaseConflict)(nil),
}
- file_operations_proto_msgTypes[40].OneofWrappers = []interface{}{
+ file_operations_proto_msgTypes[41].OneofWrappers = []interface{}{
(*UserApplyPatchRequest_Header_)(nil),
(*UserApplyPatchRequest_Patches)(nil),
}
@@ -5792,7 +5948,7 @@ func file_operations_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_operations_proto_rawDesc,
NumEnums: 2,
- NumMessages: 46,
+ NumMessages: 47,
NumExtensions: 0,
NumServices: 1,
},
diff --git a/proto/go/gitalypb/repository.pb.go b/proto/go/gitalypb/repository.pb.go
index ca005e2b5..bdfd812ed 100644
--- a/proto/go/gitalypb/repository.pb.go
+++ b/proto/go/gitalypb/repository.pb.go
@@ -6163,7 +6163,7 @@ var file_repository_proto_rawDesc = []byte{
0x1c, 0x0a, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x09, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x14, 0x0a,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,
- 0x6c, 0x75, 0x65, 0x32, 0xaf, 0x20, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
+ 0x6c, 0x75, 0x65, 0x32, 0xb2, 0x20, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f,
0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5d, 0x0a, 0x10, 0x52, 0x65, 0x70,
0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x12, 0x1f, 0x2e,
0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
@@ -6399,34 +6399,34 @@ var file_repository_proto_rawDesc = []byte{
0x79, 0x2e, 0x46, 0x75, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x46, 0x75, 0x6c, 0x6c, 0x50,
0x61, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x09, 0xfa, 0x97, 0x28,
- 0x02, 0x08, 0x02, 0x88, 0x02, 0x01, 0x12, 0x4a, 0x0a, 0x09, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
+ 0x02, 0x08, 0x02, 0x88, 0x02, 0x01, 0x12, 0x4d, 0x0a, 0x09, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
0x41, 0x6c, 0x6c, 0x12, 0x18, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x6d,
0x6f, 0x76, 0x65, 0x41, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e,
0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x41, 0x6c, 0x6c,
- 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x08, 0xfa, 0x97, 0x28, 0x04, 0x08, 0x01,
- 0x10, 0x02, 0x12, 0x5d, 0x0a, 0x10, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6f,
- 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e,
- 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79,
- 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
- 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08,
- 0x02, 0x12, 0x60, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x70, 0x6f,
- 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e,
- 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72,
- 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c,
- 0x79, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
- 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97, 0x28,
- 0x02, 0x08, 0x01, 0x12, 0x60, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x74,
- 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61, 0x6c,
- 0x79, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
- 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x69, 0x74,
- 0x61, 0x6c, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69,
- 0x62, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa,
- 0x97, 0x28, 0x02, 0x08, 0x02, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2e,
- 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, 0x72, 0x67, 0x2f, 0x67,
- 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2f, 0x76, 0x31, 0x36, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
- 0x67, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f,
- 0x74, 0x6f, 0x33,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0b, 0xfa, 0x97, 0x28, 0x04, 0x08, 0x01,
+ 0x10, 0x02, 0x88, 0x02, 0x01, 0x12, 0x5d, 0x0a, 0x10, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52,
+ 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1f, 0x2e, 0x67, 0x69, 0x74, 0x61,
+ 0x6c, 0x79, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
+ 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x69, 0x74,
+ 0x61, 0x6c, 0x79, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69,
+ 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0xfa, 0x97,
+ 0x28, 0x02, 0x08, 0x02, 0x12, 0x60, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52,
+ 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x2e, 0x67, 0x69, 0x74, 0x61,
+ 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69,
+ 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x67, 0x69,
+ 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x70, 0x6f,
+ 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06,
+ 0xfa, 0x97, 0x28, 0x02, 0x08, 0x01, 0x12, 0x60, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c,
+ 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x67, 0x69,
+ 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x74, 0x74, 0x72,
+ 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e,
+ 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x69, 0x6c, 0x65, 0x41, 0x74,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x22, 0x06, 0xfa, 0x97, 0x28, 0x02, 0x08, 0x02, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x6c,
+ 0x61, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x69, 0x74, 0x6c, 0x61, 0x62, 0x2d, 0x6f, 0x72,
+ 0x67, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x2f, 0x76, 0x31, 0x36, 0x2f, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x79, 0x70, 0x62, 0x62, 0x06,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
diff --git a/proto/go/gitalypb/repository_grpc.pb.go b/proto/go/gitalypb/repository_grpc.pb.go
index d5af55b94..b407b948c 100644
--- a/proto/go/gitalypb/repository_grpc.pb.go
+++ b/proto/go/gitalypb/repository_grpc.pb.go
@@ -192,7 +192,9 @@ type RepositoryServiceClient interface {
// FullPath reads the "gitlab.fullpath" configuration from the repository's
// gitconfig. Returns an error in case the full path has not been configured.
FullPath(ctx context.Context, in *FullPathRequest, opts ...grpc.CallOption) (*FullPathResponse, error)
+ // Deprecated: Do not use.
// RemoveAll deletes all repositories on a specified storage.
+ // Deprecated in favour of individually removing repositories with RemoveRepository.
RemoveAll(ctx context.Context, in *RemoveAllRequest, opts ...grpc.CallOption) (*RemoveAllResponse, error)
// BackupRepository creates a full or incremental backup streamed directly to
// object-storage. The backup is created synchronously. The destination must
@@ -957,6 +959,7 @@ func (c *repositoryServiceClient) FullPath(ctx context.Context, in *FullPathRequ
return out, nil
}
+// Deprecated: Do not use.
func (c *repositoryServiceClient) RemoveAll(ctx context.Context, in *RemoveAllRequest, opts ...grpc.CallOption) (*RemoveAllResponse, error) {
out := new(RemoveAllResponse)
err := c.cc.Invoke(ctx, "/gitaly.RepositoryService/RemoveAll", in, out, opts...)
@@ -1167,7 +1170,9 @@ type RepositoryServiceServer interface {
// FullPath reads the "gitlab.fullpath" configuration from the repository's
// gitconfig. Returns an error in case the full path has not been configured.
FullPath(context.Context, *FullPathRequest) (*FullPathResponse, error)
+ // Deprecated: Do not use.
// RemoveAll deletes all repositories on a specified storage.
+ // Deprecated in favour of individually removing repositories with RemoveRepository.
RemoveAll(context.Context, *RemoveAllRequest) (*RemoveAllResponse, error)
// BackupRepository creates a full or incremental backup streamed directly to
// object-storage. The backup is created synchronously. The destination must
diff --git a/proto/log.proto b/proto/log.proto
index 1df859a7e..5339a725a 100644
--- a/proto/log.proto
+++ b/proto/log.proto
@@ -60,6 +60,22 @@ message LogEntry {
string path = 1;
}
+ // Housekeeping models a housekeeping run. It is supposed to handle housekeeping tasks for repositories such as the
+ // cleanup of unneeded files and optimizations for the repository's data structures. It is a collection of smaller
+ // tasks.
+ message Housekeeping {
+ // PackRefs models a pack-refs housekeeping task. This task is to pack loose references into a singular packed-refs
+ // file to optimize ref accessing time. In other words, it's a wrapper for git-pack-refs command.
+ message PackRefs {
+ // pruned_refs is the list of fully qualified references to be pruned. Gitaly removes the loose reference files on
+ // the disk. They still stay intact in the packed-refs.
+ repeated bytes pruned_refs = 1;
+ }
+
+ // pack_refs signifies if the housekeeping run includes a pack-refs task.
+ PackRefs pack_refs = 1;
+ }
+
// relative_path is the relative path of the repository the changes in the
// log entry are targeting.
string relative_path = 1;
@@ -82,6 +98,8 @@ message LogEntry {
RepositoryCreation repository_creation = 7;
// alternate_update records a change to the repository's 'objects/info/alternates' file.
AlternateUpdate alternate_update = 8;
+ // housekeeping, when set, indicates this log entry contains a housekeeping task.
+ Housekeeping housekeeping = 9;
}
// LSN serializes a log sequence number. It's used for storing a partition's
diff --git a/proto/operations.proto b/proto/operations.proto
index 27a792572..72df2f6d8 100644
--- a/proto/operations.proto
+++ b/proto/operations.proto
@@ -745,6 +745,20 @@ message UserRevertResponse {
CreateTreeError create_tree_error_code = 5;
}
+// UserRevertError is an error returned by the UserRevert RPC.
+message UserRevertError {
+ oneof error {
+ // merge_conflict is returned if there is a conflict when applying the revert.
+ MergeConflictError merge_conflict = 1;
+ // changes_already_applied is returned if the result after applying the revert is empty.
+ ChangesAlreadyAppliedError changes_already_applied = 2;
+ // custom_hook contains the error message if the pre-receive hook failed.
+ CustomHookError custom_hook = 3;
+ // not_ancestor is returned if the old tip of the target branch is not an ancestor of the new commit.
+ NotAncestorError not_ancestor = 4;
+ }
+}
+
// UserCommitFilesActionHeader contains the details of the action to be performed.
message UserCommitFilesActionHeader {
// ActionType is the type of action to perform.
diff --git a/proto/repository.proto b/proto/repository.proto
index 25d446f61..4a5d8dbf8 100644
--- a/proto/repository.proto
+++ b/proto/repository.proto
@@ -381,11 +381,13 @@ service RepositoryService {
}
// RemoveAll deletes all repositories on a specified storage.
+ // Deprecated in favour of individually removing repositories with RemoveRepository.
rpc RemoveAll(RemoveAllRequest) returns (RemoveAllResponse) {
option (op_type) = {
op: MUTATOR
scope_level: STORAGE
};
+ option deprecated = true;
}
// BackupRepository creates a full or incremental backup streamed directly to
diff --git a/tools/protoc-gen-doc/go.mod b/tools/protoc-gen-doc/go.mod
index 80c25593d..d4c380964 100644
--- a/tools/protoc-gen-doc/go.mod
+++ b/tools/protoc-gen-doc/go.mod
@@ -9,7 +9,7 @@ require (
github.com/Masterminds/sprig v2.15.0+incompatible // indirect
github.com/aokoli/goutils v1.0.1 // indirect
github.com/envoyproxy/protoc-gen-validate v0.3.0-java // indirect
- github.com/gogo/protobuf v1.1.1 // indirect
+ github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.1.2 // indirect
github.com/huandu/xstrings v1.0.0 // indirect
@@ -20,3 +20,5 @@ require (
google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4 // indirect
google.golang.org/protobuf v1.27.1 // indirect
)
+
+exclude github.com/gogo/protobuf v1.1.1 // CVE-2021-3121
diff --git a/tools/protoc-gen-doc/go.sum b/tools/protoc-gen-doc/go.sum
index 77e6c2f95..8b2ba1352 100644
--- a/tools/protoc-gen-doc/go.sum
+++ b/tools/protoc-gen-doc/go.sum
@@ -26,8 +26,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/envoyproxy/protoc-gen-validate v0.3.0-java h1:bV5JGEB1ouEzZa0hgVDFFiClrUEuGWRaAc/3mxR2QK0=
github.com/envoyproxy/protoc-gen-validate v0.3.0-java/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -58,6 +58,8 @@ github.com/huandu/xstrings v1.0.0 h1:pO2K/gKgKaat5LdpAhxhluX2GPQMaI3W5FUz/I/UnWk
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
github.com/imdario/mergo v0.3.4 h1:mKkfHkZWD8dC7WxKx3N9WCF0Y+dLau45704YQmY6H94=
github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007 h1:28i1IjGcx8AofiB4N3q5Yls55VEaitzuEPkFJEVgGkA=
github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -72,6 +74,8 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -84,6 +88,8 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -92,7 +98,9 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -100,11 +108,14 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -119,6 +130,8 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/tools/protogem/build-proto-gem b/tools/protogem/build-proto-gem
index aca451170..bfacea8f2 100755
--- a/tools/protogem/build-proto-gem
+++ b/tools/protogem/build-proto-gem
@@ -16,10 +16,18 @@ def parse_options
option_parser = OptionParser.new do |opts|
opts.banner = "Usage: build-proto-gem [options]"
+ opts.on("-n", "--name NAME", "Name of the gem. By default, the name is hard-coded to `gitaly`") do |name|
+ options[:gem_name] = name
+ end
+
opts.on_tail("--skip-verify-tag", "Skip verification that this is run for a tagged Gitaly commit") do
options[:skip_verify_tag] = true
end
+ opts.on("-w", "--working-dir DIR", "Working dir of the gem. If not specified, a temporary dir is used") do |path|
+ options[:working_dir] = path
+ end
+
opts.on("-o", "--output PATH", "output path for the gem") do |path|
options[:output_path] = File.absolute_path(path)
end
@@ -41,7 +49,15 @@ def main(options)
abort "Version string #{version.inspect} does not look like a Gitaly Release tag (e.g. \"v1.0.2\"). Aborting."
end
- if !options[:skip_verify_tag]
+ if options[:skip_verify_tag]
+ matches = /^(\d+\.\d+\.\d+).*/.match(version)
+ if matches.nil?
+ abort "Invalid version number #{version}"
+ end
+
+ ref = capture!(%w[git rev-parse --short HEAD]).chomp
+ version = "#{matches[1]}-#{ref}"
+ else
ref = capture!(%w[git describe --tag]).chomp
if ref != "v#{version}"
abort "Checkout tag v#{version} to publish.\n\t git checkout v#{version}"
@@ -54,9 +70,15 @@ def main(options)
puts 'Testing for staged changes'
run!(%w[git diff --quiet --cached --exit-code])
- Dir.mktmpdir do |output_dir|
+ if options[:working_dir]
+ output_dir = File.absolute_path(options[:working_dir])
generate_sources(output_dir, version)
- build_gem(output_dir, options[:output_path])
+ build_gem(options, output_dir, options[:output_path])
+ else
+ Dir.mktmpdir do |output_dir|
+ generate_sources(output_dir, version)
+ build_gem(options, output_dir, options[:output_path])
+ end
end
end
@@ -77,7 +99,7 @@ def generate_sources(output_dir, version)
write_ruby_requires(output_dir)
end
-def build_gem(output_dir, output_path)
+def build_gem(options, output_dir, output_path)
gemspec = <<~EOT
# coding: utf-8
prefix = 'ruby/proto'
@@ -85,7 +107,7 @@ def build_gem(output_dir, output_path)
require 'gitaly/version'
Gem::Specification.new do |spec|
- spec.name = "gitaly"
+ spec.name = "#{options[:gem_name] || 'gitaly'}"
spec.version = Gitaly::VERSION
spec.authors = ["GitLab Engineering"]
spec.email = ["engineering@gitlab.com"]
@@ -108,7 +130,7 @@ def build_gem(output_dir, output_path)
end
EOT
- gemspec_path = File.join(output_dir, 'gitaly.gemspec')
+ gemspec_path = File.absolute_path(File.join(output_dir, 'gitaly.gemspec'))
open(gemspec_path, 'w') { |f| f.write(gemspec) }
run!(['gem', 'build', gemspec_path, '--output', output_path], output_dir)
diff --git a/tools/protolint/go.mod b/tools/protolint/go.mod
index 3d6d9158b..786c618c5 100644
--- a/tools/protolint/go.mod
+++ b/tools/protolint/go.mod
@@ -23,11 +23,19 @@ require (
github.com/oklog/run v1.0.0 // indirect
github.com/yoheimuta/go-protoparser/v4 v4.7.0 // indirect
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
- golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41 // indirect
- golang.org/x/text v0.3.7 // indirect
+ golang.org/x/sys v0.5.0 // indirect
+ golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect
google.golang.org/grpc v1.46.2 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
+
+exclude (
+ // GO-2022-1059, GO-2021-0113, GO-2020-0015
+ golang.org/x/text v0.3.0
+ golang.org/x/text v0.3.3
+ golang.org/x/text v0.3.5
+ golang.org/x/text v0.3.7
+)
diff --git a/tools/protolint/go.sum b/tools/protolint/go.sum
index e25a4f751..745d96ec4 100644
--- a/tools/protolint/go.sum
+++ b/tools/protolint/go.sum
@@ -148,15 +148,11 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41 h1:ohgcoMbSofXygzo6AD2I1kz3BFmW1QArPYTtwEM3UXc=
-golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=