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

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZeger-Jan van de Weg <git@zjvandeweg.nl>2019-07-05 12:28:17 +0300
committerZeger-Jan van de Weg <git@zjvandeweg.nl>2019-07-05 12:28:17 +0300
commit2375f7a95e0b0affc7e3439083b084af348505b4 (patch)
tree2693a49918682c12ea22e4085b8c569ac9e62497
parent873a408c49ef1b34fce0cd47a80aa192f426d7c0 (diff)
parentb9c9aec5cf53de4ea1a7fc3b067dd56d7828e080 (diff)
Merge branch 'jv-host-gitaly-proto' into 'master'
Start preparation for migrating .proto files See merge request gitlab-org/gitaly!1349
-rwxr-xr-x_support/migrate-proto55
-rw-r--r--_support/run.rb7
-rw-r--r--proto/CONTRIBUTING.md12
-rw-r--r--proto/DEPRECATION.md16
-rw-r--r--proto/LICENSE21
-rw-r--r--proto/README.md7
-rw-r--r--proto/README.orig.md319
-rw-r--r--proto/RELEASE.md30
-rw-r--r--proto/VERSION1
-rw-r--r--proto/blob.proto118
-rw-r--r--proto/cleanup.proto69
-rw-r--r--proto/commit.proto386
-rw-r--r--proto/conflicts.proto66
-rw-r--r--proto/diff.proto145
-rw-r--r--proto/namespace.proto60
-rw-r--r--proto/objectpool.proto102
-rw-r--r--proto/operations.proto432
-rw-r--r--proto/ref.proto317
-rw-r--r--proto/remote.proto118
-rw-r--r--proto/repository-service.proto594
-rw-r--r--proto/server.proto29
-rw-r--r--proto/shared.proto135
-rw-r--r--proto/smarthttp.proto91
-rw-r--r--proto/ssh.proto99
-rw-r--r--proto/storage.proto37
-rw-r--r--proto/wiki.proto204
26 files changed, 3470 insertions, 0 deletions
diff --git a/_support/migrate-proto b/_support/migrate-proto
new file mode 100755
index 000000000..c1529a5a4
--- /dev/null
+++ b/_support/migrate-proto
@@ -0,0 +1,55 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require_relative 'run.rb'
+
+require 'tempfile'
+
+SOURCE = 'https://gitlab.com/gitlab-org/gitaly-proto'
+
+def main
+ root = capture!(%w[git rev-parse --show-toplevel]).chomp
+
+ Dir.mktmpdir do |dir|
+ Dir.chdir(dir) do
+ clone
+ modify
+ copy(root)
+ end
+ end
+end
+
+def clone
+ run!(%W[git clone --quiet --depth=1 #{SOURCE}.git .])
+end
+
+def modify
+ Dir['*.proto'].each do |proto|
+ tmp = proto + '.bak'
+ run2!(%W[sed s/gitlab-org\\/gitaly-proto/gitlab-org\\/gitaly\\/proto/ #{proto}], out: tmp)
+ FileUtils.mv(tmp, proto)
+ end
+
+ FileUtils.mv('README.md', 'README.orig.md')
+
+ commit_id = capture!(%w[git rev-parse HEAD]).chomp
+ readme = <<EOS
+# Vendored copy of gitaly-proto
+
+Vendored from #{SOURCE} at #{commit_id}.
+
+Migration in progress, see
+https://gitlab.com/gitlab-org/gitaly/issues/1761. Do not edit files in
+this directory, your changes will be ignored and overwritten.
+EOS
+ IO.write('README.md', readme)
+end
+
+def copy(root)
+ dest = File.join(root, 'proto')
+ FileUtils.rm(Dir["#{dest}/*.proto"])
+ file_list = Dir['*.proto'] + Dir['*.md'] + %w[LICENSE VERSION]
+ FileUtils.cp(file_list, dest)
+end
+
+main
diff --git a/_support/run.rb b/_support/run.rb
index f6d84829a..a62959180 100644
--- a/_support/run.rb
+++ b/_support/run.rb
@@ -5,6 +5,13 @@ def run!(cmd, chdir='.')
end
end
+def run2!(cmd, chdir: '.', out: 1)
+ GitalySupport.print_cmd(cmd)
+ unless system(*cmd, chdir: chdir, out: out)
+ GitalySupport.fail_cmd!(cmd)
+ end
+end
+
def capture!(cmd, chdir='.')
GitalySupport.print_cmd(cmd)
output = IO.popen(cmd, chdir: chdir) { |io| io.read }
diff --git a/proto/CONTRIBUTING.md b/proto/CONTRIBUTING.md
new file mode 100644
index 000000000..442c2b00c
--- /dev/null
+++ b/proto/CONTRIBUTING.md
@@ -0,0 +1,12 @@
+## Developer Certificate of Origin + License
+
+By contributing to GitLab B.V., You accept and agree to the following terms and
+conditions for Your present and future Contributions submitted to GitLab B.V.
+Except for the license granted herein to GitLab B.V. and recipients of software
+distributed by GitLab B.V., You reserve all right, title, and interest in and to
+Your Contributions. All Contributions are subject to the following DCO + License
+terms.
+
+[DCO + License](https://gitlab.com/gitlab-org/dco/blob/master/README.md)
+
+_This notice should stay as the first item in the CONTRIBUTING.md file._
diff --git a/proto/DEPRECATION.md b/proto/DEPRECATION.md
new file mode 100644
index 000000000..f2f145bc0
--- /dev/null
+++ b/proto/DEPRECATION.md
@@ -0,0 +1,16 @@
+# RPC deprecation process for gitaly-proto
+
+First create a deprecation issue at
+https://gitlab.com/gitlab-org/gitaly/issues with the title `Deprecate
+RPC FooBar`. Use label `Deprecation`. Below is a template for the
+issue description.
+
+```
+We are deprecating RPC FooBar because **REASONS**.
+
+- [ ] put a deprecation comment `// DEPRECATED: <ISSUE-LINK>` in gitaly-proto **Merge Request LINK**
+- [ ] find all client-side uses of RPC and list below
+- [ ] update all client-side uses to no longer use RPC **ADD Merge Request LINKS**
+- [ ] wait for a GitLab release in which the RPC is no longer occurring in client side code **LINK TO GITLAB-CE RELEASE TAG**
+- [ ] delete the server side implementation of the old RPC in https://gitlab.com/gitlab-org/gitaly **Merge Request LINK**
+```
diff --git a/proto/LICENSE b/proto/LICENSE
new file mode 100644
index 000000000..c7344c56d
--- /dev/null
+++ b/proto/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016-2017 GitLab B.V.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/proto/README.md b/proto/README.md
new file mode 100644
index 000000000..0584f35e3
--- /dev/null
+++ b/proto/README.md
@@ -0,0 +1,7 @@
+# Vendored copy of gitaly-proto
+
+Vendored from https://gitlab.com/gitlab-org/gitaly-proto at f5d7f3cf76555acb5a14ae80eb7db4ad9ff2c553.
+
+Migration in progress, see
+https://gitlab.com/gitlab-org/gitaly/issues/1761. Do not edit files in
+this directory, your changes will be ignored and overwritten.
diff --git a/proto/README.orig.md b/proto/README.orig.md
new file mode 100644
index 000000000..67dae178b
--- /dev/null
+++ b/proto/README.orig.md
@@ -0,0 +1,319 @@
+# Protobuf specifications and client libraries for Gitaly
+
+Gitaly is part of GitLab. It is a [server
+application](https://gitlab.com/gitlab-org/gitaly) that uses its own
+gRPC protocol to communicate with its clients. This repository
+contains the protocol definition and automatically generated wrapper
+code for Go and Ruby.
+
+The .proto files define the remote procedure calls for interacting
+with Gitaly. We keep auto-generated client libraries for Ruby and Go
+in their respective subdirectories. The list of RPCs can be
+[found here](https://gitlab-org.gitlab.io/gitaly-proto/).
+
+Run `make` from the root of the repository to regenerate the client
+libraries after updating .proto files.
+
+See
+[developers.google.com](https://developers.google.com/protocol-buffers/docs/proto3)
+for documentation of the 'proto3' Protocol buffer specification
+language.
+
+## Issues
+
+We have disabled the issue tracker of the gitaly-proto project. Please use the
+[Gitaly issue tracker](https://gitlab.com/gitlab-org/gitaly/issues).
+
+## gRPC/Protobuf concepts
+
+The core Protobuf concepts we use are rpc, service and message. We use
+these to define the Gitaly **protocol**.
+
+- **rpc** a function that can be called from the client and that gets
+ executed on the server. Belongs to a service. Can have one of four
+ request/response signatures: message/message (example: get metadata for
+ commit xxx), message/stream (example: get contents of blob xxx),
+ stream/message (example: create new blob with contents xxx),
+ stream/stream (example: git SSH session).
+- **service** a logical group of RPC's.
+- **message** like a JSON object except it has pre-defined types.
+- **stream** an unbounded sequence of messages. In the Ruby clients
+ this looks like an Enumerator.
+
+gRPC provides an implementation framework based on these Protobuf concepts.
+
+- A gRPC **server** implements one or more services behind a network
+ listener. Example: the Gitaly server application.
+- The gRPC toolchain automatically generates **client libraries** that
+ handle serialization and connection management. Example: the Go
+ client package and Ruby gem in this repository.
+- gRPC **clients** use the client libraries to make remote procedure
+ calls. These clients must decide what network address to reach their
+ gRPC servers on and handle connection reuse: it is possible to
+ spread different gRPC services over multiple connections to the same
+ gRPC server.
+- Officially a gRPC connection is called a **channel**. In the Go gRPC
+ library these channels are called **client connections** because
+ 'channel' is already a concept in Go itself. In Ruby a gRPC channel
+ is an instance of GRPC::Core::Channel. We use the word 'connection'
+ in this document. The underlying transport of gRPC, HTTP/2, allows
+ multiple remote procedure calls to happen at the same time on a
+ single connection to a gRPC server. In principle, a multi-threaded
+ gRPC client needs only one connection to a gRPC server.
+
+## Design decisions
+
+1. In Gitaly's case there is one server application
+ https://gitlab.com/gitlab-org/gitaly which implements all services
+ in the protocol.
+1. In default GitLab installations each Gitaly client interacts with
+ exactly 1 Gitaly server, on the same host, via a Unix domain socket.
+ In a larger installation each Gitaly client will interact with many
+ different Gitaly servers (one per GitLab storage shard) via TCP
+ connections.
+1. Gitaly uses
+ [grpc.Errorf](https://godoc.org/google.golang.org/grpc#Errorf) to
+ return meaningful
+ [errors](https://godoc.org/google.golang.org/grpc/codes#Code) to its
+ clients.
+1. Each RPC `FooBar` has its own `FooBarRequest` and `FooBarResponse`
+ message types. Try to keep the structure of these messages as flat as
+ possible. Only add abstractions when they have a practical benefit.
+1. We never make backwards incompatible changes to an RPC that is
+ already implemented on either the client side or server side.
+ Instead we just create a new RPC call and start a deprecation
+ procedure (see below) for the old one.
+1. It is encouraged to put comments (starting with `//`) in .proto files.
+ Please put comments on their own lines. This will cause them to be
+ treated as documentation by the protoc compiler.
+1. When choosing an RPC name don't use the service name as context.
+ Good: `service CommitService { rpc CommitExists }`. Bad:
+ `service CommitService { rpc Exists }`.
+
+### RPC naming conventions
+
+Gitaly-Proto has RPCs that are resource based, for example when querying for a
+commit. Another class of RPCs are operations, where the result might be empty
+or one of the RPC error codes but the fact that the operation took place is
+of importance.
+
+For all RPCs, start the name with a verb, followed by an entity, and if required
+followed by a further specification. For example:
+- GetCommit
+- RepackRepositoryIncremental
+- CreateRepositoryFromBundle
+
+For resource RPCs the verbs in use are limited to: Get, List, Create, Update,
+Delete, or Is. Where both Get and List as verbs denote these operations have no side
+effects. These verbs differ in terms of the expected number of results the query
+yields. Get queries are limited to one result, and are expected to return one
+result to the client. List queries have zero or more results, and generally will
+create a gRPC stream for their results. When the `Is` verb is used, this RPC
+is expected to return a boolean, or an error. For example: `IsRepositoryEmpty`.
+
+
+When an operation based RPC is defined, the verb should map to the first verb in
+the Git command it represents. Example; FetchRemote.
+
+Note that the current interface defined in this repository does not yet abide
+fully to these conventions. Newly defined RPCs should, though, so eventually
+gitaly-proto converges to a common standard.
+
+### Common field names and types
+
+As a general principle, remember that Git does not enforce encodings on
+most data inside repositories, so we can rarely assume data to be a
+Protobuf "string" (which implies UTF-8).
+
+1. `bytes revision`: for fields that accept any of branch names / tag
+ names / commit ID's. Uses `bytes` to be encoding agnostic.
+2. `string commit_id`: for fields that accept a commit ID.
+3. `bytes ref`: for fields that accept a refname.
+4. `bytes path`: for paths inside Git repositories, i.e., inside Git
+ `tree` objects.
+5. `string relative_path`: for paths on disk on a Gitaly server,
+ created by "us" (GitLab the application) instead of the user, we
+ want to use UTF-8, or better, ASCII.
+
+### Stream patterns
+
+These are some patterns we already use, or want to use going forward.
+
+#### Stream response of many small items
+
+```
+rpc FooBar(FooBarRequest) returns (stream FooBarResponse);
+
+message FooBarResponse {
+ message Item {
+ // ...
+ }
+ repeated Item items = 1;
+}
+```
+
+A typical example of an "Item" would be a commit. To avoid the penalty
+of network IO for each Item we return, we batch them together. You can
+think of this as a kind of buffered IO at the level of the Item
+messages. In Go, to ease the bookkeeping you can use
+[gitlab.com/gitlab-org/gitaly/internal/helper/chunker](https://godoc.org/gitlab.com/gitlab-org/gitaly/internal/helper/chunker).
+
+#### Single large item split over multiple messages
+
+```
+rpc FooBar(FooBarRequest) returns (stream FooBarResponse);
+
+message FooBarResponse {
+ message Header {
+ // ...
+ }
+
+ oneof payload {
+ Header header = 1;
+ bytes data = 2;
+ }
+}
+```
+
+A typical example of a large item would be the contents of a Git blob.
+The header might contain the blob OID and the blob size. Only the first
+message in the response stream has `header` set, all others have `data`
+but no `header`.
+
+In the particular case where you're sending back raw binary data from
+Go, you can use
+[gitlab.com/gitlab-org/gitaly/streamio](https://godoc.org/gitlab.com/gitlab-org/gitaly/streamio)
+to turn your gRPC response stream into an `io.Writer`.
+
+> Note that a number of existing RPC's do not use this pattern exactly;
+> they don't use `oneof`. In practice this creates ambiguity (does the
+> first message contain non-empty `data`?) and encourages complex
+> optimization in the server implementation (trying to squeeze data into
+> the first response message). Using `oneof` avoids this ambiguity.
+
+#### Many large items split over multiple messages
+
+```
+rpc FooBar(FooBarRequest) returns (stream FooBarResponse);
+
+message FooBarResponse {
+ message Header {
+ // ...
+ }
+
+ oneof payload {
+ Header header = 1;
+ bytes data = 2;
+ }
+}
+```
+
+This looks the same as the "single large item" case above, except
+whenever a new large item begins, we send a new message with a non-empty
+`header` field.
+
+#### Footers
+
+If the RPC requires it we can also send a footer using `oneof`. But by
+default, we prefer headers.
+
+### RPC Annotations
+
+In preparation for Gitaly HA, we are now requiring all RPC's to be annotated
+with an appropriate designation. All methods must contain one of the following lines:
+
+- `option (op_type).op = ACCESSOR;`
+ - Designates an RPC as being read-only (i.e. side effect free)
+- `option (op_type).op = MUTATOR;`
+ - Designates that an RPC modifies the repository
+
+Failing to designate an RPC correctly will result in a CI error. For example:
+
+`--gitaly_out: server.proto: Method ServerInfo missing op_type option`
+
+Additionally, all mutator RPC's require additional annotations to clearly
+indicate what is being modified:
+
+- When an RPC modifies a server-wide resource, the scope should specify `SERVER`.
+- When an RPC modifies a specific repository, the scope should specify `REPOSITORY`.
+ - Additionally, every RPC with `REPOSITORY` scope, should also specify the target repository.
+
+The target repository represents the location or address of the repository
+being modified by the operation. This is needed by Praefect (Gitaly HA) in
+order to properly schedule replications to keep repository replicas up to date.
+
+The target repository annotation specifies where the target repository can be
+found in the message. The annotation looks similar to an IP address, but
+variable in length (e.g. "1", "1.1", "1.1.1"). Each dot delimited field
+represents the field number of how to traverse the protobuf request message to
+find the target repository. The target repository **must** be of protobuf
+message type `gitaly.Repository`.
+
+See our examples of [valid](go/internal/linter/testdata/valid.proto) and
+[invalid](go/internal/linter/testdata/invalid.proto) proto annotations.
+
+### Go Package
+
+If adding new protobuf files, make sure to correctly set the `go_package` option
+near the top of the file:
+
+`option go_package = "gitlab.com/gitlab-org/gitaly-proto/go/gitalypb";`
+
+This allows other protobuf files to locate and import the Go generated stubs. If
+you forget to add a `go_package` option, you may receive an error similar to:
+
+`blob.proto is missing the go_package option`
+
+## Contributing
+
+The CI at https://gitlab.com/gitlab-org/gitaly-proto regenerates the
+client libraries to guard against the mistake of updating the .proto
+files but not the client libraries. This check uses `git diff` to look
+for changes. Some of the code in the Go client libraries is sensitive
+to implementation details of the Go standard library (specifically,
+the output of gzip). **Use the same Go version as .gitlab-ci.yml (Go
+1.11)** when generating new client libraries for a merge request.
+
+[DCO + License](CONTRIBUTING.md)
+
+### Build process
+
+After you change or add a .proto file you need to re-generate the Go
+and Ruby libraries before committing your change.
+
+```
+# Re-generate Go and Ruby libraries
+make generate
+```
+
+## How to deprecate an RPC call
+
+See [DEPRECATION.md](DEPRECATION.md).
+
+## Release
+
+This will tag and release the gitaly-proto library, including
+pushing the gem to rubygems.org
+
+```
+make release version=X.Y.Z
+```
+
+
+## How to manually push the gem
+
+If the release script fails the gem may not be pushed. This is how you can do that after the fact:
+
+```shell
+# Use a sub-shell to limit scope of 'set -e'
+(
+ set -e
+
+ # Replace X.Y.Z with the version you are pushing
+ GEM_VERSION=X.Y.Z
+
+ git checkout v$GEM_VERSION
+ gem build gitaly.gemspec
+ gem push gitaly-$GEM_VERSION.gem
+)
+```
diff --git a/proto/RELEASE.md b/proto/RELEASE.md
new file mode 100644
index 000000000..db92f239e
--- /dev/null
+++ b/proto/RELEASE.md
@@ -0,0 +1,30 @@
+# Gitaly-proto release process
+
+## Requirements
+
+- Ruby
+- Bundler
+- Go 1.10
+
+## 1. Install dependencies
+
+If you have done a release before this may not be needed.
+
+```
+_support/install-protoc
+```
+
+## 2. Release
+
+This will:
+
+- do a final consistency check
+- create a version bump commit
+- create a tag
+- build the gem
+- ask for confirmation
+- push the gem and the tag out to the world
+
+```
+_support/release 1.2.3
+```
diff --git a/proto/VERSION b/proto/VERSION
new file mode 100644
index 000000000..39fc130ef
--- /dev/null
+++ b/proto/VERSION
@@ -0,0 +1 @@
+1.36.0
diff --git a/proto/blob.proto b/proto/blob.proto
new file mode 100644
index 000000000..f786a26c5
--- /dev/null
+++ b/proto/blob.proto
@@ -0,0 +1,118 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "shared.proto";
+
+service BlobService {
+ // GetBlob returns the contents of a blob object referenced by its object
+ // ID. We use a stream to return a chunked arbitrarily large binary
+ // response
+ rpc GetBlob(GetBlobRequest) returns (stream GetBlobResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc GetBlobs(GetBlobsRequest) returns (stream GetBlobsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc GetLFSPointers(GetLFSPointersRequest) returns (stream GetLFSPointersResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc GetNewLFSPointers(GetNewLFSPointersRequest) returns (stream GetNewLFSPointersResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc GetAllLFSPointers(GetAllLFSPointersRequest) returns (stream GetAllLFSPointersResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+}
+
+message GetBlobRequest {
+
+ Repository repository = 1;
+ // Object ID (SHA1) of the blob we want to get
+ string oid = 2;
+ // Maximum number of bytes we want to receive. Use '-1' to get the full blob no matter how big.
+ int64 limit = 3;
+}
+
+message GetBlobResponse {
+ // Blob size; present only in first response message
+ int64 size = 1;
+ // Chunk of blob data
+ bytes data = 2;
+ // Object ID of the actual blob returned. Empty if no blob was found.
+ string oid = 3;
+}
+
+message GetBlobsRequest {
+
+ message RevisionPath {
+ string revision = 1;
+ bytes path = 2;
+ }
+
+ Repository repository = 1;
+ // Revision/Path pairs of the blobs we want to get.
+ repeated RevisionPath revision_paths = 2;
+ // Maximum number of bytes we want to receive. Use '-1' to get the full blobs no matter how big.
+ int64 limit = 3;
+}
+
+message GetBlobsResponse {
+ // Blob size; present only on the first message per blob
+ int64 size = 1;
+ // Chunk of blob data, could span over multiple messages.
+ bytes data = 2;
+ // Object ID of the current blob. Only present on the first message per blob. Empty if no blob was found.
+ string oid = 3;
+ bool is_submodule = 4;
+ int32 mode = 5;
+ string revision = 6;
+ bytes path = 7;
+}
+
+message LFSPointer {
+ int64 size = 1;
+ bytes data = 2;
+ string oid = 3;
+}
+
+message NewBlobObject {
+ int64 size = 1;
+ string oid = 2;
+ bytes path = 3;
+}
+
+message GetLFSPointersRequest {
+
+ Repository repository = 1;
+ repeated string blob_ids = 2;
+}
+
+message GetLFSPointersResponse {
+ repeated LFSPointer lfs_pointers = 1;
+}
+
+message GetNewLFSPointersRequest {
+
+ Repository repository = 1;
+ bytes revision = 2;
+ int32 limit = 3;
+ // Note: When `not_in_all` is true, `not_in_refs` is ignored
+ bool not_in_all = 4;
+ repeated bytes not_in_refs = 5;
+}
+
+message GetNewLFSPointersResponse {
+ repeated LFSPointer lfs_pointers = 1;
+}
+
+message GetAllLFSPointersRequest {
+ Repository repository = 1;
+ bytes revision = 2;
+}
+
+message GetAllLFSPointersResponse {
+ repeated LFSPointer lfs_pointers = 1;
+}
diff --git a/proto/cleanup.proto b/proto/cleanup.proto
new file mode 100644
index 000000000..276a17e02
--- /dev/null
+++ b/proto/cleanup.proto
@@ -0,0 +1,69 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "shared.proto";
+
+service CleanupService {
+ // Deprecated in favour of ApplyBfgObjectMapStream
+ rpc ApplyBfgObjectMap(stream ApplyBfgObjectMapRequest) returns (ApplyBfgObjectMapResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+
+ rpc ApplyBfgObjectMapStream(stream ApplyBfgObjectMapStreamRequest) returns (stream ApplyBfgObjectMapStreamResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+
+ rpc CloseSession(CloseSessionRequest) returns (CloseSessionResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ scope_level: SERVER,
+ };
+ }
+}
+
+message ApplyBfgObjectMapRequest {
+ Repository repository = 1;
+ // A raw object-map file as generated by BFG: https://rtyley.github.io/bfg-repo-cleaner
+ // Each line in the file has two object SHAs, space-separated - the original
+ // SHA of the object, and the SHA after BFG has rewritten the object.
+ bytes object_map = 2;
+}
+
+message ApplyBfgObjectMapResponse {}
+
+message ApplyBfgObjectMapStreamRequest {
+ // Only available on the first message
+ Repository repository = 1;
+
+ // A raw object-map file as generated by BFG: https://rtyley.github.io/bfg-repo-cleaner
+ // Each line in the file has two object SHAs, space-separated - the original
+ // SHA of the object, and the SHA after BFG has rewritten the object.
+ bytes object_map = 2;
+}
+
+message ApplyBfgObjectMapStreamResponse {
+ // We send back each parsed entry in the request's object map so the client
+ // can take action
+ message Entry {
+ ObjectType type = 1;
+ string old_oid = 2;
+ string new_oid = 3;
+ }
+
+ repeated Entry entries = 1;
+}
+
+message CloseSessionRequest{
+ string session_id = 1;
+}
+
+message CloseSessionResponse{}
diff --git a/proto/commit.proto b/proto/commit.proto
new file mode 100644
index 000000000..d338809fb
--- /dev/null
+++ b/proto/commit.proto
@@ -0,0 +1,386 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "shared.proto";
+import "google/protobuf/timestamp.proto";
+
+service CommitService {
+ rpc CommitIsAncestor(CommitIsAncestorRequest) returns (CommitIsAncestorResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc TreeEntry(TreeEntryRequest) returns (stream TreeEntryResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc CommitsBetween(CommitsBetweenRequest) returns (stream CommitsBetweenResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc CountCommits(CountCommitsRequest) returns (CountCommitsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc CountDivergingCommits(CountDivergingCommitsRequest) returns (CountDivergingCommitsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc GetTreeEntries(GetTreeEntriesRequest) returns (stream GetTreeEntriesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc ListFiles(ListFilesRequest) returns (stream ListFilesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc FindCommit(FindCommitRequest) returns (FindCommitResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc CommitStats(CommitStatsRequest) returns (CommitStatsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ // Use a stream to paginate the result set
+ rpc FindAllCommits(FindAllCommitsRequest) returns (stream FindAllCommitsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc FindCommits(FindCommitsRequest) returns (stream FindCommitsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc CommitLanguages(CommitLanguagesRequest) returns (CommitLanguagesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc RawBlame(RawBlameRequest) returns (stream RawBlameResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc LastCommitForPath(LastCommitForPathRequest) returns (LastCommitForPathResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc ListLastCommitsForTree(ListLastCommitsForTreeRequest) returns (stream ListLastCommitsForTreeResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc CommitsByMessage(CommitsByMessageRequest) returns (stream CommitsByMessageResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc ListCommitsByOid(ListCommitsByOidRequest) returns (stream ListCommitsByOidResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc FilterShasWithSignatures(stream FilterShasWithSignaturesRequest) returns (stream FilterShasWithSignaturesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+
+ // ExtractCommitSignature returns a stream because the signed text may be
+ // arbitrarily large and signature verification is impossible without the
+ // full text.
+ rpc ExtractCommitSignature(ExtractCommitSignatureRequest) returns (stream ExtractCommitSignatureResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc GetCommitSignatures(GetCommitSignaturesRequest) returns (stream GetCommitSignaturesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+
+ rpc GetCommitMessages(GetCommitMessagesRequest) returns (stream GetCommitMessagesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+}
+
+message CommitStatsRequest {
+ Repository repository = 1;
+ bytes revision = 2;
+}
+
+message CommitStatsResponse {
+ // OID is the commit. Empty means not found
+ string oid = 1;
+ int32 additions = 2;
+ int32 deletions = 3;
+}
+
+message CommitIsAncestorRequest {
+ Repository repository = 1;
+ string ancestor_id = 2;
+ string child_id = 3;
+}
+
+message CommitIsAncestorResponse {
+ bool value = 1;
+}
+
+message TreeEntryRequest {
+ Repository repository = 1;
+ // commit ID or refname
+ bytes revision = 2;
+ // entry path relative to repository root
+ bytes path = 3;
+ int64 limit = 4;
+}
+
+message TreeEntryResponse {
+ // TODO: Replace this enum with ObjectType in shared.proto
+ enum ObjectType {
+ COMMIT = 0;
+ BLOB = 1;
+ TREE = 2;
+ TAG = 3;
+ }
+ ObjectType type = 1;
+ // SHA1 object ID
+ string oid = 2;
+ int64 size = 3;
+ // file mode
+ int32 mode = 4;
+ // raw object contents
+ bytes data = 5;
+}
+
+message CommitsBetweenRequest {
+ Repository repository = 1;
+ bytes from = 2;
+ bytes to = 3;
+}
+
+message CommitsBetweenResponse {
+ repeated GitCommit commits = 1;
+}
+
+message CountCommitsRequest {
+ Repository repository = 1;
+ bytes revision = 2;
+ google.protobuf.Timestamp after = 3;
+ google.protobuf.Timestamp before = 4;
+ bytes path = 5;
+ int32 max_count = 6;
+ // all and revision are mutually exclusive
+ bool all = 7;
+}
+
+message CountCommitsResponse {
+ int32 count = 1;
+}
+
+message CountDivergingCommitsRequest {
+ Repository repository = 1;
+ bytes from = 2;
+ bytes to = 3;
+ reserved 4;
+ reserved 5;
+ reserved 6;
+ int32 max_count = 7;
+}
+
+message CountDivergingCommitsResponse {
+ int32 left_count = 1;
+ int32 right_count = 2;
+}
+
+message TreeEntry {
+ // TODO: Replace this enum with ObjectType in shared.proto
+ enum EntryType {
+ BLOB = 0;
+ TREE = 1;
+ COMMIT = 3;
+ }
+ // OID of the object this tree entry points to
+ string oid = 1;
+ // OID of the tree attached to commit_oid
+ string root_oid = 2;
+ // Path relative to repository root
+ bytes path = 3;
+ EntryType type = 4;
+ // File mode e.g. 0644
+ int32 mode = 5;
+ // The commit object via which this entry was retrieved
+ string commit_oid = 6;
+ // Relative path of the first subdir that doesn't have only one directory descendant
+ bytes flat_path = 7;
+}
+
+message GetTreeEntriesRequest {
+ Repository repository = 1;
+ bytes revision = 2;
+ bytes path = 3;
+ bool recursive = 4;
+}
+
+message GetTreeEntriesResponse {
+ repeated TreeEntry entries = 1;
+}
+
+message ListFilesRequest {
+ Repository repository = 1;
+ bytes revision = 2;
+}
+
+// A single 'page' of the paginated response
+message ListFilesResponse {
+ // Remember to force encoding utf-8 on the client side
+ repeated bytes paths = 1;
+}
+
+message FindCommitRequest {
+ Repository repository = 1;
+ bytes revision = 2;
+}
+
+message FindCommitResponse {
+ // commit is nil when the commit was not found
+ GitCommit commit = 1;
+}
+
+message ListCommitsByOidRequest {
+ Repository repository = 1;
+ repeated string oid = 2;
+}
+
+message ListCommitsByOidResponse {
+ repeated GitCommit commits = 1;
+}
+
+message FindAllCommitsRequest {
+ Repository repository = 1;
+ // When nil, return all commits reachable by any branch in the repo
+ bytes revision = 2;
+ int32 max_count = 3;
+ int32 skip = 4;
+ enum Order {
+ NONE = 0;
+ TOPO = 1;
+ DATE = 2;
+ }
+ Order order = 5;
+}
+
+// A single 'page' of the result set
+message FindAllCommitsResponse {
+ repeated GitCommit commits = 1;
+}
+
+message FindCommitsRequest {
+ Repository repository = 1;
+ bytes revision = 2;
+ int32 limit = 3;
+ int32 offset = 4;
+ repeated bytes paths = 5;
+ bool follow = 6;
+ bool skip_merges = 7;
+ bool disable_walk = 8;
+ google.protobuf.Timestamp after = 9;
+ google.protobuf.Timestamp before = 10;
+ // all and revision are mutually exclusive
+ bool all = 11;
+}
+
+// A single 'page' of the result set
+message FindCommitsResponse {
+ repeated GitCommit commits = 1;
+}
+
+message CommitLanguagesRequest {
+ Repository repository = 1;
+ bytes revision = 2;
+}
+
+message CommitLanguagesResponse {
+ message Language {
+ string name = 1;
+ float share = 2;
+ string color = 3;
+ }
+ repeated Language languages = 1;
+}
+
+message RawBlameRequest {
+ Repository repository = 1;
+ bytes revision = 2;
+ bytes path = 3;
+}
+
+message RawBlameResponse {
+ bytes data = 1;
+}
+
+message LastCommitForPathRequest {
+ Repository repository = 1;
+ bytes revision = 2;
+ bytes path = 3;
+}
+
+message LastCommitForPathResponse {
+ // commit is nil when the commit was not found
+ GitCommit commit = 1;
+}
+
+message ListLastCommitsForTreeRequest {
+ Repository repository = 1;
+ string revision = 2;
+ bytes path = 3;
+
+ // limit == -1 will get the last commit for all paths
+ int32 limit = 4;
+ int32 offset = 5;
+}
+
+message ListLastCommitsForTreeResponse {
+ message CommitForTree {
+ reserved 1;
+
+ GitCommit commit = 2;
+ reserved 3;
+ bytes path_bytes = 4;
+ }
+ repeated CommitForTree commits = 1;
+}
+
+message CommitsByMessageRequest {
+ Repository repository = 1;
+ bytes revision = 2;
+ int32 offset = 3;
+ int32 limit = 4;
+ bytes path = 5;
+ string query = 6;
+}
+
+// One 'page' of the paginated response of CommitsByMessage
+message CommitsByMessageResponse {
+ repeated GitCommit commits = 1;
+}
+
+message FilterShasWithSignaturesRequest {
+ Repository repository = 1;
+ repeated bytes shas = 2;
+}
+
+message FilterShasWithSignaturesResponse {
+ repeated bytes shas = 1;
+}
+
+message ExtractCommitSignatureRequest {
+ Repository repository = 1;
+ string commit_id = 2;
+}
+
+// Either of the 'signature' and 'signed_text' fields may be present. It
+// is up to the caller to stitch them together.
+message ExtractCommitSignatureResponse {
+ bytes signature = 1;
+ bytes signed_text = 2;
+}
+
+message GetCommitSignaturesRequest {
+ Repository repository = 1;
+ repeated string commit_ids = 2;
+}
+
+message GetCommitSignaturesResponse {
+ // Only present for a new commit signature data.
+ string commit_id = 1;
+ // See ExtractCommitSignatureResponse above for how these fields should be handled.
+ bytes signature = 2;
+ bytes signed_text = 3;
+}
+
+message GetCommitMessagesRequest {
+ Repository repository = 1;
+ repeated string commit_ids = 2;
+}
+
+message GetCommitMessagesResponse {
+ // Only present for a new commit message
+ string commit_id = 1;
+ bytes message = 2;
+}
diff --git a/proto/conflicts.proto b/proto/conflicts.proto
new file mode 100644
index 000000000..f0c20d02f
--- /dev/null
+++ b/proto/conflicts.proto
@@ -0,0 +1,66 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "shared.proto";
+
+service ConflictsService {
+ rpc ListConflictFiles(ListConflictFilesRequest) returns (stream ListConflictFilesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc ResolveConflicts(stream ResolveConflictsRequest) returns (ResolveConflictsResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1.1"
+ };
+ }
+}
+
+message ListConflictFilesRequest {
+ Repository repository = 1;
+ string our_commit_oid = 2;
+ string their_commit_oid = 3;
+}
+
+message ConflictFileHeader {
+ Repository repository = 1;
+ string commit_oid = 2;
+ bytes their_path = 3;
+ bytes our_path = 4;
+ int32 our_mode = 5;
+}
+
+message ConflictFile {
+ oneof conflict_file_payload {
+ ConflictFileHeader header = 1;
+ bytes content = 2;
+ }
+}
+
+message ListConflictFilesResponse {
+ repeated ConflictFile files = 1;
+}
+
+message ResolveConflictsRequestHeader {
+ Repository repository = 1;
+ string our_commit_oid = 2;
+ Repository target_repository = 3;
+ string their_commit_oid = 4;
+ bytes source_branch = 5;
+ bytes target_branch = 6;
+ bytes commit_message = 7;
+ User user = 8;
+}
+
+message ResolveConflictsRequest {
+ oneof resolve_conflicts_request_payload {
+ ResolveConflictsRequestHeader header = 1;
+ bytes files_json = 2;
+ }
+}
+
+message ResolveConflictsResponse {
+ string resolution_error = 1;
+}
diff --git a/proto/diff.proto b/proto/diff.proto
new file mode 100644
index 000000000..7548ad88f
--- /dev/null
+++ b/proto/diff.proto
@@ -0,0 +1,145 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "shared.proto";
+
+service DiffService {
+ // Returns stream of CommitDiffResponse with patches chunked over messages
+ rpc CommitDiff(CommitDiffRequest) returns (stream CommitDiffResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ // Return a stream so we can divide the response in chunks of deltas
+ rpc CommitDelta(CommitDeltaRequest) returns (stream CommitDeltaResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc CommitPatch(CommitPatchRequest) returns (stream CommitPatchResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc RawDiff(RawDiffRequest) returns (stream RawDiffResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc RawPatch(RawPatchRequest) returns (stream RawPatchResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc DiffStats(DiffStatsRequest) returns (stream DiffStatsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+}
+
+message CommitDiffRequest {
+ Repository repository = 1;
+ string left_commit_id = 2;
+ string right_commit_id = 3;
+ bool ignore_whitespace_change = 4;
+ repeated bytes paths = 5;
+ bool collapse_diffs = 6;
+ bool enforce_limits = 7;
+
+ // These limits are only enforced when enforce_limits == true.
+ int32 max_files = 8;
+ int32 max_lines = 9;
+ int32 max_bytes = 10;
+ // Limitation of a single diff patch,
+ // patches surpassing this limit are pruned by default.
+ // If this is 0 you will get back empty patches.
+ int32 max_patch_bytes = 14;
+
+ // These limits are only enforced if collapse_diffs == true.
+ int32 safe_max_files = 11;
+ int32 safe_max_lines = 12;
+ int32 safe_max_bytes = 13;
+}
+
+// A CommitDiffResponse corresponds to a single changed file in a commit.
+message CommitDiffResponse {
+ reserved 8;
+
+ bytes from_path = 1;
+ bytes to_path = 2;
+ // Blob ID as returned via `git diff --full-index`
+ string from_id = 3;
+ string to_id = 4;
+ int32 old_mode = 5;
+ int32 new_mode = 6;
+ bool binary = 7;
+ bytes raw_patch_data = 9;
+ bool end_of_patch = 10;
+ // Indicates the diff file at which we overflow according to the limitations sent,
+ // in which case only this attribute will be set.
+ bool overflow_marker = 11;
+ // Indicates the patch surpassed a "safe" limit and was therefore pruned, but
+ // the client may still request the full patch on a separate request.
+ bool collapsed = 12;
+ // Indicates the patch was pruned since it surpassed a hard limit, and can
+ // therefore not be expanded.
+ bool too_large = 13;
+}
+
+message CommitDeltaRequest {
+ Repository repository = 1;
+ string left_commit_id = 2;
+ string right_commit_id = 3;
+ repeated bytes paths = 4;
+}
+
+message CommitDelta {
+ bytes from_path = 1;
+ bytes to_path = 2;
+ // Blob ID as returned via `git diff --full-index`
+ string from_id = 3;
+ string to_id = 4;
+ int32 old_mode = 5;
+ int32 new_mode = 6;
+}
+
+message CommitDeltaResponse {
+ repeated CommitDelta deltas = 1;
+}
+
+message CommitPatchRequest {
+ Repository repository = 1;
+ bytes revision = 2;
+}
+
+message CommitPatchResponse {
+ bytes data = 1;
+}
+
+message RawDiffRequest {
+ Repository repository = 1;
+ string left_commit_id = 2;
+ string right_commit_id = 3;
+}
+
+message RawDiffResponse {
+ bytes data = 1;
+}
+
+message RawPatchRequest {
+ Repository repository = 1;
+ string left_commit_id = 2;
+ string right_commit_id = 3;
+}
+
+message RawPatchResponse {
+ bytes data = 1;
+}
+
+message DiffStatsRequest {
+ Repository repository = 1;
+ string left_commit_id = 2;
+ string right_commit_id = 3;
+}
+
+message DiffStats {
+ bytes path = 1;
+ int32 additions = 2;
+ int32 deletions = 3;
+}
+
+message DiffStatsResponse {
+ repeated DiffStats stats = 1;
+}
diff --git a/proto/namespace.proto b/proto/namespace.proto
new file mode 100644
index 000000000..1a8aa2294
--- /dev/null
+++ b/proto/namespace.proto
@@ -0,0 +1,60 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "shared.proto";
+
+service NamespaceService {
+ rpc AddNamespace(AddNamespaceRequest) returns (AddNamespaceResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ scope_level: SERVER,
+ };
+ }
+ rpc RemoveNamespace(RemoveNamespaceRequest) returns (RemoveNamespaceResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ scope_level: SERVER,
+ };
+ }
+ rpc RenameNamespace(RenameNamespaceRequest) returns (RenameNamespaceResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ scope_level: SERVER,
+ };
+ }
+ rpc NamespaceExists(NamespaceExistsRequest) returns (NamespaceExistsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+}
+
+message AddNamespaceRequest {
+ string storage_name = 1;
+ string name = 2;
+}
+
+message RemoveNamespaceRequest {
+ string storage_name = 1;
+ string name = 2;
+}
+
+message RenameNamespaceRequest {
+ string storage_name = 1;
+ string from = 2;
+ string to = 3;
+}
+
+message NamespaceExistsRequest {
+ string storage_name = 1;
+ string name = 2;
+}
+
+message NamespaceExistsResponse {
+ bool exists = 1;
+}
+
+message AddNamespaceResponse {}
+message RemoveNamespaceResponse {}
+message RenameNamespaceResponse {}
diff --git a/proto/objectpool.proto b/proto/objectpool.proto
new file mode 100644
index 000000000..c0aedd55b
--- /dev/null
+++ b/proto/objectpool.proto
@@ -0,0 +1,102 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "shared.proto";
+
+service ObjectPoolService {
+ rpc CreateObjectPool(CreateObjectPoolRequest) returns (CreateObjectPoolResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1.1"
+ };
+ }
+ rpc DeleteObjectPool(DeleteObjectPoolRequest) returns (DeleteObjectPoolResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1.1"
+ };
+ }
+
+ // Repositories are assumed to be stored on the same disk
+ rpc LinkRepositoryToObjectPool(LinkRepositoryToObjectPoolRequest) returns (LinkRepositoryToObjectPoolResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1.1"
+ };
+ }
+ rpc UnlinkRepositoryFromObjectPool(UnlinkRepositoryFromObjectPoolRequest) returns (UnlinkRepositoryFromObjectPoolResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+
+ rpc ReduplicateRepository(ReduplicateRepositoryRequest) returns (ReduplicateRepositoryResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc DisconnectGitAlternates(DisconnectGitAlternatesRequest) returns (DisconnectGitAlternatesResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc FetchIntoObjectPool(FetchIntoObjectPoolRequest) returns (FetchIntoObjectPoolResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+}
+
+// Creates an object pool from the repository. The client is responsible for
+// joining this pool later with this repository.
+message CreateObjectPoolRequest {
+ ObjectPool object_pool = 1;
+ Repository origin = 2;
+}
+message CreateObjectPoolResponse {}
+
+// Removes the directory from disk, caller is responsible for leaving the object
+// pool before calling this RPC
+message DeleteObjectPoolRequest {
+ ObjectPool object_pool = 1;
+}
+message DeleteObjectPoolResponse {}
+
+message LinkRepositoryToObjectPoolRequest {
+ ObjectPool object_pool = 1;
+ Repository repository = 2;
+}
+message LinkRepositoryToObjectPoolResponse {}
+
+// This RPC doesn't require the ObjectPool as it will remove the alternates file
+// from the pool participant. The caller is responsible no data loss occurs.
+message UnlinkRepositoryFromObjectPoolRequest {
+ Repository repository = 1;
+ ObjectPool object_pool = 2;
+}
+message UnlinkRepositoryFromObjectPoolResponse {}
+
+message ReduplicateRepositoryRequest {
+ Repository repository = 1;
+}
+message ReduplicateRepositoryResponse {}
+
+message DisconnectGitAlternatesRequest {
+ Repository repository = 1;
+}
+
+message DisconnectGitAlternatesResponse {}
+
+message FetchIntoObjectPoolRequest {
+ Repository origin = 1;
+ ObjectPool object_pool = 2;
+ bool repack = 3;
+}
+message FetchIntoObjectPoolResponse {}
diff --git a/proto/operations.proto b/proto/operations.proto
new file mode 100644
index 000000000..681b0b075
--- /dev/null
+++ b/proto/operations.proto
@@ -0,0 +1,432 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "shared.proto";
+
+service OperationService {
+ rpc UserCreateBranch(UserCreateBranchRequest) returns (UserCreateBranchResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc UserUpdateBranch(UserUpdateBranchRequest) returns (UserUpdateBranchResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc UserDeleteBranch(UserDeleteBranchRequest) returns (UserDeleteBranchResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc UserCreateTag(UserCreateTagRequest) returns (UserCreateTagResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc UserDeleteTag(UserDeleteTagRequest) returns (UserDeleteTagResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc UserMergeToRef(UserMergeToRefRequest) returns (UserMergeToRefResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc UserMergeBranch(stream UserMergeBranchRequest) returns (stream UserMergeBranchResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc UserFFBranch(UserFFBranchRequest) returns (UserFFBranchResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc UserCherryPick(UserCherryPickRequest) returns (UserCherryPickResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc UserCommitFiles(stream UserCommitFilesRequest) returns (UserCommitFilesResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1.1"
+ };
+ }
+ rpc UserRebase(UserRebaseRequest) returns (UserRebaseResponse) {
+ option deprecated = true;
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc UserRebaseConfirmable(stream UserRebaseConfirmableRequest) returns (stream UserRebaseConfirmableResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1.1"
+ };
+ }
+ rpc UserRevert(UserRevertRequest) returns (UserRevertResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc UserSquash(UserSquashRequest) returns (UserSquashResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc UserApplyPatch(stream UserApplyPatchRequest) returns (UserApplyPatchResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1.1"
+ };
+ }
+ rpc UserUpdateSubmodule(UserUpdateSubmoduleRequest) returns (UserUpdateSubmoduleResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+}
+
+message UserCreateBranchRequest {
+ Repository repository = 1;
+ bytes branch_name = 2;
+ User user = 3;
+ bytes start_point = 4;
+}
+
+message UserCreateBranchResponse {
+ Branch branch = 1;
+ // Error returned by the pre-receive hook. If no error was thrown,
+ // it's the empty string ("")
+ string pre_receive_error = 2;
+}
+
+message UserUpdateBranchRequest {
+ Repository repository = 1;
+ bytes branch_name = 2;
+ User user = 3;
+ bytes newrev = 4;
+ bytes oldrev = 5;
+}
+
+message UserUpdateBranchResponse {
+ string pre_receive_error = 1;
+}
+
+message UserDeleteBranchRequest {
+ Repository repository = 1;
+ bytes branch_name = 2;
+ User user = 3;
+}
+
+message UserDeleteBranchResponse {
+ string pre_receive_error = 1;
+}
+
+message UserDeleteTagRequest {
+ Repository repository = 1;
+ bytes tag_name = 2;
+ User user = 3;
+}
+
+message UserDeleteTagResponse {
+ string pre_receive_error = 1;
+}
+
+message UserCreateTagRequest {
+ Repository repository = 1;
+ bytes tag_name = 2;
+ User user = 3;
+ bytes target_revision = 4;
+ bytes message = 5;
+}
+
+message UserCreateTagResponse {
+ Tag tag = 1;
+ bool exists = 2;
+ string pre_receive_error = 3;
+}
+
+message UserMergeBranchRequest {
+ // First message
+ Repository repository = 1;
+ User user = 2;
+ string commit_id = 3;
+ bytes branch = 4;
+ bytes message = 5;
+
+ // Second message
+ // Tell the server to apply the merge to the branch
+ bool apply = 6;
+}
+
+message UserMergeBranchResponse {
+ // First message
+ // The merge commit the branch will be updated to. The caller can still abort the merge.
+ string commit_id = 1;
+
+ reserved 2;
+ // Second message
+ // If set, the merge has been applied to the branch.
+ OperationBranchUpdate branch_update = 3;
+ string pre_receive_error = 4;
+}
+
+message UserMergeToRefRequest {
+ // UserMergeRef creates a merge commit and updates target_ref to point to that
+ // new commit. The first parent of the merge commit (the main line) is taken
+ // from first_parent_ref. The second parent is specified by its commit ID in source_sha.
+ // If target_ref already exists it will be overwritten.
+ Repository repository = 1;
+ User user = 2;
+ string source_sha = 3;
+ // branch is deprecated in favor of `first_parent_ref`.
+ bytes branch = 4;
+ bytes target_ref = 5;
+ bytes message = 6;
+ bytes first_parent_ref = 7;
+}
+
+message UserMergeToRefResponse {
+ string commit_id = 1;
+ string pre_receive_error = 2;
+}
+
+message OperationBranchUpdate {
+ // If this string is non-empty the branch has been updated.
+ string commit_id = 1;
+ // Used for cache invalidation in GitLab
+ bool repo_created = 2;
+ // Used for cache invalidation in GitLab
+ bool branch_created = 3;
+}
+
+message UserFFBranchRequest {
+ Repository repository = 1;
+ User user = 2;
+ string commit_id = 3;
+ bytes branch = 4;
+}
+
+message UserFFBranchResponse {
+ OperationBranchUpdate branch_update = 1;
+ string pre_receive_error = 2;
+}
+
+message UserCherryPickRequest {
+ Repository repository = 1;
+ User user = 2;
+ GitCommit commit = 3;
+ bytes branch_name = 4;
+ bytes message = 5;
+ bytes start_branch_name = 6;
+ Repository start_repository = 7;
+}
+
+message UserCherryPickResponse {
+ OperationBranchUpdate branch_update = 1;
+ string create_tree_error = 2;
+ string commit_error = 3;
+ string pre_receive_error = 4;
+}
+
+message UserRevertRequest {
+ Repository repository = 1;
+ User user = 2;
+ GitCommit commit = 3;
+ bytes branch_name = 4;
+ bytes message = 5;
+ bytes start_branch_name = 6;
+ Repository start_repository = 7;
+}
+
+message UserRevertResponse {
+ OperationBranchUpdate branch_update = 1;
+ string create_tree_error = 2;
+ string commit_error = 3;
+ string pre_receive_error = 4;
+}
+
+message UserCommitFilesActionHeader {
+ enum ActionType {
+ CREATE = 0;
+ CREATE_DIR = 1;
+ UPDATE = 2;
+ MOVE = 3;
+ DELETE = 4;
+ CHMOD = 5;
+ }
+ ActionType action = 1;
+ bytes file_path = 2;
+ bytes previous_path = 3;
+ bool base64_content = 4;
+ bool execute_filemode = 5;
+ // Move actions that change the file path, but not its content, should set
+ // infer_content to true instead of populating the content field. Ignored for
+ // other action types.
+ bool infer_content = 6;
+}
+
+message UserCommitFilesAction {
+ oneof user_commit_files_action_payload {
+ UserCommitFilesActionHeader header = 1;
+ bytes content = 2;
+ }
+}
+
+message UserCommitFilesRequestHeader {
+ Repository repository = 1;
+ User user = 2;
+ bytes branch_name = 3;
+ bytes commit_message = 4;
+ bytes commit_author_name = 5;
+ bytes commit_author_email = 6;
+ bytes start_branch_name = 7;
+ Repository start_repository = 8;
+ bool force = 9;
+ string start_sha = 10;
+}
+
+message UserCommitFilesRequest {
+ oneof user_commit_files_request_payload {
+ // For each request stream there should be first a request with a header and
+ // then n requests with actions
+ UserCommitFilesRequestHeader header = 1;
+ UserCommitFilesAction action = 2;
+ }
+}
+
+message UserCommitFilesResponse {
+ OperationBranchUpdate branch_update = 1;
+ string index_error = 2;
+ string pre_receive_error = 3;
+}
+
+// DEPRECATED: https://gitlab.com/gitlab-org/gitaly/issues/1628
+message UserRebaseRequest {
+ option deprecated = true;
+
+ Repository repository = 1;
+ User user = 2;
+ string rebase_id = 3;
+ bytes branch = 4;
+ string branch_sha = 5;
+ Repository remote_repository = 6;
+ bytes remote_branch = 7;
+}
+
+// DEPRECATED: https://gitlab.com/gitlab-org/gitaly/issues/1628
+message UserRebaseResponse {
+ option deprecated = true;
+
+ string rebase_sha = 1;
+ string pre_receive_error = 2;
+ string git_error = 3;
+}
+
+message UserRebaseConfirmableRequest {
+ message Header {
+ Repository repository = 1;
+ User user = 2;
+ string rebase_id = 3;
+ bytes branch = 4;
+ string branch_sha = 5;
+ Repository remote_repository = 6;
+ bytes remote_branch = 7;
+ }
+
+ oneof user_rebase_confirmable_request_payload {
+ // For each request stream there must be first a request with a header
+ // containing details about the rebase to perform.
+ Header header = 1;
+ // A second request must be made to confirm that the rebase should
+ // be applied to the branch.
+ bool apply = 2;
+ }
+}
+
+message UserRebaseConfirmableResponse {
+ oneof user_rebase_confirmable_response_payload {
+ // The first response will contain the rebase commit the branch will
+ // be updated to. The caller can still abort the rebase.
+ string rebase_sha = 1;
+ // The second response confirms that the rebase has been applied to
+ // the branch.
+ bool rebase_applied = 2;
+ }
+ string pre_receive_error = 3;
+ string git_error = 4;
+}
+
+message UserSquashRequest {
+ Repository repository = 1;
+ User user = 2;
+ string squash_id = 3;
+ bytes branch = 4;
+ string start_sha = 5;
+ string end_sha = 6;
+ User author = 7;
+ bytes commit_message = 8;
+}
+
+message UserSquashResponse {
+ string squash_sha = 1;
+ // DEPRECATED: https://gitlab.com/gitlab-org/gitaly/proto/merge_requests/161
+ reserved 2;
+ reserved "pre_receive_error";
+ string git_error = 3;
+}
+
+message UserApplyPatchRequest {
+ message Header {
+ Repository repository = 1;
+ User user = 2;
+ bytes target_branch = 3;
+ }
+
+ oneof user_apply_patch_request_payload {
+ Header header = 1;
+ bytes patches = 2;
+ }
+}
+
+message UserApplyPatchResponse {
+ OperationBranchUpdate branch_update = 1;
+}
+
+message UserUpdateSubmoduleRequest {
+ Repository repository = 1;
+ User user = 2;
+ string commit_sha = 3;
+ bytes branch = 4;
+ bytes submodule = 5;
+ bytes commit_message = 6;
+}
+
+message UserUpdateSubmoduleResponse {
+ OperationBranchUpdate branch_update = 1;
+ string pre_receive_error = 2;
+ // DEPRECATED: https://gitlab.com/gitlab-org/gitaly/proto/merge_requests/237
+ reserved 3;
+ reserved "create_tree_error";
+ string commit_error = 4;
+}
diff --git a/proto/ref.proto b/proto/ref.proto
new file mode 100644
index 000000000..a749d0965
--- /dev/null
+++ b/proto/ref.proto
@@ -0,0 +1,317 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "blob.proto";
+import "shared.proto";
+import "google/protobuf/timestamp.proto";
+
+service RefService {
+ rpc FindDefaultBranchName(FindDefaultBranchNameRequest) returns (FindDefaultBranchNameResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc FindAllBranchNames(FindAllBranchNamesRequest) returns (stream FindAllBranchNamesResponse) {
+ option (op_type).op = ACCESSOR;
+
+ }
+ rpc FindAllTagNames(FindAllTagNamesRequest) returns (stream FindAllTagNamesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ // Find a Ref matching the given constraints. Response may be empty.
+ rpc FindRefName(FindRefNameRequest) returns (FindRefNameResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ // Return a stream so we can divide the response in chunks of branches
+ rpc FindLocalBranches(FindLocalBranchesRequest) returns (stream FindLocalBranchesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc FindAllBranches(FindAllBranchesRequest) returns (stream FindAllBranchesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc FindAllTags(FindAllTagsRequest) returns (stream FindAllTagsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc FindAllRemoteBranches(FindAllRemoteBranchesRequest) returns (stream FindAllRemoteBranchesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc RefExists(RefExistsRequest) returns (RefExistsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc CreateBranch(CreateBranchRequest) returns (CreateBranchResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc DeleteBranch(DeleteBranchRequest) returns (DeleteBranchResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc FindBranch(FindBranchRequest) returns (FindBranchResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc DeleteRefs(DeleteRefsRequest) returns (DeleteRefsResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+
+ rpc ListBranchNamesContainingCommit(ListBranchNamesContainingCommitRequest) returns (stream ListBranchNamesContainingCommitResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc ListTagNamesContainingCommit(ListTagNamesContainingCommitRequest) returns (stream ListTagNamesContainingCommitResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+
+ rpc GetTagMessages(GetTagMessagesRequest) returns (stream GetTagMessagesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+
+ // Returns commits that are only reachable from the ref passed
+ rpc ListNewCommits(ListNewCommitsRequest) returns (stream ListNewCommitsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+
+ rpc ListNewBlobs(ListNewBlobsRequest) returns (stream ListNewBlobsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc PackRefs(PackRefsRequest) returns (PackRefsResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+}
+
+message ListNewBlobsRequest {
+ Repository repository = 1;
+ string commit_id = 2;
+ // Limit the number of revs to be returned fro mgit-rev-list
+ // If the limit is set to zero, all items will be returned
+ uint32 limit = 3;
+}
+
+message ListNewBlobsResponse {
+ repeated NewBlobObject new_blob_objects = 1;
+}
+
+message FindDefaultBranchNameRequest {
+ Repository repository = 1;
+}
+
+message FindDefaultBranchNameResponse {
+ bytes name = 1;
+}
+
+message FindAllBranchNamesRequest {
+ Repository repository = 1;
+}
+
+message FindAllBranchNamesResponse {
+ repeated bytes names = 1;
+}
+
+message FindAllTagNamesRequest {
+ Repository repository = 1;
+}
+
+message FindAllTagNamesResponse {
+ repeated bytes names = 1;
+}
+
+message FindRefNameRequest {
+ Repository repository = 1;
+ // Require that the resulting ref contains this commit as an ancestor
+ string commit_id = 2;
+ // Example prefix: "refs/heads/". Type bytes because that is the type of ref names.
+ bytes prefix = 3;
+}
+
+message FindRefNameResponse {
+ // Example name: "refs/heads/master". Cannot assume UTF8, so the type is bytes.
+ bytes name = 1;
+}
+
+message FindLocalBranchesRequest {
+ Repository repository = 1;
+ enum SortBy {
+ NAME = 0;
+ UPDATED_ASC = 1;
+ UPDATED_DESC = 2;
+ }
+ SortBy sort_by = 2;
+}
+
+message FindLocalBranchesResponse {
+ repeated FindLocalBranchResponse branches = 1;
+}
+
+message FindLocalBranchResponse {
+ bytes name = 1;
+ string commit_id = 2;
+ bytes commit_subject = 3;
+ FindLocalBranchCommitAuthor commit_author = 4;
+ FindLocalBranchCommitAuthor commit_committer = 5;
+}
+
+message FindLocalBranchCommitAuthor {
+ bytes name = 1;
+ bytes email = 2;
+ google.protobuf.Timestamp date = 3;
+}
+
+message FindAllBranchesRequest {
+ Repository repository = 1;
+ // Only return branches that are merged into root ref
+ bool merged_only = 2;
+ // If merged_only is true, this is a list of branches from which we
+ // return those merged into the root ref
+ repeated bytes merged_branches = 3;
+}
+
+message FindAllBranchesResponse {
+ message Branch {
+ bytes name = 1;
+ GitCommit target = 2;
+ }
+ repeated Branch branches = 1;
+}
+
+message FindAllTagsRequest {
+ Repository repository = 1;
+}
+
+message FindAllTagsResponse {
+ repeated Tag tags = 1;
+}
+
+message RefExistsRequest {
+ Repository repository = 1;
+ // Any ref, e.g. 'refs/heads/master' or 'refs/tags/v1.0.1'. Must start with 'refs/'.
+ bytes ref = 2;
+}
+
+message RefExistsResponse {
+ bool value = 1;
+}
+
+message CreateBranchRequest {
+ Repository repository = 1;
+ bytes name = 2;
+ bytes start_point = 3;
+}
+
+message CreateBranchResponse {
+ enum Status {
+ OK = 0;
+ ERR_EXISTS = 1;
+ ERR_INVALID = 2;
+ ERR_INVALID_START_POINT = 3;
+ }
+ Status status = 1;
+ Branch branch = 2;
+}
+
+message DeleteBranchRequest {
+ Repository repository = 1;
+ bytes name = 2;
+}
+
+// Not clear if we need to do status signaling; we can add fields later.
+message DeleteBranchResponse {}
+
+message FindBranchRequest {
+ Repository repository = 1;
+ // Name can be 'master' but also 'refs/heads/master'
+ bytes name = 2;
+}
+
+message FindBranchResponse {
+ Branch branch = 1;
+}
+
+message DeleteRefsRequest{
+ Repository repository = 1;
+ // The following two fields are mutually exclusive
+ repeated bytes except_with_prefix = 2;
+ repeated bytes refs = 3;
+}
+
+message DeleteRefsResponse {
+ string git_error = 1;
+}
+
+message ListBranchNamesContainingCommitRequest {
+ Repository repository = 1;
+ string commit_id = 2;
+
+ // Limit the number of tag names to be returned
+ // If the limit is set to zero, all items will be returned
+ uint32 limit = 3;
+}
+
+message ListBranchNamesContainingCommitResponse {
+ reserved 1;
+ repeated bytes branch_names = 2;
+}
+
+message ListTagNamesContainingCommitRequest {
+ Repository repository = 1;
+ string commit_id = 2;
+
+ // Limit the number of tag names to be returned
+ // If the limit is set to zero, all items will be returned
+ uint32 limit = 3;
+}
+
+message ListTagNamesContainingCommitResponse {
+ reserved 1;
+ repeated bytes tag_names = 2;
+}
+
+message GetTagMessagesRequest {
+ reserved 2;
+ reserved "tag_names";
+
+ Repository repository = 1;
+ repeated string tag_ids = 3;
+}
+
+message GetTagMessagesResponse {
+ reserved 1;
+ reserved "tag_name";
+
+ bytes message = 2;
+ // Only present for a new tag message
+ string tag_id = 3;
+}
+
+message ListNewCommitsRequest {
+ Repository repository = 1;
+ string commit_id = 2;
+}
+
+message ListNewCommitsResponse {
+ repeated GitCommit commits = 1;
+}
+
+message FindAllRemoteBranchesRequest {
+ Repository repository = 1;
+ string remote_name = 2;
+}
+
+message FindAllRemoteBranchesResponse {
+ repeated Branch branches = 1;
+}
+
+message PackRefsRequest {
+ Repository repository = 1;
+ bool all_refs = 2;
+}
+
+message PackRefsResponse{}
diff --git a/proto/remote.proto b/proto/remote.proto
new file mode 100644
index 000000000..9b76b149c
--- /dev/null
+++ b/proto/remote.proto
@@ -0,0 +1,118 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "shared.proto";
+
+service RemoteService {
+ rpc AddRemote(AddRemoteRequest) returns (AddRemoteResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc FetchInternalRemote(FetchInternalRemoteRequest) returns (FetchInternalRemoteResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc RemoveRemote(RemoveRemoteRequest) returns (RemoveRemoteResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+
+ rpc UpdateRemoteMirror(stream UpdateRemoteMirrorRequest) returns (UpdateRemoteMirrorResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc FindRemoteRepository(FindRemoteRepositoryRequest) returns (FindRemoteRepositoryResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc FindRemoteRootRef(FindRemoteRootRefRequest) returns (FindRemoteRootRefResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc ListRemotes(ListRemotesRequest) returns (stream ListRemotesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+}
+
+message AddRemoteRequest {
+ Repository repository = 1;
+ string name = 2;
+ string url = 3;
+ // DEPRECATED: https://gitlab.com/gitlab-org/gitaly/proto/merge_requests/137
+ reserved 4;
+ reserved "mirror_refmap";
+ // If any, the remote is configured as a mirror with those mappings
+ repeated string mirror_refmaps = 5;
+}
+
+message AddRemoteResponse {}
+
+message RemoveRemoteRequest {
+ Repository repository = 1;
+ string name = 2;
+}
+
+message RemoveRemoteResponse {
+ bool result = 1;
+}
+
+message FetchInternalRemoteRequest {
+ Repository repository = 1;
+ Repository remote_repository = 2;
+}
+
+message FetchInternalRemoteResponse {
+ bool result = 1;
+}
+
+message UpdateRemoteMirrorRequest {
+ Repository repository = 1;
+ string ref_name = 2;
+ repeated bytes only_branches_matching = 3;
+ string ssh_key = 4;
+ string known_hosts = 5;
+}
+
+message UpdateRemoteMirrorResponse {}
+
+message FindRemoteRepositoryRequest {
+ string remote = 1;
+}
+
+// This migth throw a GRPC Unavailable code, to signal the request failure
+// is transient.
+message FindRemoteRepositoryResponse {
+ bool exists = 1;
+}
+
+message FindRemoteRootRefRequest {
+ Repository repository = 1;
+ string remote = 2;
+}
+
+message FindRemoteRootRefResponse {
+ string ref = 1;
+}
+
+message ListRemotesRequest {
+ Repository repository = 1;
+}
+
+message ListRemotesResponse {
+ message Remote {
+ string name = 1;
+ string fetch_url = 2;
+ string push_url = 3;
+ }
+
+ repeated Remote remotes = 1;
+}
diff --git a/proto/repository-service.proto b/proto/repository-service.proto
new file mode 100644
index 000000000..017564338
--- /dev/null
+++ b/proto/repository-service.proto
@@ -0,0 +1,594 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "shared.proto";
+
+service RepositoryService {
+ rpc RepositoryExists(RepositoryExistsRequest) returns (RepositoryExistsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc RepackIncremental(RepackIncrementalRequest) returns (RepackIncrementalResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc RepackFull(RepackFullRequest) returns (RepackFullResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc GarbageCollect(GarbageCollectRequest) returns (GarbageCollectResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc RepositorySize(RepositorySizeRequest) returns (RepositorySizeResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc ApplyGitattributes(ApplyGitattributesRequest) returns (ApplyGitattributesResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc FetchRemote(FetchRemoteRequest) returns (FetchRemoteResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc CreateRepository(CreateRepositoryRequest) returns (CreateRepositoryResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc GetArchive(GetArchiveRequest) returns (stream GetArchiveResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc HasLocalBranches(HasLocalBranchesRequest) returns (HasLocalBranchesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc FetchSourceBranch(FetchSourceBranchRequest) returns (FetchSourceBranchResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc Fsck(FsckRequest) returns (FsckResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc WriteRef(WriteRefRequest) returns (WriteRefResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc FindMergeBase(FindMergeBaseRequest) returns (FindMergeBaseResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc CreateFork(CreateForkRequest) returns (CreateForkResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc IsRebaseInProgress(IsRebaseInProgressRequest) returns (IsRebaseInProgressResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc IsSquashInProgress(IsSquashInProgressRequest) returns (IsSquashInProgressResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+
+ rpc CreateRepositoryFromURL(CreateRepositoryFromURLRequest) returns (CreateRepositoryFromURLResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc CreateBundle(CreateBundleRequest) returns (stream CreateBundleResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ scope_level: SERVER,
+ };
+ }
+ rpc CreateRepositoryFromBundle(stream CreateRepositoryFromBundleRequest) returns (CreateRepositoryFromBundleResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc WriteConfig(WriteConfigRequest) returns (WriteConfigResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc SetConfig(SetConfigRequest) returns (SetConfigResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc DeleteConfig(DeleteConfigRequest) returns (DeleteConfigResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc FindLicense(FindLicenseRequest) returns (FindLicenseResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc GetInfoAttributes(GetInfoAttributesRequest) returns (stream GetInfoAttributesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc CalculateChecksum(CalculateChecksumRequest) returns (CalculateChecksumResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc Cleanup(CleanupRequest) returns (CleanupResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc GetSnapshot(GetSnapshotRequest) returns (stream GetSnapshotResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc CreateRepositoryFromSnapshot(CreateRepositoryFromSnapshotRequest) returns (CreateRepositoryFromSnapshotResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc GetRawChanges(GetRawChangesRequest) returns (stream GetRawChangesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc SearchFilesByContent(SearchFilesByContentRequest) returns (stream SearchFilesByContentResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc SearchFilesByName(SearchFilesByNameRequest) returns (stream SearchFilesByNameResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc RestoreCustomHooks(stream RestoreCustomHooksRequest) returns (RestoreCustomHooksResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc BackupCustomHooks(BackupCustomHooksRequest) returns (stream BackupCustomHooksResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc PreFetch(PreFetchRequest) returns (PreFetchResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc FetchHTTPRemote(FetchHTTPRemoteRequest) returns (FetchHTTPRemoteResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc GetObjectDirectorySize(GetObjectDirectorySizeRequest) returns (GetObjectDirectorySizeResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc CloneFromPool(CloneFromPoolRequest) returns (CloneFromPoolResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc CloneFromPoolInternal(CloneFromPoolInternalRequest) returns (CloneFromPoolInternalResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+}
+
+message RepositoryExistsRequest {
+ Repository repository = 1;
+}
+
+message RepositoryExistsResponse {
+ bool exists = 1;
+}
+
+message RepackIncrementalRequest {
+ Repository repository = 1;
+}
+
+message RepackIncrementalResponse {}
+
+message RepackFullRequest {
+ Repository repository = 1;
+ bool create_bitmap = 2;
+}
+
+message RepackFullResponse {}
+
+message GarbageCollectRequest {
+ Repository repository = 1;
+ bool create_bitmap = 2;
+}
+
+message GarbageCollectResponse {}
+
+message CleanupRequest {
+ Repository repository = 1;
+}
+
+message CleanupResponse {}
+
+message RepositorySizeRequest {
+ Repository repository = 1;
+}
+
+message RepositorySizeResponse {
+ // Repository size in kilobytes
+ int64 size = 1;
+}
+
+message ApplyGitattributesRequest {
+ Repository repository = 1;
+ bytes revision = 2;
+}
+
+message ApplyGitattributesResponse {}
+
+message FetchRemoteRequest {
+ Repository repository = 1;
+ string remote = 2;
+ bool force = 3;
+ bool no_tags = 4;
+ int32 timeout = 5;
+ string ssh_key = 6;
+ string known_hosts = 7;
+ reserved 8;
+ bool no_prune = 9;
+ Remote remote_params = 10;
+}
+
+message FetchRemoteResponse {}
+
+message CreateRepositoryRequest {
+ Repository repository = 1;
+}
+
+message CreateRepositoryResponse {}
+
+message GetArchiveRequest {
+ enum Format {
+ ZIP = 0;
+ TAR = 1;
+ TAR_GZ = 2;
+ TAR_BZ2 = 3;
+ }
+
+ Repository repository = 1;
+ string commit_id = 2;
+ string prefix = 3;
+ Format format = 4;
+ bytes path = 5;
+}
+
+message GetArchiveResponse {
+ bytes data = 1;
+}
+
+message HasLocalBranchesRequest {
+ Repository repository = 1;
+}
+
+message HasLocalBranchesResponse {
+ bool value = 1;
+}
+
+message FetchSourceBranchRequest {
+ Repository repository = 1;
+ Repository source_repository = 2;
+ bytes source_branch = 3;
+ bytes target_ref = 4;
+}
+
+message FetchSourceBranchResponse {
+ bool result = 1;
+}
+
+message FsckRequest {
+ Repository repository = 1;
+}
+
+message FsckResponse {
+ bytes error = 1;
+}
+
+message WriteRefRequest {
+ Repository repository = 1;
+ bytes ref = 2;
+ bytes revision = 3;
+ bytes old_revision = 4;
+ bool force = 5;
+ // This used to be a boolean indicating whether or not to shell out or use
+ // the rugged implementation
+ reserved 6;
+}
+
+message WriteRefResponse {
+ // This used to contain an error message. Since we're shelling out
+ // all exceptions are wrapped in GRPC errors.
+ reserved 1;
+}
+
+message FindMergeBaseRequest {
+ Repository repository = 1;
+ // We use a repeated field because rugged supports finding a base
+ // for more than 2 revisions, so if we needed that in the future we don't
+ // need to change the protocol.
+ repeated bytes revisions = 2;
+}
+
+message FindMergeBaseResponse {
+ string base = 1;
+}
+
+message CreateForkRequest {
+ Repository repository = 1;
+ Repository source_repository = 2;
+}
+
+message CreateForkResponse {}
+
+message IsRebaseInProgressRequest {
+ Repository repository = 1;
+ string rebase_id = 2;
+}
+
+message IsRebaseInProgressResponse {
+ bool in_progress = 1;
+}
+
+message IsSquashInProgressRequest {
+ Repository repository = 1;
+ string squash_id = 2;
+}
+
+message IsSquashInProgressResponse {
+ bool in_progress = 1;
+}
+
+message CreateRepositoryFromURLRequest {
+ Repository repository = 1;
+ string url = 2;
+}
+
+message CreateRepositoryFromURLResponse {}
+
+message CreateBundleRequest {
+ Repository repository = 1;
+}
+
+message CreateBundleResponse {
+ bytes data = 1;
+}
+
+message WriteConfigRequest {
+ Repository repository = 1;
+ string full_path = 2;
+}
+
+message WriteConfigResponse {
+ bytes error = 1;
+}
+
+message SetConfigRequest {
+ Repository repository = 1;
+ message Entry {
+ string key = 1;
+ oneof value {
+ string value_str = 2;
+ int32 value_int32 = 3;
+ bool value_bool = 4;
+ }
+ }
+ repeated Entry entries = 2;
+}
+
+message SetConfigResponse {}
+
+message DeleteConfigRequest {
+ Repository repository = 1;
+ repeated string keys = 2;
+}
+
+message DeleteConfigResponse {}
+
+message RestoreCustomHooksRequest {
+ Repository repository = 1;
+ bytes data = 2;
+}
+
+message RestoreCustomHooksResponse {}
+
+message BackupCustomHooksRequest {
+ Repository repository = 1;
+}
+
+message BackupCustomHooksResponse {
+ bytes data = 1;
+}
+
+message CreateRepositoryFromBundleRequest {
+ // Only available on the first message
+ Repository repository = 1;
+ bytes data = 2;
+}
+
+message CreateRepositoryFromBundleResponse {}
+
+message FindLicenseRequest {
+ Repository repository = 1;
+}
+
+message FindLicenseResponse {
+ string license_short_name = 1;
+}
+
+message GetInfoAttributesRequest {
+ Repository repository = 1;
+}
+
+message GetInfoAttributesResponse {
+ bytes attributes = 1;
+}
+
+message CalculateChecksumRequest {
+ Repository repository = 1;
+}
+
+message CalculateChecksumResponse {
+ string checksum = 1;
+}
+
+message GetSnapshotRequest {
+ Repository repository = 1;
+}
+
+message GetSnapshotResponse {
+ bytes data = 1;
+}
+
+message CreateRepositoryFromSnapshotRequest {
+ Repository repository = 1;
+ string http_url = 2;
+ string http_auth = 3;
+}
+
+message CreateRepositoryFromSnapshotResponse {}
+
+message GetRawChangesRequest {
+ Repository repository = 1;
+ string from_revision = 2;
+ string to_revision = 3;
+}
+
+message GetRawChangesResponse {
+ message RawChange {
+ enum Operation {
+ UNKNOWN = 0;
+ ADDED = 1;
+ COPIED = 2;
+ DELETED = 3;
+ MODIFIED = 4;
+ RENAMED = 5;
+ TYPE_CHANGED = 6;
+ }
+
+ string blob_id = 1;
+ int64 size= 2;
+
+ // use fields 9 and 10 in place of 3 and 4 (respectively)
+ string new_path = 3 [deprecated=true];
+ string old_path = 4 [deprecated=true];
+
+ Operation operation= 5;
+ string raw_operation = 6;
+ int32 old_mode = 7;
+ int32 new_mode = 8;
+
+ // the following fields, 9 and 10, will eventually replace 3 and 4
+ bytes new_path_bytes = 9;
+ bytes old_path_bytes = 10;
+ }
+
+ repeated RawChange raw_changes = 1;
+}
+
+message SearchFilesByNameRequest {
+ Repository repository = 1;
+ string query = 2;
+ bytes ref = 3;
+}
+
+message SearchFilesByNameResponse {
+ repeated bytes files = 1;
+}
+
+message SearchFilesByContentRequest {
+ Repository repository = 1;
+ string query = 2;
+ bytes ref = 3;
+ bool chunked_response = 4;
+}
+
+message SearchFilesByContentResponse {
+ repeated bytes matches = 1;
+ bytes match_data = 2;
+ bool end_of_match = 3;
+}
+
+message PreFetchRequest {
+ Repository source_repository = 1;
+ Repository target_repository = 2;
+ ObjectPool object_pool = 3;
+}
+
+message PreFetchResponse {}
+
+message Remote {
+ string url = 1;
+ string name = 2;
+ string http_authorization_header = 3;
+ repeated string mirror_refmaps = 4;
+}
+
+message FetchHTTPRemoteRequest {
+ Repository repository = 1;
+ Remote remote = 2;
+ int32 timeout = 3;
+}
+
+message FetchHTTPRemoteResponse {}
+
+message GetObjectDirectorySizeRequest {
+ Repository repository = 1;
+}
+
+message GetObjectDirectorySizeResponse {
+ // Object directory size in kilobytes
+ int64 size = 1;
+}
+
+message CloneFromPoolRequest {
+ Repository repository = 1;
+ ObjectPool pool = 2;
+ Remote remote = 3;
+}
+
+message CloneFromPoolResponse {
+}
+
+message CloneFromPoolInternalRequest {
+ Repository repository = 1;
+ ObjectPool pool = 2;
+ Repository source_repository = 3;
+}
+
+message CloneFromPoolInternalResponse {
+}
diff --git a/proto/server.proto b/proto/server.proto
new file mode 100644
index 000000000..f56dded92
--- /dev/null
+++ b/proto/server.proto
@@ -0,0 +1,29 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "shared.proto";
+
+service ServerService {
+ rpc ServerInfo(ServerInfoRequest) returns (ServerInfoResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+}
+
+message ServerInfoRequest {}
+
+message ServerInfoResponse {
+ message StorageStatus {
+ string storage_name = 1;
+ bool readable = 2;
+ bool writeable = 3;
+ string fs_type = 4;
+ string filesystem_id = 5;
+ }
+
+ string server_version = 1;
+ string git_version = 2;
+ repeated StorageStatus storage_statuses = 3;
+}
diff --git a/proto/shared.proto b/proto/shared.proto
new file mode 100644
index 000000000..c252349c9
--- /dev/null
+++ b/proto/shared.proto
@@ -0,0 +1,135 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "google/protobuf/timestamp.proto";
+import "google/protobuf/descriptor.proto";
+
+message OperationMsg {
+ enum Operation {
+ UNKNOWN = 0;
+ MUTATOR = 1;
+ ACCESSOR = 2;
+ }
+
+ Operation op = 1;
+
+ enum Scope {
+ REPOSITORY = 0;
+ SERVER = 1;
+ }
+
+ // Scope level indicates how a mutating RPC affects Gitaly:
+ // - REPOSITORY: mutation is scoped to only a single repo
+ // - SERVER: mutation affects the entire server and potentially all repos
+ Scope scope_level = 2;
+
+ // If this operation modifies a repository, this field will
+ // specify the location of the Repository field within the
+ // request message. The field is specified in an OID style
+ // formatted string.
+ //
+ // For example, if the target repository is at the top level
+ // of a message at field 1, then the string will be "1"
+ //
+ // If the target repository is nested deeper in the message,
+ // then it will be necessary to specify a nested OID string.
+ //
+ // For example, the following OID refers to a target repo field
+ // nested in a one-of field, both at field one: "1.1"
+ string target_repository_field = 3;
+}
+
+enum ObjectType {
+ UNKNOWN = 0;
+ COMMIT = 1;
+ BLOB = 2;
+ TREE = 3;
+ TAG = 4;
+}
+
+extend google.protobuf.MethodOptions {
+ // Random high number..
+ OperationMsg op_type = 82303;
+}
+
+message Repository {
+ // DEPRECATED: https://gitlab.com/gitlab-org/gitaly/issues/151
+ reserved 1;
+ reserved "path";
+
+ string storage_name = 2;
+ string relative_path = 3;
+ // Sets the GIT_OBJECT_DIRECTORY envvar on git commands to the value of this field.
+ // It influences the object storage directory the SHA1 directories are created underneath.
+ string git_object_directory = 4;
+ // Sets the GIT_ALTERNATE_OBJECT_DIRECTORIES envvar on git commands to the values of this field.
+ // It influences the list of Git object directories which can be used to search for Git objects.
+ repeated string git_alternate_object_directories = 5;
+ // Used in callbacks to GitLab so that it knows what repository the event is
+ // associated with. May be left empty on RPC's that do not perform callbacks.
+ // During project creation, `gl_repository` may not be known.
+ string gl_repository = 6;
+ reserved 7;
+ // The human-readable GitLab project path (e.g. gitlab-org/gitlab-ce).
+ // When hashed storage is use, this associates a project path with its
+ // path on disk. The name can change over time (e.g. when a project is
+ // renamed). This is primarily used for logging/debugging at the
+ // moment.
+ string gl_project_path = 8;
+}
+
+// Corresponds to Gitlab::Git::Commit
+message GitCommit {
+ string id = 1;
+ bytes subject = 2;
+ bytes body = 3;
+ CommitAuthor author = 4;
+ CommitAuthor committer = 5;
+ repeated string parent_ids = 6;
+ // If body exceeds a certain threshold, it will be nullified,
+ // but its size will be set in body_size so we can know if
+ // a commit had a body in the first place.
+ int64 body_size = 7;
+}
+
+message CommitAuthor {
+ bytes name = 1;
+ bytes email = 2;
+ google.protobuf.Timestamp date = 3;
+}
+
+message ExitStatus {
+ int32 value = 1;
+}
+
+// Corresponds to Gitlab::Git::Branch
+message Branch {
+ bytes name = 1;
+ GitCommit target_commit = 2;
+}
+
+message Tag {
+ bytes name = 1;
+ string id = 2;
+ GitCommit target_commit = 3;
+ // If message exceeds a certain threshold, it will be nullified,
+ // but its size will be set in message_size so we can know if
+ // a tag had a message in the first place.
+ bytes message = 4;
+ int64 message_size = 5;
+ CommitAuthor tagger = 6;
+}
+
+message User {
+ string gl_id = 1;
+ bytes name = 2;
+ bytes email = 3;
+ string gl_username = 4;
+}
+
+message ObjectPool {
+ Repository repository = 1;
+}
diff --git a/proto/smarthttp.proto b/proto/smarthttp.proto
new file mode 100644
index 000000000..92311b169
--- /dev/null
+++ b/proto/smarthttp.proto
@@ -0,0 +1,91 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "shared.proto";
+
+service SmartHTTPService {
+ // The response body for GET /info/refs?service=git-upload-pack
+ // Will be invoked when the user executes a `git fetch`, meaning the server
+ // will upload the packs to that user. The user doesn't upload new objects.
+ rpc InfoRefsUploadPack(InfoRefsRequest) returns (stream InfoRefsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+
+ // The response body for GET /info/refs?service=git-receive-pack
+ // Will be invoked when the user executes a `git push`, meaning the server
+ // will receive new objects in the pack from the user.
+ rpc InfoRefsReceivePack(InfoRefsRequest) returns (stream InfoRefsResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+
+ // Request and response body for POST /upload-pack
+ rpc PostUploadPack(stream PostUploadPackRequest) returns (stream PostUploadPackResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+
+ // Request and response body for POST /receive-pack
+ rpc PostReceivePack(stream PostReceivePackRequest) returns (stream PostReceivePackResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+}
+
+message InfoRefsRequest {
+ Repository repository = 1;
+ // Parameters to use with git -c (key=value pairs)
+ repeated string git_config_options = 2;
+
+ // Git protocol version
+ string git_protocol = 3;
+}
+
+message InfoRefsResponse {
+ bytes data = 1;
+}
+
+message PostUploadPackRequest {
+ // repository should only be present in the first message of the stream
+ Repository repository = 1;
+ // Raw data to be copied to stdin of 'git upload-pack'
+ bytes data = 2;
+ // Parameters to use with git -c (key=value pairs)
+ repeated string git_config_options = 3;
+
+ // Git protocol version
+ string git_protocol = 4;
+}
+
+message PostUploadPackResponse {
+ // Raw data from stdout of 'git upload-pack'
+ bytes data = 1;
+}
+
+message PostReceivePackRequest {
+ // repository should only be present in the first message of the stream
+ Repository repository = 1;
+ // Raw data to be copied to stdin of 'git receive-pack'
+ bytes data = 2;
+ // gl_id, gl_repository, and gl_username become env variables, used by the Git {pre,post}-receive
+ // hooks. They should only be present in the first message of the stream.
+ string gl_id = 3;
+ string gl_repository = 4;
+ string gl_username = 5;
+ // Git protocol version
+ string git_protocol = 6;
+
+ // Parameters to use with git -c (key=value pairs)
+ repeated string git_config_options = 7;
+}
+
+message PostReceivePackResponse {
+ // Raw data from stdout of 'git receive-pack'
+ bytes data = 1;
+}
diff --git a/proto/ssh.proto b/proto/ssh.proto
new file mode 100644
index 000000000..ee8d15fdb
--- /dev/null
+++ b/proto/ssh.proto
@@ -0,0 +1,99 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "shared.proto";
+
+service SSHService {
+ // To forward 'git upload-pack' to Gitaly for SSH sessions
+ rpc SSHUploadPack(stream SSHUploadPackRequest) returns (stream SSHUploadPackResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+
+ // To forward 'git receive-pack' to Gitaly for SSH sessions
+ rpc SSHReceivePack(stream SSHReceivePackRequest) returns (stream SSHReceivePackResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+
+ // To forward 'git upload-archive' to Gitaly for SSH sessions
+ rpc SSHUploadArchive(stream SSHUploadArchiveRequest) returns (stream SSHUploadArchiveResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+}
+
+message SSHUploadPackRequest {
+ // 'repository' must be present in the first message.
+ Repository repository = 1;
+ // A chunk of raw data to be copied to 'git upload-pack' standard input
+ bytes stdin = 2;
+ // Prevent re-use of field id 3 and/or the "git_config_parameters" name
+ reserved 3;
+ reserved "git_config_parameters";
+ // Parameters to use with git -c (key=value pairs)
+ repeated string git_config_options = 4;
+
+ // Git protocol version
+ string git_protocol = 5;
+}
+
+message SSHUploadPackResponse {
+ // A chunk of raw data from 'git upload-pack' standard output
+ bytes stdout = 1;
+ // A chunk of raw data from 'git upload-pack' standard error
+ bytes stderr = 2;
+ // This field may be nil. This is intentional: only when the remote
+ // command has finished can we return its exit status.
+ ExitStatus exit_status = 3;
+}
+
+message SSHReceivePackRequest {
+ // 'repository' must be present in the first message.
+ Repository repository = 1;
+ // A chunk of raw data to be copied to 'git upload-pack' standard input
+ bytes stdin = 2;
+ // Contents of GL_ID, GL_REPOSITORY, and GL_USERNAME environment variables
+ // for 'git receive-pack'
+ string gl_id = 3;
+ string gl_repository = 4;
+ string gl_username = 5;
+
+ // Git protocol version
+ string git_protocol = 6;
+
+ // Parameters to use with git -c (key=value pairs)
+ repeated string git_config_options = 7;
+}
+
+message SSHReceivePackResponse {
+ // A chunk of raw data from 'git receive-pack' standard output
+ bytes stdout = 1;
+ // A chunk of raw data from 'git receive-pack' standard error
+ bytes stderr = 2;
+ // This field may be nil. This is intentional: only when the remote
+ // command has finished can we return its exit status.
+ ExitStatus exit_status = 3;
+}
+
+message SSHUploadArchiveRequest {
+ // 'repository' must be present in the first message.
+ Repository repository = 1;
+ // A chunk of raw data to be copied to 'git upload-archive' standard input
+ bytes stdin = 2;
+}
+
+message SSHUploadArchiveResponse {
+ // A chunk of raw data from 'git upload-archive' standard output
+ bytes stdout = 1;
+ // A chunk of raw data from 'git upload-archive' standard error
+ bytes stderr = 2;
+ // This value will only be set on the last message
+ ExitStatus exit_status = 3;
+}
diff --git a/proto/storage.proto b/proto/storage.proto
new file mode 100644
index 000000000..903c8137c
--- /dev/null
+++ b/proto/storage.proto
@@ -0,0 +1,37 @@
+syntax = "proto3";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+import "shared.proto";
+
+service StorageService {
+ rpc ListDirectories(ListDirectoriesRequest) returns (stream ListDirectoriesResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ scope_level: SERVER,
+ };
+ }
+ rpc DeleteAllRepositories(DeleteAllRepositoriesRequest) returns (DeleteAllRepositoriesResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ scope_level: SERVER,
+ };
+ }
+}
+
+message ListDirectoriesRequest {
+ string storage_name = 1;
+ uint32 depth = 2;
+}
+
+message ListDirectoriesResponse {
+ repeated string paths = 1;
+}
+
+message DeleteAllRepositoriesRequest {
+ string storage_name = 1;
+}
+
+message DeleteAllRepositoriesResponse {}
diff --git a/proto/wiki.proto b/proto/wiki.proto
new file mode 100644
index 000000000..00f0a9c8f
--- /dev/null
+++ b/proto/wiki.proto
@@ -0,0 +1,204 @@
+syntax = "proto3";
+
+import "shared.proto";
+
+package gitaly;
+
+option go_package = "gitlab.com/gitlab-org/gitaly/proto/go/gitalypb";
+
+service WikiService {
+ rpc WikiGetPageVersions(WikiGetPageVersionsRequest) returns (stream WikiGetPageVersionsResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc WikiWritePage(stream WikiWritePageRequest) returns (WikiWritePageResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc WikiUpdatePage(stream WikiUpdatePageRequest) returns (WikiUpdatePageResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ rpc WikiDeletePage(WikiDeletePageRequest) returns (WikiDeletePageResponse) {
+ option (op_type) = {
+ op: MUTATOR
+ target_repository_field: "1"
+ };
+ }
+ // WikiFindPage returns a stream because the page's raw_data field may be arbitrarily large.
+ rpc WikiFindPage(WikiFindPageRequest) returns (stream WikiFindPageResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc WikiFindFile(WikiFindFileRequest) returns (stream WikiFindFileResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc WikiGetAllPages(WikiGetAllPagesRequest) returns (stream WikiGetAllPagesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc WikiListPages(WikiListPagesRequest) returns (stream WikiListPagesResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+ rpc WikiGetFormattedData(WikiGetFormattedDataRequest) returns (stream WikiGetFormattedDataResponse) {
+ option (op_type).op = ACCESSOR;
+ }
+}
+
+message WikiCommitDetails {
+ bytes name = 1;
+ bytes email = 2;
+ bytes message = 3;
+ int32 user_id = 4;
+ bytes user_name = 5;
+}
+
+message WikiPageVersion {
+ GitCommit commit = 1;
+ string format = 2;
+}
+
+message WikiPage {
+ // These fields are only present in the first message of a WikiPage stream
+ WikiPageVersion version = 1;
+ string format = 2;
+ bytes title = 3;
+ string url_path = 4;
+ bytes path = 5;
+ bytes name = 6;
+ bool historical = 7;
+
+ // This field is present in all messages of a WikiPage stream
+ bytes raw_data = 8;
+}
+
+message WikiGetPageVersionsRequest {
+ Repository repository = 1;
+ bytes page_path = 2;
+
+ int32 page = 3;
+ int32 per_page = 4;
+}
+
+message WikiGetPageVersionsResponse {
+ repeated WikiPageVersion versions = 1;
+}
+
+// This message is sent in a stream because the 'content' field may be large.
+message WikiWritePageRequest {
+ // These following fields are only present in the first message.
+ Repository repository = 1;
+ bytes name = 2;
+ string format = 3;
+ WikiCommitDetails commit_details = 4;
+ // This field is present in all messages.
+ bytes content = 5;
+}
+
+message WikiWritePageResponse {
+ bytes duplicate_error = 1;
+}
+
+message WikiUpdatePageRequest {
+ // There fields are only present in the first message of the stream
+ Repository repository = 1;
+ bytes page_path = 2;
+ bytes title = 3;
+ string format = 4;
+ WikiCommitDetails commit_details = 5;
+
+ // This field is present in all messages
+ bytes content = 6;
+}
+
+message WikiUpdatePageResponse {
+ bytes error = 1;
+}
+
+message WikiDeletePageRequest {
+ Repository repository = 1;
+ bytes page_path = 2;
+ WikiCommitDetails commit_details = 3;
+}
+
+message WikiDeletePageResponse {}
+
+message WikiFindPageRequest {
+ Repository repository = 1;
+ bytes title = 2;
+ bytes revision = 3;
+ bytes directory = 4;
+}
+
+// WikiFindPageResponse is a stream because we need multiple WikiPage
+// messages to send the raw_data field.
+message WikiFindPageResponse {
+ WikiPage page = 1;
+}
+
+message WikiFindFileRequest {
+ Repository repository = 1;
+ bytes name = 2;
+ // Optional: revision
+ bytes revision = 3;
+}
+
+message WikiFindFileResponse {
+ // If 'name' is empty, the file was not found.
+ bytes name = 1;
+ string mime_type = 2;
+ bytes raw_data = 3;
+ bytes path = 4;
+}
+
+message WikiGetAllPagesRequest {
+ Repository repository = 1;
+ // Passing 0 means no limit is applied
+ uint32 limit = 2;
+ bool direction_desc = 3;
+
+ enum SortBy {
+ TITLE = 0;
+ CREATED_AT = 1;
+ }
+ SortBy sort = 4;
+}
+
+// The WikiGetAllPagesResponse stream is a concatenation of WikiPage streams
+message WikiGetAllPagesResponse {
+ WikiPage page = 1;
+ // When end_of_page is true it signals a change of page for the next Response message (if any)
+ bool end_of_page = 2;
+}
+
+message WikiGetFormattedDataRequest {
+ Repository repository = 1;
+ bytes title = 2;
+ bytes revision = 3;
+ bytes directory = 4;
+}
+
+message WikiGetFormattedDataResponse {
+ bytes data = 1;
+}
+
+message WikiListPagesRequest {
+ Repository repository = 1;
+ // Passing 0 means no limit is applied
+ uint32 limit = 2;
+ bool direction_desc = 3;
+
+ enum SortBy {
+ TITLE = 0;
+ CREATED_AT = 1;
+ }
+ SortBy sort = 4;
+
+ uint32 offset = 5;
+}
+
+// The WikiListPagesResponse stream is a concatenation of WikiPage streams without content
+message WikiListPagesResponse {
+ WikiPage page = 1;
+}