diff options
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 @@ -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: ×tamppb.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= |